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.
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
29 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
34 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
35 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
36 // LordHavoc: counts usage of each QuakeC statement
37 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)"};
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"};
43 qboolean prvm_runawaycheck = true;
45 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
46 // enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)
47 qboolean prvm_boundscheck = true;
49 extern sizebuf_t vm_tempstringsbuf;
51 //============================================================================
59 void PRVM_MEM_Alloc(void)
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->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
81 for(i = 0; i < prog->max_edicts; i++)
83 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
84 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
90 PRVM_MEM_IncreaseEdicts
93 void PRVM_MEM_IncreaseEdicts(void)
96 int oldmaxedicts = prog->max_edicts;
97 void *oldedictsfields = prog->edictsfields;
98 void *oldedictprivate = prog->edictprivate;
100 if(prog->max_edicts >= prog->limit_edicts)
103 PRVM_GCALL(begin_increase_edicts)();
106 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
108 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
109 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
111 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
112 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
114 //set e and v pointers
115 for(i = 0; i < prog->max_edicts; i++)
117 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
118 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
121 PRVM_GCALL(end_increase_edicts)();
123 Mem_Free(oldedictsfields);
124 Mem_Free(oldedictprivate);
127 //============================================================================
130 int PRVM_ED_FindFieldOffset(const char *field)
133 d = PRVM_ED_FindField(field);
139 int PRVM_ED_FindGlobalOffset(const char *global)
142 d = PRVM_ED_FindGlobal(global);
148 func_t PRVM_ED_FindFunctionOffset(const char *function)
151 f = PRVM_ED_FindFunction(function);
154 return (func_t)(f - prog->functions);
157 qboolean PRVM_ProgLoaded(int prognr)
159 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
162 return (prog_list[prognr].loaded ? TRUE : FALSE);
167 PRVM_SetProgFromString
170 // perhaps add a return value when the str doesnt exist
171 qboolean PRVM_SetProgFromString(const char *str)
174 for(; i < PRVM_MAXPROGS ; i++)
175 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
177 if(prog_list[i].loaded)
179 prog = &prog_list[i];
184 Con_Printf("%s not loaded !\n",PRVM_NAME);
189 Con_Printf("Invalid program name %s !\n", str);
198 void PRVM_SetProg(int prognr)
200 if(0 <= prognr && prognr < PRVM_MAXPROGS)
202 if(prog_list[prognr].loaded)
203 prog = &prog_list[prognr];
205 PRVM_ERROR("%i not loaded !", prognr);
208 PRVM_ERROR("Invalid program number %i", prognr);
215 Sets everything to NULL
218 void PRVM_ED_ClearEdict (prvm_edict_t *e)
220 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
221 e->priv.required->free = false;
223 // AK: Let the init_edict function determine if something needs to be initialized
224 PRVM_GCALL(init_edict)(e);
227 const char *PRVM_AllocationOrigin(void)
230 if(prog->leaktest_active)
231 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
233 buf = (char *)PRVM_Alloc(128);
234 PRVM_ShortStackTrace(buf, 128);
243 Returns if this particular edict could get allocated by PRVM_ED_Alloc
246 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
248 if(!e->priv.required->free)
250 if(e->priv.required->freetime < prog->starttime + 2)
252 if(realtime > e->priv.required->freetime + 1)
254 return false; // entity slot still blocked because the entity was freed less than one second ago
261 Either finds a free edict, or allocates a new one.
262 Try to avoid reusing an entity that was recently freed, because it
263 can cause the client to think the entity morphed into something else
264 instead of being removed and recreated, which can cause interpolated
265 angles and bad trails.
268 prvm_edict_t *PRVM_ED_Alloc (void)
273 // the client qc dont need maxclients
274 // thus it doesnt need to use svs.maxclients
275 // AK: changed i=svs.maxclients+1
276 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
277 // although the menu/client has no world
278 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
280 e = PRVM_EDICT_NUM(i);
281 if(PRVM_ED_CanAlloc(e))
283 PRVM_ED_ClearEdict (e);
284 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
289 if (i == prog->limit_edicts)
290 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
293 if (prog->num_edicts >= prog->max_edicts)
294 PRVM_MEM_IncreaseEdicts();
296 e = PRVM_EDICT_NUM(i);
297 PRVM_ED_ClearEdict (e);
299 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
308 Marks the edict as free
309 FIXME: walk all entities and NULL out references to this entity
312 void PRVM_ED_Free (prvm_edict_t *ed)
314 // dont delete the null entity (world) or reserved edicts
315 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
318 PRVM_GCALL(free_edict)(ed);
320 ed->priv.required->free = true;
321 ed->priv.required->freetime = realtime;
322 if(ed->priv.required->allocation_origin)
324 PRVM_Free((char *)ed->priv.required->allocation_origin);
325 ed->priv.required->allocation_origin = NULL;
329 //===========================================================================
336 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
341 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
343 def = &prog->globaldefs[i];
355 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
360 for (i=0 ; i<prog->progs->numfielddefs ; i++)
362 def = &prog->fielddefs[i];
374 ddef_t *PRVM_ED_FindField (const char *name)
379 for (i=0 ; i<prog->progs->numfielddefs ; i++)
381 def = &prog->fielddefs[i];
382 if (!strcmp(PRVM_GetString(def->s_name), name))
393 ddef_t *PRVM_ED_FindGlobal (const char *name)
398 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
400 def = &prog->globaldefs[i];
401 if (!strcmp(PRVM_GetString(def->s_name), name))
413 mfunction_t *PRVM_ED_FindFunction (const char *name)
418 for (i=0 ; i<prog->progs->numfunctions ; i++)
420 func = &prog->functions[i];
421 if (!strcmp(PRVM_GetString(func->s_name), name))
432 Returns a string describing *data in a type specific manner
435 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
437 static char line[MAX_INPUTLINE];
442 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
447 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
451 if (n < 0 || n >= prog->limit_edicts)
452 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
454 dpsnprintf (line, sizeof(line), "entity %i", n);
457 f = prog->functions + val->function;
458 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
461 def = PRVM_ED_FieldAtOfs ( val->_int );
462 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
465 dpsnprintf (line, sizeof(line), "void");
468 // LordHavoc: changed from %5.1f to %10.4f
469 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
472 // LordHavoc: changed from %5.1f to %10.4f
473 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
476 dpsnprintf (line, sizeof(line), "pointer");
479 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
490 Returns a string describing *data in a type specific manner
491 Easier to parse than PR_ValueString
494 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
496 static char line[MAX_INPUTLINE];
502 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
507 // Parse the string a bit to turn special characters
508 // (like newline, specifically) into escape codes,
509 // this fixes saving games from various mods
510 s = PRVM_GetString (val->string);
511 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
540 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
543 f = prog->functions + val->function;
544 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
547 def = PRVM_ED_FieldAtOfs ( val->_int );
548 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
551 dpsnprintf (line, sizeof (line), "void");
554 dpsnprintf (line, sizeof (line), "%f", val->_float);
557 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
560 dpsnprintf (line, sizeof (line), "bad type %i", type);
571 Returns a string with a description and the contents of a global,
572 padded to 20 field width
575 char *PRVM_GlobalString (int ofs)
581 static char line[128];
583 val = (void *)&prog->globals.generic[ofs];
584 def = PRVM_ED_GlobalAtOfs(ofs);
586 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
589 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
590 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
594 //for ( ; i<20 ; i++)
595 // strcat (line," ");
601 char *PRVM_GlobalStringNoContents (int ofs)
605 static char line[128];
607 def = PRVM_ED_GlobalAtOfs(ofs);
609 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
611 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
614 //for ( ; i<20 ; i++)
615 // strcat (line," ");
629 // LordHavoc: optimized this to print out much more quickly (tempstring)
630 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
631 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
639 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
641 if (ed->priv.required->free)
643 Con_Printf("%s: FREE\n",PRVM_NAME);
648 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
649 for (i=1 ; i<prog->progs->numfielddefs ; i++)
651 d = &prog->fielddefs[i];
652 name = PRVM_GetString(d->s_name);
653 if (name[strlen(name)-2] == '_')
654 continue; // skip _x, _y, _z vars
656 // Check Field Name Wildcard
657 if(wildcard_fieldname)
658 if( !matchpattern(name, wildcard_fieldname, 1) )
659 // Didn't match; skip
662 v = (int *)((char *)ed->fields.vp + d->ofs*4);
664 // if the value is still all 0, skip the field
665 type = d->type & ~DEF_SAVEGLOBAL;
667 for (j=0 ; j<prvm_type_size[type] ; j++)
670 if (j == prvm_type_size[type])
673 if (strlen(name) > sizeof(tempstring2)-4)
675 memcpy (tempstring2, name, sizeof(tempstring2)-4);
676 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
677 tempstring2[sizeof(tempstring2)-1] = 0;
680 strlcat(tempstring, name, sizeof(tempstring));
681 for (l = strlen(name);l < 14;l++)
682 strlcat(tempstring, " ", sizeof(tempstring));
683 strlcat(tempstring, " ", sizeof(tempstring));
685 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
686 if (strlen(name) > sizeof(tempstring2)-4)
688 memcpy (tempstring2, name, sizeof(tempstring2)-4);
689 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
690 tempstring2[sizeof(tempstring2)-1] = 0;
693 strlcat(tempstring, name, sizeof(tempstring));
694 strlcat(tempstring, "\n", sizeof(tempstring));
695 if (strlen(tempstring) >= sizeof(tempstring)/2)
697 Con_Print(tempstring);
702 Con_Print(tempstring);
712 extern cvar_t developer_entityparsing;
713 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
723 if (ed->priv.required->free)
729 for (i=1 ; i<prog->progs->numfielddefs ; i++)
731 d = &prog->fielddefs[i];
732 name = PRVM_GetString(d->s_name);
734 if(developer_entityparsing.integer)
735 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
737 if (name[strlen(name)-2] == '_')
738 continue; // skip _x, _y, _z vars
740 v = (int *)((char *)ed->fields.vp + d->ofs*4);
742 // if the value is still all 0, skip the field
743 type = d->type & ~DEF_SAVEGLOBAL;
744 for (j=0 ; j<prvm_type_size[type] ; j++)
747 if (j == prvm_type_size[type])
750 FS_Printf(f,"\"%s\" ",name);
751 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
752 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
753 prog->statestring = NULL;
759 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
761 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
766 PRVM_ED_PrintEdicts_f
768 For debugging, prints all the entities in the current server
771 void PRVM_ED_PrintEdicts_f (void)
774 const char *wildcard_fieldname;
776 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
778 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
783 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
787 wildcard_fieldname = Cmd_Argv(2);
789 wildcard_fieldname = NULL;
791 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
792 for (i=0 ; i<prog->num_edicts ; i++)
793 PRVM_ED_PrintNum (i, wildcard_fieldname);
802 For debugging, prints a single edict
805 void PRVM_ED_PrintEdict_f (void)
808 const char *wildcard_fieldname;
810 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
812 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
817 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
820 i = atoi (Cmd_Argv(2));
821 if (i >= prog->num_edicts)
823 Con_Print("Bad edict number\n");
828 // Optional Wildcard Provided
829 wildcard_fieldname = Cmd_Argv(3);
832 wildcard_fieldname = NULL;
833 PRVM_ED_PrintNum (i, wildcard_fieldname);
845 // 2 possibilities : 1. just displaying the active edict count
846 // 2. making a function pointer [x]
847 void PRVM_ED_Count_f (void)
855 Con_Print("prvm_count <program name>\n");
860 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
863 if(prog->count_edicts)
864 prog->count_edicts();
868 for (i=0 ; i<prog->num_edicts ; i++)
870 ent = PRVM_EDICT_NUM(i);
871 if (ent->priv.required->free)
876 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
877 Con_Printf("active :%3i\n", active);
884 ==============================================================================
888 FIXME: need to tag constants, doesn't really work
889 ==============================================================================
897 void PRVM_ED_WriteGlobals (qfile_t *f)
905 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
907 def = &prog->globaldefs[i];
909 if ( !(def->type & DEF_SAVEGLOBAL) )
911 type &= ~DEF_SAVEGLOBAL;
913 if (type != ev_string && type != ev_float && type != ev_entity)
916 name = PRVM_GetString(def->s_name);
918 if(developer_entityparsing.integer)
919 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
921 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
922 FS_Printf(f,"\"%s\" ", name);
923 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
924 prog->statestring = NULL;
934 void PRVM_ED_ParseGlobals (const char *data)
936 char keyname[MAX_INPUTLINE];
942 if (!COM_ParseToken_Simple(&data, false, false))
943 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
944 if (com_token[0] == '}')
947 if (developer_entityparsing.integer)
948 Con_Printf("Key: \"%s\"", com_token);
950 strlcpy (keyname, com_token, sizeof(keyname));
953 if (!COM_ParseToken_Simple(&data, false, true))
954 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
956 if (developer_entityparsing.integer)
957 Con_Printf(" \"%s\"\n", com_token);
959 if (com_token[0] == '}')
960 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
962 key = PRVM_ED_FindGlobal (keyname);
965 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
969 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
970 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
974 //============================================================================
981 Can parse either fields or globals
982 returns false if error
985 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
994 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
996 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
997 switch (key->type & ~DEF_SAVEGLOBAL)
1000 l = (int)strlen(s) + 1;
1001 val->string = PRVM_AllocString(l, &new_p);
1002 for (i = 0;i < l;i++)
1004 if (s[i] == '\\' && s[i+1] && parsebackslash)
1009 else if (s[i] == 'r')
1020 while (*s && ISWHITESPACE(*s))
1022 val->_float = atof(s);
1026 for (i = 0;i < 3;i++)
1028 while (*s && ISWHITESPACE(*s))
1032 val->vector[i] = atof(s);
1033 while (!ISWHITESPACE(*s))
1041 while (*s && ISWHITESPACE(*s))
1044 if (i >= prog->limit_edicts)
1045 Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME);
1046 while (i >= prog->max_edicts)
1047 PRVM_MEM_IncreaseEdicts();
1048 // if IncreaseEdicts was called the base pointer needs to be updated
1050 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1051 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1057 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1060 def = PRVM_ED_FindField(s + 1);
1063 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1066 val->_int = def->ofs;
1070 func = PRVM_ED_FindFunction(s);
1073 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1076 val->function = func - prog->functions;
1080 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1090 Console command to send a string to QC function GameCommand of the
1094 sv_cmd adminmsg 3 "do not teamkill"
1095 cl_cmd someclientcommand
1096 menu_cmd somemenucommand
1098 All progs can support this extension; sg calls it in server QC, cg in client
1102 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1106 Con_Printf("%s text...\n", whichcmd);
1111 if(!PRVM_SetProgFromString(whichprogs))
1112 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1113 // also, it makes printing error messages easier!
1115 Con_Printf("%s program not loaded.\n", whichprogs);
1119 if(!prog->funcoffsets.GameCommand)
1121 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1125 int restorevm_tempstringsbuf_cursize;
1130 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1132 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1133 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1138 void PRVM_GameCommand_Server_f(void)
1140 PRVM_GameCommand("server", "sv_cmd");
1142 void PRVM_GameCommand_Client_f(void)
1144 PRVM_GameCommand("client", "cl_cmd");
1146 void PRVM_GameCommand_Menu_f(void)
1148 PRVM_GameCommand("menu", "menu_cmd");
1155 Console command to set a field of a specified edict
1158 void PRVM_ED_EdictSet_f(void)
1165 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1170 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1172 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1176 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1178 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1179 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1181 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1187 ====================
1190 Parses an edict out of the given string, returning the new position
1191 ed should be a properly initialized empty edict.
1192 Used for initial level load and for savegames.
1193 ====================
1195 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1205 // go through all the dictionary pairs
1209 if (!COM_ParseToken_Simple(&data, false, false))
1210 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1211 if (developer_entityparsing.integer)
1212 Con_Printf("Key: \"%s\"", com_token);
1213 if (com_token[0] == '}')
1216 // anglehack is to allow QuakeEd to write single scalar angles
1217 // and allow them to be turned into vectors. (FIXME...)
1218 if (!strcmp(com_token, "angle"))
1220 strlcpy (com_token, "angles", sizeof(com_token));
1226 // FIXME: change light to _light to get rid of this hack
1227 if (!strcmp(com_token, "light"))
1228 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1230 strlcpy (keyname, com_token, sizeof(keyname));
1232 // another hack to fix keynames with trailing spaces
1233 n = strlen(keyname);
1234 while (n && keyname[n-1] == ' ')
1241 if (!COM_ParseToken_Simple(&data, false, false))
1242 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1243 if (developer_entityparsing.integer)
1244 Con_Printf(" \"%s\"\n", com_token);
1246 if (com_token[0] == '}')
1247 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1251 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1255 // keynames with a leading underscore are used for utility comments,
1256 // and are immediately discarded by quake
1257 if (keyname[0] == '_')
1260 key = PRVM_ED_FindField (keyname);
1263 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1270 strlcpy (temp, com_token, sizeof(temp));
1271 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1274 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1275 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1279 ent->priv.required->free = true;
1287 PRVM_ED_LoadFromFile
1289 The entities are directly placed in the array, rather than allocated with
1290 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1291 number references out of order.
1293 Creates a server's entity / program execution context by
1294 parsing textual entity definitions out of an ent file.
1296 Used for both fresh maps and savegame loads. A fresh map would also need
1297 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1300 void PRVM_ED_LoadFromFile (const char *data)
1303 int parsed, inhibited, spawned, died;
1304 const char *funcname;
1316 // parse the opening brace
1317 if (!COM_ParseToken_Simple(&data, false, false))
1319 if (com_token[0] != '{')
1320 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1322 // CHANGED: this is not conform to PR_LoadFromFile
1323 if(prog->loadintoworld)
1325 prog->loadintoworld = false;
1326 ent = PRVM_EDICT_NUM(0);
1329 ent = PRVM_ED_Alloc();
1332 if (ent != prog->edicts) // hack
1333 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1335 data = PRVM_ED_ParseEdict (data, ent);
1338 // remove the entity ?
1339 if(prog->load_edict && !prog->load_edict(ent))
1346 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1349 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1350 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1353 if(ent->priv.required->free)
1360 // immediately call spawn function, but only if there is a self global and a classname
1362 if(!ent->priv.required->free)
1363 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1365 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1368 Con_Print("No classname for:\n");
1369 PRVM_ED_Print(ent, NULL);
1374 // look for the spawn function
1375 funcname = PRVM_GetString(handle);
1376 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1378 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1379 func = PRVM_ED_FindFunction (funcname);
1383 // check for OnEntityNoSpawnFunction
1384 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1387 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1388 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1392 if (developer.integer) // don't confuse non-developers with errors
1394 Con_Print("No spawn function for:\n");
1395 PRVM_ED_Print(ent, NULL);
1398 continue; // not included in "inhibited" count
1404 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1405 PRVM_ExecuteProgram (func - prog->functions, "");
1409 if(!ent->priv.required->free)
1410 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1413 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1414 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1418 if (ent->priv.required->free)
1422 Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1425 void PRVM_FindOffsets(void)
1427 // field and global searches use -1 for NULL
1428 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1429 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1430 // functions use 0 for NULL
1431 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1433 // server and client qc use a lot of similar fields, so this is combined
1434 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1435 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1436 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1437 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1438 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1439 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1440 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1441 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1442 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1443 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1444 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1445 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1446 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1447 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1448 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1449 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1450 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1451 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1452 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1453 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1454 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1455 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1456 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1457 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1458 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1459 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1460 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1461 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1462 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1463 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1464 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1465 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1466 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1467 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1468 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1469 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1470 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1471 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1472 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1473 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1474 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1475 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1476 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1477 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1478 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1479 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1480 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1481 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1482 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1483 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1484 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1485 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1486 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1487 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1488 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1489 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1490 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1491 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1492 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1493 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1494 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1495 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1496 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1497 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1498 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1499 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1500 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1501 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1502 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1503 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1504 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1505 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1506 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1507 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1508 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1509 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1510 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1511 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1512 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1513 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1514 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1515 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1516 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1517 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1518 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1519 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1520 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1521 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1522 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1523 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1524 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1525 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1526 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1527 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1528 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1529 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1530 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1531 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1532 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1533 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1534 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1535 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1536 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1537 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1538 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1539 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1540 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1541 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1542 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1543 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1544 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1545 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1546 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1547 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1548 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1549 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1550 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1551 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1552 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1553 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1554 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1555 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1556 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1557 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1558 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1559 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1560 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1561 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1562 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1563 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1564 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1565 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1566 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1567 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1568 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1569 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1570 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1571 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1572 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1573 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1574 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1575 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1576 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1577 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1578 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1579 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1580 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1581 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1582 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1583 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1584 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1585 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1586 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1587 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1588 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1589 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1590 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1591 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1592 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1593 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1594 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1595 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1596 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1597 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1598 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1599 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1600 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1602 // menu qc only uses some functions, nothing else
1603 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1604 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1605 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1606 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1607 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1608 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1613 typedef struct dpfield_s
1620 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1622 dpfield_t dpfields[] =
1633 void PRVM_LeakTest(void);
1634 void PRVM_ResetProg(void)
1637 PRVM_GCALL(reset_cmd)();
1638 Mem_FreePool(&prog->progs_mempool);
1639 memset(prog,0,sizeof(prvm_prog_t));
1640 prog->starttime = Sys_DoubleTime();
1648 void PRVM_LoadLNO( const char *progname ) {
1649 fs_offset_t filesize;
1651 unsigned int *header;
1654 FS_StripExtension( progname, filename, sizeof( filename ) );
1655 strlcat( filename, ".lno", sizeof( filename ) );
1657 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1663 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1664 <Spike> SafeWrite (h, &version, sizeof(int));
1665 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1666 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1667 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1668 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1669 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1671 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1676 header = (unsigned int *) lno;
1677 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1678 LittleLong( header[ 1 ] ) == 1 &&
1679 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1680 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1681 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1682 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1684 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1685 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1695 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1699 ddef_t *infielddefs;
1700 dfunction_t *dfunctions;
1701 fs_offset_t filesize;
1703 if( prog->loaded ) {
1704 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1707 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1708 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1709 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1710 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1712 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1714 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1716 // byte swap the header
1717 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1718 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1720 if (prog->progs->version != PROG_VERSION && prog->progs->version != PROG_EXTENDED)
1721 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i or %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION, PROG_EXTENDED);
1722 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1723 PRVM_ERROR ("%s: %s system vars have been modified (CRC of progs.dat systemvars %i != engine %i), progdefs.h is out of date", PRVM_NAME, filename, prog->progs->crc, prog->headercrc);
1725 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1726 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1728 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1729 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1730 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1731 prog->stringssize = prog->progs->numstrings;
1733 prog->numknownstrings = 0;
1734 prog->maxknownstrings = 0;
1735 prog->knownstrings = NULL;
1736 prog->knownstrings_freeable = NULL;
1738 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1740 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1742 // we need to expand the fielddefs list to include all the engine fields,
1743 // so allocate a new place for it
1744 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1746 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1748 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1750 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1752 // moved edict_size calculation down below field adding code
1754 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1755 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1757 // byte swap the lumps
1758 for (i=0 ; i<prog->progs->numstatements ; i++)
1760 prog->statements[i].op = LittleShort(prog->statements[i].op);
1761 prog->statements[i].a = LittleShort(prog->statements[i].a);
1762 prog->statements[i].b = LittleShort(prog->statements[i].b);
1763 prog->statements[i].c = LittleShort(prog->statements[i].c);
1766 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1767 for (i = 0;i < prog->progs->numfunctions;i++)
1769 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1770 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1771 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1772 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1773 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1774 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1775 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1776 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1777 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1778 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1781 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1783 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1784 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1785 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1786 // TODO bounds check ofs, s_name
1789 // copy the progs fields to the new fields list
1790 for (i = 0;i < prog->progs->numfielddefs;i++)
1792 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1793 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1794 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1795 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1796 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1797 // TODO bounds check ofs, s_name
1800 // append the required fields
1801 for (i = 0;i < (int) numrequiredfields;i++)
1803 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1804 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1805 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1806 // TODO bounds check ofs, s_name
1807 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1808 prog->progs->entityfields += 3;
1810 prog->progs->entityfields++;
1811 prog->progs->numfielddefs++;
1814 // check required functions
1815 for(i=0 ; i < numrequiredfunc ; i++)
1816 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1817 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1819 // check required globals
1820 for(i=0 ; i < numrequiredglobals ; i++)
1821 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1822 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1824 for (i=0 ; i<prog->progs->numglobals ; i++)
1825 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1827 // moved edict_size calculation down here, below field adding code
1828 // LordHavoc: this no longer includes the prvm_edict_t header
1829 prog->edict_size = prog->progs->entityfields * 4;
1830 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1832 // LordHavoc: bounds check anything static
1833 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1839 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1840 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (int/general) (statement %d) in %s", i, PRVM_NAME);
1844 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1845 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (float) (statement %d) in %s", i, PRVM_NAME);
1849 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1850 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (string not null) (statement %d) in %s", i, PRVM_NAME);
1853 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1854 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1857 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1858 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASE (statement %d) in %s", i, PRVM_NAME);
1861 if ((unsigned short) st->a >= prog->progs->numglobals ||
1862 (unsigned short) st->b >= prog->progs->numglobals ||
1863 st->c + i < 0 || st->c + i >= prog->progs->numstatements)
1864 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASERANGE (statement %d) in %s", i, PRVM_NAME);
1866 // global global global
1972 case OP_ADDSTOREP_F:
1973 case OP_ADDSTOREP_V:
1974 case OP_SUBSTOREP_F:
1975 case OP_SUBSTOREP_V:
1976 case OP_MULSTOREP_F:
1977 case OP_MULSTOREP_V:
1978 case OP_DIVSTOREP_F:
1979 case OP_GLOBALADDRESS:
1980 case OP_POINTER_ADD:
1986 case OP_FETCH_GBL_F:
1987 case OP_FETCH_GBL_S:
1988 case OP_FETCH_GBL_E:
1989 case OP_FETCH_GBL_FNC:
1990 case OP_FETCH_GBL_V:
1991 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1992 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1994 // global none global
2003 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2004 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2026 // case OP_STOREP_C:
2030 case OP_GSTOREP_ENT:
2031 case OP_GSTOREP_FLD:
2033 case OP_GSTOREP_FNC:
2051 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2052 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2082 if ((unsigned short) st->a >= prog->progs->numglobals)
2083 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2085 // TODO: add fte instruction support here too
2088 // this writes to OFS_RETURN and has no parameters
2091 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2095 if(prog->progs->numstatements < 1)
2097 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2099 else switch(prog->statements[prog->progs->numstatements - 1].op)
2106 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2110 PRVM_LoadLNO(filename);
2114 prog->loaded = TRUE;
2116 // set flags & ddef_ts in prog
2122 PRVM_GCALL(init_cmd)();
2129 void PRVM_Fields_f (void)
2131 int i, j, ednum, used, usedamount;
2133 char tempstring[MAX_INPUTLINE], tempstring2[260];
2143 Con_Print("no progs loaded\n");
2150 Con_Print("prvm_fields <program name>\n");
2155 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2158 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2159 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2161 ed = PRVM_EDICT_NUM(ednum);
2162 if (ed->priv.required->free)
2164 for (i = 1;i < prog->progs->numfielddefs;i++)
2166 d = &prog->fielddefs[i];
2167 name = PRVM_GetString(d->s_name);
2168 if (name[strlen(name)-2] == '_')
2169 continue; // skip _x, _y, _z vars
2170 v = (int *)((char *)ed->fields.vp + d->ofs*4);
2171 // if the value is still all 0, skip the field
2172 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2185 for (i = 0;i < prog->progs->numfielddefs;i++)
2187 d = &prog->fielddefs[i];
2188 name = PRVM_GetString(d->s_name);
2189 if (name[strlen(name)-2] == '_')
2190 continue; // skip _x, _y, _z vars
2191 switch(d->type & ~DEF_SAVEGLOBAL)
2194 strlcat(tempstring, "string ", sizeof(tempstring));
2197 strlcat(tempstring, "entity ", sizeof(tempstring));
2200 strlcat(tempstring, "function ", sizeof(tempstring));
2203 strlcat(tempstring, "field ", sizeof(tempstring));
2206 strlcat(tempstring, "void ", sizeof(tempstring));
2209 strlcat(tempstring, "float ", sizeof(tempstring));
2212 strlcat(tempstring, "vector ", sizeof(tempstring));
2215 strlcat(tempstring, "pointer ", sizeof(tempstring));
2218 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2219 strlcat(tempstring, tempstring2, sizeof(tempstring));
2222 if (strlen(name) > sizeof(tempstring2)-4)
2224 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2225 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2226 tempstring2[sizeof(tempstring2)-1] = 0;
2229 strlcat(tempstring, name, sizeof(tempstring));
2230 for (j = (int)strlen(name);j < 25;j++)
2231 strlcat(tempstring, " ", sizeof(tempstring));
2232 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2233 strlcat(tempstring, tempstring2, sizeof(tempstring));
2234 strlcat(tempstring, "\n", sizeof(tempstring));
2235 if (strlen(tempstring) >= sizeof(tempstring)/2)
2237 Con_Print(tempstring);
2243 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2247 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", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2252 void PRVM_Globals_f (void)
2255 const char *wildcard;
2261 Con_Print("no progs loaded\n");
2264 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2266 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2271 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2274 if( Cmd_Argc() == 3)
2275 wildcard = Cmd_Argv(2);
2279 Con_Printf("%s :", PRVM_NAME);
2281 for (i = 0;i < prog->progs->numglobaldefs;i++)
2284 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2289 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2291 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2301 void PRVM_Global_f(void)
2304 if( Cmd_Argc() != 3 ) {
2305 Con_Printf( "prvm_global <program name> <global name>\n" );
2310 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2313 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2315 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2317 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2326 void PRVM_GlobalSet_f(void)
2329 if( Cmd_Argc() != 4 ) {
2330 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2335 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2338 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2340 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2342 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2351 void PRVM_Init (void)
2353 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2354 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2355 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2356 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2357 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)");
2358 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)");
2359 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2360 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2361 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2362 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)");
2363 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2364 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2365 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2366 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2368 // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2369 prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2371 Cvar_RegisterVariable (&prvm_traceqc);
2372 Cvar_RegisterVariable (&prvm_statementprofiling);
2373 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2374 Cvar_RegisterVariable (&prvm_leaktest);
2375 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2376 Cvar_RegisterVariable (&prvm_errordump);
2378 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2379 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2389 void PRVM_InitProg(int prognr)
2391 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2392 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2394 prog = &prog_list[prognr];
2399 memset(prog, 0, sizeof(prvm_prog_t));
2400 prog->starttime = Sys_DoubleTime();
2402 prog->error_cmd = Host_Error;
2403 prog->leaktest_active = prvm_leaktest.integer;
2406 int PRVM_GetProgNr(void)
2408 return prog - prog_list;
2411 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2413 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2416 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2418 _Mem_Free(buffer, filename, fileline);
2421 void _PRVM_FreeAll(const char *filename, int fileline)
2424 prog->fielddefs = NULL;
2425 prog->functions = NULL;
2426 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2429 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2430 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2432 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2437 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2439 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2443 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2446 n = e - prog->edicts;
2447 if ((unsigned int)n >= prog->limit_edicts)
2448 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2452 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2454 // return e - prog->edicts;
2457 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2458 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2459 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2462 n = e - prog->edicts;
2463 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2464 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2465 return n;// EXPERIMENTAL
2466 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2468 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2470 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2471 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2472 return prog->edicts + n; // EXPERIMENTAL
2473 //return prog->edicts + ((n) / (progs->entityfields * 4));
2478 sizebuf_t vm_tempstringsbuf;
2480 const char *PRVM_GetString(int num)
2484 if (num < prog->stringssize)
2485 return prog->strings + num;
2488 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2490 num -= prog->stringssize;
2491 if (num < vm_tempstringsbuf.cursize)
2492 return (char *)vm_tempstringsbuf.data + num;
2495 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2502 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2512 // special range reserved for tempstrings
2514 if (num < vm_tempstringsbuf.cursize)
2515 return (char *)vm_tempstringsbuf.data + num;
2518 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2524 if (num < prog->numknownstrings)
2526 if (!prog->knownstrings[num])
2528 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2531 return prog->knownstrings[num];
2535 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2541 int PRVM_SetEngineString(const char *s)
2546 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2547 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2548 // if it's in the tempstrings area, use a reserved range
2549 // (otherwise we'd get millions of useless string offsets cluttering the database)
2550 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2552 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2554 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2556 // see if it's a known string address
2557 for (i = 0;i < prog->numknownstrings;i++)
2558 if (prog->knownstrings[i] == s)
2560 // new unknown engine string
2561 if (developer.integer >= 200)
2562 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2563 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2564 if (!prog->knownstrings[i])
2566 if (i >= prog->numknownstrings)
2568 if (i >= prog->maxknownstrings)
2570 const char **oldstrings = prog->knownstrings;
2571 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2572 const char **oldstrings_origin = prog->knownstrings_origin;
2573 prog->maxknownstrings += 128;
2574 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2575 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2576 if(prog->leaktest_active)
2577 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2578 if (prog->numknownstrings)
2580 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2581 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2582 if(prog->leaktest_active)
2583 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2586 prog->numknownstrings++;
2588 prog->firstfreeknownstring = i + 1;
2589 prog->knownstrings[i] = s;
2590 prog->knownstrings_freeable[i] = false;
2591 if(prog->leaktest_active)
2592 prog->knownstrings_origin[i] = NULL;
2596 // temp string handling
2598 // all tempstrings go into this buffer consecutively, and it is reset
2599 // whenever PRVM_ExecuteProgram returns to the engine
2600 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2601 // restores it on return, so multiple recursive calls can share the same
2603 // the buffer size is automatically grown as needed
2605 int PRVM_SetTempString(const char *s)
2611 size = (int)strlen(s) + 1;
2612 if (developer.integer >= 300)
2613 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2614 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2616 sizebuf_t old = vm_tempstringsbuf;
2617 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2618 PRVM_ERROR("PRVM_SetTempString: ran out of tempstring memory! (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", vm_tempstringsbuf.cursize, size);
2619 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2620 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2621 vm_tempstringsbuf.maxsize *= 2;
2622 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2624 if (developer.integer >= 100)
2625 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2626 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2628 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2633 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2635 vm_tempstringsbuf.cursize += size;
2636 return PRVM_SetEngineString(t);
2639 int PRVM_AllocString(size_t bufferlength, char **pointer)
2644 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2645 if (!prog->knownstrings[i])
2647 if (i >= prog->numknownstrings)
2649 if (i >= prog->maxknownstrings)
2651 const char **oldstrings = prog->knownstrings;
2652 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2653 const char **oldstrings_origin = prog->knownstrings_origin;
2654 prog->maxknownstrings += 128;
2655 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2656 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2657 if(prog->leaktest_active)
2658 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2659 if (prog->numknownstrings)
2661 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2662 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2663 if(prog->leaktest_active)
2664 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2666 // TODO why not Mem_Free the old ones?
2668 prog->numknownstrings++;
2670 prog->firstfreeknownstring = i + 1;
2671 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2672 prog->knownstrings_freeable[i] = true;
2673 if(prog->leaktest_active)
2674 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2676 *pointer = (char *)(prog->knownstrings[i]);
2680 void PRVM_FreeString(int num)
2683 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2684 else if (num >= 0 && num < prog->stringssize)
2685 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2686 else if (num < 0 && num >= -prog->numknownstrings)
2689 if (!prog->knownstrings[num])
2690 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2691 if (!prog->knownstrings_freeable[num])
2692 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2693 PRVM_Free((char *)prog->knownstrings[num]);
2694 if(prog->leaktest_active)
2695 if(prog->knownstrings_origin[num])
2696 PRVM_Free((char *)prog->knownstrings_origin[num]);
2697 prog->knownstrings[num] = NULL;
2698 prog->knownstrings_freeable[num] = false;
2699 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2702 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2705 static qboolean PRVM_IsStringReferenced(string_t string)
2709 for (i = 0;i < prog->progs->numglobaldefs;i++)
2711 ddef_t *d = &prog->globaldefs[i];
2712 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2714 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2718 for(j = 0; j < prog->num_edicts; ++j)
2720 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2721 if (ed->priv.required->free)
2723 for (i=0; i<prog->progs->numfielddefs; ++i)
2725 ddef_t *d = &prog->fielddefs[i];
2726 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2728 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2736 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2738 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2739 return true; // world or clients
2740 switch(prog - prog_list)
2742 case PRVM_SERVERPROG:
2744 entvars_t *ev = edict->fields.server;
2745 if(ev->solid) // can block other stuff, or is a trigger?
2747 if(ev->modelindex) // visible ent?
2749 if(ev->effects) // particle effect?
2751 if(ev->think) // has a think function?
2752 if(ev->nextthink > 0) // that actually will eventually run?
2756 if(*prvm_leaktest_ignore_classnames.string)
2758 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2763 case PRVM_CLIENTPROG:
2765 // TODO someone add more stuff here
2766 cl_entvars_t *ev = edict->fields.client;
2767 if(ev->entnum) // csqc networked
2769 if(ev->modelindex) // visible ent?
2771 if(ev->effects) // particle effect?
2773 if(ev->think) // has a think function?
2774 if(ev->nextthink > 0) // that actually will eventually run?
2776 if(*prvm_leaktest_ignore_classnames.string)
2778 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2784 // menu prog does not have classnames
2790 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2793 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2794 const char *targetname = NULL;
2796 switch(prog - prog_list)
2798 case PRVM_SERVERPROG:
2799 targetname = PRVM_GetString(edict->fields.server->targetname);
2804 if(!*targetname) // ""
2807 for (i = 0;i < prog->progs->numglobaldefs;i++)
2809 ddef_t *d = &prog->globaldefs[i];
2810 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2812 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2816 for(j = 0; j < prog->num_edicts; ++j)
2818 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2819 if (ed->priv.required->mark < mark)
2825 const char *target = PRVM_GetString(ed->fields.server->target);
2827 if(!strcmp(target, targetname))
2830 for (i=0; i<prog->progs->numfielddefs; ++i)
2832 ddef_t *d = &prog->fielddefs[i];
2833 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2835 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2843 static void PRVM_MarkReferencedEdicts(void)
2849 for(j = 0; j < prog->num_edicts; ++j)
2851 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2852 if(ed->priv.required->free)
2854 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2861 for(j = 0; j < prog->num_edicts; ++j)
2863 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2864 if(ed->priv.required->free)
2866 if(ed->priv.required->mark)
2868 if(PRVM_IsEdictReferenced(ed, stage))
2870 ed->priv.required->mark = stage + 1;
2877 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2880 void PRVM_LeakTest(void)
2883 qboolean leaked = false;
2885 if(!prog->leaktest_active)
2889 for (i = 0; i < prog->numknownstrings; ++i)
2891 if(prog->knownstrings[i])
2892 if(prog->knownstrings_freeable[i])
2893 if(prog->knownstrings_origin[i])
2894 if(!PRVM_IsStringReferenced(-1 - i))
2896 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2902 PRVM_MarkReferencedEdicts();
2903 for(j = 0; j < prog->num_edicts; ++j)
2905 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2906 if(ed->priv.required->free)
2908 if(!ed->priv.required->mark)
2909 if(ed->priv.required->allocation_origin)
2911 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2912 PRVM_ED_Print(ed, NULL);
2918 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2920 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2922 if(stringbuffer->origin)
2924 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2929 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2931 if(prog->openfiles[i])
2932 if(prog->openfiles_origin[i])
2934 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2939 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2941 if(prog->opensearches[i])
2942 if(prog->opensearches_origin[i])
2944 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2950 Con_Printf("Congratulations. No leaks found.\n");