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_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 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LordHavoc: counts usage of each QuakeC statement
36 cvar_t prvm_statementprofiling = {0, "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 = {0, "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_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 cvar_t prvm_leaktest_ignore_classnames = {0, "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)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42 cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
43 cvar_t prvm_reuseedicts_startuptime = {0, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
44 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
46 static double prvm_reuseedicts_always_allow = 0;
47 qboolean prvm_runawaycheck = true;
49 //============================================================================
57 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
61 // reserve space for the null entity aka world
62 // check bound of max_edicts
63 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
64 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
66 // edictprivate_size has to be min as big prvm_edict_private_t
67 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
70 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
72 // alloc edict private space
73 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
76 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
77 prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
80 for(i = 0; i < prog->max_edicts; i++)
82 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
83 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
89 PRVM_MEM_IncreaseEdicts
92 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
96 if(prog->max_edicts >= prog->limit_edicts)
99 prog->begin_increase_edicts(prog);
102 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
104 prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
105 prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
106 prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
108 //set e and v pointers
109 for(i = 0; i < prog->max_edicts; i++)
111 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
112 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
115 prog->end_increase_edicts(prog);
118 //============================================================================
121 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
124 d = PRVM_ED_FindField(prog, field);
130 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
133 d = PRVM_ED_FindGlobal(prog, global);
139 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
142 f = PRVM_ED_FindFunction(prog, function);
145 return (func_t)(f - prog->functions);
153 prvm_prog_t *PRVM_ProgFromString(const char *str)
155 if (!strcmp(str, "server"))
157 if (!strcmp(str, "client"))
160 if (!strcmp(str, "menu"))
168 PRVM_FriendlyProgFromString
171 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
173 prvm_prog_t *prog = PRVM_ProgFromString(str);
176 Con_Printf("%s: unknown program name\n", str);
181 Con_Printf("%s: program is not loaded\n", str);
191 Sets everything to NULL
194 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
196 memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
197 e->priv.required->free = false;
199 // AK: Let the init_edict function determine if something needs to be initialized
200 prog->init_edict(prog, e);
203 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
206 if(prog->leaktest_active)
207 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
209 buf = (char *)PRVM_Alloc(128);
210 PRVM_ShortStackTrace(prog, buf, 128);
219 Returns if this particular edict could get allocated by PRVM_ED_Alloc
222 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
224 if(!e->priv.required->free)
226 if(prvm_reuseedicts_always_allow == realtime)
228 if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
229 return false; // never allow reuse in same frame (causes networking trouble)
230 if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
232 if(realtime > e->priv.required->freetime + 1)
234 return false; // entity slot still blocked because the entity was freed less than one second ago
241 Either finds a free edict, or allocates a new one.
242 Try to avoid reusing an entity that was recently freed, because it
243 can cause the client to think the entity morphed into something else
244 instead of being removed and recreated, which can cause interpolated
245 angles and bad trails.
248 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
253 // the client qc dont need maxclients
254 // thus it doesnt need to use svs.maxclients
255 // AK: changed i=svs.maxclients+1
256 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
257 // although the menu/client has no world
258 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
260 e = PRVM_EDICT_NUM(i);
261 if(PRVM_ED_CanAlloc(prog, e))
263 PRVM_ED_ClearEdict (prog, e);
264 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
269 if (i == prog->limit_edicts)
270 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
273 if (prog->num_edicts >= prog->max_edicts)
274 PRVM_MEM_IncreaseEdicts(prog);
276 e = PRVM_EDICT_NUM(i);
277 PRVM_ED_ClearEdict(prog, e);
279 e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
288 Marks the edict as free
289 FIXME: walk all entities and NULL out references to this entity
292 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
294 // dont delete the null entity (world) or reserved edicts
295 if (ed - prog->edicts <= prog->reserved_edicts)
298 prog->free_edict(prog, ed);
300 ed->priv.required->free = true;
301 ed->priv.required->freetime = realtime;
302 if(ed->priv.required->allocation_origin)
304 Mem_Free((char *)ed->priv.required->allocation_origin);
305 ed->priv.required->allocation_origin = NULL;
309 //===========================================================================
316 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
321 for (i = 0;i < prog->numglobaldefs;i++)
323 def = &prog->globaldefs[i];
335 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
340 for (i = 0;i < prog->numfielddefs;i++)
342 def = &prog->fielddefs[i];
354 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
359 for (i = 0;i < prog->numfielddefs;i++)
361 def = &prog->fielddefs[i];
362 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
373 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
378 for (i = 0;i < prog->numglobaldefs;i++)
380 def = &prog->globaldefs[i];
381 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
393 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
398 for (i = 0;i < prog->numfunctions;i++)
400 func = &prog->functions[i];
401 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
412 Returns a string describing *data in a type specific manner
415 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
421 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
426 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
430 if (n < 0 || n >= prog->max_edicts)
431 dpsnprintf (line, linelength, "entity %i (invalid!)", n);
433 dpsnprintf (line, linelength, "entity %i", n);
436 f = prog->functions + val->function;
437 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
440 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
441 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
444 dpsnprintf (line, linelength, "void");
447 // LordHavoc: changed from %5.1f to %10.4f
448 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
451 // LordHavoc: changed from %5.1f to %10.4f
452 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
455 dpsnprintf (line, linelength, "pointer");
458 dpsnprintf (line, linelength, "bad type %i", (int) type);
469 Returns a string describing *data in a type specific manner
470 Easier to parse than PR_ValueString
473 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
480 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
485 // Parse the string a bit to turn special characters
486 // (like newline, specifically) into escape codes,
487 // this fixes saving games from various mods
488 s = PRVM_GetString (prog, val->string);
489 for (i = 0;i < (int)linelength - 2 && *s;)
518 dpsnprintf (line, linelength, "%i", val->edict);
521 f = prog->functions + val->function;
522 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
525 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
526 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
529 dpsnprintf (line, linelength, "void");
532 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
535 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
538 dpsnprintf (line, linelength, "bad type %i", type);
549 Returns a string with a description and the contents of a global,
550 padded to 20 field width
553 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
559 char valuebuf[MAX_INPUTLINE];
561 val = (prvm_eval_t *)&prog->globals.fp[ofs];
562 def = PRVM_ED_GlobalAtOfs(prog, ofs);
564 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
567 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
568 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
572 //for ( ; i<20 ; i++)
573 // strcat (line," ");
579 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
584 def = PRVM_ED_GlobalAtOfs(prog, ofs);
586 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
588 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
591 //for ( ; i<20 ; i++)
592 // strcat (line," ");
606 // LordHavoc: optimized this to print out much more quickly (tempstring)
607 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
608 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
616 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
617 char valuebuf[MAX_INPUTLINE];
619 if (ed->priv.required->free)
621 Con_Printf("%s: FREE\n",prog->name);
626 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
627 for (i = 1;i < prog->numfielddefs;i++)
629 d = &prog->fielddefs[i];
630 name = PRVM_GetString(prog, d->s_name);
631 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
632 continue; // skip _x, _y, _z vars
634 // Check Field Name Wildcard
635 if(wildcard_fieldname)
636 if( !matchpattern(name, wildcard_fieldname, 1) )
637 // Didn't match; skip
640 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
642 // if the value is still all 0, skip the field
643 type = d->type & ~DEF_SAVEGLOBAL;
645 for (j=0 ; j<prvm_type_size[type] ; j++)
648 if (j == prvm_type_size[type])
651 if (strlen(name) > sizeof(tempstring2)-4)
653 memcpy (tempstring2, name, sizeof(tempstring2)-4);
654 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
655 tempstring2[sizeof(tempstring2)-1] = 0;
658 strlcat(tempstring, name, sizeof(tempstring));
659 for (l = strlen(name);l < 14;l++)
660 strlcat(tempstring, " ", sizeof(tempstring));
661 strlcat(tempstring, " ", sizeof(tempstring));
663 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
664 if (strlen(name) > sizeof(tempstring2)-4)
666 memcpy (tempstring2, name, sizeof(tempstring2)-4);
667 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
668 tempstring2[sizeof(tempstring2)-1] = 0;
671 strlcat(tempstring, name, sizeof(tempstring));
672 strlcat(tempstring, "\n", sizeof(tempstring));
673 if (strlen(tempstring) >= sizeof(tempstring)/2)
675 Con_Print(tempstring);
680 Con_Print(tempstring);
690 extern cvar_t developer_entityparsing;
691 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
699 char valuebuf[MAX_INPUTLINE];
703 if (ed->priv.required->free)
709 for (i = 1;i < prog->numfielddefs;i++)
711 d = &prog->fielddefs[i];
712 name = PRVM_GetString(prog, d->s_name);
714 if(developer_entityparsing.integer)
715 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
717 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
718 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
719 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?)
721 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
723 // if the value is still all 0, skip the field
724 type = d->type & ~DEF_SAVEGLOBAL;
725 for (j=0 ; j<prvm_type_size[type] ; j++)
728 if (j == prvm_type_size[type])
731 FS_Printf(f,"\"%s\" ",name);
732 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
733 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
734 prog->statestring = NULL;
740 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
742 PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
747 PRVM_ED_PrintEdicts_f
749 For debugging, prints all the entities in the current server
752 void PRVM_ED_PrintEdicts_f (void)
756 const char *wildcard_fieldname;
758 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
760 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
764 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
768 wildcard_fieldname = Cmd_Argv(2);
770 wildcard_fieldname = NULL;
772 Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
773 for (i=0 ; i<prog->num_edicts ; i++)
774 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
781 For debugging, prints a single edict
784 static void PRVM_ED_PrintEdict_f (void)
788 const char *wildcard_fieldname;
790 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
792 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
796 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
799 i = atoi (Cmd_Argv(2));
800 if (i >= prog->num_edicts)
802 Con_Print("Bad edict number\n");
806 // Optional Wildcard Provided
807 wildcard_fieldname = Cmd_Argv(3);
810 wildcard_fieldname = NULL;
811 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
821 // 2 possibilities : 1. just displaying the active edict count
822 // 2. making a function pointer [x]
823 static void PRVM_ED_Count_f (void)
829 Con_Print("prvm_count <program name>\n");
833 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
836 prog->count_edicts(prog);
840 ==============================================================================
844 FIXME: need to tag constants, doesn't really work
845 ==============================================================================
853 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
860 char valuebuf[MAX_INPUTLINE];
863 for (i = 0;i < prog->numglobaldefs;i++)
865 def = &prog->globaldefs[i];
867 if ( !(def->type & DEF_SAVEGLOBAL) )
869 type &= ~DEF_SAVEGLOBAL;
871 if (type != ev_string && type != ev_float && type != ev_entity)
874 name = PRVM_GetString(prog, def->s_name);
876 if(developer_entityparsing.integer)
877 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
879 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
880 FS_Printf(f,"\"%s\" ", name);
881 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
882 prog->statestring = NULL;
892 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
894 char keyname[MAX_INPUTLINE];
900 if (!COM_ParseToken_Simple(&data, false, false, true))
901 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
902 if (com_token[0] == '}')
905 if (developer_entityparsing.integer)
906 Con_Printf("Key: \"%s\"", com_token);
908 strlcpy (keyname, com_token, sizeof(keyname));
911 if (!COM_ParseToken_Simple(&data, false, true, true))
912 prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
914 if (developer_entityparsing.integer)
915 Con_Printf(" \"%s\"\n", com_token);
917 if (com_token[0] == '}')
918 prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
920 key = PRVM_ED_FindGlobal (prog, keyname);
923 Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
927 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
928 prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
932 //============================================================================
939 Can parse either fields or globals
940 returns false if error
943 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
952 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
954 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
955 switch (key->type & ~DEF_SAVEGLOBAL)
958 l = (int)strlen(s) + 1;
959 val->string = PRVM_AllocString(prog, l, &new_p);
960 for (i = 0;i < l;i++)
962 if (s[i] == '\\' && s[i+1] && parsebackslash)
967 else if (s[i] == 'r')
978 while (*s && ISWHITESPACE(*s))
980 val->_float = atof(s);
984 for (i = 0;i < 3;i++)
986 while (*s && ISWHITESPACE(*s))
990 val->vector[i] = atof(s);
991 while (!ISWHITESPACE(*s))
999 while (*s && ISWHITESPACE(*s))
1002 if (i >= prog->limit_edicts)
1003 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);
1004 while (i >= prog->max_edicts)
1005 PRVM_MEM_IncreaseEdicts(prog);
1006 // if IncreaseEdicts was called the base pointer needs to be updated
1008 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1009 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1015 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1018 def = PRVM_ED_FindField(prog, s + 1);
1021 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1024 val->_int = def->ofs;
1028 func = PRVM_ED_FindFunction(prog, s);
1031 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1034 val->function = func - prog->functions;
1038 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);
1048 Console command to send a string to QC function GameCommand of the
1052 sv_cmd adminmsg 3 "do not teamkill"
1053 cl_cmd someclientcommand
1054 menu_cmd somemenucommand
1056 All progs can support this extension; sg calls it in server QC, cg in client
1060 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1065 Con_Printf("%s text...\n", whichcmd);
1069 if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1072 if(!PRVM_allfunction(GameCommand))
1074 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1078 int restorevm_tempstringsbuf_cursize;
1083 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1084 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1085 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1086 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1089 static void PRVM_GameCommand_Server_f(void)
1091 PRVM_GameCommand("server", "sv_cmd");
1093 static void PRVM_GameCommand_Client_f(void)
1095 PRVM_GameCommand("client", "cl_cmd");
1097 static void PRVM_GameCommand_Menu_f(void)
1099 PRVM_GameCommand("menu", "menu_cmd");
1106 Console command to load a field of a specified edict
1109 static void PRVM_ED_EdictGet_f(void)
1116 char valuebuf[MAX_INPUTLINE];
1118 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1120 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1124 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1127 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1129 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1131 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1135 v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1136 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1139 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1140 if (cvar && cvar->flags & CVAR_READONLY)
1142 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1145 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1148 Con_Printf("%s\n", s);
1154 static void PRVM_ED_GlobalGet_f(void)
1160 char valuebuf[MAX_INPUTLINE];
1162 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1164 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1168 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1171 key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1174 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1178 v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1179 s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1182 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1183 if (cvar && cvar->flags & CVAR_READONLY)
1185 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1188 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1191 Con_Printf("%s\n", s);
1201 Console command to set a field of a specified edict
1204 static void PRVM_ED_EdictSet_f(void)
1212 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1216 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1219 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1221 if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1222 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1224 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1228 ====================
1231 Parses an edict out of the given string, returning the new position
1232 ed should be a properly initialized empty edict.
1233 Used for initial level load and for savegames.
1234 ====================
1236 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1246 // go through all the dictionary pairs
1250 if (!COM_ParseToken_Simple(&data, false, false, true))
1251 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1252 if (developer_entityparsing.integer)
1253 Con_Printf("Key: \"%s\"", com_token);
1254 if (com_token[0] == '}')
1257 // anglehack is to allow QuakeEd to write single scalar angles
1258 // and allow them to be turned into vectors. (FIXME...)
1259 if (!strcmp(com_token, "angle"))
1261 strlcpy (com_token, "angles", sizeof(com_token));
1267 // FIXME: change light to _light to get rid of this hack
1268 if (!strcmp(com_token, "light"))
1269 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1271 strlcpy (keyname, com_token, sizeof(keyname));
1273 // another hack to fix keynames with trailing spaces
1274 n = strlen(keyname);
1275 while (n && keyname[n-1] == ' ')
1282 if (!COM_ParseToken_Simple(&data, false, false, true))
1283 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1284 if (developer_entityparsing.integer)
1285 Con_Printf(" \"%s\"\n", com_token);
1287 if (com_token[0] == '}')
1288 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1292 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1296 // keynames with a leading underscore are used for utility comments,
1297 // and are immediately discarded by quake
1298 if (keyname[0] == '_')
1301 key = PRVM_ED_FindField (prog, keyname);
1304 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1311 strlcpy (temp, com_token, sizeof(temp));
1312 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1315 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1316 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1320 ent->priv.required->free = true;
1328 PRVM_ED_LoadFromFile
1330 The entities are directly placed in the array, rather than allocated with
1331 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1332 number references out of order.
1334 Creates a server's entity / program execution context by
1335 parsing textual entity definitions out of an ent file.
1337 Used for both fresh maps and savegame loads. A fresh map would also need
1338 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1341 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1344 int parsed, inhibited, spawned, died;
1345 const char *funcname;
1354 prvm_reuseedicts_always_allow = realtime;
1359 // parse the opening brace
1360 if (!COM_ParseToken_Simple(&data, false, false, true))
1362 if (com_token[0] != '{')
1363 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1365 // CHANGED: this is not conform to PR_LoadFromFile
1366 if(prog->loadintoworld)
1368 prog->loadintoworld = false;
1369 ent = PRVM_EDICT_NUM(0);
1372 ent = PRVM_ED_Alloc(prog);
1375 if (ent != prog->edicts) // hack
1376 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1378 data = PRVM_ED_ParseEdict (prog, data, ent);
1381 // remove the entity ?
1382 if(!prog->load_edict(prog, ent))
1384 PRVM_ED_Free(prog, ent);
1389 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1392 PRVM_serverglobalfloat(time) = sv.time;
1393 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1394 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1397 if(ent->priv.required->free)
1404 // immediately call spawn function, but only if there is a self global and a classname
1406 if(!ent->priv.required->free)
1408 if (!PRVM_alledictstring(ent, classname))
1410 Con_Print("No classname for:\n");
1411 PRVM_ED_Print(prog, ent, NULL);
1412 PRVM_ED_Free (prog, ent);
1416 // look for the spawn function
1417 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1418 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1420 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1421 func = PRVM_ED_FindFunction (prog, funcname);
1425 // check for OnEntityNoSpawnFunction
1426 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1429 PRVM_serverglobalfloat(time) = sv.time;
1430 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1431 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1435 if (developer.integer > 0) // don't confuse non-developers with errors
1437 Con_Print("No spawn function for:\n");
1438 PRVM_ED_Print(prog, ent, NULL);
1440 PRVM_ED_Free (prog, ent);
1441 continue; // not included in "inhibited" count
1447 PRVM_serverglobalfloat(time) = sv.time;
1448 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1449 prog->ExecuteProgram(prog, func - prog->functions, "");
1453 if(!ent->priv.required->free)
1454 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1457 PRVM_serverglobalfloat(time) = sv.time;
1458 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1459 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1463 if (ent->priv.required->free)
1467 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);
1469 prvm_reuseedicts_always_allow = 0;
1472 static void PRVM_FindOffsets(prvm_prog_t *prog)
1474 // field and global searches use -1 for NULL
1475 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1476 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1477 // function searches use 0 for NULL
1478 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1479 #define PRVM_DECLARE_serverglobalfloat(x)
1480 #define PRVM_DECLARE_serverglobalvector(x)
1481 #define PRVM_DECLARE_serverglobalstring(x)
1482 #define PRVM_DECLARE_serverglobaledict(x)
1483 #define PRVM_DECLARE_serverglobalfunction(x)
1484 #define PRVM_DECLARE_clientglobalfloat(x)
1485 #define PRVM_DECLARE_clientglobalvector(x)
1486 #define PRVM_DECLARE_clientglobalstring(x)
1487 #define PRVM_DECLARE_clientglobaledict(x)
1488 #define PRVM_DECLARE_clientglobalfunction(x)
1489 #define PRVM_DECLARE_menuglobalfloat(x)
1490 #define PRVM_DECLARE_menuglobalvector(x)
1491 #define PRVM_DECLARE_menuglobalstring(x)
1492 #define PRVM_DECLARE_menuglobaledict(x)
1493 #define PRVM_DECLARE_menuglobalfunction(x)
1494 #define PRVM_DECLARE_serverfieldfloat(x)
1495 #define PRVM_DECLARE_serverfieldvector(x)
1496 #define PRVM_DECLARE_serverfieldstring(x)
1497 #define PRVM_DECLARE_serverfieldedict(x)
1498 #define PRVM_DECLARE_serverfieldfunction(x)
1499 #define PRVM_DECLARE_clientfieldfloat(x)
1500 #define PRVM_DECLARE_clientfieldvector(x)
1501 #define PRVM_DECLARE_clientfieldstring(x)
1502 #define PRVM_DECLARE_clientfieldedict(x)
1503 #define PRVM_DECLARE_clientfieldfunction(x)
1504 #define PRVM_DECLARE_menufieldfloat(x)
1505 #define PRVM_DECLARE_menufieldvector(x)
1506 #define PRVM_DECLARE_menufieldstring(x)
1507 #define PRVM_DECLARE_menufieldedict(x)
1508 #define PRVM_DECLARE_menufieldfunction(x)
1509 #define PRVM_DECLARE_serverfunction(x)
1510 #define PRVM_DECLARE_clientfunction(x)
1511 #define PRVM_DECLARE_menufunction(x)
1512 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1513 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1514 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1515 #include "prvm_offsets.h"
1516 #undef PRVM_DECLARE_serverglobalfloat
1517 #undef PRVM_DECLARE_serverglobalvector
1518 #undef PRVM_DECLARE_serverglobalstring
1519 #undef PRVM_DECLARE_serverglobaledict
1520 #undef PRVM_DECLARE_serverglobalfunction
1521 #undef PRVM_DECLARE_clientglobalfloat
1522 #undef PRVM_DECLARE_clientglobalvector
1523 #undef PRVM_DECLARE_clientglobalstring
1524 #undef PRVM_DECLARE_clientglobaledict
1525 #undef PRVM_DECLARE_clientglobalfunction
1526 #undef PRVM_DECLARE_menuglobalfloat
1527 #undef PRVM_DECLARE_menuglobalvector
1528 #undef PRVM_DECLARE_menuglobalstring
1529 #undef PRVM_DECLARE_menuglobaledict
1530 #undef PRVM_DECLARE_menuglobalfunction
1531 #undef PRVM_DECLARE_serverfieldfloat
1532 #undef PRVM_DECLARE_serverfieldvector
1533 #undef PRVM_DECLARE_serverfieldstring
1534 #undef PRVM_DECLARE_serverfieldedict
1535 #undef PRVM_DECLARE_serverfieldfunction
1536 #undef PRVM_DECLARE_clientfieldfloat
1537 #undef PRVM_DECLARE_clientfieldvector
1538 #undef PRVM_DECLARE_clientfieldstring
1539 #undef PRVM_DECLARE_clientfieldedict
1540 #undef PRVM_DECLARE_clientfieldfunction
1541 #undef PRVM_DECLARE_menufieldfloat
1542 #undef PRVM_DECLARE_menufieldvector
1543 #undef PRVM_DECLARE_menufieldstring
1544 #undef PRVM_DECLARE_menufieldedict
1545 #undef PRVM_DECLARE_menufieldfunction
1546 #undef PRVM_DECLARE_serverfunction
1547 #undef PRVM_DECLARE_clientfunction
1548 #undef PRVM_DECLARE_menufunction
1549 #undef PRVM_DECLARE_field
1550 #undef PRVM_DECLARE_global
1551 #undef PRVM_DECLARE_function
1556 typedef struct dpfield_s
1563 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1565 dpfield_t dpfields[] =
1576 #define PO_HASHSIZE 16384
1577 typedef struct po_string_s
1580 struct po_string_s *nextonhashchain;
1585 po_string_t *hashtable[PO_HASHSIZE];
1588 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1597 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1598 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1599 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1600 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1601 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1602 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1603 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1605 if(*in >= 0 && *in <= 0x1F)
1610 *out++ = '0' + ((*in & 0700) >> 6);
1611 *out++ = '0' + ((*in & 0070) >> 3);
1612 *out++ = '0' + (*in & 0007) ;
1629 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1642 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1643 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1644 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1645 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1646 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1647 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1648 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1649 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1653 if(*in >= '0' && *in <= '7')
1656 *out = (*out << 3) | (*in - '0');
1659 if(*in >= '0' && *in <= '7')
1662 *out = (*out << 3) | (*in - '0');
1673 if(outsize > 0) { *out++ = *in; --outsize; }
1688 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1693 char inbuf[MAX_INPUTLINE];
1694 char decodedbuf[MAX_INPUTLINE];
1697 po_string_t thisstr;
1700 for (i = 0; i < 2; ++i)
1702 const char *buf = (const char *)
1703 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1704 // first read filename2, then read filename
1705 // so that progs.dat.de.po wins over common.de.po
1706 // and within file, last item wins
1713 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1714 memset(po, 0, sizeof(*po));
1717 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1725 p = strchr(p, '\n');
1731 if(*p == '\r' || *p == '\n')
1736 if(!strncmp(p, "msgid \"", 7))
1741 else if(!strncmp(p, "msgstr \"", 8))
1748 p = strchr(p, '\n');
1758 q = strchr(p, '\n');
1765 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1767 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1768 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1769 decodedpos += strlen(decodedbuf + decodedpos);
1779 Mem_Free(thisstr.key);
1780 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1781 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1783 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1785 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1786 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1787 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1788 thisstr.nextonhashchain = po->hashtable[hashindex];
1789 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1790 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1791 memset(&thisstr, 0, sizeof(thisstr));
1795 Mem_Free((char *) buf);
1800 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1802 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1803 po_string_t *p = po->hashtable[hashindex];
1806 if(!strcmp(str, p->key))
1808 p = p->nextonhashchain;
1812 static void PRVM_PO_Destroy(po_t *po)
1815 for(i = 0; i < PO_HASHSIZE; ++i)
1817 po_string_t *p = po->hashtable[i];
1821 p = p->nextonhashchain;
1830 void PRVM_LeakTest(prvm_prog_t *prog);
1831 void PRVM_Prog_Reset(prvm_prog_t *prog)
1835 PRVM_LeakTest(prog);
1836 prog->reset_cmd(prog);
1837 Mem_FreePool(&prog->progs_mempool);
1839 PRVM_PO_Destroy((po_t *) prog->po);
1841 memset(prog,0,sizeof(prvm_prog_t));
1842 prog->break_statement = -1;
1843 prog->watch_global_type = ev_void;
1844 prog->watch_field_type = ev_void;
1852 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1853 fs_offset_t filesize;
1855 unsigned int *header;
1858 FS_StripExtension( progname, filename, sizeof( filename ) );
1859 strlcat( filename, ".lno", sizeof( filename ) );
1861 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1867 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1868 <Spike> SafeWrite (h, &version, sizeof(int));
1869 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1870 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1871 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1872 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1873 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1875 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1881 header = (unsigned int *) lno;
1882 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1883 LittleLong( header[ 1 ] ) == 1 &&
1884 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1885 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1886 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1887 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1889 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1890 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1892 /* gmqcc suports columnums */
1893 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1895 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1896 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1907 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1908 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)
1911 dprograms_t *dprograms;
1912 dstatement_t *instatements;
1913 ddef_t *infielddefs;
1914 ddef_t *inglobaldefs;
1916 dfunction_t *infunctions;
1918 fs_offset_t filesize;
1919 int requiredglobalspace;
1935 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1937 Host_LockSession(); // all progs can use the session cvar
1938 Crypto_LoadKeys(); // all progs might use the keys at init time
1942 dprograms = (dprograms_t *) data;
1946 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1947 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1948 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1949 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1951 prog->profiletime = Sys_DirtyTime();
1952 prog->starttime = realtime;
1954 Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1956 requiredglobalspace = 0;
1957 for (i = 0;i < numrequiredglobals;i++)
1958 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1960 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1962 // byte swap the header
1963 prog->progs_version = LittleLong(dprograms->version);
1964 prog->progs_crc = LittleLong(dprograms->crc);
1965 if (prog->progs_version != PROG_VERSION)
1966 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1967 instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1968 prog->progs_numstatements = LittleLong(dprograms->numstatements);
1969 inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1970 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1971 infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1972 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1973 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1974 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1975 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1976 prog->progs_numstrings = LittleLong(dprograms->numstrings);
1977 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1978 prog->progs_numglobals = LittleLong(dprograms->numglobals);
1979 prog->progs_entityfields = LittleLong(dprograms->entityfields);
1981 prog->numstatements = prog->progs_numstatements;
1982 prog->numglobaldefs = prog->progs_numglobaldefs;
1983 prog->numfielddefs = prog->progs_numfielddefs;
1984 prog->numfunctions = prog->progs_numfunctions;
1985 prog->numstrings = prog->progs_numstrings;
1986 prog->numglobals = prog->progs_numglobals;
1987 prog->entityfields = prog->progs_entityfields;
1989 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1990 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1991 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1992 memcpy(prog->strings, instrings, prog->progs_numstrings);
1993 prog->stringssize = prog->progs_numstrings;
1995 prog->numknownstrings = 0;
1996 prog->maxknownstrings = 0;
1997 prog->knownstrings = NULL;
1998 prog->knownstrings_freeable = NULL;
2000 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2002 // we need to expand the globaldefs and fielddefs to include engine defs
2003 prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2004 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2005 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2006 // when trying to return the last or second-last global
2007 // (RETURN always returns a vector, there is no RETURN_F instruction)
2008 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2009 // we need to convert the statements to our memory format
2010 prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2011 // allocate space for profiling statement usage
2012 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2013 // functions need to be converted to the memory format
2014 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2016 for (i = 0;i < prog->progs_numfunctions;i++)
2018 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2019 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2020 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2021 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2022 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2023 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2024 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2025 if(prog->functions[i].first_statement >= prog->numstatements)
2026 prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2027 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2030 // copy the globaldefs to the new globaldefs list
2031 for (i=0 ; i<prog->numglobaldefs ; i++)
2033 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2034 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2035 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2036 // TODO bounds check ofs, s_name
2039 // append the required globals
2040 for (i = 0;i < numrequiredglobals;i++)
2042 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2043 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2044 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2045 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2046 prog->numglobals += 3;
2049 prog->numglobaldefs++;
2052 // copy the progs fields to the new fields list
2053 for (i = 0;i < prog->numfielddefs;i++)
2055 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2056 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2057 prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2058 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2059 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2060 // TODO bounds check ofs, s_name
2063 // append the required fields
2064 for (i = 0;i < numrequiredfields;i++)
2066 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2067 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2068 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2069 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2070 prog->entityfields += 3;
2072 prog->entityfields++;
2073 prog->numfielddefs++;
2076 // LordHavoc: TODO: reorder globals to match engine struct
2077 // LordHavoc: TODO: reorder fields to match engine struct
2078 #define remapglobal(index) (index)
2079 #define remapfield(index) (index)
2082 // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2083 for (i = 0;i < prog->progs_numglobals;i++)
2085 u.i = LittleLong(inglobals[i]);
2086 // most globals are 0, we only need to deal with the ones that are not
2089 d = u.i & 0xFF800000;
2090 if ((d == 0xFF800000) || (d == 0))
2092 // Looks like an integer (expand to int64)
2093 prog->globals.ip[remapglobal(i)] = u.i;
2097 // Looks like a float (expand to double)
2098 prog->globals.fp[remapglobal(i)] = u.f;
2103 // LordHavoc: TODO: support 32bit progs statement formats
2104 // copy, remap globals in statements, bounds check
2105 for (i = 0;i < prog->progs_numstatements;i++)
2107 op = (opcode_t)LittleShort(instatements[i].op);
2108 a = (unsigned short)LittleShort(instatements[i].a);
2109 b = (unsigned short)LittleShort(instatements[i].b);
2110 c = (unsigned short)LittleShort(instatements[i].c);
2116 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2117 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2118 prog->statements[i].op = op;
2119 prog->statements[i].operand[0] = remapglobal(a);
2120 prog->statements[i].operand[1] = -1;
2121 prog->statements[i].operand[2] = -1;
2122 prog->statements[i].jumpabsolute = i + b;
2126 if (a + i < 0 || a + i >= prog->progs_numstatements)
2127 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2128 prog->statements[i].op = op;
2129 prog->statements[i].operand[0] = -1;
2130 prog->statements[i].operand[1] = -1;
2131 prog->statements[i].operand[2] = -1;
2132 prog->statements[i].jumpabsolute = i + a;
2135 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2137 // global global global
2172 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2173 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2174 prog->statements[i].op = op;
2175 prog->statements[i].operand[0] = remapglobal(a);
2176 prog->statements[i].operand[1] = remapglobal(b);
2177 prog->statements[i].operand[2] = remapglobal(c);
2178 prog->statements[i].jumpabsolute = -1;
2180 // global none global
2186 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2187 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2188 prog->statements[i].op = op;
2189 prog->statements[i].operand[0] = remapglobal(a);
2190 prog->statements[i].operand[1] = -1;
2191 prog->statements[i].operand[2] = remapglobal(c);
2192 prog->statements[i].jumpabsolute = -1;
2208 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2209 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2210 prog->statements[i].op = op;
2211 prog->statements[i].operand[0] = remapglobal(a);
2212 prog->statements[i].operand[1] = remapglobal(b);
2213 prog->statements[i].operand[2] = -1;
2214 prog->statements[i].jumpabsolute = -1;
2228 if ( a >= prog->progs_numglobals)
2229 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2230 prog->statements[i].op = op;
2231 prog->statements[i].operand[0] = remapglobal(a);
2232 prog->statements[i].operand[1] = -1;
2233 prog->statements[i].operand[2] = -1;
2234 prog->statements[i].jumpabsolute = -1;
2238 if(prog->numstatements < 1)
2240 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2242 else switch(prog->statements[prog->numstatements - 1].op)
2249 prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2253 // we're done with the file now
2255 Mem_Free(dprograms);
2258 // check required functions
2259 for(i=0 ; i < numrequiredfunc ; i++)
2260 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2261 prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2263 PRVM_LoadLNO(prog, filename);
2265 PRVM_Init_Exec(prog);
2267 if(*prvm_language.string)
2268 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2269 // later idea: include a list of authorized .po file checksums with the csprogs
2271 qboolean deftrans = prog == CLVM_prog;
2272 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2273 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2275 for (i=0 ; i<prog->numglobaldefs ; i++)
2278 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2279 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2280 if(name && !strncmp(name, "dotranslate_", 12))
2287 if(!strcmp(prvm_language.string, "dump"))
2289 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2290 Con_Printf("Dumping to %s.pot\n", realfilename);
2293 for (i=0 ; i<prog->numglobaldefs ; i++)
2296 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2297 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2298 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2300 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2301 const char *value = PRVM_GetString(prog, val->string);
2304 char buf[MAX_INPUTLINE];
2305 PRVM_PO_UnparseString(buf, value, sizeof(buf));
2306 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2315 po_t *po = PRVM_PO_Load(
2316 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2317 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2318 prog->progs_mempool);
2321 for (i=0 ; i<prog->numglobaldefs ; i++)
2324 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2325 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2326 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2328 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2329 const char *value = PRVM_GetString(prog, val->string);
2332 value = PRVM_PO_Lookup(po, value);
2334 val->string = PRVM_SetEngineString(prog, value);
2342 for (i=0 ; i<prog->numglobaldefs ; i++)
2345 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2346 //Con_Printf("found var %s\n", name);
2348 && !strncmp(name, "autocvar_", 9)
2349 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2352 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2353 cvar_t *cvar = Cvar_FindVar(name + 9);
2354 //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2359 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2360 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2363 if((float)((int)(val->_float)) == val->_float)
2364 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2366 dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2370 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2373 value = PRVM_GetString(prog, val->string);
2376 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2379 cvar = Cvar_Get(name + 9, value, 0, NULL);
2380 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2382 val->string = PRVM_SetEngineString(prog, cvar->string);
2383 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2386 prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2387 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2388 cvar->globaldefindex[prog - prvm_prog_list] = i;
2390 else if((cvar->flags & CVAR_PRIVATE) == 0)
2392 // MUST BE SYNCED WITH cvar.c Cvar_Set
2395 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2398 val->_float = cvar->value;
2402 VectorClear(val->vector);
2403 for (j = 0;j < 3;j++)
2405 while (*s && ISWHITESPACE(*s))
2409 val->vector[j] = atof(s);
2410 while (!ISWHITESPACE(*s))
2417 val->string = PRVM_SetEngineString(prog, cvar->string);
2418 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2421 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2424 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2425 cvar->globaldefindex[prog - prvm_prog_list] = i;
2428 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2434 prog->loaded = TRUE;
2436 PRVM_UpdateBreakpoints(prog);
2438 // set flags & ddef_ts in prog
2442 PRVM_FindOffsets(prog);
2444 prog->init_cmd(prog);
2447 PRVM_MEM_Alloc(prog);
2451 static void PRVM_Fields_f (void)
2454 int i, j, ednum, used, usedamount;
2456 char tempstring[MAX_INPUTLINE], tempstring2[260];
2466 Con_Print("no progs loaded\n");
2473 Con_Print("prvm_fields <program name>\n");
2477 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2480 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2481 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2483 ed = PRVM_EDICT_NUM(ednum);
2484 if (ed->priv.required->free)
2486 for (i = 1;i < prog->numfielddefs;i++)
2488 d = &prog->fielddefs[i];
2489 name = PRVM_GetString(prog, d->s_name);
2490 if (name[strlen(name)-2] == '_')
2491 continue; // skip _x, _y, _z vars
2492 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2493 // if the value is still all 0, skip the field
2494 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2496 if (val->ivector[j])
2507 for (i = 0;i < prog->numfielddefs;i++)
2509 d = &prog->fielddefs[i];
2510 name = PRVM_GetString(prog, d->s_name);
2511 if (name[strlen(name)-2] == '_')
2512 continue; // skip _x, _y, _z vars
2513 switch(d->type & ~DEF_SAVEGLOBAL)
2516 strlcat(tempstring, "string ", sizeof(tempstring));
2519 strlcat(tempstring, "entity ", sizeof(tempstring));
2522 strlcat(tempstring, "function ", sizeof(tempstring));
2525 strlcat(tempstring, "field ", sizeof(tempstring));
2528 strlcat(tempstring, "void ", sizeof(tempstring));
2531 strlcat(tempstring, "float ", sizeof(tempstring));
2534 strlcat(tempstring, "vector ", sizeof(tempstring));
2537 strlcat(tempstring, "pointer ", sizeof(tempstring));
2540 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2541 strlcat(tempstring, tempstring2, sizeof(tempstring));
2544 if (strlen(name) > sizeof(tempstring2)-4)
2546 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2547 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2548 tempstring2[sizeof(tempstring2)-1] = 0;
2551 strlcat(tempstring, name, sizeof(tempstring));
2552 for (j = (int)strlen(name);j < 25;j++)
2553 strlcat(tempstring, " ", sizeof(tempstring));
2554 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2555 strlcat(tempstring, tempstring2, sizeof(tempstring));
2556 strlcat(tempstring, "\n", sizeof(tempstring));
2557 if (strlen(tempstring) >= sizeof(tempstring)/2)
2559 Con_Print(tempstring);
2565 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2569 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);
2572 static void PRVM_Globals_f (void)
2576 const char *wildcard;
2582 Con_Print("no progs loaded\n");
2585 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2587 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2591 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2594 if( Cmd_Argc() == 3)
2595 wildcard = Cmd_Argv(2);
2599 Con_Printf("%s :", prog->name);
2601 for (i = 0;i < prog->numglobaldefs;i++)
2604 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2609 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2611 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2619 static void PRVM_Global_f(void)
2623 char valuebuf[MAX_INPUTLINE];
2624 if( Cmd_Argc() != 3 ) {
2625 Con_Printf( "prvm_global <program name> <global name>\n" );
2629 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2632 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2634 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2636 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2644 static void PRVM_GlobalSet_f(void)
2648 if( Cmd_Argc() != 4 ) {
2649 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2653 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2656 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2658 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2660 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2664 ======================
2665 Break- and Watchpoints
2666 ======================
2670 char break_statement[256];
2671 char watch_global[256];
2673 char watch_field[256];
2676 static debug_data_t debug_data[PRVM_PROG_MAX];
2678 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2681 Con_Printf("PRVM_Breakpoint: %s\n", text);
2682 PRVM_PrintState(prog, stack_index);
2683 if (prvm_breakpointdump.integer)
2684 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2687 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2689 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2690 if (memcmp(o, n, sz))
2693 char valuebuf_o[128];
2694 char valuebuf_n[128];
2695 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2696 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2697 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2698 PRVM_Breakpoint(prog, stack_index, buf);
2703 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2705 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2708 if (debug->break_statement[0])
2710 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2712 prog->break_statement = atoi(debug->break_statement);
2713 prog->break_stack_index = 0;
2718 func = PRVM_ED_FindFunction (prog, debug->break_statement);
2721 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2722 prog->break_statement = -1;
2726 prog->break_statement = func->first_statement;
2727 prog->break_stack_index = 1;
2730 if (prog->break_statement >= -1)
2731 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2734 prog->break_statement = -1;
2736 if (debug->watch_global[0])
2738 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2741 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2742 prog->watch_global_type = ev_void;
2746 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2747 prog->watch_global = global->ofs;
2748 prog->watch_global_type = (etype_t)global->type;
2749 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2751 if (prog->watch_global_type != ev_void)
2752 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2755 prog->watch_global_type = ev_void;
2757 if (debug->watch_field[0])
2759 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2762 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2763 prog->watch_field_type = ev_void;
2767 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2768 prog->watch_edict = debug->watch_edict;
2769 prog->watch_field = field->ofs;
2770 prog->watch_field_type = (etype_t)field->type;
2771 if (prog->watch_edict < prog->num_edicts)
2772 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2774 memset(&prog->watch_edictfield_value, 0, sz);
2776 if (prog->watch_edict != ev_void)
2777 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2780 prog->watch_field_type = ev_void;
2783 static void PRVM_Breakpoint_f(void)
2787 if( Cmd_Argc() == 2 ) {
2788 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2791 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2792 debug->break_statement[0] = 0;
2794 PRVM_UpdateBreakpoints(prog);
2797 if( Cmd_Argc() != 3 ) {
2798 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2802 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2806 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2807 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2809 PRVM_UpdateBreakpoints(prog);
2812 static void PRVM_GlobalWatchpoint_f(void)
2816 if( Cmd_Argc() == 2 ) {
2817 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2820 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2821 debug->watch_global[0] = 0;
2823 PRVM_UpdateBreakpoints(prog);
2826 if( Cmd_Argc() != 3 ) {
2827 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2831 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2835 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2836 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2838 PRVM_UpdateBreakpoints(prog);
2841 static void PRVM_EdictWatchpoint_f(void)
2845 if( Cmd_Argc() == 2 ) {
2846 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2849 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2850 debug->watch_field[0] = 0;
2852 PRVM_UpdateBreakpoints(prog);
2855 if( Cmd_Argc() != 4 ) {
2856 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2860 if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2864 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2865 debug->watch_edict = atoi(Cmd_Argv(2));
2866 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2868 PRVM_UpdateBreakpoints(prog);
2876 void PRVM_Init (void)
2878 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2879 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2880 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2881 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2882 Cmd_AddCommand ("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");
2883 Cmd_AddCommand ("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)");
2884 Cmd_AddCommand ("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2885 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2886 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2887 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2888 Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2889 Cmd_AddCommand ("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");
2890 Cmd_AddCommand ("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");
2891 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2892 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2893 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2894 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2896 Cmd_AddCommand ("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");
2897 Cmd_AddCommand ("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");
2898 Cmd_AddCommand ("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");
2900 Cvar_RegisterVariable (&prvm_language);
2901 Cvar_RegisterVariable (&prvm_traceqc);
2902 Cvar_RegisterVariable (&prvm_statementprofiling);
2903 Cvar_RegisterVariable (&prvm_timeprofiling);
2904 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2905 Cvar_RegisterVariable (&prvm_leaktest);
2906 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2907 Cvar_RegisterVariable (&prvm_errordump);
2908 Cvar_RegisterVariable (&prvm_breakpointdump);
2909 Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2910 Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2912 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2913 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2923 void PRVM_Prog_Init(prvm_prog_t *prog)
2925 PRVM_Prog_Reset(prog);
2926 prog->leaktest_active = prvm_leaktest.integer != 0;
2929 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2930 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2932 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2936 #define PRVM_KNOWNSTRINGBASE 0x40000000
2938 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2943 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2946 else if (num < prog->stringssize)
2948 // constant string from progs.dat
2949 return prog->strings + num;
2951 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2953 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2954 num -= prog->stringssize;
2955 if (num < prog->tempstringsbuf.cursize)
2956 return (char *)prog->tempstringsbuf.data + num;
2959 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2963 else if (num & PRVM_KNOWNSTRINGBASE)
2966 num = num - PRVM_KNOWNSTRINGBASE;
2967 if (num >= 0 && num < prog->numknownstrings)
2969 if (!prog->knownstrings[num])
2971 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2974 return prog->knownstrings[num];
2978 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2984 // invalid string offset
2985 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2990 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2993 i = i - PRVM_KNOWNSTRINGBASE;
2994 if(i < 0 || i >= prog->numknownstrings)
2995 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2996 old = prog->knownstrings[i];
2997 prog->knownstrings[i] = s;
3001 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3006 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3007 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3008 // if it's in the tempstrings area, use a reserved range
3009 // (otherwise we'd get millions of useless string offsets cluttering the database)
3010 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3011 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3012 // see if it's a known string address
3013 for (i = 0;i < prog->numknownstrings;i++)
3014 if (prog->knownstrings[i] == s)
3015 return PRVM_KNOWNSTRINGBASE + i;
3016 // new unknown engine string
3017 if (developer_insane.integer)
3018 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3019 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3020 if (!prog->knownstrings[i])
3022 if (i >= prog->numknownstrings)
3024 if (i >= prog->maxknownstrings)
3026 const char **oldstrings = prog->knownstrings;
3027 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3028 const char **oldstrings_origin = prog->knownstrings_origin;
3029 prog->maxknownstrings += 128;
3030 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3031 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3032 if(prog->leaktest_active)
3033 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3034 if (prog->numknownstrings)
3036 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3037 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3038 if(prog->leaktest_active)
3039 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3042 prog->numknownstrings++;
3044 prog->firstfreeknownstring = i + 1;
3045 prog->knownstrings[i] = s;
3046 prog->knownstrings_freeable[i] = false;
3047 if(prog->leaktest_active)
3048 prog->knownstrings_origin[i] = NULL;
3049 return PRVM_KNOWNSTRINGBASE + i;
3052 // temp string handling
3054 // all tempstrings go into this buffer consecutively, and it is reset
3055 // whenever PRVM_ExecuteProgram returns to the engine
3056 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3057 // restores it on return, so multiple recursive calls can share the same
3059 // the buffer size is automatically grown as needed
3061 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3067 size = (int)strlen(s) + 1;
3068 if (developer_insane.integer)
3069 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3070 if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3072 sizebuf_t old = prog->tempstringsbuf;
3073 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3074 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);
3075 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3076 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3077 prog->tempstringsbuf.maxsize *= 2;
3078 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3080 Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3081 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3083 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3088 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3090 prog->tempstringsbuf.cursize += size;
3091 return PRVM_SetEngineString(prog, t);
3094 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3099 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3100 if (!prog->knownstrings[i])
3102 if (i >= prog->numknownstrings)
3104 if (i >= prog->maxknownstrings)
3106 const char **oldstrings = prog->knownstrings;
3107 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3108 const char **oldstrings_origin = prog->knownstrings_origin;
3109 prog->maxknownstrings += 128;
3110 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3111 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3112 if(prog->leaktest_active)
3113 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3114 if (prog->numknownstrings)
3116 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3117 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3118 if(prog->leaktest_active)
3119 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3122 Mem_Free((char **)oldstrings);
3123 if (oldstrings_freeable)
3124 Mem_Free((unsigned char *)oldstrings_freeable);
3125 if (oldstrings_origin)
3126 Mem_Free((char **)oldstrings_origin);
3128 prog->numknownstrings++;
3130 prog->firstfreeknownstring = i + 1;
3131 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3132 prog->knownstrings_freeable[i] = true;
3133 if(prog->leaktest_active)
3134 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3136 *pointer = (char *)(prog->knownstrings[i]);
3137 return PRVM_KNOWNSTRINGBASE + i;
3140 void PRVM_FreeString(prvm_prog_t *prog, int num)
3143 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3144 else if (num >= 0 && num < prog->stringssize)
3145 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3146 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3148 num = num - PRVM_KNOWNSTRINGBASE;
3149 if (!prog->knownstrings[num])
3150 prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3151 if (!prog->knownstrings_freeable[num])
3152 prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3153 PRVM_Free((char *)prog->knownstrings[num]);
3154 if(prog->leaktest_active)
3155 if(prog->knownstrings_origin[num])
3156 PRVM_Free((char *)prog->knownstrings_origin[num]);
3157 prog->knownstrings[num] = NULL;
3158 prog->knownstrings_freeable[num] = false;
3159 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3162 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3165 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3169 for (i = 0;i < prog->numglobaldefs;i++)
3171 ddef_t *d = &prog->globaldefs[i];
3172 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3174 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3178 for(j = 0; j < prog->num_edicts; ++j)
3180 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3181 if (ed->priv.required->free)
3183 for (i=0; i<prog->numfielddefs; ++i)
3185 ddef_t *d = &prog->fielddefs[i];
3186 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3188 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3196 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3200 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3201 return true; // world or clients
3202 if (prog == SVVM_prog)
3204 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3206 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3208 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3210 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3211 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3213 if(PRVM_serveredictfloat(edict, takedamage))
3215 if(*prvm_leaktest_ignore_classnames.string)
3217 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3221 else if (prog == CLVM_prog)
3223 // TODO someone add more stuff here
3224 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3226 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3228 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3230 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3231 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3233 if(*prvm_leaktest_ignore_classnames.string)
3235 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3241 // menu prog does not have classnames
3246 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3249 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3250 const char *targetname = NULL;
3252 if (prog == SVVM_prog)
3253 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3256 if(!*targetname) // ""
3261 for (i = 0;i < prog->numglobaldefs;i++)
3263 ddef_t *d = &prog->globaldefs[i];
3264 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3266 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3271 for(j = 0; j < prog->num_edicts; ++j)
3273 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3274 if (ed->priv.required->mark < mark)
3280 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3282 if(!strcmp(target, targetname))
3285 for (i=0; i<prog->numfielddefs; ++i)
3287 ddef_t *d = &prog->fielddefs[i];
3288 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3290 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3298 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3304 for(j = 0; j < prog->num_edicts; ++j)
3306 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3307 if(ed->priv.required->free)
3309 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3316 for(j = 0; j < prog->num_edicts; ++j)
3318 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3319 if(ed->priv.required->free)
3321 if(ed->priv.required->mark)
3323 if(PRVM_IsEdictReferenced(prog, ed, stage))
3325 ed->priv.required->mark = stage + 1;
3332 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3335 void PRVM_LeakTest(prvm_prog_t *prog)
3338 qboolean leaked = false;
3340 if(!prog->leaktest_active)
3344 for (i = 0; i < prog->numknownstrings; ++i)
3346 if(prog->knownstrings[i])
3347 if(prog->knownstrings_freeable[i])
3348 if(prog->knownstrings_origin[i])
3349 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3351 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3357 PRVM_MarkReferencedEdicts(prog);
3358 for(j = 0; j < prog->num_edicts; ++j)
3360 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3361 if(ed->priv.required->free)
3363 if(!ed->priv.required->mark)
3364 if(ed->priv.required->allocation_origin)
3366 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3367 PRVM_ED_Print(prog, ed, NULL);
3372 ed->priv.required->mark = 0; // clear marks again when done
3375 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3377 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3379 if(stringbuffer->origin)
3381 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3386 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3388 if(prog->openfiles[i])
3389 if(prog->openfiles_origin[i])
3391 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3396 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3398 if(prog->opensearches[i])
3399 if(prog->opensearches_origin[i])
3401 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3407 Con_Printf("Congratulations. No leaks found.\n");