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)"};
41 extern sizebuf_t vm_tempstringsbuf;
43 //============================================================================
51 void PRVM_MEM_Alloc(void)
55 // reserve space for the null entity aka world
56 // check bound of max_edicts
57 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
58 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
60 // edictprivate_size has to be min as big prvm_edict_private_t
61 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
64 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
66 // alloc edict private space
67 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
70 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
73 for(i = 0; i < prog->max_edicts; i++)
75 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
76 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
82 PRVM_MEM_IncreaseEdicts
85 void PRVM_MEM_IncreaseEdicts(void)
88 int oldmaxedicts = prog->max_edicts;
89 void *oldedictsfields = prog->edictsfields;
90 void *oldedictprivate = prog->edictprivate;
92 if(prog->max_edicts >= prog->limit_edicts)
95 PRVM_GCALL(begin_increase_edicts)();
98 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
100 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
101 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
103 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
104 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
106 //set e and v pointers
107 for(i = 0; i < prog->max_edicts; i++)
109 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
110 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
113 PRVM_GCALL(end_increase_edicts)();
115 Mem_Free(oldedictsfields);
116 Mem_Free(oldedictprivate);
119 //============================================================================
122 int PRVM_ED_FindFieldOffset(const char *field)
125 d = PRVM_ED_FindField(field);
131 int PRVM_ED_FindGlobalOffset(const char *global)
134 d = PRVM_ED_FindGlobal(global);
140 func_t PRVM_ED_FindFunctionOffset(const char *function)
143 f = PRVM_ED_FindFunction(function);
146 return (func_t)(f - prog->functions);
149 qboolean PRVM_ProgLoaded(int prognr)
151 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
154 return (prog_list[prognr].loaded ? TRUE : FALSE);
159 PRVM_SetProgFromString
162 // perhaps add a return value when the str doesnt exist
163 qboolean PRVM_SetProgFromString(const char *str)
166 for(; i < PRVM_MAXPROGS ; i++)
167 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
169 if(prog_list[i].loaded)
171 prog = &prog_list[i];
176 Con_Printf("%s not loaded !\n",PRVM_NAME);
181 Con_Printf("Invalid program name %s !\n", str);
190 void PRVM_SetProg(int prognr)
192 if(0 <= prognr && prognr < PRVM_MAXPROGS)
194 if(prog_list[prognr].loaded)
195 prog = &prog_list[prognr];
197 PRVM_ERROR("%i not loaded !", prognr);
200 PRVM_ERROR("Invalid program number %i", prognr);
207 Sets everything to NULL
210 void PRVM_ED_ClearEdict (prvm_edict_t *e)
212 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
213 e->priv.required->free = false;
215 // AK: Let the init_edict function determine if something needs to be initialized
216 PRVM_GCALL(init_edict)(e);
223 Either finds a free edict, or allocates a new one.
224 Try to avoid reusing an entity that was recently freed, because it
225 can cause the client to think the entity morphed into something else
226 instead of being removed and recreated, which can cause interpolated
227 angles and bad trails.
230 prvm_edict_t *PRVM_ED_Alloc (void)
235 // the client qc dont need maxclients
236 // thus it doesnt need to use svs.maxclients
237 // AK: changed i=svs.maxclients+1
238 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
239 // although the menu/client has no world
240 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
242 e = PRVM_EDICT_NUM(i);
243 // the first couple seconds of server time can involve a lot of
244 // freeing and allocating, so relax the replacement policy
245 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 ) )
247 PRVM_ED_ClearEdict (e);
252 if (i == prog->limit_edicts)
253 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
256 if (prog->num_edicts >= prog->max_edicts)
257 PRVM_MEM_IncreaseEdicts();
259 e = PRVM_EDICT_NUM(i);
260 PRVM_ED_ClearEdict (e);
269 Marks the edict as free
270 FIXME: walk all entities and NULL out references to this entity
273 void PRVM_ED_Free (prvm_edict_t *ed)
275 // dont delete the null entity (world) or reserved edicts
276 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
279 PRVM_GCALL(free_edict)(ed);
281 ed->priv.required->free = true;
282 ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
285 //===========================================================================
292 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
297 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
299 def = &prog->globaldefs[i];
311 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
316 for (i=0 ; i<prog->progs->numfielddefs ; i++)
318 def = &prog->fielddefs[i];
330 ddef_t *PRVM_ED_FindField (const char *name)
335 for (i=0 ; i<prog->progs->numfielddefs ; i++)
337 def = &prog->fielddefs[i];
338 if (!strcmp(PRVM_GetString(def->s_name), name))
349 ddef_t *PRVM_ED_FindGlobal (const char *name)
354 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
356 def = &prog->globaldefs[i];
357 if (!strcmp(PRVM_GetString(def->s_name), name))
369 mfunction_t *PRVM_ED_FindFunction (const char *name)
374 for (i=0 ; i<prog->progs->numfunctions ; i++)
376 func = &prog->functions[i];
377 if (!strcmp(PRVM_GetString(func->s_name), name))
388 Returns a string describing *data in a type specific manner
391 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
393 static char line[MAX_INPUTLINE];
398 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
403 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
407 if (n < 0 || n >= prog->limit_edicts)
408 sprintf (line, "entity %i (invalid!)", n);
410 sprintf (line, "entity %i", n);
413 f = prog->functions + val->function;
414 sprintf (line, "%s()", PRVM_GetString(f->s_name));
417 def = PRVM_ED_FieldAtOfs ( val->_int );
418 sprintf (line, ".%s", PRVM_GetString(def->s_name));
421 sprintf (line, "void");
424 // LordHavoc: changed from %5.1f to %10.4f
425 sprintf (line, "%10.4f", val->_float);
428 // LordHavoc: changed from %5.1f to %10.4f
429 sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
432 sprintf (line, "pointer");
435 sprintf (line, "bad type %i", (int) type);
446 Returns a string describing *data in a type specific manner
447 Easier to parse than PR_ValueString
450 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
452 static char line[MAX_INPUTLINE];
458 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
463 // Parse the string a bit to turn special characters
464 // (like newline, specifically) into escape codes,
465 // this fixes saving games from various mods
466 s = PRVM_GetString (val->string);
467 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
486 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
489 f = prog->functions + val->function;
490 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
493 def = PRVM_ED_FieldAtOfs ( val->_int );
494 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
497 dpsnprintf (line, sizeof (line), "void");
500 dpsnprintf (line, sizeof (line), "%f", val->_float);
503 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
506 dpsnprintf (line, sizeof (line), "bad type %i", type);
517 Returns a string with a description and the contents of a global,
518 padded to 20 field width
521 char *PRVM_GlobalString (int ofs)
527 static char line[128];
529 val = (void *)&prog->globals.generic[ofs];
530 def = PRVM_ED_GlobalAtOfs(ofs);
532 sprintf (line,"GLOBAL%i", ofs);
535 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
536 sprintf (line,"%s (=%s)", PRVM_GetString(def->s_name), s);
540 //for ( ; i<20 ; i++)
541 // strcat (line," ");
547 char *PRVM_GlobalStringNoContents (int ofs)
551 static char line[128];
553 def = PRVM_ED_GlobalAtOfs(ofs);
555 sprintf (line,"GLOBAL%i", ofs);
557 sprintf (line,"%s", PRVM_GetString(def->s_name));
560 //for ( ; i<20 ; i++)
561 // strcat (line," ");
575 // LordHavoc: optimized this to print out much more quickly (tempstring)
576 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
577 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
585 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
587 if (ed->priv.required->free)
589 Con_Printf("%s: FREE\n",PRVM_NAME);
594 sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
595 for (i=1 ; i<prog->progs->numfielddefs ; i++)
597 d = &prog->fielddefs[i];
598 name = PRVM_GetString(d->s_name);
599 if (name[strlen(name)-2] == '_')
600 continue; // skip _x, _y, _z vars
602 // Check Field Name Wildcard
603 if(wildcard_fieldname)
604 if( !matchpattern(name, wildcard_fieldname, 1) )
605 // Didn't match; skip
608 v = (int *)((char *)ed->fields.vp + d->ofs*4);
610 // if the value is still all 0, skip the field
611 type = d->type & ~DEF_SAVEGLOBAL;
613 for (j=0 ; j<prvm_type_size[type] ; j++)
616 if (j == prvm_type_size[type])
619 if (strlen(name) > sizeof(tempstring2)-4)
621 memcpy (tempstring2, name, sizeof(tempstring2)-4);
622 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
623 tempstring2[sizeof(tempstring2)-1] = 0;
626 strlcat(tempstring, name, sizeof(tempstring));
627 for (l = strlen(name);l < 14;l++)
628 strlcat(tempstring, " ", sizeof(tempstring));
629 strlcat(tempstring, " ", sizeof(tempstring));
631 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
632 if (strlen(name) > sizeof(tempstring2)-4)
634 memcpy (tempstring2, name, sizeof(tempstring2)-4);
635 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
636 tempstring2[sizeof(tempstring2)-1] = 0;
639 strlcat(tempstring, name, sizeof(tempstring));
640 strlcat(tempstring, "\n", sizeof(tempstring));
641 if (strlen(tempstring) >= sizeof(tempstring)/2)
643 Con_Print(tempstring);
648 Con_Print(tempstring);
658 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
668 if (ed->priv.required->free)
674 for (i=1 ; i<prog->progs->numfielddefs ; i++)
676 d = &prog->fielddefs[i];
677 name = PRVM_GetString(d->s_name);
678 if (name[strlen(name)-2] == '_')
679 continue; // skip _x, _y, _z vars
681 v = (int *)((char *)ed->fields.vp + d->ofs*4);
683 // if the value is still all 0, skip the field
684 type = d->type & ~DEF_SAVEGLOBAL;
685 for (j=0 ; j<prvm_type_size[type] ; j++)
688 if (j == prvm_type_size[type])
691 FS_Printf(f,"\"%s\" ",name);
692 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
698 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
700 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
705 PRVM_ED_PrintEdicts_f
707 For debugging, prints all the entities in the current server
710 void PRVM_ED_PrintEdicts_f (void)
713 const char *wildcard_fieldname;
715 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
717 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
722 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
726 wildcard_fieldname = Cmd_Argv(2);
728 wildcard_fieldname = NULL;
730 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
731 for (i=0 ; i<prog->num_edicts ; i++)
732 PRVM_ED_PrintNum (i, wildcard_fieldname);
741 For debugging, prints a single edict
744 void PRVM_ED_PrintEdict_f (void)
747 const char *wildcard_fieldname;
749 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
751 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
756 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
759 i = atoi (Cmd_Argv(2));
760 if (i >= prog->num_edicts)
762 Con_Print("Bad edict number\n");
767 // Optional Wildcard Provided
768 wildcard_fieldname = Cmd_Argv(3);
771 wildcard_fieldname = NULL;
772 PRVM_ED_PrintNum (i, wildcard_fieldname);
784 // 2 possibilities : 1. just displaying the active edict count
785 // 2. making a function pointer [x]
786 void PRVM_ED_Count_f (void)
794 Con_Print("prvm_count <program name>\n");
799 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
802 if(prog->count_edicts)
803 prog->count_edicts();
807 for (i=0 ; i<prog->num_edicts ; i++)
809 ent = PRVM_EDICT_NUM(i);
810 if (ent->priv.required->free)
815 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
816 Con_Printf("active :%3i\n", active);
823 ==============================================================================
827 FIXME: need to tag constants, doesn't really work
828 ==============================================================================
836 void PRVM_ED_WriteGlobals (qfile_t *f)
844 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
846 def = &prog->globaldefs[i];
848 if ( !(def->type & DEF_SAVEGLOBAL) )
850 type &= ~DEF_SAVEGLOBAL;
852 if (type != ev_string && type != ev_float && type != ev_entity)
855 name = PRVM_GetString(def->s_name);
856 FS_Printf(f,"\"%s\" ", name);
857 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
867 void PRVM_ED_ParseGlobals (const char *data)
869 char keyname[MAX_INPUTLINE];
875 if (!COM_ParseToken_Simple(&data, false, false))
876 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
877 if (com_token[0] == '}')
880 strlcpy (keyname, com_token, sizeof(keyname));
883 if (!COM_ParseToken_Simple(&data, false, true))
884 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
886 if (com_token[0] == '}')
887 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
889 key = PRVM_ED_FindGlobal (keyname);
892 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
896 if (!PRVM_ED_ParseEpair(NULL, key, com_token))
897 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
901 //============================================================================
908 Can parse either fields or globals
909 returns false if error
912 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
921 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
923 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
924 switch (key->type & ~DEF_SAVEGLOBAL)
927 l = (int)strlen(s) + 1;
928 val->string = PRVM_AllocString(l, &new_p);
929 for (i = 0;i < l;i++)
931 if (s[i] == '\\' && i < l-1)
936 else if (s[i] == 'r')
947 while (*s && *s <= ' ')
949 val->_float = atof(s);
953 for (i = 0;i < 3;i++)
955 while (*s && *s <= ' ')
959 val->vector[i] = atof(s);
968 while (*s && *s <= ' ')
971 if (i >= prog->limit_edicts)
972 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);
973 while (i >= prog->max_edicts)
974 PRVM_MEM_IncreaseEdicts();
975 // if IncreaseEdicts was called the base pointer needs to be updated
977 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
978 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
982 def = PRVM_ED_FindField(s);
985 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
988 val->_int = def->ofs;
992 func = PRVM_ED_FindFunction(s);
995 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
998 val->function = func - prog->functions;
1002 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1012 Console command to send a string to QC function GameCommand of the
1016 sv_cmd adminmsg 3 "do not teamkill"
1017 cl_cmd someclientcommand
1018 menu_cmd somemenucommand
1020 All progs can support this extension; sg calls it in server QC, cg in client
1024 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1028 Con_Printf("%s text...\n", whichcmd);
1033 if(!PRVM_SetProgFromString(whichprogs))
1034 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1035 // also, it makes printing error messages easier!
1037 Con_Printf("%s program not loaded.\n", whichprogs);
1041 if(!prog->funcoffsets.GameCommand)
1043 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1047 int restorevm_tempstringsbuf_cursize;
1052 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1053 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1054 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1055 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1060 void PRVM_GameCommand_Server_f(void)
1062 PRVM_GameCommand("server", "sv_cmd");
1064 void PRVM_GameCommand_Client_f(void)
1066 PRVM_GameCommand("client", "cl_cmd");
1068 void PRVM_GameCommand_Menu_f(void)
1070 PRVM_GameCommand("menu", "menu_cmd");
1077 Console command to set a field of a specified edict
1080 void PRVM_ED_EdictSet_f(void)
1087 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1092 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1094 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1098 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1100 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1101 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1103 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4));
1109 ====================
1112 Parses an edict out of the given string, returning the new position
1113 ed should be a properly initialized empty edict.
1114 Used for initial level load and for savegames.
1115 ====================
1117 extern cvar_t developer_entityparsing;
1118 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1128 // go through all the dictionary pairs
1132 if (!COM_ParseToken_Simple(&data, false, false))
1133 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1134 if (developer_entityparsing.integer)
1135 Con_Printf("Key: \"%s\"", com_token);
1136 if (com_token[0] == '}')
1139 // anglehack is to allow QuakeEd to write single scalar angles
1140 // and allow them to be turned into vectors. (FIXME...)
1141 if (!strcmp(com_token, "angle"))
1143 strlcpy (com_token, "angles", sizeof(com_token));
1149 // FIXME: change light to _light to get rid of this hack
1150 if (!strcmp(com_token, "light"))
1151 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1153 strlcpy (keyname, com_token, sizeof(keyname));
1155 // another hack to fix keynames with trailing spaces
1156 n = strlen(keyname);
1157 while (n && keyname[n-1] == ' ')
1164 if (!COM_ParseToken_Simple(&data, false, true))
1165 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1166 if (developer_entityparsing.integer)
1167 Con_Printf(" \"%s\"\n", com_token);
1169 if (com_token[0] == '}')
1170 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1174 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1178 // keynames with a leading underscore are used for utility comments,
1179 // and are immediately discarded by quake
1180 if (keyname[0] == '_')
1183 key = PRVM_ED_FindField (keyname);
1186 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1193 strlcpy (temp, com_token, sizeof(temp));
1194 sprintf (com_token, "0 %s 0", temp);
1197 if (!PRVM_ED_ParseEpair(ent, key, com_token))
1198 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1202 ent->priv.required->free = true;
1210 PRVM_ED_LoadFromFile
1212 The entities are directly placed in the array, rather than allocated with
1213 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1214 number references out of order.
1216 Creates a server's entity / program execution context by
1217 parsing textual entity definitions out of an ent file.
1219 Used for both fresh maps and savegame loads. A fresh map would also need
1220 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1223 void PRVM_ED_LoadFromFile (const char *data)
1226 int parsed, inhibited, spawned, died;
1238 // parse the opening brace
1239 if (!COM_ParseToken_Simple(&data, false, false))
1241 if (com_token[0] != '{')
1242 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1244 // CHANGED: this is not conform to PR_LoadFromFile
1245 if(prog->loadintoworld)
1247 prog->loadintoworld = false;
1248 ent = PRVM_EDICT_NUM(0);
1251 ent = PRVM_ED_Alloc();
1254 if (ent != prog->edicts) // hack
1255 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1257 data = PRVM_ED_ParseEdict (data, ent);
1260 // remove the entity ?
1261 if(prog->load_edict && !prog->load_edict(ent))
1269 // immediately call spawn function, but only if there is a self global and a classname
1271 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1273 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1276 Con_Print("No classname for:\n");
1277 PRVM_ED_Print(ent, NULL);
1282 // look for the spawn function
1283 func = PRVM_ED_FindFunction (PRVM_GetString(handle));
1287 // check for OnEntityNoSpawnFunction
1288 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1291 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1292 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1296 if (developer.integer) // don't confuse non-developers with errors
1298 Con_Print("No spawn function for:\n");
1299 PRVM_ED_Print(ent, NULL);
1308 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1309 PRVM_ExecuteProgram (func - prog->functions, "");
1314 if (ent->priv.required->free)
1318 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);
1321 void PRVM_FindOffsets(void)
1323 // field and global searches use -1 for NULL
1324 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1325 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1326 // functions use 0 for NULL
1327 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1329 // server and client qc use a lot of similar fields, so this is combined
1330 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1331 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1332 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1333 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1334 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1335 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1336 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1337 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1338 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1339 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1340 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1341 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1342 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1343 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1344 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1345 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1346 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1347 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1348 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1349 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1350 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1351 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1352 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1353 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1354 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1355 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1356 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1357 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1358 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1359 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1360 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1361 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1362 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1363 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1364 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1365 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1366 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1367 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1368 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1369 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1370 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1371 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1372 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1373 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1374 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1375 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1376 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1377 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1378 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1379 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1380 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1381 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1382 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1383 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1384 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1385 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1386 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1387 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1388 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1389 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1390 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1391 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1392 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1393 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1394 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1395 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1396 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1397 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1398 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1399 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1400 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1401 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1402 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1403 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1404 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1405 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1406 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1407 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1408 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1409 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1410 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1411 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1412 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1413 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1414 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1415 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1416 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1417 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1418 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1419 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1420 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1421 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1422 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1423 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1424 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1425 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1426 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1427 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1428 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1429 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1430 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1431 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1432 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1433 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1434 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1435 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1436 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1437 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1438 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1439 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1440 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1441 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1442 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1443 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1444 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1445 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1446 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1447 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1448 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1449 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1450 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1451 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1452 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1453 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1454 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1455 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1456 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1457 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1458 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1459 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1460 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1461 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1462 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1463 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1465 // menu qc only uses some functions, nothing else
1466 prog->funcoffsets.m_display = PRVM_ED_FindFunctionOffset("m_display");
1467 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1468 prog->funcoffsets.m_hide = PRVM_ED_FindFunctionOffset("m_hide");
1469 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1470 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1471 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1472 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1473 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1478 typedef struct dpfield_s
1485 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1487 dpfield_t dpfields[] =
1498 void PRVM_ResetProg()
1500 PRVM_GCALL(reset_cmd)();
1501 Mem_FreePool(&prog->progs_mempool);
1502 memset(prog,0,sizeof(prvm_prog_t));
1510 void PRVM_LoadLNO( const char *progname ) {
1511 fs_offset_t filesize;
1513 unsigned int *header;
1516 FS_StripExtension( progname, filename, sizeof( filename ) );
1517 strlcat( filename, ".lno", sizeof( filename ) );
1519 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1525 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1526 <Spike> SafeWrite (h, &version, sizeof(int));
1527 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1528 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1529 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1530 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1531 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1533 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1538 header = (unsigned int *) lno;
1539 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1540 LittleLong( header[ 1 ] ) == 1 &&
1541 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1542 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1543 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1544 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1546 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1547 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1557 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1561 ddef_t *infielddefs;
1562 dfunction_t *dfunctions;
1563 fs_offset_t filesize;
1565 if( prog->loaded ) {
1566 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1569 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1570 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1571 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1573 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1575 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1577 // byte swap the header
1578 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1579 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1581 if (prog->progs->version != PROG_VERSION)
1582 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1583 if (prog->progs->crc != prog->headercrc)
1584 PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
1586 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1587 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1589 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1590 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1591 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1592 prog->stringssize = prog->progs->numstrings;
1594 prog->numknownstrings = 0;
1595 prog->maxknownstrings = 0;
1596 prog->knownstrings = NULL;
1597 prog->knownstrings_freeable = NULL;
1599 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1601 // we need to expand the fielddefs list to include all the engine fields,
1602 // so allocate a new place for it
1603 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1605 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1607 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1609 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1611 // moved edict_size calculation down below field adding code
1613 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1614 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1616 // byte swap the lumps
1617 for (i=0 ; i<prog->progs->numstatements ; i++)
1619 prog->statements[i].op = LittleShort(prog->statements[i].op);
1620 prog->statements[i].a = LittleShort(prog->statements[i].a);
1621 prog->statements[i].b = LittleShort(prog->statements[i].b);
1622 prog->statements[i].c = LittleShort(prog->statements[i].c);
1625 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1626 for (i = 0;i < prog->progs->numfunctions;i++)
1628 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1629 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1630 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1631 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1632 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1633 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1634 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1637 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1639 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1640 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1641 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1644 // copy the progs fields to the new fields list
1645 for (i = 0;i < prog->progs->numfielddefs;i++)
1647 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1648 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1649 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1650 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1651 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1654 // append the required fields
1655 for (i = 0;i < (int) numrequiredfields;i++)
1657 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1658 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1659 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1660 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1661 prog->progs->entityfields += 3;
1663 prog->progs->entityfields++;
1664 prog->progs->numfielddefs++;
1667 // check required functions
1668 for(i=0 ; i < numrequiredfunc ; i++)
1669 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1670 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1672 // check required globals
1673 for(i=0 ; i < numrequiredglobals ; i++)
1674 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1675 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1677 for (i=0 ; i<prog->progs->numglobals ; i++)
1678 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1680 // moved edict_size calculation down here, below field adding code
1681 // LordHavoc: this no longer includes the prvm_edict_t header
1682 prog->edict_size = prog->progs->entityfields * 4;
1683 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1685 // LordHavoc: bounds check anything static
1686 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1692 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1693 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1696 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1697 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1699 // global global global
1734 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1735 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1737 // global none global
1743 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1744 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1760 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1761 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1775 if ((unsigned short) st->a >= prog->progs->numglobals)
1776 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1779 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1784 PRVM_LoadLNO(filename);
1788 prog->loaded = TRUE;
1790 // set flags & ddef_ts in prog
1796 PRVM_GCALL(init_cmd)();
1803 void PRVM_Fields_f (void)
1805 int i, j, ednum, used, usedamount;
1807 char tempstring[MAX_INPUTLINE], tempstring2[260];
1817 Con_Print("no progs loaded\n");
1824 Con_Print("prvm_fields <program name>\n");
1829 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1832 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1833 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1835 ed = PRVM_EDICT_NUM(ednum);
1836 if (ed->priv.required->free)
1838 for (i = 1;i < prog->progs->numfielddefs;i++)
1840 d = &prog->fielddefs[i];
1841 name = PRVM_GetString(d->s_name);
1842 if (name[strlen(name)-2] == '_')
1843 continue; // skip _x, _y, _z vars
1844 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1845 // if the value is still all 0, skip the field
1846 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1859 for (i = 0;i < prog->progs->numfielddefs;i++)
1861 d = &prog->fielddefs[i];
1862 name = PRVM_GetString(d->s_name);
1863 if (name[strlen(name)-2] == '_')
1864 continue; // skip _x, _y, _z vars
1865 switch(d->type & ~DEF_SAVEGLOBAL)
1868 strlcat(tempstring, "string ", sizeof(tempstring));
1871 strlcat(tempstring, "entity ", sizeof(tempstring));
1874 strlcat(tempstring, "function ", sizeof(tempstring));
1877 strlcat(tempstring, "field ", sizeof(tempstring));
1880 strlcat(tempstring, "void ", sizeof(tempstring));
1883 strlcat(tempstring, "float ", sizeof(tempstring));
1886 strlcat(tempstring, "vector ", sizeof(tempstring));
1889 strlcat(tempstring, "pointer ", sizeof(tempstring));
1892 sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1893 strlcat(tempstring, tempstring2, sizeof(tempstring));
1896 if (strlen(name) > sizeof(tempstring2)-4)
1898 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1899 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1900 tempstring2[sizeof(tempstring2)-1] = 0;
1903 strlcat(tempstring, name, sizeof(tempstring));
1904 for (j = (int)strlen(name);j < 25;j++)
1905 strlcat(tempstring, " ", sizeof(tempstring));
1906 sprintf(tempstring2, "%5d", counts[i]);
1907 strlcat(tempstring, tempstring2, sizeof(tempstring));
1908 strlcat(tempstring, "\n", sizeof(tempstring));
1909 if (strlen(tempstring) >= sizeof(tempstring)/2)
1911 Con_Print(tempstring);
1917 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1921 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);
1926 void PRVM_Globals_f (void)
1929 const char *wildcard;
1935 Con_Print("no progs loaded\n");
1938 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1940 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
1945 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1948 if( Cmd_Argc() == 3)
1949 wildcard = Cmd_Argv(2);
1953 Con_Printf("%s :", PRVM_NAME);
1955 for (i = 0;i < prog->progs->numglobaldefs;i++)
1958 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
1963 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
1965 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
1975 void PRVM_Global_f(void)
1978 if( Cmd_Argc() != 3 ) {
1979 Con_Printf( "prvm_global <program name> <global name>\n" );
1984 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1987 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1989 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1991 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2000 void PRVM_GlobalSet_f(void)
2003 if( Cmd_Argc() != 4 ) {
2004 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2009 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2012 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2014 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2016 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) );
2025 void PRVM_Init (void)
2027 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2028 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2029 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2030 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2031 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)");
2032 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2033 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2034 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2035 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)");
2036 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2037 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2038 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2039 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2040 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2041 Cvar_RegisterVariable (&prvm_boundscheck);
2042 Cvar_RegisterVariable (&prvm_traceqc);
2043 Cvar_RegisterVariable (&prvm_statementprofiling);
2053 void PRVM_InitProg(int prognr)
2055 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2056 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2058 prog = &prog_list[prognr];
2063 memset(prog, 0, sizeof(prvm_prog_t));
2065 prog->error_cmd = Host_Error;
2068 int PRVM_GetProgNr()
2070 return prog - prog_list;
2073 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2075 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2078 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2080 _Mem_Free(buffer, filename, fileline);
2083 void _PRVM_FreeAll(const char *filename, int fileline)
2086 prog->fielddefs = NULL;
2087 prog->functions = NULL;
2088 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2091 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2092 prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
2094 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2099 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2101 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2105 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2108 n = e - prog->edicts;
2109 if ((unsigned int)n >= prog->limit_edicts)
2110 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2114 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2116 // return e - prog->edicts;
2119 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2120 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2121 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2124 n = e - prog->edicts;
2125 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2126 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2127 return n;// EXPERIMENTAL
2128 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2130 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2132 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2133 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2134 return prog->edicts + n; // EXPERIMENTAL
2135 //return prog->edicts + ((n) / (progs->entityfields * 4));
2140 sizebuf_t vm_tempstringsbuf;
2142 const char *PRVM_GetString(int num)
2146 if (num < prog->stringssize)
2147 return prog->strings + num;
2150 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2152 num -= prog->stringssize;
2153 if (num < vm_tempstringsbuf.cursize)
2154 return (char *)vm_tempstringsbuf.data + num;
2157 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)", num, vm_tempstringsbuf.cursize);
2164 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)", num, prog->stringssize);
2174 // special range reserved for tempstrings
2176 if (num < vm_tempstringsbuf.cursize)
2177 return (char *)vm_tempstringsbuf.data + num;
2180 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)", num, vm_tempstringsbuf.cursize);
2186 if (num < prog->numknownstrings)
2188 if (!prog->knownstrings[num])
2189 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)", num);
2190 return prog->knownstrings[num];
2194 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)", num, prog->numknownstrings);
2200 int PRVM_SetEngineString(const char *s)
2205 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2206 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2207 // if it's in the tempstrings area, use a reserved range
2208 // (otherwise we'd get millions of useless string offsets cluttering the database)
2209 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2211 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2213 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2215 // see if it's a known string address
2216 for (i = 0;i < prog->numknownstrings;i++)
2217 if (prog->knownstrings[i] == s)
2219 // new unknown engine string
2220 if (developer.integer >= 200)
2221 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2222 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2223 if (!prog->knownstrings[i])
2225 if (i >= prog->numknownstrings)
2227 if (i >= prog->maxknownstrings)
2229 const char **oldstrings = prog->knownstrings;
2230 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2231 prog->maxknownstrings += 128;
2232 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2233 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2234 if (prog->numknownstrings)
2236 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2237 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2240 prog->numknownstrings++;
2242 prog->firstfreeknownstring = i + 1;
2243 prog->knownstrings[i] = s;
2247 // temp string handling
2249 // all tempstrings go into this buffer consecutively, and it is reset
2250 // whenever PRVM_ExecuteProgram returns to the engine
2251 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2252 // restores it on return, so multiple recursive calls can share the same
2254 // the buffer size is automatically grown as needed
2256 int PRVM_SetTempString(const char *s)
2262 size = (int)strlen(s) + 1;
2263 if (developer.integer >= 300)
2264 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2265 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2267 sizebuf_t old = vm_tempstringsbuf;
2268 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2269 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);
2270 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2271 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2272 vm_tempstringsbuf.maxsize *= 2;
2273 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2275 if (developer.integer >= 100)
2276 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2277 vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2279 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2284 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2286 vm_tempstringsbuf.cursize += size;
2287 return PRVM_SetEngineString(t);
2290 int PRVM_AllocString(size_t bufferlength, char **pointer)
2295 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2296 if (!prog->knownstrings[i])
2298 if (i >= prog->numknownstrings)
2300 if (i >= prog->maxknownstrings)
2302 const char **oldstrings = prog->knownstrings;
2303 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2304 prog->maxknownstrings += 128;
2305 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2306 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2307 if (prog->numknownstrings)
2309 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2310 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2313 prog->numknownstrings++;
2315 prog->firstfreeknownstring = i + 1;
2316 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2317 prog->knownstrings_freeable[i] = true;
2319 *pointer = (char *)(prog->knownstrings[i]);
2323 void PRVM_FreeString(int num)
2326 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2327 else if (num >= 0 && num < prog->stringssize)
2328 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2329 else if (num < 0 && num >= -prog->numknownstrings)
2332 if (!prog->knownstrings[num])
2333 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2334 if (!prog->knownstrings[num])
2335 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2336 PRVM_Free((char *)prog->knownstrings[num]);
2337 prog->knownstrings[num] = NULL;
2338 prog->knownstrings_freeable[num] = false;
2339 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2342 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);