]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_edict.c
Merge branch 'master' into dp1
[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: prints every opcode as it executes - warning: this is significant spew
35 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
36 // LordHavoc: counts usage of each QuakeC statement
37 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)"};
38 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42
43 qboolean prvm_runawaycheck = true;
44
45 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
46 // 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)
47 qboolean prvm_boundscheck = true;
48
49 extern sizebuf_t vm_tempstringsbuf;
50
51 //============================================================================
52 // mempool handling
53
54 /*
55 ===============
56 PRVM_MEM_Alloc
57 ===============
58 */
59 void PRVM_MEM_Alloc(void)
60 {
61         int i;
62
63         // reserve space for the null entity aka world
64         // check bound of max_edicts
65         prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
66         prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
67
68         // edictprivate_size has to be min as big prvm_edict_private_t
69         prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
70
71         // alloc edicts
72         prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
73
74         // alloc edict private space
75         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
76
77         // alloc edict fields
78         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
79
80         // set edict pointers
81         for(i = 0; i < prog->max_edicts; i++)
82         {
83                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
84                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
85         }
86 }
87
88 /*
89 ===============
90 PRVM_MEM_IncreaseEdicts
91 ===============
92 */
93 void PRVM_MEM_IncreaseEdicts(void)
94 {
95         int             i;
96         int             oldmaxedicts = prog->max_edicts;
97         void    *oldedictsfields = prog->edictsfields;
98         void    *oldedictprivate = prog->edictprivate;
99
100         if(prog->max_edicts >= prog->limit_edicts)
101                 return;
102
103         PRVM_GCALL(begin_increase_edicts)();
104
105         // increase edicts
106         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
107
108         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
109         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
110
111         memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
112         memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
113
114         //set e and v pointers
115         for(i = 0; i < prog->max_edicts; i++)
116         {
117                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
118                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
119         }
120
121         PRVM_GCALL(end_increase_edicts)();
122
123         Mem_Free(oldedictsfields);
124         Mem_Free(oldedictprivate);
125 }
126
127 //============================================================================
128 // normal prvm
129
130 int PRVM_ED_FindFieldOffset(const char *field)
131 {
132         ddef_t *d;
133         d = PRVM_ED_FindField(field);
134         if (!d)
135                 return -1;
136         return d->ofs;
137 }
138
139 int PRVM_ED_FindGlobalOffset(const char *global)
140 {
141         ddef_t *d;
142         d = PRVM_ED_FindGlobal(global);
143         if (!d)
144                 return -1;
145         return d->ofs;
146 }
147
148 func_t PRVM_ED_FindFunctionOffset(const char *function)
149 {
150         mfunction_t *f;
151         f = PRVM_ED_FindFunction(function);
152         if (!f)
153                 return 0;
154         return (func_t)(f - prog->functions);
155 }
156
157 qboolean PRVM_ProgLoaded(int prognr)
158 {
159         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
160                 return FALSE;
161
162         return (prog_list[prognr].loaded ? TRUE : FALSE);
163 }
164
165 /*
166 =================
167 PRVM_SetProgFromString
168 =================
169 */
170 // perhaps add a return value when the str doesnt exist
171 qboolean PRVM_SetProgFromString(const char *str)
172 {
173         int i = 0;
174         for(; i < PRVM_MAXPROGS ; i++)
175                 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
176                 {
177                         if(prog_list[i].loaded)
178                         {
179                                 prog = &prog_list[i];
180                                 return TRUE;
181                         }
182                         else
183                         {
184                                 Con_Printf("%s not loaded !\n",PRVM_NAME);
185                                 return FALSE;
186                         }
187                 }
188
189         Con_Printf("Invalid program name %s !\n", str);
190         return FALSE;
191 }
192
193 /*
194 =================
195 PRVM_SetProg
196 =================
197 */
198 void PRVM_SetProg(int prognr)
199 {
200         if(0 <= prognr && prognr < PRVM_MAXPROGS)
201         {
202                 if(prog_list[prognr].loaded)
203                         prog = &prog_list[prognr];
204                 else
205                         PRVM_ERROR("%i not loaded !", prognr);
206                 return;
207         }
208         PRVM_ERROR("Invalid program number %i", prognr);
209 }
210
211 /*
212 =================
213 PRVM_ED_ClearEdict
214
215 Sets everything to NULL
216 =================
217 */
218 void PRVM_ED_ClearEdict (prvm_edict_t *e)
219 {
220         memset (e->fields.vp, 0, prog->progs->entityfields * 4);
221         e->priv.required->free = false;
222
223         // AK: Let the init_edict function determine if something needs to be initialized
224         PRVM_GCALL(init_edict)(e);
225 }
226
227 const char *PRVM_AllocationOrigin(void)
228 {
229         char *buf = NULL;
230         if(prog->leaktest_active)
231         if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
232         {
233                 buf = (char *)PRVM_Alloc(128);
234                 PRVM_ShortStackTrace(buf, 128);
235         }
236         return buf;
237 }
238
239 /*
240 =================
241 PRVM_ED_CanAlloc
242
243 Returns if this particular edict could get allocated by PRVM_ED_Alloc
244 =================
245 */
246 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
247 {
248         if(!e->priv.required->free)
249                 return false;
250         if(e->priv.required->freetime < prog->starttime + 2)
251                 return true;
252         if(realtime > e->priv.required->freetime + 1)
253                 return true;
254         return false; // entity slot still blocked because the entity was freed less than one second ago
255 }
256
257 /*
258 =================
259 PRVM_ED_Alloc
260
261 Either finds a free edict, or allocates a new one.
262 Try to avoid reusing an entity that was recently freed, because it
263 can cause the client to think the entity morphed into something else
264 instead of being removed and recreated, which can cause interpolated
265 angles and bad trails.
266 =================
267 */
268 prvm_edict_t *PRVM_ED_Alloc (void)
269 {
270         int                     i;
271         prvm_edict_t            *e;
272
273         // the client qc dont need maxclients
274         // thus it doesnt need to use svs.maxclients
275         // AK:  changed i=svs.maxclients+1
276         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
277         //              although the menu/client has no world
278         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
279         {
280                 e = PRVM_EDICT_NUM(i);
281                 if(PRVM_ED_CanAlloc(e))
282                 {
283                         PRVM_ED_ClearEdict (e);
284                         e->priv.required->allocation_origin = PRVM_AllocationOrigin();
285                         return e;
286                 }
287         }
288
289         if (i == prog->limit_edicts)
290                 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
291
292         prog->num_edicts++;
293         if (prog->num_edicts >= prog->max_edicts)
294                 PRVM_MEM_IncreaseEdicts();
295
296         e = PRVM_EDICT_NUM(i);
297         PRVM_ED_ClearEdict (e);
298
299         e->priv.required->allocation_origin = PRVM_AllocationOrigin();
300
301         return e;
302 }
303
304 /*
305 =================
306 PRVM_ED_Free
307
308 Marks the edict as free
309 FIXME: walk all entities and NULL out references to this entity
310 =================
311 */
312 void PRVM_ED_Free (prvm_edict_t *ed)
313 {
314         // dont delete the null entity (world) or reserved edicts
315         if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
316                 return;
317
318         PRVM_GCALL(free_edict)(ed);
319
320         ed->priv.required->free = true;
321         ed->priv.required->freetime = realtime;
322         if(ed->priv.required->allocation_origin)
323         {
324                 PRVM_Free((char *)ed->priv.required->allocation_origin);
325                 ed->priv.required->allocation_origin = NULL;
326         }
327 }
328
329 //===========================================================================
330
331 /*
332 ============
333 PRVM_ED_GlobalAtOfs
334 ============
335 */
336 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
337 {
338         ddef_t          *def;
339         int                     i;
340
341         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
342         {
343                 def = &prog->globaldefs[i];
344                 if (def->ofs == ofs)
345                         return def;
346         }
347         return NULL;
348 }
349
350 /*
351 ============
352 PRVM_ED_FieldAtOfs
353 ============
354 */
355 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
356 {
357         ddef_t          *def;
358         int                     i;
359
360         for (i=0 ; i<prog->progs->numfielddefs ; i++)
361         {
362                 def = &prog->fielddefs[i];
363                 if (def->ofs == ofs)
364                         return def;
365         }
366         return NULL;
367 }
368
369 /*
370 ============
371 PRVM_ED_FindField
372 ============
373 */
374 ddef_t *PRVM_ED_FindField (const char *name)
375 {
376         ddef_t *def;
377         int i;
378
379         for (i=0 ; i<prog->progs->numfielddefs ; i++)
380         {
381                 def = &prog->fielddefs[i];
382                 if (!strcmp(PRVM_GetString(def->s_name), name))
383                         return def;
384         }
385         return NULL;
386 }
387
388 /*
389 ============
390 PRVM_ED_FindGlobal
391 ============
392 */
393 ddef_t *PRVM_ED_FindGlobal (const char *name)
394 {
395         ddef_t *def;
396         int i;
397
398         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
399         {
400                 def = &prog->globaldefs[i];
401                 if (!strcmp(PRVM_GetString(def->s_name), name))
402                         return def;
403         }
404         return NULL;
405 }
406
407
408 /*
409 ============
410 PRVM_ED_FindFunction
411 ============
412 */
413 mfunction_t *PRVM_ED_FindFunction (const char *name)
414 {
415         mfunction_t             *func;
416         int                             i;
417
418         for (i=0 ; i<prog->progs->numfunctions ; i++)
419         {
420                 func = &prog->functions[i];
421                 if (!strcmp(PRVM_GetString(func->s_name), name))
422                         return func;
423         }
424         return NULL;
425 }
426
427
428 /*
429 ============
430 PRVM_ValueString
431
432 Returns a string describing *data in a type specific manner
433 =============
434 */
435 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
436 {
437         static char line[MAX_INPUTLINE];
438         ddef_t *def;
439         mfunction_t *f;
440         int n;
441
442         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
443
444         switch (type)
445         {
446         case ev_string:
447                 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
448                 break;
449         case ev_entity:
450                 n = val->edict;
451                 if (n < 0 || n >= prog->limit_edicts)
452                         dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
453                 else
454                         dpsnprintf (line, sizeof(line), "entity %i", n);
455                 break;
456         case ev_function:
457                 f = prog->functions + val->function;
458                 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
459                 break;
460         case ev_field:
461                 def = PRVM_ED_FieldAtOfs ( val->_int );
462                 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
463                 break;
464         case ev_void:
465                 dpsnprintf (line, sizeof(line), "void");
466                 break;
467         case ev_float:
468                 // LordHavoc: changed from %5.1f to %10.4f
469                 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
470                 break;
471         case ev_vector:
472                 // LordHavoc: changed from %5.1f to %10.4f
473                 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
474                 break;
475         case ev_pointer:
476                 dpsnprintf (line, sizeof(line), "pointer");
477                 break;
478         default:
479                 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
480                 break;
481         }
482
483         return line;
484 }
485
486 /*
487 ============
488 PRVM_UglyValueString
489
490 Returns a string describing *data in a type specific manner
491 Easier to parse than PR_ValueString
492 =============
493 */
494 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
495 {
496         static char line[MAX_INPUTLINE];
497         int i;
498         const char *s;
499         ddef_t *def;
500         mfunction_t *f;
501
502         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
503
504         switch (type)
505         {
506         case ev_string:
507                 // Parse the string a bit to turn special characters
508                 // (like newline, specifically) into escape codes,
509                 // this fixes saving games from various mods
510                 s = PRVM_GetString (val->string);
511                 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
512                 {
513                         if (*s == '\n')
514                         {
515                                 line[i++] = '\\';
516                                 line[i++] = 'n';
517                         }
518                         else if (*s == '\r')
519                         {
520                                 line[i++] = '\\';
521                                 line[i++] = 'r';
522                         }
523                         else if (*s == '\\')
524                         {
525                                 line[i++] = '\\';
526                                 line[i++] = '\\';
527                         }
528                         else if (*s == '"')
529                         {
530                                 line[i++] = '\\';
531                                 line[i++] = '"';
532                         }
533                         else
534                                 line[i++] = *s;
535                         s++;
536                 }
537                 line[i] = '\0';
538                 break;
539         case ev_entity:
540                 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
541                 break;
542         case ev_function:
543                 f = prog->functions + val->function;
544                 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
545                 break;
546         case ev_field:
547                 def = PRVM_ED_FieldAtOfs ( val->_int );
548                 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
549                 break;
550         case ev_void:
551                 dpsnprintf (line, sizeof (line), "void");
552                 break;
553         case ev_float:
554                 dpsnprintf (line, sizeof (line), "%f", val->_float);
555                 break;
556         case ev_vector:
557                 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
558                 break;
559         default:
560                 dpsnprintf (line, sizeof (line), "bad type %i", type);
561                 break;
562         }
563
564         return line;
565 }
566
567 /*
568 ============
569 PRVM_GlobalString
570
571 Returns a string with a description and the contents of a global,
572 padded to 20 field width
573 ============
574 */
575 char *PRVM_GlobalString (int ofs)
576 {
577         char    *s;
578         //size_t        i;
579         ddef_t  *def;
580         void    *val;
581         static char     line[128];
582
583         val = (void *)&prog->globals.generic[ofs];
584         def = PRVM_ED_GlobalAtOfs(ofs);
585         if (!def)
586                 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
587         else
588         {
589                 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
590                 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
591         }
592
593         //i = strlen(line);
594         //for ( ; i<20 ; i++)
595         //      strcat (line," ");
596         //strcat (line," ");
597
598         return line;
599 }
600
601 char *PRVM_GlobalStringNoContents (int ofs)
602 {
603         //size_t        i;
604         ddef_t  *def;
605         static char     line[128];
606
607         def = PRVM_ED_GlobalAtOfs(ofs);
608         if (!def)
609                 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
610         else
611                 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
612
613         //i = strlen(line);
614         //for ( ; i<20 ; i++)
615         //      strcat (line," ");
616         //strcat (line," ");
617
618         return line;
619 }
620
621
622 /*
623 =============
624 PRVM_ED_Print
625
626 For debugging
627 =============
628 */
629 // LordHavoc: optimized this to print out much more quickly (tempstring)
630 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
631 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
632 {
633         size_t  l;
634         ddef_t  *d;
635         int             *v;
636         int             i, j;
637         const char      *name;
638         int             type;
639         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
640
641         if (ed->priv.required->free)
642         {
643                 Con_Printf("%s: FREE\n",PRVM_NAME);
644                 return;
645         }
646
647         tempstring[0] = 0;
648         dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
649         for (i=1 ; i<prog->progs->numfielddefs ; i++)
650         {
651                 d = &prog->fielddefs[i];
652                 name = PRVM_GetString(d->s_name);
653                 if (name[strlen(name)-2] == '_')
654                         continue;       // skip _x, _y, _z vars
655
656                 // Check Field Name Wildcard
657                 if(wildcard_fieldname)
658                         if( !matchpattern(name, wildcard_fieldname, 1) )
659                                 // Didn't match; skip
660                                 continue;
661
662                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
663
664         // if the value is still all 0, skip the field
665                 type = d->type & ~DEF_SAVEGLOBAL;
666
667                 for (j=0 ; j<prvm_type_size[type] ; j++)
668                         if (v[j])
669                                 break;
670                 if (j == prvm_type_size[type])
671                         continue;
672
673                 if (strlen(name) > sizeof(tempstring2)-4)
674                 {
675                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
676                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
677                         tempstring2[sizeof(tempstring2)-1] = 0;
678                         name = tempstring2;
679                 }
680                 strlcat(tempstring, name, sizeof(tempstring));
681                 for (l = strlen(name);l < 14;l++)
682                         strlcat(tempstring, " ", sizeof(tempstring));
683                 strlcat(tempstring, " ", sizeof(tempstring));
684
685                 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
686                 if (strlen(name) > sizeof(tempstring2)-4)
687                 {
688                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
689                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
690                         tempstring2[sizeof(tempstring2)-1] = 0;
691                         name = tempstring2;
692                 }
693                 strlcat(tempstring, name, sizeof(tempstring));
694                 strlcat(tempstring, "\n", sizeof(tempstring));
695                 if (strlen(tempstring) >= sizeof(tempstring)/2)
696                 {
697                         Con_Print(tempstring);
698                         tempstring[0] = 0;
699                 }
700         }
701         if (tempstring[0])
702                 Con_Print(tempstring);
703 }
704
705 /*
706 =============
707 PRVM_ED_Write
708
709 For savegames
710 =============
711 */
712 extern cvar_t developer_entityparsing;
713 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
714 {
715         ddef_t  *d;
716         int             *v;
717         int             i, j;
718         const char      *name;
719         int             type;
720
721         FS_Print(f, "{\n");
722
723         if (ed->priv.required->free)
724         {
725                 FS_Print(f, "}\n");
726                 return;
727         }
728
729         for (i=1 ; i<prog->progs->numfielddefs ; i++)
730         {
731                 d = &prog->fielddefs[i];
732                 name = PRVM_GetString(d->s_name);
733
734                 if(developer_entityparsing.integer)
735                         Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
736
737                 if (name[strlen(name)-2] == '_')
738                         continue;       // skip _x, _y, _z vars
739
740                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
741
742         // if the value is still all 0, skip the field
743                 type = d->type & ~DEF_SAVEGLOBAL;
744                 for (j=0 ; j<prvm_type_size[type] ; j++)
745                         if (v[j])
746                                 break;
747                 if (j == prvm_type_size[type])
748                         continue;
749
750                 FS_Printf(f,"\"%s\" ",name);
751                 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
752                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
753                 prog->statestring = NULL;
754         }
755
756         FS_Print(f, "}\n");
757 }
758
759 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
760 {
761         PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
762 }
763
764 /*
765 =============
766 PRVM_ED_PrintEdicts_f
767
768 For debugging, prints all the entities in the current server
769 =============
770 */
771 void PRVM_ED_PrintEdicts_f (void)
772 {
773         int             i;
774         const char *wildcard_fieldname;
775
776         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
777         {
778                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
779                 return;
780         }
781
782         PRVM_Begin;
783         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
784                 return;
785
786         if( Cmd_Argc() == 3)
787                 wildcard_fieldname = Cmd_Argv(2);
788         else
789                 wildcard_fieldname = NULL;
790
791         Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
792         for (i=0 ; i<prog->num_edicts ; i++)
793                 PRVM_ED_PrintNum (i, wildcard_fieldname);
794
795         PRVM_End;
796 }
797
798 /*
799 =============
800 PRVM_ED_PrintEdict_f
801
802 For debugging, prints a single edict
803 =============
804 */
805 void PRVM_ED_PrintEdict_f (void)
806 {
807         int             i;
808         const char      *wildcard_fieldname;
809
810         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
811         {
812                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
813                 return;
814         }
815
816         PRVM_Begin;
817         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
818                 return;
819
820         i = atoi (Cmd_Argv(2));
821         if (i >= prog->num_edicts)
822         {
823                 Con_Print("Bad edict number\n");
824                 PRVM_End;
825                 return;
826         }
827         if( Cmd_Argc() == 4)
828                 // Optional Wildcard Provided
829                 wildcard_fieldname = Cmd_Argv(3);
830         else
831                 // Use All
832                 wildcard_fieldname = NULL;
833         PRVM_ED_PrintNum (i, wildcard_fieldname);
834
835         PRVM_End;
836 }
837
838 /*
839 =============
840 PRVM_ED_Count
841
842 For debugging
843 =============
844 */
845 // 2 possibilities : 1. just displaying the active edict count
846 //                                       2. making a function pointer [x]
847 void PRVM_ED_Count_f (void)
848 {
849         int             i;
850         prvm_edict_t    *ent;
851         int             active;
852
853         if(Cmd_Argc() != 2)
854         {
855                 Con_Print("prvm_count <program name>\n");
856                 return;
857         }
858
859         PRVM_Begin;
860         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
861                 return;
862
863         if(prog->count_edicts)
864                 prog->count_edicts();
865         else
866         {
867                 active = 0;
868                 for (i=0 ; i<prog->num_edicts ; i++)
869                 {
870                         ent = PRVM_EDICT_NUM(i);
871                         if (ent->priv.required->free)
872                                 continue;
873                         active++;
874                 }
875
876                 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
877                 Con_Printf("active    :%3i\n", active);
878         }
879
880         PRVM_End;
881 }
882
883 /*
884 ==============================================================================
885
886                                         ARCHIVING GLOBALS
887
888 FIXME: need to tag constants, doesn't really work
889 ==============================================================================
890 */
891
892 /*
893 =============
894 PRVM_ED_WriteGlobals
895 =============
896 */
897 void PRVM_ED_WriteGlobals (qfile_t *f)
898 {
899         ddef_t          *def;
900         int                     i;
901         const char              *name;
902         int                     type;
903
904         FS_Print(f,"{\n");
905         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
906         {
907                 def = &prog->globaldefs[i];
908                 type = def->type;
909                 if ( !(def->type & DEF_SAVEGLOBAL) )
910                         continue;
911                 type &= ~DEF_SAVEGLOBAL;
912
913                 if (type != ev_string && type != ev_float && type != ev_entity)
914                         continue;
915
916                 name = PRVM_GetString(def->s_name);
917
918                 if(developer_entityparsing.integer)
919                         Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
920
921                 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
922                 FS_Printf(f,"\"%s\" ", name);
923                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
924                 prog->statestring = NULL;
925         }
926         FS_Print(f,"}\n");
927 }
928
929 /*
930 =============
931 PRVM_ED_ParseGlobals
932 =============
933 */
934 void PRVM_ED_ParseGlobals (const char *data)
935 {
936         char keyname[MAX_INPUTLINE];
937         ddef_t *key;
938
939         while (1)
940         {
941                 // parse key
942                 if (!COM_ParseToken_Simple(&data, false, false))
943                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
944                 if (com_token[0] == '}')
945                         break;
946
947                 if (developer_entityparsing.integer)
948                         Con_Printf("Key: \"%s\"", com_token);
949
950                 strlcpy (keyname, com_token, sizeof(keyname));
951
952                 // parse value
953                 if (!COM_ParseToken_Simple(&data, false, true))
954                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
955
956                 if (developer_entityparsing.integer)
957                         Con_Printf(" \"%s\"\n", com_token);
958
959                 if (com_token[0] == '}')
960                         PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
961
962                 key = PRVM_ED_FindGlobal (keyname);
963                 if (!key)
964                 {
965                         Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
966                         continue;
967                 }
968
969                 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
970                         PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
971         }
972 }
973
974 //============================================================================
975
976
977 /*
978 =============
979 PRVM_ED_ParseEval
980
981 Can parse either fields or globals
982 returns false if error
983 =============
984 */
985 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
986 {
987         int i, l;
988         char *new_p;
989         ddef_t *def;
990         prvm_eval_t *val;
991         mfunction_t *func;
992
993         if (ent)
994                 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
995         else
996                 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
997         switch (key->type & ~DEF_SAVEGLOBAL)
998         {
999         case ev_string:
1000                 l = (int)strlen(s) + 1;
1001                 val->string = PRVM_AllocString(l, &new_p);
1002                 for (i = 0;i < l;i++)
1003                 {
1004                         if (s[i] == '\\' && s[i+1] && parsebackslash)
1005                         {
1006                                 i++;
1007                                 if (s[i] == 'n')
1008                                         *new_p++ = '\n';
1009                                 else if (s[i] == 'r')
1010                                         *new_p++ = '\r';
1011                                 else
1012                                         *new_p++ = s[i];
1013                         }
1014                         else
1015                                 *new_p++ = s[i];
1016                 }
1017                 break;
1018
1019         case ev_float:
1020                 while (*s && ISWHITESPACE(*s))
1021                         s++;
1022                 val->_float = atof(s);
1023                 break;
1024
1025         case ev_vector:
1026                 for (i = 0;i < 3;i++)
1027                 {
1028                         while (*s && ISWHITESPACE(*s))
1029                                 s++;
1030                         if (!*s)
1031                                 break;
1032                         val->vector[i] = atof(s);
1033                         while (!ISWHITESPACE(*s))
1034                                 s++;
1035                         if (!*s)
1036                                 break;
1037                 }
1038                 break;
1039
1040         case ev_entity:
1041                 while (*s && ISWHITESPACE(*s))
1042                         s++;
1043                 i = atoi(s);
1044                 if (i >= prog->limit_edicts)
1045                         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);
1046                 while (i >= prog->max_edicts)
1047                         PRVM_MEM_IncreaseEdicts();
1048                 // if IncreaseEdicts was called the base pointer needs to be updated
1049                 if (ent)
1050                         val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1051                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1052                 break;
1053
1054         case ev_field:
1055                 if (*s != '.')
1056                 {
1057                         Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1058                         return false;
1059                 }
1060                 def = PRVM_ED_FindField(s + 1);
1061                 if (!def)
1062                 {
1063                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1064                         return false;
1065                 }
1066                 val->_int = def->ofs;
1067                 break;
1068
1069         case ev_function:
1070                 func = PRVM_ED_FindFunction(s);
1071                 if (!func)
1072                 {
1073                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1074                         return false;
1075                 }
1076                 val->function = func - prog->functions;
1077                 break;
1078
1079         default:
1080                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1081                 return false;
1082         }
1083         return true;
1084 }
1085
1086 /*
1087 =============
1088 PRVM_GameCommand_f
1089
1090 Console command to send a string to QC function GameCommand of the
1091 indicated progs
1092
1093 Usage:
1094   sv_cmd adminmsg 3 "do not teamkill"
1095   cl_cmd someclientcommand
1096   menu_cmd somemenucommand
1097
1098 All progs can support this extension; sg calls it in server QC, cg in client
1099 QC, mg in menu QC.
1100 =============
1101 */
1102 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1103 {
1104         if(Cmd_Argc() < 1)
1105         {
1106                 Con_Printf("%s text...\n", whichcmd);
1107                 return;
1108         }
1109
1110         PRVM_Begin;
1111         if(!PRVM_SetProgFromString(whichprogs))
1112         // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1113         // also, it makes printing error messages easier!
1114         {
1115                 Con_Printf("%s program not loaded.\n", whichprogs);
1116                 return;
1117         }
1118
1119         if(!prog->funcoffsets.GameCommand)
1120         {
1121                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1122         }
1123         else
1124         {
1125                 int restorevm_tempstringsbuf_cursize;
1126                 const char *s;
1127
1128                 s = Cmd_Args();
1129
1130                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1132                 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1133                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1134         }
1135
1136         PRVM_End;
1137 }
1138 void PRVM_GameCommand_Server_f(void)
1139 {
1140         PRVM_GameCommand("server", "sv_cmd");
1141 }
1142 void PRVM_GameCommand_Client_f(void)
1143 {
1144         PRVM_GameCommand("client", "cl_cmd");
1145 }
1146 void PRVM_GameCommand_Menu_f(void)
1147 {
1148         PRVM_GameCommand("menu", "menu_cmd");
1149 }
1150
1151 /*
1152 =============
1153 PRVM_ED_EdictSet_f
1154
1155 Console command to set a field of a specified edict
1156 =============
1157 */
1158 void PRVM_ED_EdictSet_f(void)
1159 {
1160         prvm_edict_t *ed;
1161         ddef_t *key;
1162
1163         if(Cmd_Argc() != 5)
1164         {
1165                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1166                 return;
1167         }
1168
1169         PRVM_Begin;
1170         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1171         {
1172                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1173                 return;
1174         }
1175
1176         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1177
1178         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1179                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1180         else
1181                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1182
1183         PRVM_End;
1184 }
1185
1186 /*
1187 ====================
1188 PRVM_ED_ParseEdict
1189
1190 Parses an edict out of the given string, returning the new position
1191 ed should be a properly initialized empty edict.
1192 Used for initial level load and for savegames.
1193 ====================
1194 */
1195 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1196 {
1197         ddef_t *key;
1198         qboolean anglehack;
1199         qboolean init;
1200         char keyname[256];
1201         size_t n;
1202
1203         init = false;
1204
1205 // go through all the dictionary pairs
1206         while (1)
1207         {
1208         // parse key
1209                 if (!COM_ParseToken_Simple(&data, false, false))
1210                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1211                 if (developer_entityparsing.integer)
1212                         Con_Printf("Key: \"%s\"", com_token);
1213                 if (com_token[0] == '}')
1214                         break;
1215
1216                 // anglehack is to allow QuakeEd to write single scalar angles
1217                 // and allow them to be turned into vectors. (FIXME...)
1218                 if (!strcmp(com_token, "angle"))
1219                 {
1220                         strlcpy (com_token, "angles", sizeof(com_token));
1221                         anglehack = true;
1222                 }
1223                 else
1224                         anglehack = false;
1225
1226                 // FIXME: change light to _light to get rid of this hack
1227                 if (!strcmp(com_token, "light"))
1228                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1229
1230                 strlcpy (keyname, com_token, sizeof(keyname));
1231
1232                 // another hack to fix keynames with trailing spaces
1233                 n = strlen(keyname);
1234                 while (n && keyname[n-1] == ' ')
1235                 {
1236                         keyname[n-1] = 0;
1237                         n--;
1238                 }
1239
1240         // parse value
1241                 if (!COM_ParseToken_Simple(&data, false, false))
1242                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1243                 if (developer_entityparsing.integer)
1244                         Con_Printf(" \"%s\"\n", com_token);
1245
1246                 if (com_token[0] == '}')
1247                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1248
1249                 init = true;
1250
1251                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1252                 if (!keyname[0])
1253                         continue;
1254
1255 // keynames with a leading underscore are used for utility comments,
1256 // and are immediately discarded by quake
1257                 if (keyname[0] == '_')
1258                         continue;
1259
1260                 key = PRVM_ED_FindField (keyname);
1261                 if (!key)
1262                 {
1263                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1264                         continue;
1265                 }
1266
1267                 if (anglehack)
1268                 {
1269                         char    temp[32];
1270                         strlcpy (temp, com_token, sizeof(temp));
1271                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1272                 }
1273
1274                 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1275                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1276         }
1277
1278         if (!init)
1279                 ent->priv.required->free = true;
1280
1281         return data;
1282 }
1283
1284
1285 /*
1286 ================
1287 PRVM_ED_LoadFromFile
1288
1289 The entities are directly placed in the array, rather than allocated with
1290 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1291 number references out of order.
1292
1293 Creates a server's entity / program execution context by
1294 parsing textual entity definitions out of an ent file.
1295
1296 Used for both fresh maps and savegame loads.  A fresh map would also need
1297 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1298 ================
1299 */
1300 void PRVM_ED_LoadFromFile (const char *data)
1301 {
1302         prvm_edict_t *ent;
1303         int parsed, inhibited, spawned, died;
1304         const char *funcname;
1305         mfunction_t *func;
1306
1307         parsed = 0;
1308         inhibited = 0;
1309         spawned = 0;
1310         died = 0;
1311
1312
1313 // parse ents
1314         while (1)
1315         {
1316 // parse the opening brace
1317                 if (!COM_ParseToken_Simple(&data, false, false))
1318                         break;
1319                 if (com_token[0] != '{')
1320                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1321
1322                 // CHANGED: this is not conform to PR_LoadFromFile
1323                 if(prog->loadintoworld)
1324                 {
1325                         prog->loadintoworld = false;
1326                         ent = PRVM_EDICT_NUM(0);
1327                 }
1328                 else
1329                         ent = PRVM_ED_Alloc();
1330
1331                 // clear it
1332                 if (ent != prog->edicts)        // hack
1333                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1334
1335                 data = PRVM_ED_ParseEdict (data, ent);
1336                 parsed++;
1337
1338                 // remove the entity ?
1339                 if(prog->load_edict && !prog->load_edict(ent))
1340                 {
1341                         PRVM_ED_Free(ent);
1342                         inhibited++;
1343                         continue;
1344                 }
1345
1346                 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1347                 {
1348                         // self = ent
1349                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1350                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1351                 }
1352
1353                 if(ent->priv.required->free)
1354                 {
1355                         inhibited++;
1356                         continue;
1357                 }
1358
1359 //
1360 // immediately call spawn function, but only if there is a self global and a classname
1361 //
1362                 if(!ent->priv.required->free)
1363                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1364                 {
1365                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1366                         if (!handle)
1367                         {
1368                                 Con_Print("No classname for:\n");
1369                                 PRVM_ED_Print(ent, NULL);
1370                                 PRVM_ED_Free (ent);
1371                                 continue;
1372                         }
1373
1374                         // look for the spawn function
1375                         funcname = PRVM_GetString(handle);
1376                         func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1377                         if(!func)
1378                                 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1379                                         func = PRVM_ED_FindFunction (funcname);
1380
1381                         if (!func)
1382                         {
1383                                 // check for OnEntityNoSpawnFunction
1384                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1385                                 {
1386                                         // self = ent
1387                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1388                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1389                                 }
1390                                 else
1391                                 {
1392                                         if (developer.integer) // don't confuse non-developers with errors
1393                                         {
1394                                                 Con_Print("No spawn function for:\n");
1395                                                 PRVM_ED_Print(ent, NULL);
1396                                         }
1397                                         PRVM_ED_Free (ent);
1398                                         continue; // not included in "inhibited" count
1399                                 }
1400                         }
1401                         else
1402                         {
1403                                 // self = ent
1404                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1405                                 PRVM_ExecuteProgram (func - prog->functions, "");
1406                         }
1407                 }
1408
1409                 if(!ent->priv.required->free)
1410                 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1411                 {
1412                         // self = ent
1413                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1414                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1415                 }
1416
1417                 spawned++;
1418                 if (ent->priv.required->free)
1419                         died++;
1420         }
1421
1422         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);
1423 }
1424
1425 void PRVM_FindOffsets(void)
1426 {
1427         // field and global searches use -1 for NULL
1428         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1429         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1430         // functions use 0 for NULL
1431         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1432
1433         // server and client qc use a lot of similar fields, so this is combined
1434         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1435         prog->fieldoffsets.SendFlags                      = PRVM_ED_FindFieldOffset("SendFlags");
1436         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1437         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1438         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1439         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1440         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1441         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1442         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1443         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1444         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1445         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1446         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1447         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1448         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1449         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1450         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1451         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1452         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1453         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1454         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1455         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1456         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1457         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1458         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1459         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1460         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1461         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1462         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1463         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1464         prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
1465         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1466         prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
1467         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1468         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1469         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1470         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1471         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1472         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1473         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1474         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1475         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1476         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1477         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1478         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1479         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1480         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1481         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1482         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1483         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1484         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1485         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1486         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1487         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1488         prog->fieldoffsets.frame3                         = PRVM_ED_FindFieldOffset("frame3");
1489         prog->fieldoffsets.frame3time                     = PRVM_ED_FindFieldOffset("frame3time");
1490         prog->fieldoffsets.frame4                         = PRVM_ED_FindFieldOffset("frame4");
1491         prog->fieldoffsets.frame4time                     = PRVM_ED_FindFieldOffset("frame4time");
1492         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1493         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1494         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1495         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1496         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1497         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1498         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1499         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1500         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1501         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1502         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1503         prog->fieldoffsets.lerpfrac3                      = PRVM_ED_FindFieldOffset("lerpfrac3");
1504         prog->fieldoffsets.lerpfrac4                      = PRVM_ED_FindFieldOffset("lerpfrac4");
1505         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1506         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1507         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1508         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1509         prog->fieldoffsets.movetypesteplandevent          = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1510         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1511         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1512         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1513         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1514         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1515         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1516         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1517         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1518         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1519         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1520         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1521         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1522         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1523         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1524         prog->fieldoffsets.shadertime                     = PRVM_ED_FindFieldOffset("shadertime");
1525         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1526         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1527         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1528         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1529         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1530         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1531         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1532         prog->fieldoffsets.bouncefactor                   = PRVM_ED_FindFieldOffset("bouncefactor");
1533         prog->fieldoffsets.bouncestop                     = PRVM_ED_FindFieldOffset("bouncestop");
1534         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1535         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1536         prog->funcoffsets.CSQC_Ent_Spawn                  = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1537         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1538         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1539         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1540         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1541         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1542         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1543         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1544         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1545         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1546         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1547         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1548         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1549         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1550         prog->funcoffsets.Gecko_Query                     = PRVM_ED_FindFunctionOffset("Gecko_Query");
1551         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1552         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1553         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1554         prog->funcoffsets.SV_OnEntityPostSpawnFunction    = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1555         prog->funcoffsets.SV_OnEntityPreSpawnFunction     = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1556         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1557         prog->funcoffsets.SV_PausedTic                    = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1558         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1559         prog->funcoffsets.SV_Shutdown                     = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1560         prog->funcoffsets.URI_Get_Callback                = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1561         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1562         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1563         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1564         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1565         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1566         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1567         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1568         prog->globaloffsets.gettaginfo_forward            = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1569         prog->globaloffsets.gettaginfo_name               = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1570         prog->globaloffsets.gettaginfo_offset             = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1571         prog->globaloffsets.gettaginfo_parent             = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1572         prog->globaloffsets.gettaginfo_right              = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1573         prog->globaloffsets.gettaginfo_up                 = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1574         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1575         prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1576         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1577         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1578         prog->globaloffsets.serverdeltatime               = PRVM_ED_FindGlobalOffset("serverdeltatime");
1579         prog->globaloffsets.serverprevtime                = PRVM_ED_FindGlobalOffset("serverprevtime");
1580         prog->globaloffsets.servertime                    = PRVM_ED_FindGlobalOffset("servertime");
1581         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1582         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1583         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1584         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1585         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1586         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1587         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1588         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1589         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1590         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1591         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1592         prog->globaloffsets.trace_networkentity           = PRVM_ED_FindGlobalOffset("trace_networkentity");
1593         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1594         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1595         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1596         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1597         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1598         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1599         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1600         prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
1601
1602         // menu qc only uses some functions, nothing else
1603         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1604         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1605         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1606         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1607         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1608         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1609 }
1610
1611 // not used
1612 /*
1613 typedef struct dpfield_s
1614 {
1615         int type;
1616         char *string;
1617 }
1618 dpfield_t;
1619
1620 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1621
1622 dpfield_t dpfields[] =
1623 {
1624 };
1625 */
1626
1627 /*
1628 ===============
1629 PRVM_ResetProg
1630 ===============
1631 */
1632
1633 void PRVM_LeakTest(void);
1634 void PRVM_ResetProg(void)
1635 {
1636         PRVM_LeakTest();
1637         PRVM_GCALL(reset_cmd)();
1638         Mem_FreePool(&prog->progs_mempool);
1639         memset(prog,0,sizeof(prvm_prog_t));
1640         prog->starttime = Sys_DoubleTime();
1641 }
1642
1643 /*
1644 ===============
1645 PRVM_LoadLNO
1646 ===============
1647 */
1648 void PRVM_LoadLNO( const char *progname ) {
1649         fs_offset_t filesize;
1650         unsigned char *lno;
1651         unsigned int *header;
1652         char filename[512];
1653
1654         FS_StripExtension( progname, filename, sizeof( filename ) );
1655         strlcat( filename, ".lno", sizeof( filename ) );
1656
1657         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1658         if( !lno ) {
1659                 return;
1660         }
1661
1662 /*
1663 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1664 <Spike>    SafeWrite (h, &version, sizeof(int));
1665 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1666 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1667 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1668 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1669 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1670 */
1671         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1672                 Mem_Free(lno);
1673                 return;
1674         }
1675
1676         header = (unsigned int *) lno;
1677         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1678                 LittleLong( header[ 1 ] ) == 1 &&
1679                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1680                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1681                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1682                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1683         {
1684                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1685                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1686         }
1687         Mem_Free( lno );
1688 }
1689
1690 /*
1691 ===============
1692 PRVM_LoadProgs
1693 ===============
1694 */
1695 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1696 {
1697         int i;
1698         dstatement_t *st;
1699         ddef_t *infielddefs;
1700         dfunction_t *dfunctions;
1701         fs_offset_t filesize;
1702
1703         if( prog->loaded ) {
1704                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1705         }
1706
1707         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1708         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1709                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1710         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1711
1712         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1713
1714         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1715
1716 // byte swap the header
1717         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1718                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1719
1720         if (prog->progs->version != PROG_VERSION && prog->progs->version != PROG_EXTENDED)
1721                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i or %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION, PROG_EXTENDED);
1722         if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1723                 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);
1724
1725         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1726         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1727
1728         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1729                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1730         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1731         prog->stringssize = prog->progs->numstrings;
1732
1733         prog->numknownstrings = 0;
1734         prog->maxknownstrings = 0;
1735         prog->knownstrings = NULL;
1736         prog->knownstrings_freeable = NULL;
1737
1738         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1739
1740         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1741
1742         // we need to expand the fielddefs list to include all the engine fields,
1743         // so allocate a new place for it
1744         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1745         //                                                                                              ( + DPFIELDS                       )
1746         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1747
1748         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1749
1750         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1751
1752         // moved edict_size calculation down below field adding code
1753
1754         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1755         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1756
1757 // byte swap the lumps
1758         for (i=0 ; i<prog->progs->numstatements ; i++)
1759         {
1760                 prog->statements[i].op = LittleShort(prog->statements[i].op);
1761                 prog->statements[i].a = LittleShort(prog->statements[i].a);
1762                 prog->statements[i].b = LittleShort(prog->statements[i].b);
1763                 prog->statements[i].c = LittleShort(prog->statements[i].c);
1764         }
1765
1766         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1767         for (i = 0;i < prog->progs->numfunctions;i++)
1768         {
1769                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1770                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1771                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1772                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1773                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1774                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1775                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1776                 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1777                         PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1778                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1779         }
1780
1781         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1782         {
1783                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1784                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1785                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1786                 // TODO bounds check ofs, s_name
1787         }
1788
1789         // copy the progs fields to the new fields list
1790         for (i = 0;i < prog->progs->numfielddefs;i++)
1791         {
1792                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1793                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1794                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1795                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1796                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1797                 // TODO bounds check ofs, s_name
1798         }
1799
1800         // append the required fields
1801         for (i = 0;i < (int) numrequiredfields;i++)
1802         {
1803                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1804                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1805                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1806                 // TODO bounds check ofs, s_name
1807                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1808                         prog->progs->entityfields += 3;
1809                 else
1810                         prog->progs->entityfields++;
1811                 prog->progs->numfielddefs++;
1812         }
1813
1814         // check required functions
1815         for(i=0 ; i < numrequiredfunc ; i++)
1816                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1817                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1818
1819         // check required globals
1820         for(i=0 ; i < numrequiredglobals ; i++)
1821                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1822                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1823
1824         for (i=0 ; i<prog->progs->numglobals ; i++)
1825                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1826
1827         // moved edict_size calculation down here, below field adding code
1828         // LordHavoc: this no longer includes the prvm_edict_t header
1829         prog->edict_size = prog->progs->entityfields * 4;
1830         prog->edictareasize = prog->edict_size * prog->limit_edicts;
1831
1832         // LordHavoc: bounds check anything static
1833         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1834         {
1835                 switch (st->op)
1836                 {
1837                 case OP_IF:
1838                 case OP_IFNOT:
1839                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1840                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (int/general) (statement %d) in %s", i, PRVM_NAME);
1841                         break;
1842                 case OP_IF_F:
1843                 case OP_IFNOT_F:
1844                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1845                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (float) (statement %d) in %s", i, PRVM_NAME);
1846                         break;
1847                 case OP_IFS:
1848                 case OP_IFNOTS:
1849                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1850                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (string not null) (statement %d) in %s", i, PRVM_NAME);
1851                         break;
1852                 case OP_GOTO:
1853                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1854                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1855                         break;
1856                 case OP_CASE:
1857                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1858                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASE (statement %d) in %s", i, PRVM_NAME);
1859                         break;
1860                 case OP_CASERANGE:
1861                         if ((unsigned short) st->a >= prog->progs->numglobals ||
1862                             (unsigned short) st->b >= prog->progs->numglobals ||
1863                             st->c + i < 0 || st->c + i >= prog->progs->numstatements)
1864                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASERANGE (statement %d) in %s", i, PRVM_NAME);
1865                         break;
1866                 // global global global
1867                 case OP_ADD_F:
1868                 case OP_ADD_V:
1869                 case OP_ADD_I:
1870                 case OP_ADD_FI:
1871                 case OP_ADD_IF:
1872                 case OP_SUB_F:
1873                 case OP_SUB_I:
1874                 case OP_SUB_FI:
1875                 case OP_SUB_IF:
1876                 case OP_SUB_V:
1877                 case OP_MUL_F:
1878                 case OP_MUL_V:
1879                 case OP_MUL_FV:
1880                 case OP_MUL_VF:
1881                 case OP_MUL_I:
1882                 case OP_MUL_IF:
1883                 case OP_MUL_FI:
1884                 case OP_MUL_VI:
1885                 case OP_DIV_F:
1886                 case OP_DIV_VF:
1887                 case OP_DIV_I:
1888                 case OP_DIV_IF:
1889                 case OP_DIV_FI:
1890                 case OP_BITAND:
1891                 case OP_BITAND_I:
1892                 case OP_BITAND_IF:
1893                 case OP_BITAND_FI:
1894                 case OP_BITOR:
1895                 case OP_BITOR_I:
1896                 case OP_BITOR_IF:
1897                 case OP_BITOR_FI:
1898                 case OP_GE:
1899                 case OP_LE:
1900                 case OP_GT:
1901                 case OP_LT:
1902                 case OP_GE_I:
1903                 case OP_LE_I:
1904                 case OP_GT_I:
1905                 case OP_LT_I:
1906                 case OP_GE_IF:
1907                 case OP_LE_IF:
1908                 case OP_GT_IF:
1909                 case OP_LT_IF:
1910                 case OP_GE_FI:
1911                 case OP_LE_FI:
1912                 case OP_GT_FI:
1913                 case OP_LT_FI:
1914                 case OP_AND:
1915                 case OP_AND_I:
1916                 case OP_AND_IF:
1917                 case OP_AND_FI:
1918                 case OP_OR:
1919                 case OP_OR_I:
1920                 case OP_OR_IF:
1921                 case OP_OR_FI:
1922                 case OP_EQ_F:
1923                 case OP_EQ_V:
1924                 case OP_EQ_S:
1925                 case OP_EQ_E:
1926                 case OP_EQ_FNC:
1927                 case OP_EQ_I:
1928                 case OP_EQ_IF:
1929                 case OP_EQ_FI:
1930                 case OP_NE_F:
1931                 case OP_NE_V:
1932                 case OP_NE_S:
1933                 case OP_NE_E:
1934                 case OP_NE_FNC:
1935                 case OP_NE_I:
1936                 case OP_NE_IF:
1937                 case OP_NE_FI:
1938                 case OP_ADDRESS:
1939                 case OP_LOAD_F:
1940                 case OP_LOAD_FLD:
1941                 case OP_LOAD_ENT:
1942                 case OP_LOAD_S:
1943                 case OP_LOAD_FNC:
1944                 case OP_LOAD_V:
1945                 case OP_LOAD_P:
1946                 case OP_LOAD_I:
1947                 case OP_LOADA_I:
1948                 case OP_LOADA_F:
1949                 case OP_LOADA_FLD:
1950                 case OP_LOADA_ENT:
1951                 case OP_LOADA_S:
1952                 case OP_LOADA_FNC:
1953                 case OP_LOADA_V:
1954                 case OP_LOADP_I:
1955                 case OP_LOADP_F:
1956                 case OP_LOADP_FLD:
1957                 case OP_LOADP_ENT:
1958                 case OP_LOADP_S:
1959                 case OP_LOADP_FNC:
1960                 case OP_LOADP_V:
1961                 case OP_LOADP_C:
1962                 /*
1963                 case OP_GLOAD_I:
1964                 case OP_GLOAD_F:
1965                 case OP_GLOAD_FLD:
1966                 case OP_GLOAD_ENT:
1967                 case OP_GLOAD_S:
1968                 case OP_GLOAD_FNC:
1969                 case OP_GLOAD_V:
1970                 */
1971                 case OP_BOUNDCHECK:
1972                 case OP_ADDSTOREP_F:
1973                 case OP_ADDSTOREP_V:
1974                 case OP_SUBSTOREP_F:
1975                 case OP_SUBSTOREP_V:
1976                 case OP_MULSTOREP_F:
1977                 case OP_MULSTOREP_V:
1978                 case OP_DIVSTOREP_F:
1979                 case OP_GLOBALADDRESS:
1980                 case OP_POINTER_ADD:
1981                 case OP_ADD_SF:
1982                 case OP_SUB_S:
1983                 case OP_XOR_I:
1984                 case OP_RSHIFT_I:
1985                 case OP_LSHIFT_I:
1986                 case OP_FETCH_GBL_F:
1987                 case OP_FETCH_GBL_S:
1988                 case OP_FETCH_GBL_E:
1989                 case OP_FETCH_GBL_FNC:
1990                 case OP_FETCH_GBL_V:
1991                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1992                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1993                         break;
1994                 // global none global
1995                 case OP_NOT_F:
1996                 case OP_NOT_V:
1997                 case OP_NOT_S:
1998                 case OP_NOT_FNC:
1999                 case OP_NOT_ENT:
2000                 case OP_NOT_I:
2001                 case OP_CONV_ITOF:
2002                 case OP_CONV_FTOI:
2003                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2004                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2005                         break;
2006                 // 2 globals
2007                 case OP_STOREP_F:
2008                 case OP_STOREP_ENT:
2009                 case OP_STOREP_FLD:
2010                 case OP_STOREP_S:
2011                 case OP_STOREP_FNC:
2012                 case OP_STORE_F:
2013                 case OP_STORE_ENT:
2014                 case OP_STORE_FLD:
2015                 case OP_STORE_S:
2016                 case OP_STORE_FNC:
2017                 case OP_STORE_I:
2018                 case OP_STATE:
2019                 case OP_STOREP_V:
2020                 case OP_STORE_V:
2021                 case OP_STORE_P:
2022                 case OP_STORE_IF:
2023                 case OP_STORE_FI:
2024                 case OP_STOREP_P:
2025                 case OP_STOREP_I:
2026                 // case OP_STOREP_C:
2027                 /*
2028                 case OP_GSTOREP_I:
2029                 case OP_GSTOREP_F:
2030                 case OP_GSTOREP_ENT:
2031                 case OP_GSTOREP_FLD:
2032                 case OP_GSTOREP_S:
2033                 case OP_GSTOREP_FNC:
2034                 case OP_GSTOREP_V:
2035                 case OP_GADDRESS:
2036                 */
2037                 case OP_RAND2:
2038                 case OP_RANDV2:
2039                 case OP_BITSET:
2040                 case OP_BITCLR:
2041                 case OP_ADDSTORE_F:
2042                 case OP_ADDSTORE_V:
2043                 case OP_SUBSTORE_F:
2044                 case OP_SUBSTORE_V:
2045                 case OP_MULSTORE_F:
2046                 case OP_MULSTORE_V:
2047                 case OP_DIVSTORE_F:
2048                 //case OP_CSTATE:
2049                 //case OP_CWSTATE:
2050                 case OP_THINKTIME:
2051                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2052                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2053                         break;
2054                 // 1 global
2055                 case OP_CALL0:
2056                 case OP_CALL1:
2057                 case OP_CALL2:
2058                 case OP_CALL3:
2059                 case OP_CALL4:
2060                 case OP_CALL5:
2061                 case OP_CALL6:
2062                 case OP_CALL7:
2063                 case OP_CALL8:
2064                 case OP_CALL1H:
2065                 case OP_CALL2H:
2066                 case OP_CALL3H:
2067                 case OP_CALL4H:
2068                 case OP_CALL5H:
2069                 case OP_CALL6H:
2070                 case OP_CALL7H:
2071                 case OP_CALL8H:
2072                 case OP_DONE:
2073                 case OP_RETURN:
2074                 case OP_RAND1:
2075                 case OP_RANDV1:
2076                 case OP_SWITCH_F:
2077                 case OP_SWITCH_V:
2078                 case OP_SWITCH_S:
2079                 case OP_SWITCH_E:
2080                 case OP_SWITCH_I:
2081                 case OP_SWITCH_FNC:
2082                         if ((unsigned short) st->a >= prog->progs->numglobals)
2083                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2084                         break;
2085                         // TODO: add fte instruction support here too
2086                 case OP_RAND0:
2087                 case OP_RANDV0:
2088                         // this writes to OFS_RETURN and has no parameters
2089                         break;
2090                 default:
2091                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2092                         break;
2093                 }
2094         }
2095         if(prog->progs->numstatements < 1)
2096         {
2097                 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2098         }
2099         else switch(prog->statements[prog->progs->numstatements - 1].op)
2100         {
2101                 case OP_RETURN:
2102                 case OP_GOTO:
2103                 case OP_DONE:
2104                         break;
2105                 default:
2106                         PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2107                         break;
2108         }
2109
2110         PRVM_LoadLNO(filename);
2111
2112         PRVM_Init_Exec();
2113
2114         prog->loaded = TRUE;
2115
2116         // set flags & ddef_ts in prog
2117
2118         prog->flag = 0;
2119
2120         PRVM_FindOffsets();
2121
2122         PRVM_GCALL(init_cmd)();
2123
2124         // init mempools
2125         PRVM_MEM_Alloc();
2126 }
2127
2128
2129 void PRVM_Fields_f (void)
2130 {
2131         int i, j, ednum, used, usedamount;
2132         int *counts;
2133         char tempstring[MAX_INPUTLINE], tempstring2[260];
2134         const char *name;
2135         prvm_edict_t *ed;
2136         ddef_t *d;
2137         int *v;
2138
2139         // TODO
2140         /*
2141         if (!sv.active)
2142         {
2143                 Con_Print("no progs loaded\n");
2144                 return;
2145         }
2146         */
2147
2148         if(Cmd_Argc() != 2)
2149         {
2150                 Con_Print("prvm_fields <program name>\n");
2151                 return;
2152         }
2153
2154         PRVM_Begin;
2155         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2156                 return;
2157
2158         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2159         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2160         {
2161                 ed = PRVM_EDICT_NUM(ednum);
2162                 if (ed->priv.required->free)
2163                         continue;
2164                 for (i = 1;i < prog->progs->numfielddefs;i++)
2165                 {
2166                         d = &prog->fielddefs[i];
2167                         name = PRVM_GetString(d->s_name);
2168                         if (name[strlen(name)-2] == '_')
2169                                 continue;       // skip _x, _y, _z vars
2170                         v = (int *)((char *)ed->fields.vp + d->ofs*4);
2171                         // if the value is still all 0, skip the field
2172                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2173                         {
2174                                 if (v[j])
2175                                 {
2176                                         counts[i]++;
2177                                         break;
2178                                 }
2179                         }
2180                 }
2181         }
2182         used = 0;
2183         usedamount = 0;
2184         tempstring[0] = 0;
2185         for (i = 0;i < prog->progs->numfielddefs;i++)
2186         {
2187                 d = &prog->fielddefs[i];
2188                 name = PRVM_GetString(d->s_name);
2189                 if (name[strlen(name)-2] == '_')
2190                         continue;       // skip _x, _y, _z vars
2191                 switch(d->type & ~DEF_SAVEGLOBAL)
2192                 {
2193                 case ev_string:
2194                         strlcat(tempstring, "string   ", sizeof(tempstring));
2195                         break;
2196                 case ev_entity:
2197                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2198                         break;
2199                 case ev_function:
2200                         strlcat(tempstring, "function ", sizeof(tempstring));
2201                         break;
2202                 case ev_field:
2203                         strlcat(tempstring, "field    ", sizeof(tempstring));
2204                         break;
2205                 case ev_void:
2206                         strlcat(tempstring, "void     ", sizeof(tempstring));
2207                         break;
2208                 case ev_float:
2209                         strlcat(tempstring, "float    ", sizeof(tempstring));
2210                         break;
2211                 case ev_vector:
2212                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2213                         break;
2214                 case ev_pointer:
2215                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2216                         break;
2217                 default:
2218                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2219                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2220                         break;
2221                 }
2222                 if (strlen(name) > sizeof(tempstring2)-4)
2223                 {
2224                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2225                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2226                         tempstring2[sizeof(tempstring2)-1] = 0;
2227                         name = tempstring2;
2228                 }
2229                 strlcat(tempstring, name, sizeof(tempstring));
2230                 for (j = (int)strlen(name);j < 25;j++)
2231                         strlcat(tempstring, " ", sizeof(tempstring));
2232                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2233                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2234                 strlcat(tempstring, "\n", sizeof(tempstring));
2235                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2236                 {
2237                         Con_Print(tempstring);
2238                         tempstring[0] = 0;
2239                 }
2240                 if (counts[i])
2241                 {
2242                         used++;
2243                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2244                 }
2245         }
2246         Mem_Free(counts);
2247         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);
2248
2249         PRVM_End;
2250 }
2251
2252 void PRVM_Globals_f (void)
2253 {
2254         int i;
2255         const char *wildcard;
2256         int numculled;
2257                 numculled = 0;
2258         // TODO
2259         /*if (!sv.active)
2260         {
2261                 Con_Print("no progs loaded\n");
2262                 return;
2263         }*/
2264         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2265         {
2266                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2267                 return;
2268         }
2269
2270         PRVM_Begin;
2271         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2272                 return;
2273
2274         if( Cmd_Argc() == 3)
2275                 wildcard = Cmd_Argv(2);
2276         else
2277                 wildcard = NULL;
2278
2279         Con_Printf("%s :", PRVM_NAME);
2280
2281         for (i = 0;i < prog->progs->numglobaldefs;i++)
2282         {
2283                 if(wildcard)
2284                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2285                         {
2286                                 numculled++;
2287                                 continue;
2288                         }
2289                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2290         }
2291         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2292
2293         PRVM_End;
2294 }
2295
2296 /*
2297 ===============
2298 PRVM_Global
2299 ===============
2300 */
2301 void PRVM_Global_f(void)
2302 {
2303         ddef_t *global;
2304         if( Cmd_Argc() != 3 ) {
2305                 Con_Printf( "prvm_global <program name> <global name>\n" );
2306                 return;
2307         }
2308
2309         PRVM_Begin;
2310         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2311                 return;
2312
2313         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2314         if( !global )
2315                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2316         else
2317                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2318         PRVM_End;
2319 }
2320
2321 /*
2322 ===============
2323 PRVM_GlobalSet
2324 ===============
2325 */
2326 void PRVM_GlobalSet_f(void)
2327 {
2328         ddef_t *global;
2329         if( Cmd_Argc() != 4 ) {
2330                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2331                 return;
2332         }
2333
2334         PRVM_Begin;
2335         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2336                 return;
2337
2338         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2339         if( !global )
2340                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2341         else
2342                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2343         PRVM_End;
2344 }
2345
2346 /*
2347 ===============
2348 PRVM_Init
2349 ===============
2350 */
2351 void PRVM_Init (void)
2352 {
2353         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2354         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2355         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2356         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2357         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)");
2358         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)");
2359         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2360         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2361         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2362         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)");
2363         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2364         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2365         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2366         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2367
2368         // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2369         prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2370
2371         Cvar_RegisterVariable (&prvm_traceqc);
2372         Cvar_RegisterVariable (&prvm_statementprofiling);
2373         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2374         Cvar_RegisterVariable (&prvm_leaktest);
2375         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2376         Cvar_RegisterVariable (&prvm_errordump);
2377
2378         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2379         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2380
2381         //VM_Cmd_Init();
2382 }
2383
2384 /*
2385 ===============
2386 PRVM_InitProg
2387 ===============
2388 */
2389 void PRVM_InitProg(int prognr)
2390 {
2391         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2392                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2393
2394         prog = &prog_list[prognr];
2395
2396         if(prog->loaded)
2397                 PRVM_ResetProg();
2398
2399         memset(prog, 0, sizeof(prvm_prog_t));
2400         prog->starttime = Sys_DoubleTime();
2401
2402         prog->error_cmd = Host_Error;
2403         prog->leaktest_active = prvm_leaktest.integer;
2404 }
2405
2406 int PRVM_GetProgNr(void)
2407 {
2408         return prog - prog_list;
2409 }
2410
2411 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2412 {
2413         return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2414 }
2415
2416 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2417 {
2418         _Mem_Free(buffer, filename, fileline);
2419 }
2420
2421 void _PRVM_FreeAll(const char *filename, int fileline)
2422 {
2423         prog->progs = NULL;
2424         prog->fielddefs = NULL;
2425         prog->functions = NULL;
2426         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2427 }
2428
2429 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2430 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2431 {
2432         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2433         return 0;
2434 }
2435
2436 /*
2437 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2438 {
2439         PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2440         return 0;
2441 }
2442
2443 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2444 {
2445         int n;
2446         n = e - prog->edicts;
2447         if ((unsigned int)n >= prog->limit_edicts)
2448                 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2449         return n;
2450 }
2451
2452 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2453 //{
2454 //      return e - prog->edicts;
2455 //}
2456
2457 //#define       PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2458 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2459 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2460 {
2461         int n;
2462         n = e - prog->edicts;
2463         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2464                 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2465         return n;// EXPERIMENTAL
2466         //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2467 }
2468 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2469 {
2470         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2471                 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2472         return prog->edicts + n; // EXPERIMENTAL
2473         //return prog->edicts + ((n) / (progs->entityfields * 4));
2474 }
2475 */
2476
2477
2478 sizebuf_t vm_tempstringsbuf;
2479
2480 const char *PRVM_GetString(int num)
2481 {
2482         if (num >= 0)
2483         {
2484                 if (num < prog->stringssize)
2485                         return prog->strings + num;
2486                 else
2487 #if 1
2488                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2489                 {
2490                         num -= prog->stringssize;
2491                         if (num < vm_tempstringsbuf.cursize)
2492                                 return (char *)vm_tempstringsbuf.data + num;
2493                         else
2494                         {
2495                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2496                                 return "";
2497                         }
2498                 }
2499                 else
2500 #endif
2501                 {
2502                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2503                         return "";
2504                 }
2505         }
2506         else
2507         {
2508                 num = -1 - num;
2509 #if 0
2510                 if (num >= (1<<30))
2511                 {
2512                         // special range reserved for tempstrings
2513                         num -= (1<<30);
2514                         if (num < vm_tempstringsbuf.cursize)
2515                                 return (char *)vm_tempstringsbuf.data + num;
2516                         else
2517                         {
2518                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2519                                 return "";
2520                         }
2521                 }
2522                 else
2523 #endif
2524                 if (num < prog->numknownstrings)
2525                 {
2526                         if (!prog->knownstrings[num])
2527                         {
2528                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2529                                 return "";
2530                         }
2531                         return prog->knownstrings[num];
2532                 }
2533                 else
2534                 {
2535                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2536                         return "";
2537                 }
2538         }
2539 }
2540
2541 int PRVM_SetEngineString(const char *s)
2542 {
2543         int i;
2544         if (!s)
2545                 return 0;
2546         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2547                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2548         // if it's in the tempstrings area, use a reserved range
2549         // (otherwise we'd get millions of useless string offsets cluttering the database)
2550         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2551 #if 1
2552                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2553 #else
2554                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2555 #endif
2556         // see if it's a known string address
2557         for (i = 0;i < prog->numknownstrings;i++)
2558                 if (prog->knownstrings[i] == s)
2559                         return -1 - i;
2560         // new unknown engine string
2561         if (developer.integer >= 200)
2562                 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2563         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2564                 if (!prog->knownstrings[i])
2565                         break;
2566         if (i >= prog->numknownstrings)
2567         {
2568                 if (i >= prog->maxknownstrings)
2569                 {
2570                         const char **oldstrings = prog->knownstrings;
2571                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2572                         const char **oldstrings_origin = prog->knownstrings_origin;
2573                         prog->maxknownstrings += 128;
2574                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2575                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2576                         if(prog->leaktest_active)
2577                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2578                         if (prog->numknownstrings)
2579                         {
2580                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2581                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2582                                 if(prog->leaktest_active)
2583                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2584                         }
2585                 }
2586                 prog->numknownstrings++;
2587         }
2588         prog->firstfreeknownstring = i + 1;
2589         prog->knownstrings[i] = s;
2590         prog->knownstrings_freeable[i] = false;
2591         if(prog->leaktest_active)
2592                 prog->knownstrings_origin[i] = NULL;
2593         return -1 - i;
2594 }
2595
2596 // temp string handling
2597
2598 // all tempstrings go into this buffer consecutively, and it is reset
2599 // whenever PRVM_ExecuteProgram returns to the engine
2600 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2601 //  restores it on return, so multiple recursive calls can share the same
2602 //  buffer)
2603 // the buffer size is automatically grown as needed
2604
2605 int PRVM_SetTempString(const char *s)
2606 {
2607         int size;
2608         char *t;
2609         if (!s)
2610                 return 0;
2611         size = (int)strlen(s) + 1;
2612         if (developer.integer >= 300)
2613                 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2614         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2615         {
2616                 sizebuf_t old = vm_tempstringsbuf;
2617                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2618                         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);
2619                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2620                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2621                         vm_tempstringsbuf.maxsize *= 2;
2622                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2623                 {
2624                         if (developer.integer >= 100)
2625                                 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2626                         vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2627                         if (old.cursize)
2628                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2629                         if (old.data)
2630                                 Mem_Free(old.data);
2631                 }
2632         }
2633         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2634         memcpy(t, s, size);
2635         vm_tempstringsbuf.cursize += size;
2636         return PRVM_SetEngineString(t);
2637 }
2638
2639 int PRVM_AllocString(size_t bufferlength, char **pointer)
2640 {
2641         int i;
2642         if (!bufferlength)
2643                 return 0;
2644         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2645                 if (!prog->knownstrings[i])
2646                         break;
2647         if (i >= prog->numknownstrings)
2648         {
2649                 if (i >= prog->maxknownstrings)
2650                 {
2651                         const char **oldstrings = prog->knownstrings;
2652                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2653                         const char **oldstrings_origin = prog->knownstrings_origin;
2654                         prog->maxknownstrings += 128;
2655                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2656                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2657                         if(prog->leaktest_active)
2658                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2659                         if (prog->numknownstrings)
2660                         {
2661                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2662                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2663                                 if(prog->leaktest_active)
2664                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2665                         }
2666                         // TODO why not Mem_Free the old ones?
2667                 }
2668                 prog->numknownstrings++;
2669         }
2670         prog->firstfreeknownstring = i + 1;
2671         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2672         prog->knownstrings_freeable[i] = true;
2673         if(prog->leaktest_active)
2674                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2675         if (pointer)
2676                 *pointer = (char *)(prog->knownstrings[i]);
2677         return -1 - i;
2678 }
2679
2680 void PRVM_FreeString(int num)
2681 {
2682         if (num == 0)
2683                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2684         else if (num >= 0 && num < prog->stringssize)
2685                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2686         else if (num < 0 && num >= -prog->numknownstrings)
2687         {
2688                 num = -1 - num;
2689                 if (!prog->knownstrings[num])
2690                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2691                 if (!prog->knownstrings_freeable[num])
2692                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2693                 PRVM_Free((char *)prog->knownstrings[num]);
2694                 if(prog->leaktest_active)
2695                         if(prog->knownstrings_origin[num])
2696                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
2697                 prog->knownstrings[num] = NULL;
2698                 prog->knownstrings_freeable[num] = false;
2699                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2700         }
2701         else
2702                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2703 }
2704
2705 static qboolean PRVM_IsStringReferenced(string_t string)
2706 {
2707         int i, j;
2708
2709         for (i = 0;i < prog->progs->numglobaldefs;i++)
2710         {
2711                 ddef_t *d = &prog->globaldefs[i];
2712                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2713                         continue;
2714                 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2715                         return true;
2716         }
2717
2718         for(j = 0; j < prog->num_edicts; ++j)
2719         {
2720                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2721                 if (ed->priv.required->free)
2722                         continue;
2723                 for (i=0; i<prog->progs->numfielddefs; ++i)
2724                 {
2725                         ddef_t *d = &prog->fielddefs[i];
2726                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2727                                 continue;
2728                         if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2729                                 return true;
2730                 }
2731         }
2732
2733         return false;
2734 }
2735
2736 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2737 {
2738         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2739                 return true; // world or clients
2740         switch(prog - prog_list)
2741         {
2742                 case PRVM_SERVERPROG:
2743                         {
2744                                 entvars_t *ev = edict->fields.server;
2745                                 if(ev->solid) // can block other stuff, or is a trigger?
2746                                         return true;
2747                                 if(ev->modelindex) // visible ent?
2748                                         return true;
2749                                 if(ev->effects) // particle effect?
2750                                         return true;
2751                                 if(ev->think) // has a think function?
2752                                         if(ev->nextthink > 0) // that actually will eventually run?
2753                                                 return true;
2754                                 if(ev->takedamage)
2755                                         return true;
2756                                 if(*prvm_leaktest_ignore_classnames.string)
2757                                 {
2758                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2759                                                 return true;
2760                                 }
2761                         }
2762                         break;
2763                 case PRVM_CLIENTPROG:
2764                         {
2765                                 // TODO someone add more stuff here
2766                                 cl_entvars_t *ev = edict->fields.client;
2767                                 if(ev->entnum) // csqc networked
2768                                         return true;
2769                                 if(ev->modelindex) // visible ent?
2770                                         return true;
2771                                 if(ev->effects) // particle effect?
2772                                         return true;
2773                                 if(ev->think) // has a think function?
2774                                         if(ev->nextthink > 0) // that actually will eventually run?
2775                                                 return true;
2776                                 if(*prvm_leaktest_ignore_classnames.string)
2777                                 {
2778                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2779                                                 return true;
2780                                 }
2781                         }
2782                         break;
2783                 case PRVM_MENUPROG:
2784                         // menu prog does not have classnames
2785                         break;
2786         }
2787         return false;
2788 }
2789
2790 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2791 {
2792         int i, j;
2793         int edictnum = PRVM_NUM_FOR_EDICT(edict);
2794         const char *targetname = NULL;
2795
2796         switch(prog - prog_list)
2797         {
2798                 case PRVM_SERVERPROG:
2799                         targetname = PRVM_GetString(edict->fields.server->targetname);
2800                         break;
2801         }
2802
2803         if(targetname)
2804                 if(!*targetname) // ""
2805                         targetname = NULL;
2806
2807         for (i = 0;i < prog->progs->numglobaldefs;i++)
2808         {
2809                 ddef_t *d = &prog->globaldefs[i];
2810                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2811                         continue;
2812                 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2813                         return true;
2814         }
2815
2816         for(j = 0; j < prog->num_edicts; ++j)
2817         {
2818                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2819                 if (ed->priv.required->mark < mark)
2820                         continue;
2821                 if(ed == edict)
2822                         continue;
2823                 if(targetname)
2824                 {
2825                         const char *target = PRVM_GetString(ed->fields.server->target);
2826                         if(target)
2827                                 if(!strcmp(target, targetname))
2828                                         return true;
2829                 }
2830                 for (i=0; i<prog->progs->numfielddefs; ++i)
2831                 {
2832                         ddef_t *d = &prog->fielddefs[i];
2833                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2834                                 continue;
2835                         if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2836                                 return true;
2837                 }
2838         }
2839
2840         return false;
2841 }
2842
2843 static void PRVM_MarkReferencedEdicts(void)
2844 {
2845         int j;
2846         qboolean found_new;
2847         int stage;
2848
2849         for(j = 0; j < prog->num_edicts; ++j)
2850         {
2851                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2852                 if(ed->priv.required->free)
2853                         continue;
2854                 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2855         }
2856
2857         stage = 1;
2858         do
2859         {
2860                 found_new = false;
2861                 for(j = 0; j < prog->num_edicts; ++j)
2862                 {
2863                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2864                         if(ed->priv.required->free)
2865                                 continue;
2866                         if(ed->priv.required->mark)
2867                                 continue;
2868                         if(PRVM_IsEdictReferenced(ed, stage))
2869                         {
2870                                 ed->priv.required->mark = stage + 1;
2871                                 found_new = true;
2872                         }
2873                 }
2874                 ++stage;
2875         }
2876         while(found_new);
2877         Con_DPrintf("leak check used %d stages to find all references\n", stage);
2878 }
2879
2880 void PRVM_LeakTest(void)
2881 {
2882         int i, j;
2883         qboolean leaked = false;
2884
2885         if(!prog->leaktest_active)
2886                 return;
2887
2888         // 1. Strings
2889         for (i = 0; i < prog->numknownstrings; ++i)
2890         {
2891                 if(prog->knownstrings[i])
2892                 if(prog->knownstrings_freeable[i])
2893                 if(prog->knownstrings_origin[i])
2894                 if(!PRVM_IsStringReferenced(-1 - i))
2895                 {
2896                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2897                         leaked = true;
2898                 }
2899         }
2900
2901         // 2. Edicts
2902         PRVM_MarkReferencedEdicts();
2903         for(j = 0; j < prog->num_edicts; ++j)
2904         {
2905                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2906                 if(ed->priv.required->free)
2907                         continue;
2908                 if(!ed->priv.required->mark)
2909                 if(ed->priv.required->allocation_origin)
2910                 {
2911                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
2912                         PRVM_ED_Print(ed, NULL);
2913                         Con_Print("\n");
2914                         leaked = true;
2915                 }
2916         }
2917
2918         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2919         {
2920                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2921                 if(stringbuffer)
2922                 if(stringbuffer->origin)
2923                 {
2924                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
2925                         leaked = true;
2926                 }
2927         }
2928
2929         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2930         {
2931                 if(prog->openfiles[i])
2932                 if(prog->openfiles_origin[i])
2933                 {
2934                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
2935                         leaked = true;
2936                 }
2937         }
2938
2939         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2940         {
2941                 if(prog->opensearches[i])
2942                 if(prog->opensearches_origin[i])
2943                 {
2944                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
2945                         leaked = true;
2946                 }
2947         }
2948
2949         if(!leaked)
2950                 Con_Printf("Congratulations. No leaks found.\n");
2951 }