]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
Provide 'ascii' key input to CSQC.
[xonotic/darkplaces.git] / prvm_edict.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // AK new vm
21
22 #include "quakedef.h"
23 #include "progsvm.h"
24
25 prvm_prog_t *prog;
26
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
28
29 int             prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s);
33
34 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
35 cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)"};
36 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
37 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
38 // LordHavoc: counts usage of each QuakeC statement
39 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
40 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
41
42 extern sizebuf_t vm_tempstringsbuf;
43
44 //============================================================================
45 // mempool handling
46
47 /*
48 ===============
49 PRVM_MEM_Alloc
50 ===============
51 */
52 void PRVM_MEM_Alloc(void)
53 {
54         int i;
55
56         // reserve space for the null entity aka world
57         // check bound of max_edicts
58         prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
59         prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
60
61         // edictprivate_size has to be min as big prvm_edict_private_t
62         prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
63
64         // alloc edicts
65         prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
66
67         // alloc edict private space
68         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
69
70         // alloc edict fields
71         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
72
73         // set edict pointers
74         for(i = 0; i < prog->max_edicts; i++)
75         {
76                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
77                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
78         }
79 }
80
81 /*
82 ===============
83 PRVM_MEM_IncreaseEdicts
84 ===============
85 */
86 void PRVM_MEM_IncreaseEdicts(void)
87 {
88         int             i;
89         int             oldmaxedicts = prog->max_edicts;
90         void    *oldedictsfields = prog->edictsfields;
91         void    *oldedictprivate = prog->edictprivate;
92
93         if(prog->max_edicts >= prog->limit_edicts)
94                 return;
95
96         PRVM_GCALL(begin_increase_edicts)();
97
98         // increase edicts
99         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
100
101         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
102         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
103
104         memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
105         memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
106
107         //set e and v pointers
108         for(i = 0; i < prog->max_edicts; i++)
109         {
110                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
111                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
112         }
113
114         PRVM_GCALL(end_increase_edicts)();
115
116         Mem_Free(oldedictsfields);
117         Mem_Free(oldedictprivate);
118 }
119
120 //============================================================================
121 // normal prvm
122
123 int PRVM_ED_FindFieldOffset(const char *field)
124 {
125         ddef_t *d;
126         d = PRVM_ED_FindField(field);
127         if (!d)
128                 return -1;
129         return d->ofs;
130 }
131
132 int PRVM_ED_FindGlobalOffset(const char *global)
133 {
134         ddef_t *d;
135         d = PRVM_ED_FindGlobal(global);
136         if (!d)
137                 return -1;
138         return d->ofs;
139 }
140
141 func_t PRVM_ED_FindFunctionOffset(const char *function)
142 {
143         mfunction_t *f;
144         f = PRVM_ED_FindFunction(function);
145         if (!f)
146                 return 0;
147         return (func_t)(f - prog->functions);
148 }
149
150 qboolean PRVM_ProgLoaded(int prognr)
151 {
152         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
153                 return FALSE;
154
155         return (prog_list[prognr].loaded ? TRUE : FALSE);
156 }
157
158 /*
159 =================
160 PRVM_SetProgFromString
161 =================
162 */
163 // perhaps add a return value when the str doesnt exist
164 qboolean PRVM_SetProgFromString(const char *str)
165 {
166         int i = 0;
167         for(; i < PRVM_MAXPROGS ; i++)
168                 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
169                 {
170                         if(prog_list[i].loaded)
171                         {
172                                 prog = &prog_list[i];
173                                 return TRUE;
174                         }
175                         else
176                         {
177                                 Con_Printf("%s not loaded !\n",PRVM_NAME);
178                                 return FALSE;
179                         }
180                 }
181
182         Con_Printf("Invalid program name %s !\n", str);
183         return FALSE;
184 }
185
186 /*
187 =================
188 PRVM_SetProg
189 =================
190 */
191 void PRVM_SetProg(int prognr)
192 {
193         if(0 <= prognr && prognr < PRVM_MAXPROGS)
194         {
195                 if(prog_list[prognr].loaded)
196                         prog = &prog_list[prognr];
197                 else
198                         PRVM_ERROR("%i not loaded !", prognr);
199                 return;
200         }
201         PRVM_ERROR("Invalid program number %i", prognr);
202 }
203
204 /*
205 =================
206 PRVM_ED_ClearEdict
207
208 Sets everything to NULL
209 =================
210 */
211 void PRVM_ED_ClearEdict (prvm_edict_t *e)
212 {
213         memset (e->fields.vp, 0, prog->progs->entityfields * 4);
214         e->priv.required->free = false;
215
216         // AK: Let the init_edict function determine if something needs to be initialized
217         PRVM_GCALL(init_edict)(e);
218 }
219
220 /*
221 =================
222 PRVM_ED_Alloc
223
224 Either finds a free edict, or allocates a new one.
225 Try to avoid reusing an entity that was recently freed, because it
226 can cause the client to think the entity morphed into something else
227 instead of being removed and recreated, which can cause interpolated
228 angles and bad trails.
229 =================
230 */
231 prvm_edict_t *PRVM_ED_Alloc (void)
232 {
233         int                     i;
234         prvm_edict_t            *e;
235
236         // the client qc dont need maxclients
237         // thus it doesnt need to use svs.maxclients
238         // AK:  changed i=svs.maxclients+1
239         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
240         //              although the menu/client has no world
241         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
242         {
243                 e = PRVM_EDICT_NUM(i);
244                 // the first couple seconds of server time can involve a lot of
245                 // freeing and allocating, so relax the replacement policy
246                 if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
247                 {
248                         PRVM_ED_ClearEdict (e);
249                         return e;
250                 }
251         }
252
253         if (i == prog->limit_edicts)
254                 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
255
256         prog->num_edicts++;
257         if (prog->num_edicts >= prog->max_edicts)
258                 PRVM_MEM_IncreaseEdicts();
259
260         e = PRVM_EDICT_NUM(i);
261         PRVM_ED_ClearEdict (e);
262
263         return e;
264 }
265
266 /*
267 =================
268 PRVM_ED_Free
269
270 Marks the edict as free
271 FIXME: walk all entities and NULL out references to this entity
272 =================
273 */
274 void PRVM_ED_Free (prvm_edict_t *ed)
275 {
276         // dont delete the null entity (world) or reserved edicts
277         if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
278                 return;
279
280         PRVM_GCALL(free_edict)(ed);
281
282         ed->priv.required->free = true;
283         ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
284 }
285
286 //===========================================================================
287
288 /*
289 ============
290 PRVM_ED_GlobalAtOfs
291 ============
292 */
293 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
294 {
295         ddef_t          *def;
296         int                     i;
297
298         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
299         {
300                 def = &prog->globaldefs[i];
301                 if (def->ofs == ofs)
302                         return def;
303         }
304         return NULL;
305 }
306
307 /*
308 ============
309 PRVM_ED_FieldAtOfs
310 ============
311 */
312 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
313 {
314         ddef_t          *def;
315         int                     i;
316
317         for (i=0 ; i<prog->progs->numfielddefs ; i++)
318         {
319                 def = &prog->fielddefs[i];
320                 if (def->ofs == ofs)
321                         return def;
322         }
323         return NULL;
324 }
325
326 /*
327 ============
328 PRVM_ED_FindField
329 ============
330 */
331 ddef_t *PRVM_ED_FindField (const char *name)
332 {
333         ddef_t *def;
334         int i;
335
336         for (i=0 ; i<prog->progs->numfielddefs ; i++)
337         {
338                 def = &prog->fielddefs[i];
339                 if (!strcmp(PRVM_GetString(def->s_name), name))
340                         return def;
341         }
342         return NULL;
343 }
344
345 /*
346 ============
347 PRVM_ED_FindGlobal
348 ============
349 */
350 ddef_t *PRVM_ED_FindGlobal (const char *name)
351 {
352         ddef_t *def;
353         int i;
354
355         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
356         {
357                 def = &prog->globaldefs[i];
358                 if (!strcmp(PRVM_GetString(def->s_name), name))
359                         return def;
360         }
361         return NULL;
362 }
363
364
365 /*
366 ============
367 PRVM_ED_FindFunction
368 ============
369 */
370 mfunction_t *PRVM_ED_FindFunction (const char *name)
371 {
372         mfunction_t             *func;
373         int                             i;
374
375         for (i=0 ; i<prog->progs->numfunctions ; i++)
376         {
377                 func = &prog->functions[i];
378                 if (!strcmp(PRVM_GetString(func->s_name), name))
379                         return func;
380         }
381         return NULL;
382 }
383
384
385 /*
386 ============
387 PRVM_ValueString
388
389 Returns a string describing *data in a type specific manner
390 =============
391 */
392 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
393 {
394         static char line[MAX_INPUTLINE];
395         ddef_t *def;
396         mfunction_t *f;
397         int n;
398
399         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
400
401         switch (type)
402         {
403         case ev_string:
404                 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
405                 break;
406         case ev_entity:
407                 n = val->edict;
408                 if (n < 0 || n >= prog->limit_edicts)
409                         sprintf (line, "entity %i (invalid!)", n);
410                 else
411                         sprintf (line, "entity %i", n);
412                 break;
413         case ev_function:
414                 f = prog->functions + val->function;
415                 sprintf (line, "%s()", PRVM_GetString(f->s_name));
416                 break;
417         case ev_field:
418                 def = PRVM_ED_FieldAtOfs ( val->_int );
419                 sprintf (line, ".%s", PRVM_GetString(def->s_name));
420                 break;
421         case ev_void:
422                 sprintf (line, "void");
423                 break;
424         case ev_float:
425                 // LordHavoc: changed from %5.1f to %10.4f
426                 sprintf (line, "%10.4f", val->_float);
427                 break;
428         case ev_vector:
429                 // LordHavoc: changed from %5.1f to %10.4f
430                 sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
431                 break;
432         case ev_pointer:
433                 sprintf (line, "pointer");
434                 break;
435         default:
436                 sprintf (line, "bad type %i", (int) type);
437                 break;
438         }
439
440         return line;
441 }
442
443 /*
444 ============
445 PRVM_UglyValueString
446
447 Returns a string describing *data in a type specific manner
448 Easier to parse than PR_ValueString
449 =============
450 */
451 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
452 {
453         static char line[MAX_INPUTLINE];
454         int i;
455         const char *s;
456         ddef_t *def;
457         mfunction_t *f;
458
459         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
460
461         switch (type)
462         {
463         case ev_string:
464                 // Parse the string a bit to turn special characters
465                 // (like newline, specifically) into escape codes,
466                 // this fixes saving games from various mods
467                 s = PRVM_GetString (val->string);
468                 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
469                 {
470                         if (*s == '\n')
471                         {
472                                 line[i++] = '\\';
473                                 line[i++] = 'n';
474                         }
475                         else if (*s == '\r')
476                         {
477                                 line[i++] = '\\';
478                                 line[i++] = 'r';
479                         }
480                         else
481                                 line[i++] = *s;
482                         s++;
483                 }
484                 line[i] = '\0';
485                 break;
486         case ev_entity:
487                 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
488                 break;
489         case ev_function:
490                 f = prog->functions + val->function;
491                 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
492                 break;
493         case ev_field:
494                 def = PRVM_ED_FieldAtOfs ( val->_int );
495                 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
496                 break;
497         case ev_void:
498                 dpsnprintf (line, sizeof (line), "void");
499                 break;
500         case ev_float:
501                 dpsnprintf (line, sizeof (line), "%f", val->_float);
502                 break;
503         case ev_vector:
504                 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
505                 break;
506         default:
507                 dpsnprintf (line, sizeof (line), "bad type %i", type);
508                 break;
509         }
510
511         return line;
512 }
513
514 /*
515 ============
516 PRVM_GlobalString
517
518 Returns a string with a description and the contents of a global,
519 padded to 20 field width
520 ============
521 */
522 char *PRVM_GlobalString (int ofs)
523 {
524         char    *s;
525         //size_t        i;
526         ddef_t  *def;
527         void    *val;
528         static char     line[128];
529
530         val = (void *)&prog->globals.generic[ofs];
531         def = PRVM_ED_GlobalAtOfs(ofs);
532         if (!def)
533                 sprintf (line,"GLOBAL%i", ofs);
534         else
535         {
536                 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
537                 sprintf (line,"%s (=%s)", PRVM_GetString(def->s_name), s);
538         }
539
540         //i = strlen(line);
541         //for ( ; i<20 ; i++)
542         //      strcat (line," ");
543         //strcat (line," ");
544
545         return line;
546 }
547
548 char *PRVM_GlobalStringNoContents (int ofs)
549 {
550         //size_t        i;
551         ddef_t  *def;
552         static char     line[128];
553
554         def = PRVM_ED_GlobalAtOfs(ofs);
555         if (!def)
556                 sprintf (line,"GLOBAL%i", ofs);
557         else
558                 sprintf (line,"%s", PRVM_GetString(def->s_name));
559
560         //i = strlen(line);
561         //for ( ; i<20 ; i++)
562         //      strcat (line," ");
563         //strcat (line," ");
564
565         return line;
566 }
567
568
569 /*
570 =============
571 PRVM_ED_Print
572
573 For debugging
574 =============
575 */
576 // LordHavoc: optimized this to print out much more quickly (tempstring)
577 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
578 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
579 {
580         size_t  l;
581         ddef_t  *d;
582         int             *v;
583         int             i, j;
584         const char      *name;
585         int             type;
586         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
587
588         if (ed->priv.required->free)
589         {
590                 Con_Printf("%s: FREE\n",PRVM_NAME);
591                 return;
592         }
593
594         tempstring[0] = 0;
595         sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
596         for (i=1 ; i<prog->progs->numfielddefs ; i++)
597         {
598                 d = &prog->fielddefs[i];
599                 name = PRVM_GetString(d->s_name);
600                 if (name[strlen(name)-2] == '_')
601                         continue;       // skip _x, _y, _z vars
602
603                 // Check Field Name Wildcard
604                 if(wildcard_fieldname)
605                         if( !matchpattern(name, wildcard_fieldname, 1) )
606                                 // Didn't match; skip
607                                 continue;
608
609                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
610
611         // if the value is still all 0, skip the field
612                 type = d->type & ~DEF_SAVEGLOBAL;
613
614                 for (j=0 ; j<prvm_type_size[type] ; j++)
615                         if (v[j])
616                                 break;
617                 if (j == prvm_type_size[type])
618                         continue;
619
620                 if (strlen(name) > sizeof(tempstring2)-4)
621                 {
622                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
623                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
624                         tempstring2[sizeof(tempstring2)-1] = 0;
625                         name = tempstring2;
626                 }
627                 strlcat(tempstring, name, sizeof(tempstring));
628                 for (l = strlen(name);l < 14;l++)
629                         strlcat(tempstring, " ", sizeof(tempstring));
630                 strlcat(tempstring, " ", sizeof(tempstring));
631
632                 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
633                 if (strlen(name) > sizeof(tempstring2)-4)
634                 {
635                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
636                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
637                         tempstring2[sizeof(tempstring2)-1] = 0;
638                         name = tempstring2;
639                 }
640                 strlcat(tempstring, name, sizeof(tempstring));
641                 strlcat(tempstring, "\n", sizeof(tempstring));
642                 if (strlen(tempstring) >= sizeof(tempstring)/2)
643                 {
644                         Con_Print(tempstring);
645                         tempstring[0] = 0;
646                 }
647         }
648         if (tempstring[0])
649                 Con_Print(tempstring);
650 }
651
652 /*
653 =============
654 PRVM_ED_Write
655
656 For savegames
657 =============
658 */
659 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
660 {
661         ddef_t  *d;
662         int             *v;
663         int             i, j;
664         const char      *name;
665         int             type;
666
667         FS_Print(f, "{\n");
668
669         if (ed->priv.required->free)
670         {
671                 FS_Print(f, "}\n");
672                 return;
673         }
674
675         for (i=1 ; i<prog->progs->numfielddefs ; i++)
676         {
677                 d = &prog->fielddefs[i];
678                 name = PRVM_GetString(d->s_name);
679                 if (name[strlen(name)-2] == '_')
680                         continue;       // skip _x, _y, _z vars
681
682                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
683
684         // if the value is still all 0, skip the field
685                 type = d->type & ~DEF_SAVEGLOBAL;
686                 for (j=0 ; j<prvm_type_size[type] ; j++)
687                         if (v[j])
688                                 break;
689                 if (j == prvm_type_size[type])
690                         continue;
691
692                 FS_Printf(f,"\"%s\" ",name);
693                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
694         }
695
696         FS_Print(f, "}\n");
697 }
698
699 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
700 {
701         PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
702 }
703
704 /*
705 =============
706 PRVM_ED_PrintEdicts_f
707
708 For debugging, prints all the entities in the current server
709 =============
710 */
711 void PRVM_ED_PrintEdicts_f (void)
712 {
713         int             i;
714         const char *wildcard_fieldname;
715
716         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
717         {
718                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
719                 return;
720         }
721
722         PRVM_Begin;
723         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
724                 return;
725
726         if( Cmd_Argc() == 3)
727                 wildcard_fieldname = Cmd_Argv(2);
728         else
729                 wildcard_fieldname = NULL;
730
731         Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
732         for (i=0 ; i<prog->num_edicts ; i++)
733                 PRVM_ED_PrintNum (i, wildcard_fieldname);
734
735         PRVM_End;
736 }
737
738 /*
739 =============
740 PRVM_ED_PrintEdict_f
741
742 For debugging, prints a single edict
743 =============
744 */
745 void PRVM_ED_PrintEdict_f (void)
746 {
747         int             i;
748         const char      *wildcard_fieldname;
749
750         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
751         {
752                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
753                 return;
754         }
755
756         PRVM_Begin;
757         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
758                 return;
759
760         i = atoi (Cmd_Argv(2));
761         if (i >= prog->num_edicts)
762         {
763                 Con_Print("Bad edict number\n");
764                 PRVM_End;
765                 return;
766         }
767         if( Cmd_Argc() == 4)
768                 // Optional Wildcard Provided
769                 wildcard_fieldname = Cmd_Argv(3);
770         else
771                 // Use All
772                 wildcard_fieldname = NULL;
773         PRVM_ED_PrintNum (i, wildcard_fieldname);
774
775         PRVM_End;
776 }
777
778 /*
779 =============
780 PRVM_ED_Count
781
782 For debugging
783 =============
784 */
785 // 2 possibilities : 1. just displaying the active edict count
786 //                                       2. making a function pointer [x]
787 void PRVM_ED_Count_f (void)
788 {
789         int             i;
790         prvm_edict_t    *ent;
791         int             active;
792
793         if(Cmd_Argc() != 2)
794         {
795                 Con_Print("prvm_count <program name>\n");
796                 return;
797         }
798
799         PRVM_Begin;
800         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
801                 return;
802
803         if(prog->count_edicts)
804                 prog->count_edicts();
805         else
806         {
807                 active = 0;
808                 for (i=0 ; i<prog->num_edicts ; i++)
809                 {
810                         ent = PRVM_EDICT_NUM(i);
811                         if (ent->priv.required->free)
812                                 continue;
813                         active++;
814                 }
815
816                 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
817                 Con_Printf("active    :%3i\n", active);
818         }
819
820         PRVM_End;
821 }
822
823 /*
824 ==============================================================================
825
826                                         ARCHIVING GLOBALS
827
828 FIXME: need to tag constants, doesn't really work
829 ==============================================================================
830 */
831
832 /*
833 =============
834 PRVM_ED_WriteGlobals
835 =============
836 */
837 void PRVM_ED_WriteGlobals (qfile_t *f)
838 {
839         ddef_t          *def;
840         int                     i;
841         const char              *name;
842         int                     type;
843
844         FS_Print(f,"{\n");
845         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
846         {
847                 def = &prog->globaldefs[i];
848                 type = def->type;
849                 if ( !(def->type & DEF_SAVEGLOBAL) )
850                         continue;
851                 type &= ~DEF_SAVEGLOBAL;
852
853                 if (type != ev_string && type != ev_float && type != ev_entity)
854                         continue;
855
856                 name = PRVM_GetString(def->s_name);
857                 FS_Printf(f,"\"%s\" ", name);
858                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
859         }
860         FS_Print(f,"}\n");
861 }
862
863 /*
864 =============
865 PRVM_ED_ParseGlobals
866 =============
867 */
868 void PRVM_ED_ParseGlobals (const char *data)
869 {
870         char keyname[MAX_INPUTLINE];
871         ddef_t *key;
872
873         while (1)
874         {
875                 // parse key
876                 if (!COM_ParseToken_Simple(&data, false, false))
877                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
878                 if (com_token[0] == '}')
879                         break;
880
881                 strlcpy (keyname, com_token, sizeof(keyname));
882
883                 // parse value
884                 if (!COM_ParseToken_Simple(&data, false, true))
885                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
886
887                 if (com_token[0] == '}')
888                         PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
889
890                 key = PRVM_ED_FindGlobal (keyname);
891                 if (!key)
892                 {
893                         Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
894                         continue;
895                 }
896
897                 if (!PRVM_ED_ParseEpair(NULL, key, com_token))
898                         PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
899         }
900 }
901
902 //============================================================================
903
904
905 /*
906 =============
907 PRVM_ED_ParseEval
908
909 Can parse either fields or globals
910 returns false if error
911 =============
912 */
913 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
914 {
915         int i, l;
916         char *new_p;
917         ddef_t *def;
918         prvm_eval_t *val;
919         mfunction_t *func;
920
921         if (ent)
922                 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
923         else
924                 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
925         switch (key->type & ~DEF_SAVEGLOBAL)
926         {
927         case ev_string:
928                 l = (int)strlen(s) + 1;
929                 val->string = PRVM_AllocString(l, &new_p);
930                 for (i = 0;i < l;i++)
931                 {
932                         if (s[i] == '\\' && i < l-1)
933                         {
934                                 i++;
935                                 if (s[i] == 'n')
936                                         *new_p++ = '\n';
937                                 else if (s[i] == 'r')
938                                         *new_p++ = '\r';
939                                 else
940                                         *new_p++ = s[i];
941                         }
942                         else
943                                 *new_p++ = s[i];
944                 }
945                 break;
946
947         case ev_float:
948                 while (*s && *s <= ' ')
949                         s++;
950                 val->_float = atof(s);
951                 break;
952
953         case ev_vector:
954                 for (i = 0;i < 3;i++)
955                 {
956                         while (*s && *s <= ' ')
957                                 s++;
958                         if (!*s)
959                                 break;
960                         val->vector[i] = atof(s);
961                         while (*s > ' ')
962                                 s++;
963                         if (!*s)
964                                 break;
965                 }
966                 break;
967
968         case ev_entity:
969                 while (*s && *s <= ' ')
970                         s++;
971                 i = atoi(s);
972                 if (i >= prog->limit_edicts)
973                         Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME);
974                 while (i >= prog->max_edicts)
975                         PRVM_MEM_IncreaseEdicts();
976                 // if IncreaseEdicts was called the base pointer needs to be updated
977                 if (ent)
978                         val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
979                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
980                 break;
981
982         case ev_field:
983                 def = PRVM_ED_FindField(s);
984                 if (!def)
985                 {
986                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
987                         return false;
988                 }
989                 val->_int = def->ofs;
990                 break;
991
992         case ev_function:
993                 func = PRVM_ED_FindFunction(s);
994                 if (!func)
995                 {
996                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
997                         return false;
998                 }
999                 val->function = func - prog->functions;
1000                 break;
1001
1002         default:
1003                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1004                 return false;
1005         }
1006         return true;
1007 }
1008
1009 /*
1010 =============
1011 PRVM_GameCommand_f
1012
1013 Console command to send a string to QC function GameCommand of the
1014 indicated progs
1015
1016 Usage:
1017   sv_cmd adminmsg 3 "do not teamkill"
1018   cl_cmd someclientcommand
1019   menu_cmd somemenucommand
1020
1021 All progs can support this extension; sg calls it in server QC, cg in client
1022 QC, mg in menu QC.
1023 =============
1024 */
1025 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1026 {
1027         if(Cmd_Argc() < 1)
1028         {
1029                 Con_Printf("%s text...\n", whichcmd);
1030                 return;
1031         }
1032
1033         PRVM_Begin;
1034         if(!PRVM_SetProgFromString(whichprogs))
1035         // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1036         // also, it makes printing error messages easier!
1037         {
1038                 Con_Printf("%s program not loaded.\n", whichprogs);
1039                 return;
1040         }
1041
1042         if(!prog->funcoffsets.GameCommand)
1043         {
1044                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1045         }
1046         else
1047         {
1048                 int restorevm_tempstringsbuf_cursize;
1049                 const char *s;
1050
1051                 s = Cmd_Args();
1052
1053                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1054                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1055                 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1056                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1057         }
1058
1059         PRVM_End;
1060 }
1061 void PRVM_GameCommand_Server_f(void)
1062 {
1063         PRVM_GameCommand("server", "sv_cmd");
1064 }
1065 void PRVM_GameCommand_Client_f(void)
1066 {
1067         PRVM_GameCommand("client", "cl_cmd");
1068 }
1069 void PRVM_GameCommand_Menu_f(void)
1070 {
1071         PRVM_GameCommand("menu", "menu_cmd");
1072 }
1073
1074 /*
1075 =============
1076 PRVM_ED_EdictSet_f
1077
1078 Console command to set a field of a specified edict
1079 =============
1080 */
1081 void PRVM_ED_EdictSet_f(void)
1082 {
1083         prvm_edict_t *ed;
1084         ddef_t *key;
1085
1086         if(Cmd_Argc() != 5)
1087         {
1088                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1089                 return;
1090         }
1091
1092         PRVM_Begin;
1093         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1094         {
1095                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1096                 return;
1097         }
1098
1099         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1100
1101         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1102                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1103         else
1104                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4));
1105
1106         PRVM_End;
1107 }
1108
1109 /*
1110 ====================
1111 PRVM_ED_ParseEdict
1112
1113 Parses an edict out of the given string, returning the new position
1114 ed should be a properly initialized empty edict.
1115 Used for initial level load and for savegames.
1116 ====================
1117 */
1118 extern cvar_t developer_entityparsing;
1119 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1120 {
1121         ddef_t *key;
1122         qboolean anglehack;
1123         qboolean init;
1124         char keyname[256];
1125         size_t n;
1126
1127         init = false;
1128
1129 // go through all the dictionary pairs
1130         while (1)
1131         {
1132         // parse key
1133                 if (!COM_ParseToken_Simple(&data, false, false))
1134                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1135                 if (developer_entityparsing.integer)
1136                         Con_Printf("Key: \"%s\"", com_token);
1137                 if (com_token[0] == '}')
1138                         break;
1139
1140                 // anglehack is to allow QuakeEd to write single scalar angles
1141                 // and allow them to be turned into vectors. (FIXME...)
1142                 if (!strcmp(com_token, "angle"))
1143                 {
1144                         strlcpy (com_token, "angles", sizeof(com_token));
1145                         anglehack = true;
1146                 }
1147                 else
1148                         anglehack = false;
1149
1150                 // FIXME: change light to _light to get rid of this hack
1151                 if (!strcmp(com_token, "light"))
1152                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1153
1154                 strlcpy (keyname, com_token, sizeof(keyname));
1155
1156                 // another hack to fix keynames with trailing spaces
1157                 n = strlen(keyname);
1158                 while (n && keyname[n-1] == ' ')
1159                 {
1160                         keyname[n-1] = 0;
1161                         n--;
1162                 }
1163
1164         // parse value
1165                 if (!COM_ParseToken_Simple(&data, false, true))
1166                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1167                 if (developer_entityparsing.integer)
1168                         Con_Printf(" \"%s\"\n", com_token);
1169
1170                 if (com_token[0] == '}')
1171                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1172
1173                 init = true;
1174
1175                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1176                 if (!keyname[0])
1177                         continue;
1178
1179 // keynames with a leading underscore are used for utility comments,
1180 // and are immediately discarded by quake
1181                 if (keyname[0] == '_')
1182                         continue;
1183
1184                 key = PRVM_ED_FindField (keyname);
1185                 if (!key)
1186                 {
1187                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1188                         continue;
1189                 }
1190
1191                 if (anglehack)
1192                 {
1193                         char    temp[32];
1194                         strlcpy (temp, com_token, sizeof(temp));
1195                         sprintf (com_token, "0 %s 0", temp);
1196                 }
1197
1198                 if (!PRVM_ED_ParseEpair(ent, key, com_token))
1199                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1200         }
1201
1202         if (!init)
1203                 ent->priv.required->free = true;
1204
1205         return data;
1206 }
1207
1208
1209 /*
1210 ================
1211 PRVM_ED_LoadFromFile
1212
1213 The entities are directly placed in the array, rather than allocated with
1214 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1215 number references out of order.
1216
1217 Creates a server's entity / program execution context by
1218 parsing textual entity definitions out of an ent file.
1219
1220 Used for both fresh maps and savegame loads.  A fresh map would also need
1221 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1222 ================
1223 */
1224 void PRVM_ED_LoadFromFile (const char *data)
1225 {
1226         prvm_edict_t *ent;
1227         int parsed, inhibited, spawned, died;
1228         mfunction_t *func;
1229
1230         parsed = 0;
1231         inhibited = 0;
1232         spawned = 0;
1233         died = 0;
1234
1235
1236 // parse ents
1237         while (1)
1238         {
1239 // parse the opening brace
1240                 if (!COM_ParseToken_Simple(&data, false, false))
1241                         break;
1242                 if (com_token[0] != '{')
1243                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1244
1245                 // CHANGED: this is not conform to PR_LoadFromFile
1246                 if(prog->loadintoworld)
1247                 {
1248                         prog->loadintoworld = false;
1249                         ent = PRVM_EDICT_NUM(0);
1250                 }
1251                 else
1252                         ent = PRVM_ED_Alloc();
1253
1254                 // clear it
1255                 if (ent != prog->edicts)        // hack
1256                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1257
1258                 data = PRVM_ED_ParseEdict (data, ent);
1259                 parsed++;
1260
1261                 // remove the entity ?
1262                 if(prog->load_edict && !prog->load_edict(ent))
1263                 {
1264                         PRVM_ED_Free(ent);
1265                         inhibited++;
1266                         continue;
1267                 }
1268
1269 //
1270 // immediately call spawn function, but only if there is a self global and a classname
1271 //
1272                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1273                 {
1274                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1275                         if (!handle)
1276                         {
1277                                 Con_Print("No classname for:\n");
1278                                 PRVM_ED_Print(ent, NULL);
1279                                 PRVM_ED_Free (ent);
1280                                 continue;
1281                         }
1282
1283                         // look for the spawn function
1284                         func = PRVM_ED_FindFunction (PRVM_GetString(handle));
1285
1286                         if (!func)
1287                         {
1288                                 // check for OnEntityNoSpawnFunction
1289                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1290                                 {
1291                                         // self = ent
1292                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1293                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1294                                 }
1295                                 else
1296                                 {
1297                                         if (developer.integer) // don't confuse non-developers with errors
1298                                         {
1299                                                 Con_Print("No spawn function for:\n");
1300                                                 PRVM_ED_Print(ent, NULL);
1301                                         }
1302                                         PRVM_ED_Free (ent);
1303                                         continue;
1304                                 }
1305                         }
1306                         else
1307                         {
1308                                 // self = ent
1309                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1310                                 PRVM_ExecuteProgram (func - prog->functions, "");
1311                         }
1312                 }
1313
1314                 spawned++;
1315                 if (ent->priv.required->free)
1316                         died++;
1317         }
1318
1319         Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1320 }
1321
1322 void PRVM_FindOffsets(void)
1323 {
1324         // field and global searches use -1 for NULL
1325         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1326         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1327         // functions use 0 for NULL
1328         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1329
1330         // server and client qc use a lot of similar fields, so this is combined
1331         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1332         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1333         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1334         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1335         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1336         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1337         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1338         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1339         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1340         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1341         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1342         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1343         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1344         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1345         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1346         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1347         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1348         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1349         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1350         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1351         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1352         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1353         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1354         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1355         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1356         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1357         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1358         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1359         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1360         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1361         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1362         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1363         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1364         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1365         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1366         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1367         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1368         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1369         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1370         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1371         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1372         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1373         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1374         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1375         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1376         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1377         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1378         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1379         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1380         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1381         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1382         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1383         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1384         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1385         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1386         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1387         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1388         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1389         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1390         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1391         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1392         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1393         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1394         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1395         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1396         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1397         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1398         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1399         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1400         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1401         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1402         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1403         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1404         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1405         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1406         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1407         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1408         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1409         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1410         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1411         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1412         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1413         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1414         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1415         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1416         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1417         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1418         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1419         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1420         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1421         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1422         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1423         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1424         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1425         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1426         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1427         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1428         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1429         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1430         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1431         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1432         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1433         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1434         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1435         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1436         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1437         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1438         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1439         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1440         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1441         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1442         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1443         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1444         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1445         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1446         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1447         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1448         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1449         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1450         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1451         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1452         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1453         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1454         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1455         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1456         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1457         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1458         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1459         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1460         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1461         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1462         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1463         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1464         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1465         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1466         prog->globaloffsets.input_ascii                   = PRVM_ED_FindGlobalOffset("input_ascii");
1467
1468         // menu qc only uses some functions, nothing else
1469         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1470         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1471         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1472         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1473         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1474         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1475 }
1476
1477 // not used
1478 /*
1479 typedef struct dpfield_s
1480 {
1481         int type;
1482         char *string;
1483 }
1484 dpfield_t;
1485
1486 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1487
1488 dpfield_t dpfields[] =
1489 {
1490 };
1491 */
1492
1493 /*
1494 ===============
1495 PRVM_ResetProg
1496 ===============
1497 */
1498
1499 void PRVM_ResetProg()
1500 {
1501         PRVM_GCALL(reset_cmd)();
1502         Mem_FreePool(&prog->progs_mempool);
1503         memset(prog,0,sizeof(prvm_prog_t));
1504         prog->starttime = Sys_DoubleTime();
1505 }
1506
1507 /*
1508 ===============
1509 PRVM_LoadLNO
1510 ===============
1511 */
1512 void PRVM_LoadLNO( const char *progname ) {
1513         fs_offset_t filesize;
1514         unsigned char *lno;
1515         unsigned int *header;
1516         char filename[512];
1517
1518         FS_StripExtension( progname, filename, sizeof( filename ) );
1519         strlcat( filename, ".lno", sizeof( filename ) );
1520
1521         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1522         if( !lno ) {
1523                 return;
1524         }
1525
1526 /*
1527 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1528 <Spike>    SafeWrite (h, &version, sizeof(int));
1529 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1530 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1531 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1532 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1533 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1534 */
1535         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1536                 Mem_Free(lno);
1537                 return;
1538         }
1539
1540         header = (unsigned int *) lno;
1541         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1542                 LittleLong( header[ 1 ] ) == 1 &&
1543                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1544                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1545                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1546                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1547         {
1548                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1549                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1550         }
1551         Mem_Free( lno );
1552 }
1553
1554 /*
1555 ===============
1556 PRVM_LoadProgs
1557 ===============
1558 */
1559 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1560 {
1561         int i;
1562         dstatement_t *st;
1563         ddef_t *infielddefs;
1564         dfunction_t *dfunctions;
1565         fs_offset_t filesize;
1566
1567         if( prog->loaded ) {
1568                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1569         }
1570
1571         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1572         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1573                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1574
1575         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1576
1577         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1578
1579 // byte swap the header
1580         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1581                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1582
1583         if (prog->progs->version != PROG_VERSION)
1584                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1585         if (prog->progs->crc != prog->headercrc)
1586                 PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
1587
1588         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1589         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1590
1591         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1592                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1593         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1594         prog->stringssize = prog->progs->numstrings;
1595
1596         prog->numknownstrings = 0;
1597         prog->maxknownstrings = 0;
1598         prog->knownstrings = NULL;
1599         prog->knownstrings_freeable = NULL;
1600
1601         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1602
1603         // we need to expand the fielddefs list to include all the engine fields,
1604         // so allocate a new place for it
1605         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1606         //                                                                                              ( + DPFIELDS                       )
1607         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1608
1609         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1610
1611         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1612
1613         // moved edict_size calculation down below field adding code
1614
1615         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1616         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1617
1618 // byte swap the lumps
1619         for (i=0 ; i<prog->progs->numstatements ; i++)
1620         {
1621                 prog->statements[i].op = LittleShort(prog->statements[i].op);
1622                 prog->statements[i].a = LittleShort(prog->statements[i].a);
1623                 prog->statements[i].b = LittleShort(prog->statements[i].b);
1624                 prog->statements[i].c = LittleShort(prog->statements[i].c);
1625         }
1626
1627         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1628         for (i = 0;i < prog->progs->numfunctions;i++)
1629         {
1630                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1631                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1632                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1633                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1634                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1635                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1636                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1637         }
1638
1639         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1640         {
1641                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1642                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1643                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1644         }
1645
1646         // copy the progs fields to the new fields list
1647         for (i = 0;i < prog->progs->numfielddefs;i++)
1648         {
1649                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1650                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1651                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1652                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1653                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1654         }
1655
1656         // append the required fields
1657         for (i = 0;i < (int) numrequiredfields;i++)
1658         {
1659                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1660                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1661                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1662                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1663                         prog->progs->entityfields += 3;
1664                 else
1665                         prog->progs->entityfields++;
1666                 prog->progs->numfielddefs++;
1667         }
1668
1669         // check required functions
1670         for(i=0 ; i < numrequiredfunc ; i++)
1671                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1672                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1673
1674         // check required globals
1675         for(i=0 ; i < numrequiredglobals ; i++)
1676                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1677                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1678
1679         for (i=0 ; i<prog->progs->numglobals ; i++)
1680                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1681
1682         // moved edict_size calculation down here, below field adding code
1683         // LordHavoc: this no longer includes the prvm_edict_t header
1684         prog->edict_size = prog->progs->entityfields * 4;
1685         prog->edictareasize = prog->edict_size * prog->limit_edicts;
1686
1687         // LordHavoc: bounds check anything static
1688         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1689         {
1690                 switch (st->op)
1691                 {
1692                 case OP_IF:
1693                 case OP_IFNOT:
1694                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1695                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1696                         break;
1697                 case OP_GOTO:
1698                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1699                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1700                         break;
1701                 // global global global
1702                 case OP_ADD_F:
1703                 case OP_ADD_V:
1704                 case OP_SUB_F:
1705                 case OP_SUB_V:
1706                 case OP_MUL_F:
1707                 case OP_MUL_V:
1708                 case OP_MUL_FV:
1709                 case OP_MUL_VF:
1710                 case OP_DIV_F:
1711                 case OP_BITAND:
1712                 case OP_BITOR:
1713                 case OP_GE:
1714                 case OP_LE:
1715                 case OP_GT:
1716                 case OP_LT:
1717                 case OP_AND:
1718                 case OP_OR:
1719                 case OP_EQ_F:
1720                 case OP_EQ_V:
1721                 case OP_EQ_S:
1722                 case OP_EQ_E:
1723                 case OP_EQ_FNC:
1724                 case OP_NE_F:
1725                 case OP_NE_V:
1726                 case OP_NE_S:
1727                 case OP_NE_E:
1728                 case OP_NE_FNC:
1729                 case OP_ADDRESS:
1730                 case OP_LOAD_F:
1731                 case OP_LOAD_FLD:
1732                 case OP_LOAD_ENT:
1733                 case OP_LOAD_S:
1734                 case OP_LOAD_FNC:
1735                 case OP_LOAD_V:
1736                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1737                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1738                         break;
1739                 // global none global
1740                 case OP_NOT_F:
1741                 case OP_NOT_V:
1742                 case OP_NOT_S:
1743                 case OP_NOT_FNC:
1744                 case OP_NOT_ENT:
1745                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1746                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1747                         break;
1748                 // 2 globals
1749                 case OP_STOREP_F:
1750                 case OP_STOREP_ENT:
1751                 case OP_STOREP_FLD:
1752                 case OP_STOREP_S:
1753                 case OP_STOREP_FNC:
1754                 case OP_STORE_F:
1755                 case OP_STORE_ENT:
1756                 case OP_STORE_FLD:
1757                 case OP_STORE_S:
1758                 case OP_STORE_FNC:
1759                 case OP_STATE:
1760                 case OP_STOREP_V:
1761                 case OP_STORE_V:
1762                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1763                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1764                         break;
1765                 // 1 global
1766                 case OP_CALL0:
1767                 case OP_CALL1:
1768                 case OP_CALL2:
1769                 case OP_CALL3:
1770                 case OP_CALL4:
1771                 case OP_CALL5:
1772                 case OP_CALL6:
1773                 case OP_CALL7:
1774                 case OP_CALL8:
1775                 case OP_DONE:
1776                 case OP_RETURN:
1777                         if ((unsigned short) st->a >= prog->progs->numglobals)
1778                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1779                         break;
1780                 default:
1781                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1782                         break;
1783                 }
1784         }
1785
1786         PRVM_LoadLNO(filename);
1787
1788         PRVM_Init_Exec();
1789
1790         prog->loaded = TRUE;
1791
1792         // set flags & ddef_ts in prog
1793
1794         prog->flag = 0;
1795
1796         PRVM_FindOffsets();
1797
1798         PRVM_GCALL(init_cmd)();
1799
1800         // init mempools
1801         PRVM_MEM_Alloc();
1802 }
1803
1804
1805 void PRVM_Fields_f (void)
1806 {
1807         int i, j, ednum, used, usedamount;
1808         int *counts;
1809         char tempstring[MAX_INPUTLINE], tempstring2[260];
1810         const char *name;
1811         prvm_edict_t *ed;
1812         ddef_t *d;
1813         int *v;
1814
1815         // TODO
1816         /*
1817         if (!sv.active)
1818         {
1819                 Con_Print("no progs loaded\n");
1820                 return;
1821         }
1822         */
1823
1824         if(Cmd_Argc() != 2)
1825         {
1826                 Con_Print("prvm_fields <program name>\n");
1827                 return;
1828         }
1829
1830         PRVM_Begin;
1831         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1832                 return;
1833
1834         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1835         for (ednum = 0;ednum < prog->max_edicts;ednum++)
1836         {
1837                 ed = PRVM_EDICT_NUM(ednum);
1838                 if (ed->priv.required->free)
1839                         continue;
1840                 for (i = 1;i < prog->progs->numfielddefs;i++)
1841                 {
1842                         d = &prog->fielddefs[i];
1843                         name = PRVM_GetString(d->s_name);
1844                         if (name[strlen(name)-2] == '_')
1845                                 continue;       // skip _x, _y, _z vars
1846                         v = (int *)((char *)ed->fields.vp + d->ofs*4);
1847                         // if the value is still all 0, skip the field
1848                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1849                         {
1850                                 if (v[j])
1851                                 {
1852                                         counts[i]++;
1853                                         break;
1854                                 }
1855                         }
1856                 }
1857         }
1858         used = 0;
1859         usedamount = 0;
1860         tempstring[0] = 0;
1861         for (i = 0;i < prog->progs->numfielddefs;i++)
1862         {
1863                 d = &prog->fielddefs[i];
1864                 name = PRVM_GetString(d->s_name);
1865                 if (name[strlen(name)-2] == '_')
1866                         continue;       // skip _x, _y, _z vars
1867                 switch(d->type & ~DEF_SAVEGLOBAL)
1868                 {
1869                 case ev_string:
1870                         strlcat(tempstring, "string   ", sizeof(tempstring));
1871                         break;
1872                 case ev_entity:
1873                         strlcat(tempstring, "entity   ", sizeof(tempstring));
1874                         break;
1875                 case ev_function:
1876                         strlcat(tempstring, "function ", sizeof(tempstring));
1877                         break;
1878                 case ev_field:
1879                         strlcat(tempstring, "field    ", sizeof(tempstring));
1880                         break;
1881                 case ev_void:
1882                         strlcat(tempstring, "void     ", sizeof(tempstring));
1883                         break;
1884                 case ev_float:
1885                         strlcat(tempstring, "float    ", sizeof(tempstring));
1886                         break;
1887                 case ev_vector:
1888                         strlcat(tempstring, "vector   ", sizeof(tempstring));
1889                         break;
1890                 case ev_pointer:
1891                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
1892                         break;
1893                 default:
1894                         sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1895                         strlcat(tempstring, tempstring2, sizeof(tempstring));
1896                         break;
1897                 }
1898                 if (strlen(name) > sizeof(tempstring2)-4)
1899                 {
1900                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
1901                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1902                         tempstring2[sizeof(tempstring2)-1] = 0;
1903                         name = tempstring2;
1904                 }
1905                 strlcat(tempstring, name, sizeof(tempstring));
1906                 for (j = (int)strlen(name);j < 25;j++)
1907                         strlcat(tempstring, " ", sizeof(tempstring));
1908                 sprintf(tempstring2, "%5d", counts[i]);
1909                 strlcat(tempstring, tempstring2, sizeof(tempstring));
1910                 strlcat(tempstring, "\n", sizeof(tempstring));
1911                 if (strlen(tempstring) >= sizeof(tempstring)/2)
1912                 {
1913                         Con_Print(tempstring);
1914                         tempstring[0] = 0;
1915                 }
1916                 if (counts[i])
1917                 {
1918                         used++;
1919                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1920                 }
1921         }
1922         Mem_Free(counts);
1923         Con_Printf("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
1924
1925         PRVM_End;
1926 }
1927
1928 void PRVM_Globals_f (void)
1929 {
1930         int i;
1931         const char *wildcard;
1932         int numculled;
1933                 numculled = 0;
1934         // TODO
1935         /*if (!sv.active)
1936         {
1937                 Con_Print("no progs loaded\n");
1938                 return;
1939         }*/
1940         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1941         {
1942                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
1943                 return;
1944         }
1945
1946         PRVM_Begin;
1947         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1948                 return;
1949
1950         if( Cmd_Argc() == 3)
1951                 wildcard = Cmd_Argv(2);
1952         else
1953                 wildcard = NULL;
1954
1955         Con_Printf("%s :", PRVM_NAME);
1956
1957         for (i = 0;i < prog->progs->numglobaldefs;i++)
1958         {
1959                 if(wildcard)
1960                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
1961                         {
1962                                 numculled++;
1963                                 continue;
1964                         }
1965                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
1966         }
1967         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
1968
1969         PRVM_End;
1970 }
1971
1972 /*
1973 ===============
1974 PRVM_Global
1975 ===============
1976 */
1977 void PRVM_Global_f(void)
1978 {
1979         ddef_t *global;
1980         if( Cmd_Argc() != 3 ) {
1981                 Con_Printf( "prvm_global <program name> <global name>\n" );
1982                 return;
1983         }
1984
1985         PRVM_Begin;
1986         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1987                 return;
1988
1989         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1990         if( !global )
1991                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1992         else
1993                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
1994         PRVM_End;
1995 }
1996
1997 /*
1998 ===============
1999 PRVM_GlobalSet
2000 ===============
2001 */
2002 void PRVM_GlobalSet_f(void)
2003 {
2004         ddef_t *global;
2005         if( Cmd_Argc() != 4 ) {
2006                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2007                 return;
2008         }
2009
2010         PRVM_Begin;
2011         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2012                 return;
2013
2014         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2015         if( !global )
2016                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2017         else
2018                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) );
2019         PRVM_End;
2020 }
2021
2022 /*
2023 ===============
2024 PRVM_Init
2025 ===============
2026 */
2027 void PRVM_Init (void)
2028 {
2029         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2030         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2031         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2032         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2033         Cmd_AddCommand ("prvm_callprofile", PRVM_CallProfile_f, "prints execution statistics about the most time consuming QuakeC calls from the engine in the selected VM (server, client, menu)");
2034         Cmd_AddCommand ("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2035         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2036         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2037         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2038         Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2039         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2040         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2041         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2042         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2043         // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2044         Cvar_RegisterVariable (&prvm_boundscheck);
2045         Cvar_RegisterVariable (&prvm_traceqc);
2046         Cvar_RegisterVariable (&prvm_statementprofiling);
2047         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2048
2049         //VM_Cmd_Init();
2050 }
2051
2052 /*
2053 ===============
2054 PRVM_InitProg
2055 ===============
2056 */
2057 void PRVM_InitProg(int prognr)
2058 {
2059         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2060                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2061
2062         prog = &prog_list[prognr];
2063
2064         if(prog->loaded)
2065                 PRVM_ResetProg();
2066
2067         memset(prog, 0, sizeof(prvm_prog_t));
2068         prog->starttime = Sys_DoubleTime();
2069
2070         prog->error_cmd = Host_Error;
2071 }
2072
2073 int PRVM_GetProgNr()
2074 {
2075         return prog - prog_list;
2076 }
2077
2078 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2079 {
2080         return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2081 }
2082
2083 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2084 {
2085         _Mem_Free(buffer, filename, fileline);
2086 }
2087
2088 void _PRVM_FreeAll(const char *filename, int fileline)
2089 {
2090         prog->progs = NULL;
2091         prog->fielddefs = NULL;
2092         prog->functions = NULL;
2093         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2094 }
2095
2096 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2097 prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
2098 {
2099         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2100         return NULL;
2101 }
2102
2103 /*
2104 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2105 {
2106         PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2107         return 0;
2108 }
2109
2110 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2111 {
2112         int n;
2113         n = e - prog->edicts;
2114         if ((unsigned int)n >= prog->limit_edicts)
2115                 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2116         return n;
2117 }
2118
2119 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2120 //{
2121 //      return e - prog->edicts;
2122 //}
2123
2124 //#define       PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2125 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2126 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2127 {
2128         int n;
2129         n = e - prog->edicts;
2130         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2131                 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2132         return n;// EXPERIMENTAL
2133         //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2134 }
2135 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2136 {
2137         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2138                 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2139         return prog->edicts + n; // EXPERIMENTAL
2140         //return prog->edicts + ((n) / (progs->entityfields * 4));
2141 }
2142 */
2143
2144
2145 sizebuf_t vm_tempstringsbuf;
2146
2147 const char *PRVM_GetString(int num)
2148 {
2149         if (num >= 0)
2150         {
2151                 if (num < prog->stringssize)
2152                         return prog->strings + num;
2153                 else
2154 #if 1
2155                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2156                 {
2157                         num -= prog->stringssize;
2158                         if (num < vm_tempstringsbuf.cursize)
2159                                 return (char *)vm_tempstringsbuf.data + num;
2160                         else
2161                         {
2162                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2163                                 return "";
2164                         }
2165                 }
2166                 else
2167 #endif
2168                 {
2169                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2170                         return "";
2171                 }
2172         }
2173         else
2174         {
2175                 num = -1 - num;
2176 #if 0
2177                 if (num >= (1<<30))
2178                 {
2179                         // special range reserved for tempstrings
2180                         num -= (1<<30);
2181                         if (num < vm_tempstringsbuf.cursize)
2182                                 return (char *)vm_tempstringsbuf.data + num;
2183                         else
2184                         {
2185                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2186                                 return "";
2187                         }
2188                 }
2189                 else
2190 #endif
2191                 if (num < prog->numknownstrings)
2192                 {
2193                         if (!prog->knownstrings[num])
2194                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2195                         return prog->knownstrings[num];
2196                 }
2197                 else
2198                 {
2199                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2200                         return "";
2201                 }
2202         }
2203 }
2204
2205 int PRVM_SetEngineString(const char *s)
2206 {
2207         int i;
2208         if (!s)
2209                 return 0;
2210         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2211                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2212         // if it's in the tempstrings area, use a reserved range
2213         // (otherwise we'd get millions of useless string offsets cluttering the database)
2214         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2215 #if 1
2216                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2217 #else
2218                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2219 #endif
2220         // see if it's a known string address
2221         for (i = 0;i < prog->numknownstrings;i++)
2222                 if (prog->knownstrings[i] == s)
2223                         return -1 - i;
2224         // new unknown engine string
2225         if (developer.integer >= 200)
2226                 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2227         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2228                 if (!prog->knownstrings[i])
2229                         break;
2230         if (i >= prog->numknownstrings)
2231         {
2232                 if (i >= prog->maxknownstrings)
2233                 {
2234                         const char **oldstrings = prog->knownstrings;
2235                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2236                         prog->maxknownstrings += 128;
2237                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2238                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2239                         if (prog->numknownstrings)
2240                         {
2241                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2242                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2243                         }
2244                 }
2245                 prog->numknownstrings++;
2246         }
2247         prog->firstfreeknownstring = i + 1;
2248         prog->knownstrings[i] = s;
2249         return -1 - i;
2250 }
2251
2252 // temp string handling
2253
2254 // all tempstrings go into this buffer consecutively, and it is reset
2255 // whenever PRVM_ExecuteProgram returns to the engine
2256 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2257 //  restores it on return, so multiple recursive calls can share the same
2258 //  buffer)
2259 // the buffer size is automatically grown as needed
2260
2261 int PRVM_SetTempString(const char *s)
2262 {
2263         int size;
2264         char *t;
2265         if (!s)
2266                 return 0;
2267         size = (int)strlen(s) + 1;
2268         if (developer.integer >= 300)
2269                 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2270         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2271         {
2272                 sizebuf_t old = vm_tempstringsbuf;
2273                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2274                         PRVM_ERROR("PRVM_SetTempString: ran out of tempstring memory!  (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", vm_tempstringsbuf.cursize, size);
2275                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2276                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2277                         vm_tempstringsbuf.maxsize *= 2;
2278                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2279                 {
2280                         if (developer.integer >= 100)
2281                                 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2282                         vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2283                         if (old.cursize)
2284                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2285                         if (old.data)
2286                                 Mem_Free(old.data);
2287                 }
2288         }
2289         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2290         memcpy(t, s, size);
2291         vm_tempstringsbuf.cursize += size;
2292         return PRVM_SetEngineString(t);
2293 }
2294
2295 int PRVM_AllocString(size_t bufferlength, char **pointer)
2296 {
2297         int i;
2298         if (!bufferlength)
2299                 return 0;
2300         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2301                 if (!prog->knownstrings[i])
2302                         break;
2303         if (i >= prog->numknownstrings)
2304         {
2305                 if (i >= prog->maxknownstrings)
2306                 {
2307                         const char **oldstrings = prog->knownstrings;
2308                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2309                         prog->maxknownstrings += 128;
2310                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2311                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2312                         if (prog->numknownstrings)
2313                         {
2314                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2315                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2316                         }
2317                 }
2318                 prog->numknownstrings++;
2319         }
2320         prog->firstfreeknownstring = i + 1;
2321         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2322         prog->knownstrings_freeable[i] = true;
2323         if (pointer)
2324                 *pointer = (char *)(prog->knownstrings[i]);
2325         return -1 - i;
2326 }
2327
2328 void PRVM_FreeString(int num)
2329 {
2330         if (num == 0)
2331                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2332         else if (num >= 0 && num < prog->stringssize)
2333                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2334         else if (num < 0 && num >= -prog->numknownstrings)
2335         {
2336                 num = -1 - num;
2337                 if (!prog->knownstrings[num])
2338                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2339                 if (!prog->knownstrings[num])
2340                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2341                 PRVM_Free((char *)prog->knownstrings[num]);
2342                 prog->knownstrings[num] = NULL;
2343                 prog->knownstrings_freeable[num] = false;
2344                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2345         }
2346         else
2347                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2348 }
2349