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 ddef_t* PRVM_ED_FindGlobal(const char *name);
128 int PRVM_ED_FindGlobalOffset(const char *global)
131 d = PRVM_ED_FindGlobal(global);
137 qboolean PRVM_ProgLoaded(int prognr)
139 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
142 return (prog_list[prognr].loaded ? TRUE : FALSE);
147 PRVM_SetProgFromString
150 // perhaps add a return value when the str doesnt exist
151 qboolean PRVM_SetProgFromString(const char *str)
154 for(; i < PRVM_MAXPROGS ; i++)
155 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
157 if(prog_list[i].loaded)
159 prog = &prog_list[i];
164 Con_Printf("%s not loaded !\n",PRVM_NAME);
169 Con_Printf("Invalid program name %s !\n", str);
178 void PRVM_SetProg(int prognr)
180 if(0 <= prognr && prognr < PRVM_MAXPROGS)
182 if(prog_list[prognr].loaded)
183 prog = &prog_list[prognr];
185 PRVM_ERROR("%i not loaded !", prognr);
188 PRVM_ERROR("Invalid program number %i", prognr);
195 Sets everything to NULL
198 void PRVM_ED_ClearEdict (prvm_edict_t *e)
200 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
201 e->priv.required->free = false;
203 // AK: Let the init_edict function determine if something needs to be initialized
204 PRVM_GCALL(init_edict)(e);
211 Either finds a free edict, or allocates a new one.
212 Try to avoid reusing an entity that was recently freed, because it
213 can cause the client to think the entity morphed into something else
214 instead of being removed and recreated, which can cause interpolated
215 angles and bad trails.
218 prvm_edict_t *PRVM_ED_Alloc (void)
223 // the client qc dont need maxclients
224 // thus it doesnt need to use svs.maxclients
225 // AK: changed i=svs.maxclients+1
226 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
227 // although the menu/client has no world
228 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
230 e = PRVM_EDICT_NUM(i);
231 // the first couple seconds of server time can involve a lot of
232 // freeing and allocating, so relax the replacement policy
233 if (e->priv.required->free && ( e->priv.required->freetime < 2 || (*prog->time - e->priv.required->freetime) > 0.5 ) )
235 PRVM_ED_ClearEdict (e);
240 if (i == prog->limit_edicts)
241 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
244 if (prog->num_edicts >= prog->max_edicts)
245 PRVM_MEM_IncreaseEdicts();
247 e = PRVM_EDICT_NUM(i);
248 PRVM_ED_ClearEdict (e);
257 Marks the edict as free
258 FIXME: walk all entities and NULL out references to this entity
261 void PRVM_ED_Free (prvm_edict_t *ed)
263 // dont delete the null entity (world) or reserved edicts
264 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
267 PRVM_GCALL(free_edict)(ed);
269 ed->priv.required->free = true;
270 ed->priv.required->freetime = *prog->time;
273 //===========================================================================
280 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
285 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
287 def = &prog->globaldefs[i];
299 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
304 for (i=0 ; i<prog->progs->numfielddefs ; i++)
306 def = &prog->fielddefs[i];
318 ddef_t *PRVM_ED_FindField (const char *name)
323 for (i=0 ; i<prog->progs->numfielddefs ; i++)
325 def = &prog->fielddefs[i];
326 if (!strcmp(PRVM_GetString(def->s_name), name))
337 ddef_t *PRVM_ED_FindGlobal (const char *name)
342 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
344 def = &prog->globaldefs[i];
345 if (!strcmp(PRVM_GetString(def->s_name), name))
357 mfunction_t *PRVM_ED_FindFunction (const char *name)
362 for (i=0 ; i<prog->progs->numfunctions ; i++)
364 func = &prog->functions[i];
365 if (!strcmp(PRVM_GetString(func->s_name), name))
376 Returns a string describing *data in a type specific manner
379 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
381 static char line[MAX_INPUTLINE];
386 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
391 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
395 if (n < 0 || n >= prog->limit_edicts)
396 sprintf (line, "entity %i (invalid!)", n);
398 sprintf (line, "entity %i", n);
401 f = prog->functions + val->function;
402 sprintf (line, "%s()", PRVM_GetString(f->s_name));
405 def = PRVM_ED_FieldAtOfs ( val->_int );
406 sprintf (line, ".%s", PRVM_GetString(def->s_name));
409 sprintf (line, "void");
412 // LordHavoc: changed from %5.1f to %10.4f
413 sprintf (line, "%10.4f", val->_float);
416 // LordHavoc: changed from %5.1f to %10.4f
417 sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
420 sprintf (line, "pointer");
423 sprintf (line, "bad type %i", type);
434 Returns a string describing *data in a type specific manner
435 Easier to parse than PR_ValueString
438 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
440 static char line[MAX_INPUTLINE];
446 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
451 // Parse the string a bit to turn special characters
452 // (like newline, specifically) into escape codes,
453 // this fixes saving games from various mods
454 s = PRVM_GetString (val->string);
455 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
474 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
477 f = prog->functions + val->function;
478 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
481 def = PRVM_ED_FieldAtOfs ( val->_int );
482 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
485 dpsnprintf (line, sizeof (line), "void");
488 dpsnprintf (line, sizeof (line), "%f", val->_float);
491 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
494 dpsnprintf (line, sizeof (line), "bad type %i", type);
505 Returns a string with a description and the contents of a global,
506 padded to 20 field width
509 char *PRVM_GlobalString (int ofs)
515 static char line[128];
517 val = (void *)&prog->globals.generic[ofs];
518 def = PRVM_ED_GlobalAtOfs(ofs);
520 sprintf (line,"%i(?)", ofs);
523 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
524 sprintf (line,"%i(%s)%s", ofs, PRVM_GetString(def->s_name), s);
535 char *PRVM_GlobalStringNoContents (int ofs)
539 static char line[128];
541 def = PRVM_ED_GlobalAtOfs(ofs);
543 sprintf (line,"%i(?)", ofs);
545 sprintf (line,"%i(%s)", ofs, PRVM_GetString(def->s_name));
563 // LordHavoc: optimized this to print out much more quickly (tempstring)
564 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
565 void PRVM_ED_Print(prvm_edict_t *ed)
573 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
575 if (ed->priv.required->free)
577 Con_Printf("%s: FREE\n",PRVM_NAME);
582 sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
583 for (i=1 ; i<prog->progs->numfielddefs ; i++)
585 d = &prog->fielddefs[i];
586 name = PRVM_GetString(d->s_name);
587 if (name[strlen(name)-2] == '_')
588 continue; // skip _x, _y, _z vars
590 v = (int *)((char *)ed->fields.vp + d->ofs*4);
592 // if the value is still all 0, skip the field
593 type = d->type & ~DEF_SAVEGLOBAL;
595 for (j=0 ; j<prvm_type_size[type] ; j++)
598 if (j == prvm_type_size[type])
601 if (strlen(name) > sizeof(tempstring2)-4)
603 memcpy (tempstring2, name, sizeof(tempstring2)-4);
604 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
605 tempstring2[sizeof(tempstring2)-1] = 0;
608 strcat(tempstring, name);
609 for (l = strlen(name);l < 14;l++)
610 strcat(tempstring, " ");
611 strcat(tempstring, " ");
613 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
614 if (strlen(name) > sizeof(tempstring2)-4)
616 memcpy (tempstring2, name, sizeof(tempstring2)-4);
617 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
618 tempstring2[sizeof(tempstring2)-1] = 0;
621 strcat(tempstring, name);
622 strcat(tempstring, "\n");
623 if (strlen(tempstring) >= sizeof(tempstring)/2)
625 Con_Print(tempstring);
630 Con_Print(tempstring);
640 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
650 if (ed->priv.required->free)
656 for (i=1 ; i<prog->progs->numfielddefs ; i++)
658 d = &prog->fielddefs[i];
659 name = PRVM_GetString(d->s_name);
660 if (name[strlen(name)-2] == '_')
661 continue; // skip _x, _y, _z vars
663 v = (int *)((char *)ed->fields.vp + d->ofs*4);
665 // if the value is still all 0, skip the field
666 type = d->type & ~DEF_SAVEGLOBAL;
667 for (j=0 ; j<prvm_type_size[type] ; j++)
670 if (j == prvm_type_size[type])
673 FS_Printf(f,"\"%s\" ",name);
674 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
680 void PRVM_ED_PrintNum (int ent)
682 PRVM_ED_Print(PRVM_EDICT_NUM(ent));
687 PRVM_ED_PrintEdicts_f
689 For debugging, prints all the entities in the current server
692 void PRVM_ED_PrintEdicts_f (void)
698 Con_Print("prvm_edicts <program name>\n");
703 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
706 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
707 for (i=0 ; i<prog->num_edicts ; i++)
708 PRVM_ED_PrintNum (i);
717 For debugging, prints a single edict
720 void PRVM_ED_PrintEdict_f (void)
726 Con_Print("prvm_edict <program name> <edict number>\n");
731 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
734 i = atoi (Cmd_Argv(2));
735 if (i >= prog->num_edicts)
737 Con_Print("Bad edict number\n");
741 PRVM_ED_PrintNum (i);
753 // 2 possibilities : 1. just displaying the active edict count
754 // 2. making a function pointer [x]
755 void PRVM_ED_Count_f (void)
763 Con_Print("prvm_count <program name>\n");
768 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
771 if(prog->count_edicts)
772 prog->count_edicts();
776 for (i=0 ; i<prog->num_edicts ; i++)
778 ent = PRVM_EDICT_NUM(i);
779 if (ent->priv.required->free)
784 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
785 Con_Printf("active :%3i\n", active);
792 ==============================================================================
796 FIXME: need to tag constants, doesn't really work
797 ==============================================================================
805 void PRVM_ED_WriteGlobals (qfile_t *f)
813 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
815 def = &prog->globaldefs[i];
817 if ( !(def->type & DEF_SAVEGLOBAL) )
819 type &= ~DEF_SAVEGLOBAL;
821 if (type != ev_string && type != ev_float && type != ev_entity)
824 name = PRVM_GetString(def->s_name);
825 FS_Printf(f,"\"%s\" ", name);
826 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
836 void PRVM_ED_ParseGlobals (const char *data)
838 char keyname[MAX_INPUTLINE];
844 if (!COM_ParseToken(&data, false))
845 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
846 if (com_token[0] == '}')
849 strcpy (keyname, com_token);
852 if (!COM_ParseToken(&data, false))
853 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
855 if (com_token[0] == '}')
856 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
858 key = PRVM_ED_FindGlobal (keyname);
861 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
865 if (!PRVM_ED_ParseEpair(NULL, key, com_token))
866 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
870 //============================================================================
877 Can parse either fields or globals
878 returns false if error
881 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
890 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
892 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
893 switch (key->type & ~DEF_SAVEGLOBAL)
896 l = (int)strlen(s) + 1;
897 val->string = PRVM_AllocString(l, &new_p);
898 for (i = 0;i < l;i++)
900 if (s[i] == '\\' && i < l-1)
905 else if (s[i] == 'r')
916 while (*s && *s <= ' ')
918 val->_float = atof(s);
922 for (i = 0;i < 3;i++)
924 while (*s && *s <= ' ')
928 val->vector[i] = atof(s);
937 while (*s && *s <= ' ')
940 if (i >= prog->limit_edicts)
941 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);
942 while (i >= prog->max_edicts)
943 PRVM_MEM_IncreaseEdicts();
944 //SV_IncreaseEdicts();
945 // if SV_IncreaseEdicts was called the base pointer needs to be updated
947 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
948 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
952 def = PRVM_ED_FindField(s);
955 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
958 val->_int = def->ofs;
962 func = PRVM_ED_FindFunction(s);
965 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
968 val->function = func - prog->functions;
972 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
982 Console command to set a field of a specified edict
985 void PRVM_ED_EdictSet_f(void)
992 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
997 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
999 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1003 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1005 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1006 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1008 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4));
1014 ====================
1017 Parses an edict out of the given string, returning the new position
1018 ed should be a properly initialized empty edict.
1019 Used for initial level load and for savegames.
1020 ====================
1022 extern cvar_t developer_entityparsing;
1023 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1033 // go through all the dictionary pairs
1037 if (!COM_ParseToken(&data, false))
1038 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1039 if (developer_entityparsing.integer)
1040 Con_Printf("Key: \"%s\"", com_token);
1041 if (com_token[0] == '}')
1044 // anglehack is to allow QuakeEd to write single scalar angles
1045 // and allow them to be turned into vectors. (FIXME...)
1046 if (!strcmp(com_token, "angle"))
1048 strcpy (com_token, "angles");
1054 // FIXME: change light to _light to get rid of this hack
1055 if (!strcmp(com_token, "light"))
1056 strcpy (com_token, "light_lev"); // hack for single light def
1058 strcpy (keyname, com_token);
1060 // another hack to fix keynames with trailing spaces
1061 n = strlen(keyname);
1062 while (n && keyname[n-1] == ' ')
1069 if (!COM_ParseToken(&data, false))
1070 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1071 if (developer_entityparsing.integer)
1072 Con_Printf(" \"%s\"\n", com_token);
1074 if (com_token[0] == '}')
1075 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1079 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1083 // keynames with a leading underscore are used for utility comments,
1084 // and are immediately discarded by quake
1085 if (keyname[0] == '_')
1088 key = PRVM_ED_FindField (keyname);
1091 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1098 strcpy (temp, com_token);
1099 sprintf (com_token, "0 %s 0", temp);
1102 if (!PRVM_ED_ParseEpair(ent, key, com_token))
1103 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1107 ent->priv.required->free = true;
1115 PRVM_ED_LoadFromFile
1117 The entities are directly placed in the array, rather than allocated with
1118 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1119 number references out of order.
1121 Creates a server's entity / program execution context by
1122 parsing textual entity definitions out of an ent file.
1124 Used for both fresh maps and savegame loads. A fresh map would also need
1125 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1128 void PRVM_ED_LoadFromFile (const char *data)
1131 int parsed, inhibited, spawned, died;
1143 // parse the opening brace
1144 if (!COM_ParseToken(&data, false))
1146 if (com_token[0] != '{')
1147 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1149 // CHANGED: this is not conform to PR_LoadFromFile
1150 if(prog->loadintoworld)
1152 prog->loadintoworld = false;
1153 ent = PRVM_EDICT_NUM(0);
1156 ent = PRVM_ED_Alloc();
1159 if (ent != prog->edicts) // hack
1160 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1162 data = PRVM_ED_ParseEdict (data, ent);
1165 // remove the entity ?
1166 if(prog->load_edict && !prog->load_edict(ent))
1174 // immediately call spawn function, but only if there is a self global and a classname
1176 if(prog->self && prog->flag & PRVM_FE_CLASSNAME)
1178 string_t handle = *(string_t*)&((unsigned char*)ent->fields.vp)[PRVM_ED_FindFieldOffset("classname")];
1181 Con_Print("No classname for:\n");
1187 // look for the spawn function
1188 func = PRVM_ED_FindFunction (PRVM_GetString(handle));
1192 if (developer.integer) // don't confuse non-developers with errors
1194 Con_Print("No spawn function for:\n");
1202 PRVM_G_INT(prog->self->ofs) = PRVM_EDICT_TO_PROG(ent);
1203 PRVM_ExecuteProgram (func - prog->functions, "");
1207 if (ent->priv.required->free)
1211 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);
1216 typedef struct dpfield_s
1223 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1225 dpfield_t dpfields[] =
1236 void PRVM_ResetProg()
1238 PRVM_GCALL(reset_cmd)();
1239 Mem_FreePool(&prog->progs_mempool);
1240 memset(prog,0,sizeof(prvm_prog_t));
1248 void PRVM_LoadLNO( const char *progname ) {
1249 fs_offset_t filesize;
1251 unsigned int *header;
1254 FS_StripExtension( progname, filename, sizeof( filename ) );
1255 strlcat( filename, ".lno", sizeof( filename ) );
1257 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1263 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1264 <Spike> SafeWrite (h, &version, sizeof(int));
1265 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1266 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1267 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1268 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1269 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1271 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1276 header = (unsigned int *) lno;
1277 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1278 LittleLong( header[ 1 ] ) == 1 &&
1279 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1280 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1281 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1282 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1284 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1285 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1295 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field)
1299 ddef_t *infielddefs;
1300 dfunction_t *dfunctions;
1301 fs_offset_t filesize;
1303 if( prog->loaded ) {
1304 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1307 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1308 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1309 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1311 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, filesize/1024);
1313 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1315 // byte swap the header
1316 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1317 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1319 if (prog->progs->version != PROG_VERSION)
1320 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1321 if (prog->progs->crc != prog->headercrc)
1322 PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
1324 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1325 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1327 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1328 prog->stringssize = 0;
1329 for (i = 0;i < prog->progs->numstrings;i++)
1331 if (prog->progs->ofs_strings + prog->stringssize >= (int)filesize)
1332 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1333 prog->stringssize += (int)strlen (prog->strings + prog->stringssize) + 1;
1335 prog->numknownstrings = 0;
1336 prog->maxknownstrings = 0;
1337 prog->knownstrings = NULL;
1338 prog->knownstrings_freeable = NULL;
1340 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1342 // we need to expand the fielddefs list to include all the engine fields,
1343 // so allocate a new place for it
1344 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1346 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1348 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1350 // moved edict_size calculation down below field adding code
1352 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1353 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1355 // byte swap the lumps
1356 for (i=0 ; i<prog->progs->numstatements ; i++)
1358 prog->statements[i].op = LittleShort(prog->statements[i].op);
1359 prog->statements[i].a = LittleShort(prog->statements[i].a);
1360 prog->statements[i].b = LittleShort(prog->statements[i].b);
1361 prog->statements[i].c = LittleShort(prog->statements[i].c);
1364 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1365 for (i = 0;i < prog->progs->numfunctions;i++)
1367 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1368 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1369 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1370 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1371 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1372 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1373 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1376 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1378 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1379 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1380 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1383 // copy the progs fields to the new fields list
1384 for (i = 0;i < prog->progs->numfielddefs;i++)
1386 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1387 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1388 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1389 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1390 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1393 // append the required fields
1394 for (i = 0;i < (int) numrequiredfields;i++)
1396 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1397 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1398 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1399 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1400 prog->progs->entityfields += 3;
1402 prog->progs->entityfields++;
1403 prog->progs->numfielddefs++;
1406 // check required functions
1407 for(i=0 ; i < numrequiredfunc ; i++)
1408 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1409 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1411 for (i=0 ; i<prog->progs->numglobals ; i++)
1412 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1414 // moved edict_size calculation down here, below field adding code
1415 // LordHavoc: this no longer includes the prvm_edict_t header
1416 prog->edict_size = prog->progs->entityfields * 4;
1417 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1419 // LordHavoc: bounds check anything static
1420 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1426 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1427 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1430 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1431 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1433 // global global global
1468 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1469 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1471 // global none global
1477 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1478 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1494 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1495 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1509 if ((unsigned short) st->a >= prog->progs->numglobals)
1510 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1513 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s", st->op, i, PRVM_NAME);
1518 PRVM_LoadLNO(filename);
1522 prog->loaded = TRUE;
1524 // set flags & ddef_ts in prog
1528 prog->self = PRVM_ED_FindGlobal("self");
1530 if( PRVM_ED_FindGlobal("time") && PRVM_ED_FindGlobal("time")->type & ev_float )
1531 prog->time = &PRVM_G_FLOAT(PRVM_ED_FindGlobal("time")->ofs);
1533 if(PRVM_ED_FindField ("chain"))
1534 prog->flag |= PRVM_FE_CHAIN;
1536 if(PRVM_ED_FindField ("classname"))
1537 prog->flag |= PRVM_FE_CLASSNAME;
1539 if(PRVM_ED_FindField ("nextthink") && PRVM_ED_FindField ("frame") && PRVM_ED_FindField ("think")
1540 && prog->flag && prog->self)
1541 prog->flag |= PRVM_OP_STATE;
1543 PRVM_GCALL(init_cmd)();
1550 void PRVM_Fields_f (void)
1552 int i, j, ednum, used, usedamount;
1554 char tempstring[MAX_INPUTLINE], tempstring2[260];
1564 Con_Print("no progs loaded\n");
1571 Con_Print("prvm_fields <program name>\n");
1576 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1579 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1580 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1582 ed = PRVM_EDICT_NUM(ednum);
1583 if (ed->priv.required->free)
1585 for (i = 1;i < prog->progs->numfielddefs;i++)
1587 d = &prog->fielddefs[i];
1588 name = PRVM_GetString(d->s_name);
1589 if (name[strlen(name)-2] == '_')
1590 continue; // skip _x, _y, _z vars
1591 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1592 // if the value is still all 0, skip the field
1593 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1606 for (i = 0;i < prog->progs->numfielddefs;i++)
1608 d = &prog->fielddefs[i];
1609 name = PRVM_GetString(d->s_name);
1610 if (name[strlen(name)-2] == '_')
1611 continue; // skip _x, _y, _z vars
1612 switch(d->type & ~DEF_SAVEGLOBAL)
1615 strcat(tempstring, "string ");
1618 strcat(tempstring, "entity ");
1621 strcat(tempstring, "function ");
1624 strcat(tempstring, "field ");
1627 strcat(tempstring, "void ");
1630 strcat(tempstring, "float ");
1633 strcat(tempstring, "vector ");
1636 strcat(tempstring, "pointer ");
1639 sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1640 strcat(tempstring, tempstring2);
1643 if (strlen(name) > sizeof(tempstring2)-4)
1645 memcpy (tempstring2, name, sizeof(tempstring2)-4);
1646 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1647 tempstring2[sizeof(tempstring2)-1] = 0;
1650 strcat(tempstring, name);
1651 for (j = (int)strlen(name);j < 25;j++)
1652 strcat(tempstring, " ");
1653 sprintf(tempstring2, "%5d", counts[i]);
1654 strcat(tempstring, tempstring2);
1655 strcat(tempstring, "\n");
1656 if (strlen(tempstring) >= sizeof(tempstring)/2)
1658 Con_Print(tempstring);
1664 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1668 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);
1673 void PRVM_Globals_f (void)
1679 Con_Print("no progs loaded\n");
1682 if(Cmd_Argc () != 2)
1684 Con_Print("prvm_globals <program name>\n");
1689 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1692 Con_Printf("%s :", PRVM_NAME);
1694 for (i = 0;i < prog->progs->numglobaldefs;i++)
1695 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
1696 Con_Printf("%i global variables, totalling %i bytes\n", prog->progs->numglobals, prog->progs->numglobals * 4);
1706 void PRVM_Global_f(void)
1709 if( Cmd_Argc() != 3 ) {
1710 Con_Printf( "prvm_global <program name> <global name>\n" );
1715 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1718 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1720 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1722 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
1731 void PRVM_GlobalSet_f(void)
1734 if( Cmd_Argc() != 4 ) {
1735 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
1740 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1743 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1745 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1747 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) );
1756 void PRVM_Init (void)
1758 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
1759 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "set a property on an entity number in the selected VM (server, client, menu)");
1760 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
1761 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
1762 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)");
1763 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
1764 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
1765 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
1766 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)");
1767 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
1768 Cvar_RegisterVariable (&prvm_boundscheck);
1769 Cvar_RegisterVariable (&prvm_traceqc);
1779 void PRVM_InitProg(int prognr)
1781 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
1782 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
1784 prog = &prog_list[prognr];
1789 memset(prog, 0, sizeof(prvm_prog_t));
1791 prog->time = &prog->_time;
1792 prog->error_cmd = Host_Error;
1795 int PRVM_GetProgNr()
1797 return prog - prog_list;
1800 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
1802 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
1805 void _PRVM_Free(void *buffer, const char *filename, int fileline)
1807 _Mem_Free(buffer, filename, fileline);
1810 void _PRVM_FreeAll(const char *filename, int fileline)
1813 prog->fielddefs = NULL;
1814 prog->functions = NULL;
1815 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
1818 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
1819 prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
1821 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
1826 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
1828 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
1832 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
1835 n = e - prog->edicts;
1836 if ((unsigned int)n >= prog->limit_edicts)
1837 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
1841 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
1843 // return e - prog->edicts;
1846 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
1847 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
1848 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
1851 n = e - prog->edicts;
1852 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
1853 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
1854 return n;// EXPERIMENTAL
1855 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
1857 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
1859 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
1860 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
1861 return prog->edicts + n; // EXPERIMENTAL
1862 //return prog->edicts + ((n) / (progs->entityfields * 4));
1867 const char *PRVM_GetString(int num)
1869 if (num >= 0 && num < prog->stringssize)
1870 return prog->strings + num;
1871 else if (num < 0 && num >= -prog->numknownstrings)
1874 if (!prog->knownstrings[num])
1875 PRVM_ERROR("PRVM_GetString: attempt to get string that is already freed");
1876 return prog->knownstrings[num];
1880 PRVM_ERROR("PRVM_GetString: invalid string offset %i", num);
1885 int PRVM_SetEngineString(const char *s)
1890 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
1891 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
1892 for (i = 0;i < prog->numknownstrings;i++)
1893 if (prog->knownstrings[i] == s)
1895 // new unknown engine string
1896 if (developer.integer >= 100)
1897 Con_Printf("new engine string %p\n", s);
1898 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
1899 if (!prog->knownstrings[i])
1901 if (i >= prog->numknownstrings)
1903 if (i >= prog->maxknownstrings)
1905 const char **oldstrings = prog->knownstrings;
1906 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
1907 prog->maxknownstrings += 128;
1908 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
1909 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
1910 if (prog->numknownstrings)
1912 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
1913 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
1916 prog->numknownstrings++;
1918 prog->firstfreeknownstring = i + 1;
1919 prog->knownstrings[i] = s;
1923 int PRVM_AllocString(size_t bufferlength, char **pointer)
1928 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
1929 if (!prog->knownstrings[i])
1931 if (i >= prog->numknownstrings)
1933 if (i >= prog->maxknownstrings)
1935 const char **oldstrings = prog->knownstrings;
1936 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
1937 prog->maxknownstrings += 128;
1938 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
1939 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
1940 if (prog->numknownstrings)
1942 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
1943 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
1946 prog->numknownstrings++;
1948 prog->firstfreeknownstring = i + 1;
1949 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
1950 prog->knownstrings_freeable[i] = true;
1952 *pointer = (char *)(prog->knownstrings[i]);
1956 void PRVM_FreeString(int num)
1959 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
1960 else if (num >= 0 && num < prog->stringssize)
1961 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
1962 else if (num < 0 && num >= -prog->numknownstrings)
1965 if (!prog->knownstrings[num])
1966 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
1967 if (!prog->knownstrings[num])
1968 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
1969 PRVM_Free((char *)prog->knownstrings[num]);
1970 prog->knownstrings[num] = NULL;
1971 prog->knownstrings_freeable[num] = false;
1972 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
1975 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);