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);
34 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
35 cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "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)"};
36 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
37 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
38 // LordHavoc: counts usage of each QuakeC statement
39 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)"};
40 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
42 extern sizebuf_t vm_tempstringsbuf;
44 //============================================================================
52 void PRVM_MEM_Alloc(void)
56 // reserve space for the null entity aka world
57 // check bound of max_edicts
58 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
59 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
61 // edictprivate_size has to be min as big prvm_edict_private_t
62 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
65 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
67 // alloc edict private space
68 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
71 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
74 for(i = 0; i < prog->max_edicts; i++)
76 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
77 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
83 PRVM_MEM_IncreaseEdicts
86 void PRVM_MEM_IncreaseEdicts(void)
89 int oldmaxedicts = prog->max_edicts;
90 void *oldedictsfields = prog->edictsfields;
91 void *oldedictprivate = prog->edictprivate;
93 if(prog->max_edicts >= prog->limit_edicts)
96 PRVM_GCALL(begin_increase_edicts)();
99 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
101 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
102 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
104 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
105 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
107 //set e and v pointers
108 for(i = 0; i < prog->max_edicts; i++)
110 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
111 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
114 PRVM_GCALL(end_increase_edicts)();
116 Mem_Free(oldedictsfields);
117 Mem_Free(oldedictprivate);
120 //============================================================================
123 int PRVM_ED_FindFieldOffset(const char *field)
126 d = PRVM_ED_FindField(field);
132 int PRVM_ED_FindGlobalOffset(const char *global)
135 d = PRVM_ED_FindGlobal(global);
141 func_t PRVM_ED_FindFunctionOffset(const char *function)
144 f = PRVM_ED_FindFunction(function);
147 return (func_t)(f - prog->functions);
150 qboolean PRVM_ProgLoaded(int prognr)
152 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
155 return (prog_list[prognr].loaded ? TRUE : FALSE);
160 PRVM_SetProgFromString
163 // perhaps add a return value when the str doesnt exist
164 qboolean PRVM_SetProgFromString(const char *str)
167 for(; i < PRVM_MAXPROGS ; i++)
168 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
170 if(prog_list[i].loaded)
172 prog = &prog_list[i];
177 Con_Printf("%s not loaded !\n",PRVM_NAME);
182 Con_Printf("Invalid program name %s !\n", str);
191 void PRVM_SetProg(int prognr)
193 if(0 <= prognr && prognr < PRVM_MAXPROGS)
195 if(prog_list[prognr].loaded)
196 prog = &prog_list[prognr];
198 PRVM_ERROR("%i not loaded !", prognr);
201 PRVM_ERROR("Invalid program number %i", prognr);
208 Sets everything to NULL
211 void PRVM_ED_ClearEdict (prvm_edict_t *e)
213 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
214 e->priv.required->free = false;
216 // AK: Let the init_edict function determine if something needs to be initialized
217 PRVM_GCALL(init_edict)(e);
224 Either finds a free edict, or allocates a new one.
225 Try to avoid reusing an entity that was recently freed, because it
226 can cause the client to think the entity morphed into something else
227 instead of being removed and recreated, which can cause interpolated
228 angles and bad trails.
231 prvm_edict_t *PRVM_ED_Alloc (void)
236 // the client qc dont need maxclients
237 // thus it doesnt need to use svs.maxclients
238 // AK: changed i=svs.maxclients+1
239 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
240 // although the menu/client has no world
241 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
243 e = PRVM_EDICT_NUM(i);
244 // the first couple seconds of server time can involve a lot of
245 // freeing and allocating, so relax the replacement policy
246 if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
248 PRVM_ED_ClearEdict (e);
253 if (i == prog->limit_edicts)
254 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
257 if (prog->num_edicts >= prog->max_edicts)
258 PRVM_MEM_IncreaseEdicts();
260 e = PRVM_EDICT_NUM(i);
261 PRVM_ED_ClearEdict (e);
270 Marks the edict as free
271 FIXME: walk all entities and NULL out references to this entity
274 void PRVM_ED_Free (prvm_edict_t *ed)
276 // dont delete the null entity (world) or reserved edicts
277 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
280 PRVM_GCALL(free_edict)(ed);
282 ed->priv.required->free = true;
283 ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
286 //===========================================================================
293 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
298 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
300 def = &prog->globaldefs[i];
312 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
317 for (i=0 ; i<prog->progs->numfielddefs ; i++)
319 def = &prog->fielddefs[i];
331 ddef_t *PRVM_ED_FindField (const char *name)
336 for (i=0 ; i<prog->progs->numfielddefs ; i++)
338 def = &prog->fielddefs[i];
339 if (!strcmp(PRVM_GetString(def->s_name), name))
350 ddef_t *PRVM_ED_FindGlobal (const char *name)
355 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
357 def = &prog->globaldefs[i];
358 if (!strcmp(PRVM_GetString(def->s_name), name))
370 mfunction_t *PRVM_ED_FindFunction (const char *name)
375 for (i=0 ; i<prog->progs->numfunctions ; i++)
377 func = &prog->functions[i];
378 if (!strcmp(PRVM_GetString(func->s_name), name))
389 Returns a string describing *data in a type specific manner
392 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
394 static char line[MAX_INPUTLINE];
399 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
404 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
408 if (n < 0 || n >= prog->limit_edicts)
409 sprintf (line, "entity %i (invalid!)", n);
411 sprintf (line, "entity %i", n);
414 f = prog->functions + val->function;
415 sprintf (line, "%s()", PRVM_GetString(f->s_name));
418 def = PRVM_ED_FieldAtOfs ( val->_int );
419 sprintf (line, ".%s", PRVM_GetString(def->s_name));
422 sprintf (line, "void");
425 // LordHavoc: changed from %5.1f to %10.4f
426 sprintf (line, "%10.4f", val->_float);
429 // LordHavoc: changed from %5.1f to %10.4f
430 sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
433 sprintf (line, "pointer");
436 sprintf (line, "bad type %i", (int) type);
447 Returns a string describing *data in a type specific manner
448 Easier to parse than PR_ValueString
451 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
453 static char line[MAX_INPUTLINE];
459 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
464 // Parse the string a bit to turn special characters
465 // (like newline, specifically) into escape codes,
466 // this fixes saving games from various mods
467 s = PRVM_GetString (val->string);
468 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
487 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
490 f = prog->functions + val->function;
491 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
494 def = PRVM_ED_FieldAtOfs ( val->_int );
495 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
498 dpsnprintf (line, sizeof (line), "void");
501 dpsnprintf (line, sizeof (line), "%f", val->_float);
504 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
507 dpsnprintf (line, sizeof (line), "bad type %i", type);
518 Returns a string with a description and the contents of a global,
519 padded to 20 field width
522 char *PRVM_GlobalString (int ofs)
528 static char line[128];
530 val = (void *)&prog->globals.generic[ofs];
531 def = PRVM_ED_GlobalAtOfs(ofs);
533 sprintf (line,"GLOBAL%i", ofs);
536 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
537 sprintf (line,"%s (=%s)", PRVM_GetString(def->s_name), s);
541 //for ( ; i<20 ; i++)
542 // strcat (line," ");
548 char *PRVM_GlobalStringNoContents (int ofs)
552 static char line[128];
554 def = PRVM_ED_GlobalAtOfs(ofs);
556 sprintf (line,"GLOBAL%i", ofs);
558 sprintf (line,"%s", PRVM_GetString(def->s_name));
561 //for ( ; i<20 ; i++)
562 // strcat (line," ");
576 // LordHavoc: optimized this to print out much more quickly (tempstring)
577 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
578 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
586 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
588 if (ed->priv.required->free)
590 Con_Printf("%s: FREE\n",PRVM_NAME);
595 sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
596 for (i=1 ; i<prog->progs->numfielddefs ; i++)
598 d = &prog->fielddefs[i];
599 name = PRVM_GetString(d->s_name);
600 if (name[strlen(name)-2] == '_')
601 continue; // skip _x, _y, _z vars
603 // Check Field Name Wildcard
604 if(wildcard_fieldname)
605 if( !matchpattern(name, wildcard_fieldname, 1) )
606 // Didn't match; skip
609 v = (int *)((char *)ed->fields.vp + d->ofs*4);
611 // if the value is still all 0, skip the field
612 type = d->type & ~DEF_SAVEGLOBAL;
614 for (j=0 ; j<prvm_type_size[type] ; j++)
617 if (j == prvm_type_size[type])
620 if (strlen(name) > sizeof(tempstring2)-4)
622 memcpy (tempstring2, name, sizeof(tempstring2)-4);
623 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
624 tempstring2[sizeof(tempstring2)-1] = 0;
627 strlcat(tempstring, name, sizeof(tempstring));
628 for (l = strlen(name);l < 14;l++)
629 strlcat(tempstring, " ", sizeof(tempstring));
630 strlcat(tempstring, " ", sizeof(tempstring));
632 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
633 if (strlen(name) > sizeof(tempstring2)-4)
635 memcpy (tempstring2, name, sizeof(tempstring2)-4);
636 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
637 tempstring2[sizeof(tempstring2)-1] = 0;
640 strlcat(tempstring, name, sizeof(tempstring));
641 strlcat(tempstring, "\n", sizeof(tempstring));
642 if (strlen(tempstring) >= sizeof(tempstring)/2)
644 Con_Print(tempstring);
649 Con_Print(tempstring);
659 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
669 if (ed->priv.required->free)
675 for (i=1 ; i<prog->progs->numfielddefs ; i++)
677 d = &prog->fielddefs[i];
678 name = PRVM_GetString(d->s_name);
679 if (name[strlen(name)-2] == '_')
680 continue; // skip _x, _y, _z vars
682 v = (int *)((char *)ed->fields.vp + d->ofs*4);
684 // if the value is still all 0, skip the field
685 type = d->type & ~DEF_SAVEGLOBAL;
686 for (j=0 ; j<prvm_type_size[type] ; j++)
689 if (j == prvm_type_size[type])
692 FS_Printf(f,"\"%s\" ",name);
693 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
699 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
701 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
706 PRVM_ED_PrintEdicts_f
708 For debugging, prints all the entities in the current server
711 void PRVM_ED_PrintEdicts_f (void)
714 const char *wildcard_fieldname;
716 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
718 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
723 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
727 wildcard_fieldname = Cmd_Argv(2);
729 wildcard_fieldname = NULL;
731 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
732 for (i=0 ; i<prog->num_edicts ; i++)
733 PRVM_ED_PrintNum (i, wildcard_fieldname);
742 For debugging, prints a single edict
745 void PRVM_ED_PrintEdict_f (void)
748 const char *wildcard_fieldname;
750 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
752 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
757 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
760 i = atoi (Cmd_Argv(2));
761 if (i >= prog->num_edicts)
763 Con_Print("Bad edict number\n");
768 // Optional Wildcard Provided
769 wildcard_fieldname = Cmd_Argv(3);
772 wildcard_fieldname = NULL;
773 PRVM_ED_PrintNum (i, wildcard_fieldname);
785 // 2 possibilities : 1. just displaying the active edict count
786 // 2. making a function pointer [x]
787 void PRVM_ED_Count_f (void)
795 Con_Print("prvm_count <program name>\n");
800 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
803 if(prog->count_edicts)
804 prog->count_edicts();
808 for (i=0 ; i<prog->num_edicts ; i++)
810 ent = PRVM_EDICT_NUM(i);
811 if (ent->priv.required->free)
816 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
817 Con_Printf("active :%3i\n", active);
824 ==============================================================================
828 FIXME: need to tag constants, doesn't really work
829 ==============================================================================
837 void PRVM_ED_WriteGlobals (qfile_t *f)
845 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
847 def = &prog->globaldefs[i];
849 if ( !(def->type & DEF_SAVEGLOBAL) )
851 type &= ~DEF_SAVEGLOBAL;
853 if (type != ev_string && type != ev_float && type != ev_entity)
856 name = PRVM_GetString(def->s_name);
857 FS_Printf(f,"\"%s\" ", name);
858 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
868 void PRVM_ED_ParseGlobals (const char *data)
870 char keyname[MAX_INPUTLINE];
876 if (!COM_ParseToken_Simple(&data, false, false))
877 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
878 if (com_token[0] == '}')
881 strlcpy (keyname, com_token, sizeof(keyname));
884 if (!COM_ParseToken_Simple(&data, false, true))
885 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
887 if (com_token[0] == '}')
888 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
890 key = PRVM_ED_FindGlobal (keyname);
893 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
897 if (!PRVM_ED_ParseEpair(NULL, key, com_token))
898 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
902 //============================================================================
909 Can parse either fields or globals
910 returns false if error
913 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
922 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
924 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
925 switch (key->type & ~DEF_SAVEGLOBAL)
928 l = (int)strlen(s) + 1;
929 val->string = PRVM_AllocString(l, &new_p);
930 for (i = 0;i < l;i++)
932 if (s[i] == '\\' && i < l-1)
937 else if (s[i] == 'r')
948 while (*s && *s <= ' ')
950 val->_float = atof(s);
954 for (i = 0;i < 3;i++)
956 while (*s && *s <= ' ')
960 val->vector[i] = atof(s);
969 while (*s && *s <= ' ')
972 if (i >= prog->limit_edicts)
973 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);
974 while (i >= prog->max_edicts)
975 PRVM_MEM_IncreaseEdicts();
976 // if IncreaseEdicts was called the base pointer needs to be updated
978 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
979 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
983 def = PRVM_ED_FindField(s);
986 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
989 val->_int = def->ofs;
993 func = PRVM_ED_FindFunction(s);
996 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
999 val->function = func - prog->functions;
1003 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1013 Console command to send a string to QC function GameCommand of the
1017 sv_cmd adminmsg 3 "do not teamkill"
1018 cl_cmd someclientcommand
1019 menu_cmd somemenucommand
1021 All progs can support this extension; sg calls it in server QC, cg in client
1025 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1029 Con_Printf("%s text...\n", whichcmd);
1034 if(!PRVM_SetProgFromString(whichprogs))
1035 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1036 // also, it makes printing error messages easier!
1038 Con_Printf("%s program not loaded.\n", whichprogs);
1042 if(!prog->funcoffsets.GameCommand)
1044 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1048 int restorevm_tempstringsbuf_cursize;
1053 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1054 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1055 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1056 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1061 void PRVM_GameCommand_Server_f(void)
1063 PRVM_GameCommand("server", "sv_cmd");
1065 void PRVM_GameCommand_Client_f(void)
1067 PRVM_GameCommand("client", "cl_cmd");
1069 void PRVM_GameCommand_Menu_f(void)
1071 PRVM_GameCommand("menu", "menu_cmd");
1078 Console command to set a field of a specified edict
1081 void PRVM_ED_EdictSet_f(void)
1088 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1093 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1095 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1099 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1101 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1102 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1104 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4));
1110 ====================
1113 Parses an edict out of the given string, returning the new position
1114 ed should be a properly initialized empty edict.
1115 Used for initial level load and for savegames.
1116 ====================
1118 extern cvar_t developer_entityparsing;
1119 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1129 // go through all the dictionary pairs
1133 if (!COM_ParseToken_Simple(&data, false, false))
1134 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1135 if (developer_entityparsing.integer)
1136 Con_Printf("Key: \"%s\"", com_token);
1137 if (com_token[0] == '}')
1140 // anglehack is to allow QuakeEd to write single scalar angles
1141 // and allow them to be turned into vectors. (FIXME...)
1142 if (!strcmp(com_token, "angle"))
1144 strlcpy (com_token, "angles", sizeof(com_token));
1150 // FIXME: change light to _light to get rid of this hack
1151 if (!strcmp(com_token, "light"))
1152 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1154 strlcpy (keyname, com_token, sizeof(keyname));
1156 // another hack to fix keynames with trailing spaces
1157 n = strlen(keyname);
1158 while (n && keyname[n-1] == ' ')
1165 if (!COM_ParseToken_Simple(&data, false, true))
1166 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1167 if (developer_entityparsing.integer)
1168 Con_Printf(" \"%s\"\n", com_token);
1170 if (com_token[0] == '}')
1171 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1175 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1179 // keynames with a leading underscore are used for utility comments,
1180 // and are immediately discarded by quake
1181 if (keyname[0] == '_')
1184 key = PRVM_ED_FindField (keyname);
1187 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1194 strlcpy (temp, com_token, sizeof(temp));
1195 sprintf (com_token, "0 %s 0", temp);
1198 if (!PRVM_ED_ParseEpair(ent, key, com_token))
1199 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1203 ent->priv.required->free = true;
1211 PRVM_ED_LoadFromFile
1213 The entities are directly placed in the array, rather than allocated with
1214 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1215 number references out of order.
1217 Creates a server's entity / program execution context by
1218 parsing textual entity definitions out of an ent file.
1220 Used for both fresh maps and savegame loads. A fresh map would also need
1221 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1224 void PRVM_ED_LoadFromFile (const char *data)
1227 int parsed, inhibited, spawned, died;
1239 // parse the opening brace
1240 if (!COM_ParseToken_Simple(&data, false, false))
1242 if (com_token[0] != '{')
1243 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1245 // CHANGED: this is not conform to PR_LoadFromFile
1246 if(prog->loadintoworld)
1248 prog->loadintoworld = false;
1249 ent = PRVM_EDICT_NUM(0);
1252 ent = PRVM_ED_Alloc();
1255 if (ent != prog->edicts) // hack
1256 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1258 data = PRVM_ED_ParseEdict (data, ent);
1261 // remove the entity ?
1262 if(prog->load_edict && !prog->load_edict(ent))
1270 // immediately call spawn function, but only if there is a self global and a classname
1272 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1274 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1277 Con_Print("No classname for:\n");
1278 PRVM_ED_Print(ent, NULL);
1283 // look for the spawn function
1284 func = PRVM_ED_FindFunction (PRVM_GetString(handle));
1288 // check for OnEntityNoSpawnFunction
1289 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1292 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1293 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1297 if (developer.integer) // don't confuse non-developers with errors
1299 Con_Print("No spawn function for:\n");
1300 PRVM_ED_Print(ent, NULL);
1309 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1310 PRVM_ExecuteProgram (func - prog->functions, "");
1315 if (ent->priv.required->free)
1319 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);
1322 void PRVM_FindOffsets(void)
1324 // field and global searches use -1 for NULL
1325 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1326 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1327 // functions use 0 for NULL
1328 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1330 // server and client qc use a lot of similar fields, so this is combined
1331 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1332 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1333 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1334 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1335 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1336 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1337 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1338 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1339 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1340 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1341 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1342 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1343 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1344 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1345 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1346 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1347 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1348 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1349 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1350 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1351 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1352 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1353 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1354 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1355 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1356 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1357 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1358 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1359 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1360 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1361 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1362 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1363 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1364 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1365 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1366 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1367 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1368 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1369 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1370 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1371 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1372 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1373 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1374 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1375 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1376 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1377 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1378 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1379 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1380 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1381 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1382 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1383 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1384 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1385 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1386 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1387 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1388 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1389 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1390 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1391 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1392 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1393 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1394 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1395 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1396 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1397 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1398 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1399 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1400 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1401 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1402 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1403 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1404 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1405 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1406 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1407 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1408 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1409 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1410 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1411 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1412 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1413 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1414 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1415 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1416 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1417 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1418 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1419 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1420 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1421 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1422 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1423 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1424 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1425 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1426 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1427 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1428 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1429 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1430 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1431 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1432 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1433 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1434 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1435 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1436 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1437 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1438 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1439 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1440 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1441 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1442 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1443 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1444 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1445 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1446 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1447 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1448 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1449 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1450 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1451 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1452 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1453 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1454 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1455 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1456 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1457 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1458 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1459 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1460 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1461 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1462 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1463 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1464 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1465 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1466 prog->globaloffsets.input_ascii = PRVM_ED_FindGlobalOffset("input_ascii");
1468 // menu qc only uses some functions, nothing else
1469 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1470 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1471 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1472 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1473 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1474 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1479 typedef struct dpfield_s
1486 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1488 dpfield_t dpfields[] =
1499 void PRVM_ResetProg()
1501 PRVM_GCALL(reset_cmd)();
1502 Mem_FreePool(&prog->progs_mempool);
1503 memset(prog,0,sizeof(prvm_prog_t));
1504 prog->starttime = Sys_DoubleTime();
1512 void PRVM_LoadLNO( const char *progname ) {
1513 fs_offset_t filesize;
1515 unsigned int *header;
1518 FS_StripExtension( progname, filename, sizeof( filename ) );
1519 strlcat( filename, ".lno", sizeof( filename ) );
1521 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1527 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1528 <Spike> SafeWrite (h, &version, sizeof(int));
1529 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1530 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1531 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1532 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1533 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1535 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1540 header = (unsigned int *) lno;
1541 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1542 LittleLong( header[ 1 ] ) == 1 &&
1543 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1544 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1545 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1546 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1548 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1549 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1559 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1563 ddef_t *infielddefs;
1564 dfunction_t *dfunctions;
1565 fs_offset_t filesize;
1567 if( prog->loaded ) {
1568 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1571 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1572 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1573 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1575 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1577 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1579 // byte swap the header
1580 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1581 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1583 if (prog->progs->version != PROG_VERSION)
1584 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1585 if (prog->progs->crc != prog->headercrc)
1586 PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
1588 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1589 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1591 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1592 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1593 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1594 prog->stringssize = prog->progs->numstrings;
1596 prog->numknownstrings = 0;
1597 prog->maxknownstrings = 0;
1598 prog->knownstrings = NULL;
1599 prog->knownstrings_freeable = NULL;
1601 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1603 // we need to expand the fielddefs list to include all the engine fields,
1604 // so allocate a new place for it
1605 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1607 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1609 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1611 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1613 // moved edict_size calculation down below field adding code
1615 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1616 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1618 // byte swap the lumps
1619 for (i=0 ; i<prog->progs->numstatements ; i++)
1621 prog->statements[i].op = LittleShort(prog->statements[i].op);
1622 prog->statements[i].a = LittleShort(prog->statements[i].a);
1623 prog->statements[i].b = LittleShort(prog->statements[i].b);
1624 prog->statements[i].c = LittleShort(prog->statements[i].c);
1627 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1628 for (i = 0;i < prog->progs->numfunctions;i++)
1630 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1631 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1632 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1633 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1634 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1635 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1636 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1639 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1641 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1642 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1643 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1646 // copy the progs fields to the new fields list
1647 for (i = 0;i < prog->progs->numfielddefs;i++)
1649 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1650 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1651 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1652 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1653 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1656 // append the required fields
1657 for (i = 0;i < (int) numrequiredfields;i++)
1659 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1660 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1661 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1662 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1663 prog->progs->entityfields += 3;
1665 prog->progs->entityfields++;
1666 prog->progs->numfielddefs++;
1669 // check required functions
1670 for(i=0 ; i < numrequiredfunc ; i++)
1671 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1672 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1674 // check required globals
1675 for(i=0 ; i < numrequiredglobals ; i++)
1676 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1677 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1679 for (i=0 ; i<prog->progs->numglobals ; i++)
1680 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1682 // moved edict_size calculation down here, below field adding code
1683 // LordHavoc: this no longer includes the prvm_edict_t header
1684 prog->edict_size = prog->progs->entityfields * 4;
1685 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1687 // LordHavoc: bounds check anything static
1688 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1694 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1695 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1698 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1699 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1701 // global global global
1736 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1737 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1739 // global none global
1745 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1746 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1762 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1763 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1777 if ((unsigned short) st->a >= prog->progs->numglobals)
1778 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1781 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1786 PRVM_LoadLNO(filename);
1790 prog->loaded = TRUE;
1792 // set flags & ddef_ts in prog
1798 PRVM_GCALL(init_cmd)();
1805 void PRVM_Fields_f (void)
1807 int i, j, ednum, used, usedamount;
1809 char tempstring[MAX_INPUTLINE], tempstring2[260];
1819 Con_Print("no progs loaded\n");
1826 Con_Print("prvm_fields <program name>\n");
1831 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1834 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1835 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1837 ed = PRVM_EDICT_NUM(ednum);
1838 if (ed->priv.required->free)
1840 for (i = 1;i < prog->progs->numfielddefs;i++)
1842 d = &prog->fielddefs[i];
1843 name = PRVM_GetString(d->s_name);
1844 if (name[strlen(name)-2] == '_')
1845 continue; // skip _x, _y, _z vars
1846 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1847 // if the value is still all 0, skip the field
1848 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1861 for (i = 0;i < prog->progs->numfielddefs;i++)
1863 d = &prog->fielddefs[i];
1864 name = PRVM_GetString(d->s_name);
1865 if (name[strlen(name)-2] == '_')
1866 continue; // skip _x, _y, _z vars
1867 switch(d->type & ~DEF_SAVEGLOBAL)
1870 strlcat(tempstring, "string ", sizeof(tempstring));
1873 strlcat(tempstring, "entity ", sizeof(tempstring));
1876 strlcat(tempstring, "function ", sizeof(tempstring));
1879 strlcat(tempstring, "field ", sizeof(tempstring));
1882 strlcat(tempstring, "void ", sizeof(tempstring));
1885 strlcat(tempstring, "float ", sizeof(tempstring));
1888 strlcat(tempstring, "vector ", sizeof(tempstring));
1891 strlcat(tempstring, "pointer ", sizeof(tempstring));
1894 sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1895 strlcat(tempstring, tempstring2, sizeof(tempstring));
1898 if (strlen(name) > sizeof(tempstring2)-4)
1900 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1901 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1902 tempstring2[sizeof(tempstring2)-1] = 0;
1905 strlcat(tempstring, name, sizeof(tempstring));
1906 for (j = (int)strlen(name);j < 25;j++)
1907 strlcat(tempstring, " ", sizeof(tempstring));
1908 sprintf(tempstring2, "%5d", counts[i]);
1909 strlcat(tempstring, tempstring2, sizeof(tempstring));
1910 strlcat(tempstring, "\n", sizeof(tempstring));
1911 if (strlen(tempstring) >= sizeof(tempstring)/2)
1913 Con_Print(tempstring);
1919 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1923 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);
1928 void PRVM_Globals_f (void)
1931 const char *wildcard;
1937 Con_Print("no progs loaded\n");
1940 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1942 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
1947 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1950 if( Cmd_Argc() == 3)
1951 wildcard = Cmd_Argv(2);
1955 Con_Printf("%s :", PRVM_NAME);
1957 for (i = 0;i < prog->progs->numglobaldefs;i++)
1960 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
1965 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
1967 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
1977 void PRVM_Global_f(void)
1980 if( Cmd_Argc() != 3 ) {
1981 Con_Printf( "prvm_global <program name> <global name>\n" );
1986 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1989 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1991 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1993 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2002 void PRVM_GlobalSet_f(void)
2005 if( Cmd_Argc() != 4 ) {
2006 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2011 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2014 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2016 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2018 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) );
2027 void PRVM_Init (void)
2029 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2030 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2031 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2032 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2033 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)");
2034 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)");
2035 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2036 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2037 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2038 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)");
2039 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2040 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2041 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2042 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2043 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2044 Cvar_RegisterVariable (&prvm_boundscheck);
2045 Cvar_RegisterVariable (&prvm_traceqc);
2046 Cvar_RegisterVariable (&prvm_statementprofiling);
2047 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2057 void PRVM_InitProg(int prognr)
2059 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2060 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2062 prog = &prog_list[prognr];
2067 memset(prog, 0, sizeof(prvm_prog_t));
2068 prog->starttime = Sys_DoubleTime();
2070 prog->error_cmd = Host_Error;
2073 int PRVM_GetProgNr()
2075 return prog - prog_list;
2078 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2080 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2083 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2085 _Mem_Free(buffer, filename, fileline);
2088 void _PRVM_FreeAll(const char *filename, int fileline)
2091 prog->fielddefs = NULL;
2092 prog->functions = NULL;
2093 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2096 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2097 prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
2099 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2104 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2106 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2110 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2113 n = e - prog->edicts;
2114 if ((unsigned int)n >= prog->limit_edicts)
2115 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2119 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2121 // return e - prog->edicts;
2124 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2125 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2126 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2129 n = e - prog->edicts;
2130 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2131 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2132 return n;// EXPERIMENTAL
2133 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2135 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2137 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2138 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2139 return prog->edicts + n; // EXPERIMENTAL
2140 //return prog->edicts + ((n) / (progs->entityfields * 4));
2145 sizebuf_t vm_tempstringsbuf;
2147 const char *PRVM_GetString(int num)
2151 if (num < prog->stringssize)
2152 return prog->strings + num;
2155 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2157 num -= prog->stringssize;
2158 if (num < vm_tempstringsbuf.cursize)
2159 return (char *)vm_tempstringsbuf.data + num;
2162 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2169 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2179 // special range reserved for tempstrings
2181 if (num < vm_tempstringsbuf.cursize)
2182 return (char *)vm_tempstringsbuf.data + num;
2185 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2191 if (num < prog->numknownstrings)
2193 if (!prog->knownstrings[num])
2194 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2195 return prog->knownstrings[num];
2199 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2205 int PRVM_SetEngineString(const char *s)
2210 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2211 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2212 // if it's in the tempstrings area, use a reserved range
2213 // (otherwise we'd get millions of useless string offsets cluttering the database)
2214 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2216 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2218 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2220 // see if it's a known string address
2221 for (i = 0;i < prog->numknownstrings;i++)
2222 if (prog->knownstrings[i] == s)
2224 // new unknown engine string
2225 if (developer.integer >= 200)
2226 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2227 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2228 if (!prog->knownstrings[i])
2230 if (i >= prog->numknownstrings)
2232 if (i >= prog->maxknownstrings)
2234 const char **oldstrings = prog->knownstrings;
2235 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2236 prog->maxknownstrings += 128;
2237 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2238 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2239 if (prog->numknownstrings)
2241 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2242 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2245 prog->numknownstrings++;
2247 prog->firstfreeknownstring = i + 1;
2248 prog->knownstrings[i] = s;
2252 // temp string handling
2254 // all tempstrings go into this buffer consecutively, and it is reset
2255 // whenever PRVM_ExecuteProgram returns to the engine
2256 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2257 // restores it on return, so multiple recursive calls can share the same
2259 // the buffer size is automatically grown as needed
2261 int PRVM_SetTempString(const char *s)
2267 size = (int)strlen(s) + 1;
2268 if (developer.integer >= 300)
2269 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2270 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2272 sizebuf_t old = vm_tempstringsbuf;
2273 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2274 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);
2275 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2276 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2277 vm_tempstringsbuf.maxsize *= 2;
2278 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2280 if (developer.integer >= 100)
2281 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2282 vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2284 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2289 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2291 vm_tempstringsbuf.cursize += size;
2292 return PRVM_SetEngineString(t);
2295 int PRVM_AllocString(size_t bufferlength, char **pointer)
2300 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2301 if (!prog->knownstrings[i])
2303 if (i >= prog->numknownstrings)
2305 if (i >= prog->maxknownstrings)
2307 const char **oldstrings = prog->knownstrings;
2308 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2309 prog->maxknownstrings += 128;
2310 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2311 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2312 if (prog->numknownstrings)
2314 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2315 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2318 prog->numknownstrings++;
2320 prog->firstfreeknownstring = i + 1;
2321 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2322 prog->knownstrings_freeable[i] = true;
2324 *pointer = (char *)(prog->knownstrings[i]);
2328 void PRVM_FreeString(int num)
2331 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2332 else if (num >= 0 && num < prog->stringssize)
2333 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2334 else if (num < 0 && num >= -prog->numknownstrings)
2337 if (!prog->knownstrings[num])
2338 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2339 if (!prog->knownstrings[num])
2340 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2341 PRVM_Free((char *)prog->knownstrings[num]);
2342 prog->knownstrings[num] = NULL;
2343 prog->knownstrings_freeable[num] = false;
2344 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2347 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);