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