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!)"};
39 //============================================================================
47 void PRVM_MEM_Alloc(void)
51 // reserve space for the null entity aka world
52 // check bound of max_edicts
53 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
54 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
56 // edictprivate_size has to be min as big prvm_edict_private_t
57 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
60 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
62 // alloc edict private space
63 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
66 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
69 for(i = 0; i < prog->max_edicts; i++)
71 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
72 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
78 PRVM_MEM_IncreaseEdicts
81 void PRVM_MEM_IncreaseEdicts(void)
84 int oldmaxedicts = prog->max_edicts;
85 void *oldedictsfields = prog->edictsfields;
86 void *oldedictprivate = prog->edictprivate;
88 if(prog->max_edicts >= prog->limit_edicts)
91 PRVM_GCALL(begin_increase_edicts)();
94 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
96 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
97 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
99 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
100 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
102 //set e and v pointers
103 for(i = 0; i < prog->max_edicts; i++)
105 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
106 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
109 PRVM_GCALL(end_increase_edicts)();
111 Mem_Free(oldedictsfields);
112 Mem_Free(oldedictprivate);
115 //============================================================================
118 int PRVM_ED_FindFieldOffset(const char *field)
121 d = PRVM_ED_FindField(field);
127 qboolean PRVM_ProgLoaded(int prognr)
129 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
132 return (prog_list[prognr].loaded ? TRUE : FALSE);
137 PRVM_SetProgFromString
140 // perhaps add a return value when the str doesnt exist
141 qboolean PRVM_SetProgFromString(const char *str)
144 for(; i < PRVM_MAXPROGS ; i++)
145 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
147 if(prog_list[i].loaded)
149 prog = &prog_list[i];
154 Con_Printf("%s not loaded !\n",PRVM_NAME);
159 Con_Printf("Invalid program name %s !\n", str);
168 void PRVM_SetProg(int prognr)
170 if(0 <= prognr && prognr < PRVM_MAXPROGS)
172 if(prog_list[prognr].loaded)
173 prog = &prog_list[prognr];
175 PRVM_ERROR("%i not loaded !", prognr);
178 PRVM_ERROR("Invalid program number %i", prognr);
185 Sets everything to NULL
188 void PRVM_ED_ClearEdict (prvm_edict_t *e)
190 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
191 e->priv.required->free = false;
193 // AK: Let the init_edict function determine if something needs to be initialized
194 PRVM_GCALL(init_edict)(e);
201 Either finds a free edict, or allocates a new one.
202 Try to avoid reusing an entity that was recently freed, because it
203 can cause the client to think the entity morphed into something else
204 instead of being removed and recreated, which can cause interpolated
205 angles and bad trails.
208 prvm_edict_t *PRVM_ED_Alloc (void)
213 // the client qc dont need maxclients
214 // thus it doesnt need to use svs.maxclients
215 // AK: changed i=svs.maxclients+1
216 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
217 // although the menu/client has no world
218 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
220 e = PRVM_EDICT_NUM(i);
221 // the first couple seconds of server time can involve a lot of
222 // freeing and allocating, so relax the replacement policy
223 if (e->priv.required->free && ( e->priv.required->freetime < 2 || (*prog->time - e->priv.required->freetime) > 0.5 ) )
225 PRVM_ED_ClearEdict (e);
230 if (i == prog->limit_edicts)
231 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
234 if (prog->num_edicts >= prog->max_edicts)
235 PRVM_MEM_IncreaseEdicts();
237 e = PRVM_EDICT_NUM(i);
238 PRVM_ED_ClearEdict (e);
247 Marks the edict as free
248 FIXME: walk all entities and NULL out references to this entity
251 void PRVM_ED_Free (prvm_edict_t *ed)
253 // dont delete the null entity (world) or reserved edicts
254 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
257 PRVM_GCALL(free_edict)(ed);
259 ed->priv.required->free = true;
260 ed->priv.required->freetime = *prog->time;
263 //===========================================================================
270 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
275 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
277 def = &prog->globaldefs[i];
289 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
294 for (i=0 ; i<prog->progs->numfielddefs ; i++)
296 def = &prog->fielddefs[i];
308 ddef_t *PRVM_ED_FindField (const char *name)
313 for (i=0 ; i<prog->progs->numfielddefs ; i++)
315 def = &prog->fielddefs[i];
316 if (!strcmp(PRVM_GetString(def->s_name), name))
327 ddef_t *PRVM_ED_FindGlobal (const char *name)
332 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
334 def = &prog->globaldefs[i];
335 if (!strcmp(PRVM_GetString(def->s_name), name))
347 mfunction_t *PRVM_ED_FindFunction (const char *name)
352 for (i=0 ; i<prog->progs->numfunctions ; i++)
354 func = &prog->functions[i];
355 if (!strcmp(PRVM_GetString(func->s_name), name))
366 Returns a string describing *data in a type specific manner
369 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
371 static char line[MAX_INPUTLINE];
376 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
381 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
385 if (n < 0 || n >= prog->limit_edicts)
386 sprintf (line, "entity %i (invalid!)", n);
388 sprintf (line, "entity %i", n);
391 f = prog->functions + val->function;
392 sprintf (line, "%s()", PRVM_GetString(f->s_name));
395 def = PRVM_ED_FieldAtOfs ( val->_int );
396 sprintf (line, ".%s", PRVM_GetString(def->s_name));
399 sprintf (line, "void");
402 // LordHavoc: changed from %5.1f to %10.4f
403 sprintf (line, "%10.4f", val->_float);
406 // LordHavoc: changed from %5.1f to %10.4f
407 sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
410 sprintf (line, "pointer");
413 sprintf (line, "bad type %i", type);
424 Returns a string describing *data in a type specific manner
425 Easier to parse than PR_ValueString
428 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
430 static char line[MAX_INPUTLINE];
436 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
441 // Parse the string a bit to turn special characters
442 // (like newline, specifically) into escape codes,
443 // this fixes saving games from various mods
444 s = PRVM_GetString (val->string);
445 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
464 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
467 f = prog->functions + val->function;
468 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
471 def = PRVM_ED_FieldAtOfs ( val->_int );
472 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
475 dpsnprintf (line, sizeof (line), "void");
478 dpsnprintf (line, sizeof (line), "%f", val->_float);
481 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
484 dpsnprintf (line, sizeof (line), "bad type %i", type);
495 Returns a string with a description and the contents of a global,
496 padded to 20 field width
499 char *PRVM_GlobalString (int ofs)
505 static char line[128];
507 val = (void *)&prog->globals.generic[ofs];
508 def = PRVM_ED_GlobalAtOfs(ofs);
510 sprintf (line,"%i(?)", ofs);
513 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
514 sprintf (line,"%i(%s)%s", ofs, PRVM_GetString(def->s_name), s);
525 char *PRVM_GlobalStringNoContents (int ofs)
529 static char line[128];
531 def = PRVM_ED_GlobalAtOfs(ofs);
533 sprintf (line,"%i(?)", ofs);
535 sprintf (line,"%i(%s)", ofs, PRVM_GetString(def->s_name));
553 // LordHavoc: optimized this to print out much more quickly (tempstring)
554 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
555 void PRVM_ED_Print(prvm_edict_t *ed)
563 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
565 if (ed->priv.required->free)
567 Con_Printf("%s: FREE\n",PRVM_NAME);
572 sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
573 for (i=1 ; i<prog->progs->numfielddefs ; i++)
575 d = &prog->fielddefs[i];
576 name = PRVM_GetString(d->s_name);
577 if (name[strlen(name)-2] == '_')
578 continue; // skip _x, _y, _z vars
580 v = (int *)((char *)ed->fields.vp + d->ofs*4);
582 // if the value is still all 0, skip the field
583 type = d->type & ~DEF_SAVEGLOBAL;
585 for (j=0 ; j<prvm_type_size[type] ; j++)
588 if (j == prvm_type_size[type])
591 if (strlen(name) > sizeof(tempstring2)-4)
593 memcpy (tempstring2, name, sizeof(tempstring2)-4);
594 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
595 tempstring2[sizeof(tempstring2)-1] = 0;
598 strcat(tempstring, name);
599 for (l = strlen(name);l < 14;l++)
600 strcat(tempstring, " ");
601 strcat(tempstring, " ");
603 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
604 if (strlen(name) > sizeof(tempstring2)-4)
606 memcpy (tempstring2, name, sizeof(tempstring2)-4);
607 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
608 tempstring2[sizeof(tempstring2)-1] = 0;
611 strcat(tempstring, name);
612 strcat(tempstring, "\n");
613 if (strlen(tempstring) >= sizeof(tempstring)/2)
615 Con_Print(tempstring);
620 Con_Print(tempstring);
630 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
640 if (ed->priv.required->free)
646 for (i=1 ; i<prog->progs->numfielddefs ; i++)
648 d = &prog->fielddefs[i];
649 name = PRVM_GetString(d->s_name);
650 if (name[strlen(name)-2] == '_')
651 continue; // skip _x, _y, _z vars
653 v = (int *)((char *)ed->fields.vp + d->ofs*4);
655 // if the value is still all 0, skip the field
656 type = d->type & ~DEF_SAVEGLOBAL;
657 for (j=0 ; j<prvm_type_size[type] ; j++)
660 if (j == prvm_type_size[type])
663 FS_Printf(f,"\"%s\" ",name);
664 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
670 void PRVM_ED_PrintNum (int ent)
672 PRVM_ED_Print(PRVM_EDICT_NUM(ent));
677 PRVM_ED_PrintEdicts_f
679 For debugging, prints all the entities in the current server
682 void PRVM_ED_PrintEdicts_f (void)
688 Con_Print("prvm_edicts <program name>\n");
693 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
696 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
697 for (i=0 ; i<prog->num_edicts ; i++)
698 PRVM_ED_PrintNum (i);
707 For debugging, prints a single edict
710 void PRVM_ED_PrintEdict_f (void)
716 Con_Print("prvm_edict <program name> <edict number>\n");
721 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
724 i = atoi (Cmd_Argv(2));
725 if (i >= prog->num_edicts)
727 Con_Print("Bad edict number\n");
731 PRVM_ED_PrintNum (i);
743 // 2 possibilities : 1. just displaying the active edict count
744 // 2. making a function pointer [x]
745 void PRVM_ED_Count_f (void)
753 Con_Print("prvm_count <program name>\n");
758 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
761 if(prog->count_edicts)
762 prog->count_edicts();
766 for (i=0 ; i<prog->num_edicts ; i++)
768 ent = PRVM_EDICT_NUM(i);
769 if (ent->priv.required->free)
774 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
775 Con_Printf("active :%3i\n", active);
782 ==============================================================================
786 FIXME: need to tag constants, doesn't really work
787 ==============================================================================
795 void PRVM_ED_WriteGlobals (qfile_t *f)
803 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
805 def = &prog->globaldefs[i];
807 if ( !(def->type & DEF_SAVEGLOBAL) )
809 type &= ~DEF_SAVEGLOBAL;
811 if (type != ev_string && type != ev_float && type != ev_entity)
814 name = PRVM_GetString(def->s_name);
815 FS_Printf(f,"\"%s\" ", name);
816 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
826 void PRVM_ED_ParseGlobals (const char *data)
828 char keyname[MAX_INPUTLINE];
834 if (!COM_ParseToken(&data, false))
835 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
836 if (com_token[0] == '}')
839 strcpy (keyname, com_token);
842 if (!COM_ParseToken(&data, false))
843 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
845 if (com_token[0] == '}')
846 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
848 key = PRVM_ED_FindGlobal (keyname);
851 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
855 if (!PRVM_ED_ParseEpair(NULL, key, com_token))
856 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
860 //============================================================================
867 Can parse either fields or globals
868 returns false if error
871 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
880 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
882 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
883 switch (key->type & ~DEF_SAVEGLOBAL)
886 l = (int)strlen(s) + 1;
887 val->string = PRVM_AllocString(l, &new_p);
888 for (i = 0;i < l;i++)
890 if (s[i] == '\\' && i < l-1)
895 else if (s[i] == 'r')
906 while (*s && *s <= ' ')
908 val->_float = atof(s);
912 for (i = 0;i < 3;i++)
914 while (*s && *s <= ' ')
918 val->vector[i] = atof(s);
927 while (*s && *s <= ' ')
930 if (i >= prog->limit_edicts)
931 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);
932 while (i >= prog->max_edicts)
933 PRVM_MEM_IncreaseEdicts();
934 //SV_IncreaseEdicts();
935 // if SV_IncreaseEdicts was called the base pointer needs to be updated
937 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
938 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
942 def = PRVM_ED_FindField(s);
945 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
948 val->_int = def->ofs;
952 func = PRVM_ED_FindFunction(s);
955 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
958 val->function = func - prog->functions;
962 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
972 Console command to set a field of a specified edict
975 void PRVM_ED_EdictSet_f(void)
982 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
987 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
989 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
993 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
995 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
996 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
998 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4));
1004 ====================
1007 Parses an edict out of the given string, returning the new position
1008 ed should be a properly initialized empty edict.
1009 Used for initial level load and for savegames.
1010 ====================
1012 extern cvar_t developer_entityparsing;
1013 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1023 // go through all the dictionary pairs
1027 if (!COM_ParseToken(&data, false))
1028 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1029 if (developer_entityparsing.integer)
1030 Con_Printf("Key: \"%s\"", com_token);
1031 if (com_token[0] == '}')
1034 // anglehack is to allow QuakeEd to write single scalar angles
1035 // and allow them to be turned into vectors. (FIXME...)
1036 if (!strcmp(com_token, "angle"))
1038 strcpy (com_token, "angles");
1044 // FIXME: change light to _light to get rid of this hack
1045 if (!strcmp(com_token, "light"))
1046 strcpy (com_token, "light_lev"); // hack for single light def
1048 strcpy (keyname, com_token);
1050 // another hack to fix keynames with trailing spaces
1051 n = strlen(keyname);
1052 while (n && keyname[n-1] == ' ')
1059 if (!COM_ParseToken(&data, false))
1060 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1061 if (developer_entityparsing.integer)
1062 Con_Printf(" \"%s\"\n", com_token);
1064 if (com_token[0] == '}')
1065 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1069 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1073 // keynames with a leading underscore are used for utility comments,
1074 // and are immediately discarded by quake
1075 if (keyname[0] == '_')
1078 key = PRVM_ED_FindField (keyname);
1081 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1088 strcpy (temp, com_token);
1089 sprintf (com_token, "0 %s 0", temp);
1092 if (!PRVM_ED_ParseEpair(ent, key, com_token))
1093 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1097 ent->priv.required->free = true;
1105 PRVM_ED_LoadFromFile
1107 The entities are directly placed in the array, rather than allocated with
1108 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1109 number references out of order.
1111 Creates a server's entity / program execution context by
1112 parsing textual entity definitions out of an ent file.
1114 Used for both fresh maps and savegame loads. A fresh map would also need
1115 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1118 void PRVM_ED_LoadFromFile (const char *data)
1121 int parsed, inhibited, spawned, died;
1133 // parse the opening brace
1134 if (!COM_ParseToken(&data, false))
1136 if (com_token[0] != '{')
1137 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1139 // CHANGED: this is not conform to PR_LoadFromFile
1140 if(prog->loadintoworld)
1142 prog->loadintoworld = false;
1143 ent = PRVM_EDICT_NUM(0);
1146 ent = PRVM_ED_Alloc();
1149 if (ent != prog->edicts) // hack
1150 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1152 data = PRVM_ED_ParseEdict (data, ent);
1155 // remove the entity ?
1156 if(prog->load_edict && !prog->load_edict(ent))
1164 // immediately call spawn function, but only if there is a self global and a classname
1166 if(prog->self && prog->flag & PRVM_FE_CLASSNAME)
1168 string_t handle = *(string_t*)&((unsigned char*)ent->fields.vp)[PRVM_ED_FindFieldOffset("classname")];
1171 Con_Print("No classname for:\n");
1177 // look for the spawn function
1178 func = PRVM_ED_FindFunction (PRVM_GetString(handle));
1182 if (developer.integer) // don't confuse non-developers with errors
1184 Con_Print("No spawn function for:\n");
1192 PRVM_G_INT(prog->self->ofs) = PRVM_EDICT_TO_PROG(ent);
1193 PRVM_ExecuteProgram (func - prog->functions, "");
1197 if (ent->priv.required->free)
1201 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);
1206 typedef struct dpfield_s
1213 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1215 dpfield_t dpfields[] =
1226 void PRVM_ResetProg()
1228 PRVM_GCALL(reset_cmd)();
1229 Mem_FreePool(&prog->progs_mempool);
1230 memset(prog,0,sizeof(prvm_prog_t));
1238 void PRVM_LoadLNO( const char *progname ) {
1239 fs_offset_t filesize;
1241 unsigned int *header;
1244 FS_StripExtension( progname, filename, sizeof( filename ) );
1245 strlcat( filename, ".lno", sizeof( filename ) );
1247 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1253 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1254 <Spike> SafeWrite (h, &version, sizeof(int));
1255 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1256 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1257 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1258 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1259 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1261 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1266 header = (unsigned int *) lno;
1267 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1268 LittleLong( header[ 1 ] ) == 1 &&
1269 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1270 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1271 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1272 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1274 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1275 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1285 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field)
1289 ddef_t *infielddefs;
1290 dfunction_t *dfunctions;
1291 fs_offset_t filesize;
1293 if( prog->loaded ) {
1294 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1297 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1298 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1299 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1301 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, filesize/1024);
1303 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1305 // byte swap the header
1306 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1307 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1309 if (prog->progs->version != PROG_VERSION)
1310 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1311 if (prog->progs->crc != prog->headercrc)
1312 PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
1314 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1315 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1317 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1318 prog->stringssize = 0;
1319 for (i = 0;i < prog->progs->numstrings;i++)
1321 if (prog->progs->ofs_strings + prog->stringssize >= (int)filesize)
1322 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1323 prog->stringssize += (int)strlen (prog->strings + prog->stringssize) + 1;
1325 prog->numknownstrings = 0;
1326 prog->maxknownstrings = 0;
1327 prog->knownstrings = NULL;
1328 prog->knownstrings_freeable = NULL;
1330 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1332 // we need to expand the fielddefs list to include all the engine fields,
1333 // so allocate a new place for it
1334 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1336 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1338 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1340 // moved edict_size calculation down below field adding code
1342 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1343 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1345 // byte swap the lumps
1346 for (i=0 ; i<prog->progs->numstatements ; i++)
1348 prog->statements[i].op = LittleShort(prog->statements[i].op);
1349 prog->statements[i].a = LittleShort(prog->statements[i].a);
1350 prog->statements[i].b = LittleShort(prog->statements[i].b);
1351 prog->statements[i].c = LittleShort(prog->statements[i].c);
1354 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1355 for (i = 0;i < prog->progs->numfunctions;i++)
1357 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1358 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1359 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1360 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1361 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1362 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1363 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1366 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1368 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1369 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1370 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1373 // copy the progs fields to the new fields list
1374 for (i = 0;i < prog->progs->numfielddefs;i++)
1376 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1377 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1378 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1379 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1380 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1383 // append the required fields
1384 for (i = 0;i < (int) numrequiredfields;i++)
1386 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1387 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1388 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1389 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1390 prog->progs->entityfields += 3;
1392 prog->progs->entityfields++;
1393 prog->progs->numfielddefs++;
1396 // check required functions
1397 for(i=0 ; i < numrequiredfunc ; i++)
1398 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1399 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1401 for (i=0 ; i<prog->progs->numglobals ; i++)
1402 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1404 // moved edict_size calculation down here, below field adding code
1405 // LordHavoc: this no longer includes the prvm_edict_t header
1406 prog->edict_size = prog->progs->entityfields * 4;
1407 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1409 // LordHavoc: bounds check anything static
1410 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1416 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1417 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1420 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1421 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1423 // global global global
1458 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1459 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1461 // global none global
1467 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1468 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1484 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1485 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1499 if ((unsigned short) st->a >= prog->progs->numglobals)
1500 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1503 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s", st->op, i, PRVM_NAME);
1508 PRVM_LoadLNO(filename);
1512 prog->loaded = TRUE;
1514 // set flags & ddef_ts in prog
1518 prog->self = PRVM_ED_FindGlobal("self");
1520 if( PRVM_ED_FindGlobal("time") && PRVM_ED_FindGlobal("time")->type & ev_float )
1521 prog->time = &PRVM_G_FLOAT(PRVM_ED_FindGlobal("time")->ofs);
1523 if(PRVM_ED_FindField ("chain"))
1524 prog->flag |= PRVM_FE_CHAIN;
1526 if(PRVM_ED_FindField ("classname"))
1527 prog->flag |= PRVM_FE_CLASSNAME;
1529 if(PRVM_ED_FindField ("nextthink") && PRVM_ED_FindField ("frame") && PRVM_ED_FindField ("think")
1530 && prog->flag && prog->self)
1531 prog->flag |= PRVM_OP_STATE;
1533 PRVM_GCALL(init_cmd)();
1540 void PRVM_Fields_f (void)
1542 int i, j, ednum, used, usedamount;
1544 char tempstring[MAX_INPUTLINE], tempstring2[260];
1554 Con_Print("no progs loaded\n");
1561 Con_Print("prvm_fields <program name>\n");
1566 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1569 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1570 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1572 ed = PRVM_EDICT_NUM(ednum);
1573 if (ed->priv.required->free)
1575 for (i = 1;i < prog->progs->numfielddefs;i++)
1577 d = &prog->fielddefs[i];
1578 name = PRVM_GetString(d->s_name);
1579 if (name[strlen(name)-2] == '_')
1580 continue; // skip _x, _y, _z vars
1581 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1582 // if the value is still all 0, skip the field
1583 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1596 for (i = 0;i < prog->progs->numfielddefs;i++)
1598 d = &prog->fielddefs[i];
1599 name = PRVM_GetString(d->s_name);
1600 if (name[strlen(name)-2] == '_')
1601 continue; // skip _x, _y, _z vars
1602 switch(d->type & ~DEF_SAVEGLOBAL)
1605 strcat(tempstring, "string ");
1608 strcat(tempstring, "entity ");
1611 strcat(tempstring, "function ");
1614 strcat(tempstring, "field ");
1617 strcat(tempstring, "void ");
1620 strcat(tempstring, "float ");
1623 strcat(tempstring, "vector ");
1626 strcat(tempstring, "pointer ");
1629 sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1630 strcat(tempstring, tempstring2);
1633 if (strlen(name) > sizeof(tempstring2)-4)
1635 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1636 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1637 tempstring2[sizeof(tempstring2)-1] = 0;
1640 strcat(tempstring, name);
1641 for (j = (int)strlen(name);j < 25;j++)
1642 strcat(tempstring, " ");
1643 sprintf(tempstring2, "%5d", counts[i]);
1644 strcat(tempstring, tempstring2);
1645 strcat(tempstring, "\n");
1646 if (strlen(tempstring) >= sizeof(tempstring)/2)
1648 Con_Print(tempstring);
1654 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1658 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);
1663 void PRVM_Globals_f (void)
1669 Con_Print("no progs loaded\n");
1672 if(Cmd_Argc () != 2)
1674 Con_Print("prvm_globals <program name>\n");
1679 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1682 Con_Printf("%s :", PRVM_NAME);
1684 for (i = 0;i < prog->progs->numglobaldefs;i++)
1685 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
1686 Con_Printf("%i global variables, totalling %i bytes\n", prog->progs->numglobals, prog->progs->numglobals * 4);
1696 void PRVM_Global_f(void)
1699 if( Cmd_Argc() != 3 ) {
1700 Con_Printf( "prvm_global <program name> <global name>\n" );
1705 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1708 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1710 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1712 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
1721 void PRVM_GlobalSet_f(void)
1724 if( Cmd_Argc() != 4 ) {
1725 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
1730 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1733 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1735 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1737 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) );
1746 void PRVM_Init (void)
1748 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
1749 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "set a property on an entity number in the selected VM (server, client, menu)");
1750 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
1751 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
1752 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)");
1753 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
1754 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
1755 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
1756 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)");
1757 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
1758 Cvar_RegisterVariable (&prvm_boundscheck);
1759 Cvar_RegisterVariable (&prvm_traceqc);
1769 void PRVM_InitProg(int prognr)
1771 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
1772 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
1774 prog = &prog_list[prognr];
1779 memset(prog, 0, sizeof(prvm_prog_t));
1781 prog->time = &prog->_time;
1782 prog->error_cmd = Host_Error;
1785 int PRVM_GetProgNr()
1787 return prog - prog_list;
1790 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
1792 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
1795 void _PRVM_Free(void *buffer, const char *filename, int fileline)
1797 _Mem_Free(buffer, filename, fileline);
1800 void _PRVM_FreeAll(const char *filename, int fileline)
1803 prog->fielddefs = NULL;
1804 prog->functions = NULL;
1805 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
1808 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
1809 prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
1811 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
1816 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
1818 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
1822 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
1825 n = e - prog->edicts;
1826 if ((unsigned int)n >= prog->limit_edicts)
1827 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
1831 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
1833 // return e - prog->edicts;
1836 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
1837 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
1838 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
1841 n = e - prog->edicts;
1842 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
1843 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
1844 return n;// EXPERIMENTAL
1845 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
1847 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
1849 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
1850 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
1851 return prog->edicts + n; // EXPERIMENTAL
1852 //return prog->edicts + ((n) / (progs->entityfields * 4));
1857 const char *PRVM_GetString(int num)
1859 if (num >= 0 && num < prog->stringssize)
1860 return prog->strings + num;
1861 else if (num < 0 && num >= -prog->numknownstrings)
1864 if (!prog->knownstrings[num])
1865 PRVM_ERROR("PRVM_GetString: attempt to get string that is already freed");
1866 return prog->knownstrings[num];
1870 PRVM_ERROR("PRVM_GetString: invalid string offset %i", num);
1875 int PRVM_SetEngineString(const char *s)
1880 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
1881 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
1882 for (i = 0;i < prog->numknownstrings;i++)
1883 if (prog->knownstrings[i] == s)
1885 // new unknown engine string
1886 if (developer.integer >= 3)
1887 Con_Printf("new engine string %p\n", s);
1888 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
1889 if (!prog->knownstrings[i])
1891 if (i >= prog->numknownstrings)
1893 if (i >= prog->maxknownstrings)
1895 const char **oldstrings = prog->knownstrings;
1896 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
1897 prog->maxknownstrings += 128;
1898 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
1899 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
1900 if (prog->numknownstrings)
1902 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
1903 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
1906 prog->numknownstrings++;
1908 prog->firstfreeknownstring = i + 1;
1909 prog->knownstrings[i] = s;
1913 int PRVM_AllocString(size_t bufferlength, char **pointer)
1918 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
1919 if (!prog->knownstrings[i])
1921 if (i >= prog->numknownstrings)
1923 if (i >= prog->maxknownstrings)
1925 const char **oldstrings = prog->knownstrings;
1926 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
1927 prog->maxknownstrings += 128;
1928 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
1929 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
1930 if (prog->numknownstrings)
1932 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
1933 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
1936 prog->numknownstrings++;
1938 prog->firstfreeknownstring = i + 1;
1939 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
1940 prog->knownstrings_freeable[i] = true;
1942 *pointer = (char *)(prog->knownstrings[i]);
1946 void PRVM_FreeString(int num)
1949 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
1950 else if (num >= 0 && num < prog->stringssize)
1951 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
1952 else if (num < 0 && num >= -prog->numknownstrings)
1955 if (!prog->knownstrings[num])
1956 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
1957 if (!prog->knownstrings[num])
1958 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
1959 PRVM_Free((char *)prog->knownstrings[num]);
1960 prog->knownstrings[num] = NULL;
1961 prog->knownstrings_freeable[num] = false;
1962 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
1965 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);