]> 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_EdictGet_f
1154
1155 Console command to load a field of a specified edict
1156 =============
1157 */
1158 void PRVM_ED_EdictGet_f(void)
1159 {
1160         prvm_edict_t *ed;
1161         ddef_t *key;
1162         const char *s;
1163         prvm_eval_t *v;
1164
1165         if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1166         {
1167                 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1168                 return;
1169         }
1170
1171         PRVM_Begin;
1172         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1173         {
1174                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1175                 return;
1176         }
1177
1178         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1179
1180         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1181         {
1182                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1183                 goto fail;
1184         }
1185
1186         v = (prvm_eval_t *)((char *)ed->fields.vp + key->ofs*4);
1187         s = PRVM_UglyValueString(key->type, v);
1188         if(Cmd_Argc() == 5)
1189         {
1190                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1191                 if (cvar && cvar->flags & CVAR_READONLY)
1192                 {
1193                         Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1194                         goto fail;
1195                 }
1196                 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1197         }
1198         else
1199                 Con_Printf("%s\n", s);
1200
1201 fail:
1202         PRVM_End;
1203 }
1204
1205 void PRVM_ED_GlobalGet_f(void)
1206 {
1207         ddef_t *key;
1208         const char *s;
1209         prvm_eval_t *v;
1210
1211         if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1212         {
1213                 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1214                 return;
1215         }
1216
1217         PRVM_Begin;
1218         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1219         {
1220                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1221                 return;
1222         }
1223
1224         key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1225         if(!key)
1226         {
1227                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1228                 goto fail;
1229         }
1230
1231         v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1232         s = PRVM_UglyValueString(key->type, v);
1233         if(Cmd_Argc() == 4)
1234         {
1235                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1236                 if (cvar && cvar->flags & CVAR_READONLY)
1237                 {
1238                         Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1239                         goto fail;
1240                 }
1241                 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1242         }
1243         else
1244                 Con_Printf("%s\n", s);
1245
1246 fail:
1247         PRVM_End;
1248 }
1249
1250 /*
1251 =============
1252 PRVM_ED_EdictSet_f
1253
1254 Console command to set a field of a specified edict
1255 =============
1256 */
1257 void PRVM_ED_EdictSet_f(void)
1258 {
1259         prvm_edict_t *ed;
1260         ddef_t *key;
1261
1262         if(Cmd_Argc() != 5)
1263         {
1264                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1265                 return;
1266         }
1267
1268         PRVM_Begin;
1269         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1270         {
1271                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1272                 return;
1273         }
1274
1275         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1276
1277         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1278                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1279         else
1280                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1281
1282         PRVM_End;
1283 }
1284
1285 /*
1286 ====================
1287 PRVM_ED_ParseEdict
1288
1289 Parses an edict out of the given string, returning the new position
1290 ed should be a properly initialized empty edict.
1291 Used for initial level load and for savegames.
1292 ====================
1293 */
1294 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1295 {
1296         ddef_t *key;
1297         qboolean anglehack;
1298         qboolean init;
1299         char keyname[256];
1300         size_t n;
1301
1302         init = false;
1303
1304 // go through all the dictionary pairs
1305         while (1)
1306         {
1307         // parse key
1308                 if (!COM_ParseToken_Simple(&data, false, false))
1309                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1310                 if (developer_entityparsing.integer)
1311                         Con_Printf("Key: \"%s\"", com_token);
1312                 if (com_token[0] == '}')
1313                         break;
1314
1315                 // anglehack is to allow QuakeEd to write single scalar angles
1316                 // and allow them to be turned into vectors. (FIXME...)
1317                 if (!strcmp(com_token, "angle"))
1318                 {
1319                         strlcpy (com_token, "angles", sizeof(com_token));
1320                         anglehack = true;
1321                 }
1322                 else
1323                         anglehack = false;
1324
1325                 // FIXME: change light to _light to get rid of this hack
1326                 if (!strcmp(com_token, "light"))
1327                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1328
1329                 strlcpy (keyname, com_token, sizeof(keyname));
1330
1331                 // another hack to fix keynames with trailing spaces
1332                 n = strlen(keyname);
1333                 while (n && keyname[n-1] == ' ')
1334                 {
1335                         keyname[n-1] = 0;
1336                         n--;
1337                 }
1338
1339         // parse value
1340                 if (!COM_ParseToken_Simple(&data, false, false))
1341                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1342                 if (developer_entityparsing.integer)
1343                         Con_Printf(" \"%s\"\n", com_token);
1344
1345                 if (com_token[0] == '}')
1346                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1347
1348                 init = true;
1349
1350                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1351                 if (!keyname[0])
1352                         continue;
1353
1354 // keynames with a leading underscore are used for utility comments,
1355 // and are immediately discarded by quake
1356                 if (keyname[0] == '_')
1357                         continue;
1358
1359                 key = PRVM_ED_FindField (keyname);
1360                 if (!key)
1361                 {
1362                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1363                         continue;
1364                 }
1365
1366                 if (anglehack)
1367                 {
1368                         char    temp[32];
1369                         strlcpy (temp, com_token, sizeof(temp));
1370                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1371                 }
1372
1373                 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1374                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1375         }
1376
1377         if (!init)
1378                 ent->priv.required->free = true;
1379
1380         return data;
1381 }
1382
1383
1384 /*
1385 ================
1386 PRVM_ED_LoadFromFile
1387
1388 The entities are directly placed in the array, rather than allocated with
1389 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1390 number references out of order.
1391
1392 Creates a server's entity / program execution context by
1393 parsing textual entity definitions out of an ent file.
1394
1395 Used for both fresh maps and savegame loads.  A fresh map would also need
1396 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1397 ================
1398 */
1399 void PRVM_ED_LoadFromFile (const char *data)
1400 {
1401         prvm_edict_t *ent;
1402         int parsed, inhibited, spawned, died;
1403         const char *funcname;
1404         mfunction_t *func;
1405
1406         parsed = 0;
1407         inhibited = 0;
1408         spawned = 0;
1409         died = 0;
1410
1411
1412 // parse ents
1413         while (1)
1414         {
1415 // parse the opening brace
1416                 if (!COM_ParseToken_Simple(&data, false, false))
1417                         break;
1418                 if (com_token[0] != '{')
1419                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1420
1421                 // CHANGED: this is not conform to PR_LoadFromFile
1422                 if(prog->loadintoworld)
1423                 {
1424                         prog->loadintoworld = false;
1425                         ent = PRVM_EDICT_NUM(0);
1426                 }
1427                 else
1428                         ent = PRVM_ED_Alloc();
1429
1430                 // clear it
1431                 if (ent != prog->edicts)        // hack
1432                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1433
1434                 data = PRVM_ED_ParseEdict (data, ent);
1435                 parsed++;
1436
1437                 // remove the entity ?
1438                 if(prog->load_edict && !prog->load_edict(ent))
1439                 {
1440                         PRVM_ED_Free(ent);
1441                         inhibited++;
1442                         continue;
1443                 }
1444
1445                 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1446                 {
1447                         // self = ent
1448                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1449                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1450                 }
1451
1452                 if(ent->priv.required->free)
1453                 {
1454                         inhibited++;
1455                         continue;
1456                 }
1457
1458 //
1459 // immediately call spawn function, but only if there is a self global and a classname
1460 //
1461                 if(!ent->priv.required->free)
1462                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1463                 {
1464                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1465                         if (!handle)
1466                         {
1467                                 Con_Print("No classname for:\n");
1468                                 PRVM_ED_Print(ent, NULL);
1469                                 PRVM_ED_Free (ent);
1470                                 continue;
1471                         }
1472
1473                         // look for the spawn function
1474                         funcname = PRVM_GetString(handle);
1475                         func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1476                         if(!func)
1477                                 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1478                                         func = PRVM_ED_FindFunction (funcname);
1479
1480                         if (!func)
1481                         {
1482                                 // check for OnEntityNoSpawnFunction
1483                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1484                                 {
1485                                         // self = ent
1486                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1487                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1488                                 }
1489                                 else
1490                                 {
1491                                         if (developer.integer) // don't confuse non-developers with errors
1492                                         {
1493                                                 Con_Print("No spawn function for:\n");
1494                                                 PRVM_ED_Print(ent, NULL);
1495                                         }
1496                                         PRVM_ED_Free (ent);
1497                                         continue; // not included in "inhibited" count
1498                                 }
1499                         }
1500                         else
1501                         {
1502                                 // self = ent
1503                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1504                                 PRVM_ExecuteProgram (func - prog->functions, "");
1505                         }
1506                 }
1507
1508                 if(!ent->priv.required->free)
1509                 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1510                 {
1511                         // self = ent
1512                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1513                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1514                 }
1515
1516                 spawned++;
1517                 if (ent->priv.required->free)
1518                         died++;
1519         }
1520
1521         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);
1522 }
1523
1524 void PRVM_FindOffsets(void)
1525 {
1526         // field and global searches use -1 for NULL
1527         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1528         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1529         // functions use 0 for NULL
1530         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1531
1532         // server and client qc use a lot of similar fields, so this is combined
1533         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1534         prog->fieldoffsets.SendFlags                      = PRVM_ED_FindFieldOffset("SendFlags");
1535         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1536         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1537         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1538         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1539         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1540         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1541         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1542         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1543         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1544         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1545         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1546         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1547         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1548         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1549         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1550         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1551         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1552         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1553         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1554         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1555         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1556         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1557         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1558         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1559         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1560         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1561         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1562         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1563         prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
1564         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1565         prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
1566         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1567         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1568         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1569         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1570         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1571         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1572         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1573         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1574         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1575         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1576         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1577         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1578         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1579         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1580         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1581         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1582         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1583         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1584         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1585         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1586         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1587         prog->fieldoffsets.frame3                         = PRVM_ED_FindFieldOffset("frame3");
1588         prog->fieldoffsets.frame3time                     = PRVM_ED_FindFieldOffset("frame3time");
1589         prog->fieldoffsets.frame4                         = PRVM_ED_FindFieldOffset("frame4");
1590         prog->fieldoffsets.frame4time                     = PRVM_ED_FindFieldOffset("frame4time");
1591         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1592         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1593         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1594         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1595         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1596         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1597         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1598         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1599         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1600         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1601         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1602         prog->fieldoffsets.lerpfrac3                      = PRVM_ED_FindFieldOffset("lerpfrac3");
1603         prog->fieldoffsets.lerpfrac4                      = PRVM_ED_FindFieldOffset("lerpfrac4");
1604         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1605         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1606         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1607         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1608         prog->fieldoffsets.movetypesteplandevent          = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1609         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1610         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1611         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1612         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1613         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1614         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1615         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1616         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1617         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1618         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1619         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1620         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1621         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1622         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1623         prog->fieldoffsets.shadertime                     = PRVM_ED_FindFieldOffset("shadertime");
1624         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1625         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1626         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1627         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1628         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1629         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1630         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1631         prog->fieldoffsets.bouncefactor                   = PRVM_ED_FindFieldOffset("bouncefactor");
1632         prog->fieldoffsets.bouncestop                     = PRVM_ED_FindFieldOffset("bouncestop");
1633         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1634         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1635         prog->funcoffsets.CSQC_Ent_Spawn                  = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1636         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1637         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1638         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1639         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1640         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1641         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1642         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1643         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1644         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1645         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1646         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1647         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1648         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1649         prog->funcoffsets.Gecko_Query                     = PRVM_ED_FindFunctionOffset("Gecko_Query");
1650         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1651         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1652         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1653         prog->funcoffsets.SV_OnEntityPostSpawnFunction    = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1654         prog->funcoffsets.SV_OnEntityPreSpawnFunction     = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1655         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1656         prog->funcoffsets.SV_PausedTic                    = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1657         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1658         prog->funcoffsets.SV_Shutdown                     = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1659         prog->funcoffsets.URI_Get_Callback                = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1660         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1661         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1662         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1663         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1664         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1665         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1666         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1667         prog->globaloffsets.gettaginfo_forward            = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1668         prog->globaloffsets.gettaginfo_name               = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1669         prog->globaloffsets.gettaginfo_offset             = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1670         prog->globaloffsets.gettaginfo_parent             = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1671         prog->globaloffsets.gettaginfo_right              = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1672         prog->globaloffsets.gettaginfo_up                 = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1673         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1674         prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1675         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1676         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1677         prog->globaloffsets.serverdeltatime               = PRVM_ED_FindGlobalOffset("serverdeltatime");
1678         prog->globaloffsets.serverprevtime                = PRVM_ED_FindGlobalOffset("serverprevtime");
1679         prog->globaloffsets.servertime                    = PRVM_ED_FindGlobalOffset("servertime");
1680         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1681         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1682         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1683         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1684         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1685         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1686         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1687         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1688         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1689         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1690         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1691         prog->globaloffsets.trace_networkentity           = PRVM_ED_FindGlobalOffset("trace_networkentity");
1692         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1693         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1694         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1695         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1696         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1697         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1698         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1699         prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
1700
1701         // menu qc only uses some functions, nothing else
1702         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1703         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1704         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1705         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1706         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1707         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1708 }
1709
1710 // not used
1711 /*
1712 typedef struct dpfield_s
1713 {
1714         int type;
1715         char *string;
1716 }
1717 dpfield_t;
1718
1719 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1720
1721 dpfield_t dpfields[] =
1722 {
1723 };
1724 */
1725
1726 /*
1727 ===============
1728 PRVM_ResetProg
1729 ===============
1730 */
1731
1732 void PRVM_LeakTest(void);
1733 void PRVM_ResetProg(void)
1734 {
1735         PRVM_LeakTest();
1736         PRVM_GCALL(reset_cmd)();
1737         Mem_FreePool(&prog->progs_mempool);
1738         memset(prog,0,sizeof(prvm_prog_t));
1739         prog->starttime = Sys_DoubleTime();
1740 }
1741
1742 /*
1743 ===============
1744 PRVM_LoadLNO
1745 ===============
1746 */
1747 void PRVM_LoadLNO( const char *progname ) {
1748         fs_offset_t filesize;
1749         unsigned char *lno;
1750         unsigned int *header;
1751         char filename[512];
1752
1753         FS_StripExtension( progname, filename, sizeof( filename ) );
1754         strlcat( filename, ".lno", sizeof( filename ) );
1755
1756         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1757         if( !lno ) {
1758                 return;
1759         }
1760
1761 /*
1762 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1763 <Spike>    SafeWrite (h, &version, sizeof(int));
1764 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1765 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1766 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1767 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1768 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1769 */
1770         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1771                 Mem_Free(lno);
1772                 return;
1773         }
1774
1775         header = (unsigned int *) lno;
1776         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1777                 LittleLong( header[ 1 ] ) == 1 &&
1778                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1779                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1780                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1781                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1782         {
1783                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1784                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1785         }
1786         Mem_Free( lno );
1787 }
1788
1789 /*
1790 ===============
1791 PRVM_LoadProgs
1792 ===============
1793 */
1794 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1795 {
1796         int i;
1797         dstatement_t *st;
1798         ddef_t *infielddefs;
1799         dfunction_t *dfunctions;
1800         fs_offset_t filesize;
1801
1802         if( prog->loaded ) {
1803                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1804         }
1805
1806         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1807         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1808                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1809         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1810
1811         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1812
1813         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1814
1815 // byte swap the header
1816         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1817                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1818
1819         if (prog->progs->version != PROG_VERSION && prog->progs->version != PROG_EXTENDED)
1820                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i or %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION, PROG_EXTENDED);
1821         if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1822                 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);
1823
1824         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1825         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1826
1827         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1828                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1829         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1830         prog->stringssize = prog->progs->numstrings;
1831
1832         prog->numknownstrings = 0;
1833         prog->maxknownstrings = 0;
1834         prog->knownstrings = NULL;
1835         prog->knownstrings_freeable = NULL;
1836
1837         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1838
1839         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1840
1841         // we need to expand the fielddefs list to include all the engine fields,
1842         // so allocate a new place for it
1843         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1844         //                                                                                              ( + DPFIELDS                       )
1845         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1846
1847         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1848
1849         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1850
1851         // moved edict_size calculation down below field adding code
1852
1853         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1854         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1855
1856 // byte swap the lumps
1857         for (i=0 ; i<prog->progs->numstatements ; i++)
1858         {
1859                 prog->statements[i].op = LittleShort(prog->statements[i].op);
1860                 prog->statements[i].a = LittleShort(prog->statements[i].a);
1861                 prog->statements[i].b = LittleShort(prog->statements[i].b);
1862                 prog->statements[i].c = LittleShort(prog->statements[i].c);
1863         }
1864
1865         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1866         for (i = 0;i < prog->progs->numfunctions;i++)
1867         {
1868                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1869                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1870                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1871                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1872                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1873                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1874                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1875                 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1876                         PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1877                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1878         }
1879
1880         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1881         {
1882                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1883                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1884                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1885                 // TODO bounds check ofs, s_name
1886         }
1887
1888         // copy the progs fields to the new fields list
1889         for (i = 0;i < prog->progs->numfielddefs;i++)
1890         {
1891                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1892                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1893                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1894                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1895                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1896                 // TODO bounds check ofs, s_name
1897         }
1898
1899         // append the required fields
1900         for (i = 0;i < (int) numrequiredfields;i++)
1901         {
1902                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1903                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1904                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1905                 // TODO bounds check ofs, s_name
1906                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1907                         prog->progs->entityfields += 3;
1908                 else
1909                         prog->progs->entityfields++;
1910                 prog->progs->numfielddefs++;
1911         }
1912
1913         // check required functions
1914         for(i=0 ; i < numrequiredfunc ; i++)
1915                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1916                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1917
1918         // check required globals
1919         for(i=0 ; i < numrequiredglobals ; i++)
1920                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1921                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1922
1923         for (i=0 ; i<prog->progs->numglobals ; i++)
1924                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1925
1926         // moved edict_size calculation down here, below field adding code
1927         // LordHavoc: this no longer includes the prvm_edict_t header
1928         prog->edict_size = prog->progs->entityfields * 4;
1929         prog->edictareasize = prog->edict_size * prog->limit_edicts;
1930
1931         // LordHavoc: bounds check anything static
1932         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1933         {
1934                 switch (st->op)
1935                 {
1936                 case OP_IF:
1937                 case OP_IFNOT:
1938                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1939                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (int/general) (statement %d) in %s", i, PRVM_NAME);
1940                         break;
1941                 case OP_IF_F:
1942                 case OP_IFNOT_F:
1943                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1944                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (float) (statement %d) in %s", i, PRVM_NAME);
1945                         break;
1946                 case OP_IFS:
1947                 case OP_IFNOTS:
1948                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1949                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (string not null) (statement %d) in %s", i, PRVM_NAME);
1950                         break;
1951                 case OP_GOTO:
1952                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1953                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1954                         break;
1955                 case OP_CASE:
1956                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1957                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASE (statement %d) in %s", i, PRVM_NAME);
1958                         break;
1959                 case OP_CASERANGE:
1960                         if ((unsigned short) st->a >= prog->progs->numglobals ||
1961                             (unsigned short) st->b >= prog->progs->numglobals ||
1962                             st->c + i < 0 || st->c + i >= prog->progs->numstatements)
1963                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds CASERANGE (statement %d) in %s", i, PRVM_NAME);
1964                         break;
1965                 // global global global
1966                 case OP_ADD_F:
1967                 case OP_ADD_V:
1968                 case OP_ADD_I:
1969                 case OP_ADD_FI:
1970                 case OP_ADD_IF:
1971                 case OP_SUB_F:
1972                 case OP_SUB_I:
1973                 case OP_SUB_FI:
1974                 case OP_SUB_IF:
1975                 case OP_SUB_V:
1976                 case OP_MUL_F:
1977                 case OP_MUL_V:
1978                 case OP_MUL_FV:
1979                 case OP_MUL_VF:
1980                 case OP_MUL_I:
1981                 case OP_MUL_IF:
1982                 case OP_MUL_FI:
1983                 case OP_MUL_VI:
1984                 case OP_DIV_F:
1985                 case OP_DIV_VF:
1986                 case OP_DIV_I:
1987                 case OP_DIV_IF:
1988                 case OP_DIV_FI:
1989                 case OP_BITAND:
1990                 case OP_BITAND_I:
1991                 case OP_BITAND_IF:
1992                 case OP_BITAND_FI:
1993                 case OP_BITOR:
1994                 case OP_BITOR_I:
1995                 case OP_BITOR_IF:
1996                 case OP_BITOR_FI:
1997                 case OP_GE:
1998                 case OP_LE:
1999                 case OP_GT:
2000                 case OP_LT:
2001                 case OP_GE_I:
2002                 case OP_LE_I:
2003                 case OP_GT_I:
2004                 case OP_LT_I:
2005                 case OP_GE_IF:
2006                 case OP_LE_IF:
2007                 case OP_GT_IF:
2008                 case OP_LT_IF:
2009                 case OP_GE_FI:
2010                 case OP_LE_FI:
2011                 case OP_GT_FI:
2012                 case OP_LT_FI:
2013                 case OP_AND:
2014                 case OP_AND_I:
2015                 case OP_AND_IF:
2016                 case OP_AND_FI:
2017                 case OP_OR:
2018                 case OP_OR_I:
2019                 case OP_OR_IF:
2020                 case OP_OR_FI:
2021                 case OP_EQ_F:
2022                 case OP_EQ_V:
2023                 case OP_EQ_S:
2024                 case OP_EQ_E:
2025                 case OP_EQ_FNC:
2026                 case OP_EQ_I:
2027                 case OP_EQ_IF:
2028                 case OP_EQ_FI:
2029                 case OP_NE_F:
2030                 case OP_NE_V:
2031                 case OP_NE_S:
2032                 case OP_NE_E:
2033                 case OP_NE_FNC:
2034                 case OP_NE_I:
2035                 case OP_NE_IF:
2036                 case OP_NE_FI:
2037                 case OP_ADDRESS:
2038                 case OP_LOAD_F:
2039                 case OP_LOAD_FLD:
2040                 case OP_LOAD_ENT:
2041                 case OP_LOAD_S:
2042                 case OP_LOAD_FNC:
2043                 case OP_LOAD_V:
2044                 case OP_LOAD_P:
2045                 case OP_LOAD_I:
2046                 case OP_LOADA_I:
2047                 case OP_LOADA_F:
2048                 case OP_LOADA_FLD:
2049                 case OP_LOADA_ENT:
2050                 case OP_LOADA_S:
2051                 case OP_LOADA_FNC:
2052                 case OP_LOADA_V:
2053                 case OP_LOADP_I:
2054                 case OP_LOADP_F:
2055                 case OP_LOADP_FLD:
2056                 case OP_LOADP_ENT:
2057                 case OP_LOADP_S:
2058                 case OP_LOADP_FNC:
2059                 case OP_LOADP_V:
2060                 case OP_LOADP_C:
2061                 /*
2062                 case OP_GLOAD_I:
2063                 case OP_GLOAD_F:
2064                 case OP_GLOAD_FLD:
2065                 case OP_GLOAD_ENT:
2066                 case OP_GLOAD_S:
2067                 case OP_GLOAD_FNC:
2068                 case OP_GLOAD_V:
2069                 */
2070                 case OP_BOUNDCHECK:
2071                 case OP_ADDSTOREP_F:
2072                 case OP_ADDSTOREP_V:
2073                 case OP_SUBSTOREP_F:
2074                 case OP_SUBSTOREP_V:
2075                 case OP_MULSTOREP_F:
2076                 case OP_MULSTOREP_V:
2077                 case OP_DIVSTOREP_F:
2078                 case OP_GLOBALADDRESS:
2079                 case OP_POINTER_ADD:
2080                 case OP_ADD_SF:
2081                 case OP_SUB_S:
2082                 case OP_XOR_I:
2083                 case OP_RSHIFT_I:
2084                 case OP_LSHIFT_I:
2085                 case OP_FETCH_GBL_F:
2086                 case OP_FETCH_GBL_S:
2087                 case OP_FETCH_GBL_E:
2088                 case OP_FETCH_GBL_FNC:
2089                 case OP_FETCH_GBL_V:
2090                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2091                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2092                         break;
2093                 // global none global
2094                 case OP_NOT_F:
2095                 case OP_NOT_V:
2096                 case OP_NOT_S:
2097                 case OP_NOT_FNC:
2098                 case OP_NOT_ENT:
2099                 case OP_NOT_I:
2100                 case OP_CONV_ITOF:
2101                 case OP_CONV_FTOI:
2102                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2103                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2104                         break;
2105                 // 2 globals
2106                 case OP_STOREP_F:
2107                 case OP_STOREP_ENT:
2108                 case OP_STOREP_FLD:
2109                 case OP_STOREP_S:
2110                 case OP_STOREP_FNC:
2111                 case OP_STORE_F:
2112                 case OP_STORE_ENT:
2113                 case OP_STORE_FLD:
2114                 case OP_STORE_S:
2115                 case OP_STORE_FNC:
2116                 case OP_STORE_I:
2117                 case OP_STATE:
2118                 case OP_STOREP_V:
2119                 case OP_STORE_V:
2120                 case OP_STORE_P:
2121                 case OP_STORE_IF:
2122                 case OP_STORE_FI:
2123                 case OP_STOREP_P:
2124                 case OP_STOREP_I:
2125                 // case OP_STOREP_C:
2126                 /*
2127                 case OP_GSTOREP_I:
2128                 case OP_GSTOREP_F:
2129                 case OP_GSTOREP_ENT:
2130                 case OP_GSTOREP_FLD:
2131                 case OP_GSTOREP_S:
2132                 case OP_GSTOREP_FNC:
2133                 case OP_GSTOREP_V:
2134                 case OP_GADDRESS:
2135                 */
2136                 case OP_RAND2:
2137                 case OP_RANDV2:
2138                 case OP_BITSET:
2139                 case OP_BITCLR:
2140                 case OP_ADDSTORE_F:
2141                 case OP_ADDSTORE_V:
2142                 case OP_SUBSTORE_F:
2143                 case OP_SUBSTORE_V:
2144                 case OP_MULSTORE_F:
2145                 case OP_MULSTORE_V:
2146                 case OP_DIVSTORE_F:
2147                 //case OP_CSTATE:
2148                 //case OP_CWSTATE:
2149                 case OP_THINKTIME:
2150                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2151                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2152                         break;
2153                 // 1 global
2154                 case OP_CALL0:
2155                 case OP_CALL1:
2156                 case OP_CALL2:
2157                 case OP_CALL3:
2158                 case OP_CALL4:
2159                 case OP_CALL5:
2160                 case OP_CALL6:
2161                 case OP_CALL7:
2162                 case OP_CALL8:
2163                 case OP_CALL1H:
2164                 case OP_CALL2H:
2165                 case OP_CALL3H:
2166                 case OP_CALL4H:
2167                 case OP_CALL5H:
2168                 case OP_CALL6H:
2169                 case OP_CALL7H:
2170                 case OP_CALL8H:
2171                 case OP_DONE:
2172                 case OP_RETURN:
2173                 case OP_RAND1:
2174                 case OP_RANDV1:
2175                 case OP_SWITCH_F:
2176                 case OP_SWITCH_V:
2177                 case OP_SWITCH_S:
2178                 case OP_SWITCH_E:
2179                 case OP_SWITCH_I:
2180                 case OP_SWITCH_FNC:
2181                         if ((unsigned short) st->a >= prog->progs->numglobals)
2182                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2183                         break;
2184                         // TODO: add fte instruction support here too
2185                 case OP_RAND0:
2186                 case OP_RANDV0:
2187                         // this writes to OFS_RETURN and has no parameters
2188                         break;
2189                 default:
2190                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2191                         break;
2192                 }
2193         }
2194         if(prog->progs->numstatements < 1)
2195         {
2196                 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2197         }
2198         else switch(prog->statements[prog->progs->numstatements - 1].op)
2199         {
2200                 case OP_RETURN:
2201                 case OP_GOTO:
2202                 case OP_DONE:
2203                         break;
2204                 default:
2205                         PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2206                         break;
2207         }
2208
2209         PRVM_LoadLNO(filename);
2210
2211         PRVM_Init_Exec();
2212
2213         prog->loaded = TRUE;
2214
2215         // set flags & ddef_ts in prog
2216
2217         prog->flag = 0;
2218
2219         PRVM_FindOffsets();
2220
2221         PRVM_GCALL(init_cmd)();
2222
2223         // init mempools
2224         PRVM_MEM_Alloc();
2225 }
2226
2227
2228 void PRVM_Fields_f (void)
2229 {
2230         int i, j, ednum, used, usedamount;
2231         int *counts;
2232         char tempstring[MAX_INPUTLINE], tempstring2[260];
2233         const char *name;
2234         prvm_edict_t *ed;
2235         ddef_t *d;
2236         int *v;
2237
2238         // TODO
2239         /*
2240         if (!sv.active)
2241         {
2242                 Con_Print("no progs loaded\n");
2243                 return;
2244         }
2245         */
2246
2247         if(Cmd_Argc() != 2)
2248         {
2249                 Con_Print("prvm_fields <program name>\n");
2250                 return;
2251         }
2252
2253         PRVM_Begin;
2254         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2255                 return;
2256
2257         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2258         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2259         {
2260                 ed = PRVM_EDICT_NUM(ednum);
2261                 if (ed->priv.required->free)
2262                         continue;
2263                 for (i = 1;i < prog->progs->numfielddefs;i++)
2264                 {
2265                         d = &prog->fielddefs[i];
2266                         name = PRVM_GetString(d->s_name);
2267                         if (name[strlen(name)-2] == '_')
2268                                 continue;       // skip _x, _y, _z vars
2269                         v = (int *)((char *)ed->fields.vp + d->ofs*4);
2270                         // if the value is still all 0, skip the field
2271                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2272                         {
2273                                 if (v[j])
2274                                 {
2275                                         counts[i]++;
2276                                         break;
2277                                 }
2278                         }
2279                 }
2280         }
2281         used = 0;
2282         usedamount = 0;
2283         tempstring[0] = 0;
2284         for (i = 0;i < prog->progs->numfielddefs;i++)
2285         {
2286                 d = &prog->fielddefs[i];
2287                 name = PRVM_GetString(d->s_name);
2288                 if (name[strlen(name)-2] == '_')
2289                         continue;       // skip _x, _y, _z vars
2290                 switch(d->type & ~DEF_SAVEGLOBAL)
2291                 {
2292                 case ev_string:
2293                         strlcat(tempstring, "string   ", sizeof(tempstring));
2294                         break;
2295                 case ev_entity:
2296                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2297                         break;
2298                 case ev_function:
2299                         strlcat(tempstring, "function ", sizeof(tempstring));
2300                         break;
2301                 case ev_field:
2302                         strlcat(tempstring, "field    ", sizeof(tempstring));
2303                         break;
2304                 case ev_void:
2305                         strlcat(tempstring, "void     ", sizeof(tempstring));
2306                         break;
2307                 case ev_float:
2308                         strlcat(tempstring, "float    ", sizeof(tempstring));
2309                         break;
2310                 case ev_vector:
2311                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2312                         break;
2313                 case ev_pointer:
2314                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2315                         break;
2316                 default:
2317                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2318                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2319                         break;
2320                 }
2321                 if (strlen(name) > sizeof(tempstring2)-4)
2322                 {
2323                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2324                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2325                         tempstring2[sizeof(tempstring2)-1] = 0;
2326                         name = tempstring2;
2327                 }
2328                 strlcat(tempstring, name, sizeof(tempstring));
2329                 for (j = (int)strlen(name);j < 25;j++)
2330                         strlcat(tempstring, " ", sizeof(tempstring));
2331                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2332                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2333                 strlcat(tempstring, "\n", sizeof(tempstring));
2334                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2335                 {
2336                         Con_Print(tempstring);
2337                         tempstring[0] = 0;
2338                 }
2339                 if (counts[i])
2340                 {
2341                         used++;
2342                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2343                 }
2344         }
2345         Mem_Free(counts);
2346         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);
2347
2348         PRVM_End;
2349 }
2350
2351 void PRVM_Globals_f (void)
2352 {
2353         int i;
2354         const char *wildcard;
2355         int numculled;
2356                 numculled = 0;
2357         // TODO
2358         /*if (!sv.active)
2359         {
2360                 Con_Print("no progs loaded\n");
2361                 return;
2362         }*/
2363         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2364         {
2365                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2366                 return;
2367         }
2368
2369         PRVM_Begin;
2370         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2371                 return;
2372
2373         if( Cmd_Argc() == 3)
2374                 wildcard = Cmd_Argv(2);
2375         else
2376                 wildcard = NULL;
2377
2378         Con_Printf("%s :", PRVM_NAME);
2379
2380         for (i = 0;i < prog->progs->numglobaldefs;i++)
2381         {
2382                 if(wildcard)
2383                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2384                         {
2385                                 numculled++;
2386                                 continue;
2387                         }
2388                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2389         }
2390         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2391
2392         PRVM_End;
2393 }
2394
2395 /*
2396 ===============
2397 PRVM_Global
2398 ===============
2399 */
2400 void PRVM_Global_f(void)
2401 {
2402         ddef_t *global;
2403         if( Cmd_Argc() != 3 ) {
2404                 Con_Printf( "prvm_global <program name> <global name>\n" );
2405                 return;
2406         }
2407
2408         PRVM_Begin;
2409         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2410                 return;
2411
2412         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2413         if( !global )
2414                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2415         else
2416                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2417         PRVM_End;
2418 }
2419
2420 /*
2421 ===============
2422 PRVM_GlobalSet
2423 ===============
2424 */
2425 void PRVM_GlobalSet_f(void)
2426 {
2427         ddef_t *global;
2428         if( Cmd_Argc() != 4 ) {
2429                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2430                 return;
2431         }
2432
2433         PRVM_Begin;
2434         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2435                 return;
2436
2437         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2438         if( !global )
2439                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2440         else
2441                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2442         PRVM_End;
2443 }
2444
2445 /*
2446 ===============
2447 PRVM_Init
2448 ===============
2449 */
2450 void PRVM_Init (void)
2451 {
2452         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2453         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2454         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2455         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2456         Cmd_AddCommand ("prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2457         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)");
2458         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)");
2459         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2460         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2461         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2462         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)");
2463         Cmd_AddCommand ("prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2464         Cmd_AddCommand ("prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2465         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2466         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2467         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2468         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2469
2470         // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2471         prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2472
2473         Cvar_RegisterVariable (&prvm_traceqc);
2474         Cvar_RegisterVariable (&prvm_statementprofiling);
2475         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2476         Cvar_RegisterVariable (&prvm_leaktest);
2477         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2478         Cvar_RegisterVariable (&prvm_errordump);
2479
2480         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2481         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2482
2483         //VM_Cmd_Init();
2484 }
2485
2486 /*
2487 ===============
2488 PRVM_InitProg
2489 ===============
2490 */
2491 void PRVM_InitProg(int prognr)
2492 {
2493         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2494                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2495
2496         prog = &prog_list[prognr];
2497
2498         if(prog->loaded)
2499                 PRVM_ResetProg();
2500
2501         memset(prog, 0, sizeof(prvm_prog_t));
2502         prog->starttime = Sys_DoubleTime();
2503
2504         prog->error_cmd = Host_Error;
2505         prog->leaktest_active = prvm_leaktest.integer != 0;
2506 }
2507
2508 int PRVM_GetProgNr(void)
2509 {
2510         return prog - prog_list;
2511 }
2512
2513 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2514 {
2515         return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2516 }
2517
2518 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2519 {
2520         _Mem_Free(buffer, filename, fileline);
2521 }
2522
2523 void _PRVM_FreeAll(const char *filename, int fileline)
2524 {
2525         prog->progs = NULL;
2526         prog->fielddefs = NULL;
2527         prog->functions = NULL;
2528         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2529 }
2530
2531 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2532 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2533 {
2534         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2535         return 0;
2536 }
2537
2538 /*
2539 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2540 {
2541         PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2542         return 0;
2543 }
2544
2545 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2546 {
2547         int n;
2548         n = e - prog->edicts;
2549         if ((unsigned int)n >= prog->limit_edicts)
2550                 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2551         return n;
2552 }
2553
2554 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2555 //{
2556 //      return e - prog->edicts;
2557 //}
2558
2559 //#define       PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2560 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2561 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2562 {
2563         int n;
2564         n = e - prog->edicts;
2565         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2566                 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2567         return n;// EXPERIMENTAL
2568         //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2569 }
2570 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2571 {
2572         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2573                 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2574         return prog->edicts + n; // EXPERIMENTAL
2575         //return prog->edicts + ((n) / (progs->entityfields * 4));
2576 }
2577 */
2578
2579
2580 sizebuf_t vm_tempstringsbuf;
2581
2582 const char *PRVM_GetString(int num)
2583 {
2584         if (num >= 0)
2585         {
2586                 if (num < prog->stringssize)
2587                         return prog->strings + num;
2588                 else
2589 #if 1
2590                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2591                 {
2592                         num -= prog->stringssize;
2593                         if (num < vm_tempstringsbuf.cursize)
2594                                 return (char *)vm_tempstringsbuf.data + num;
2595                         else
2596                         {
2597                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2598                                 return "";
2599                         }
2600                 }
2601                 else
2602 #endif
2603                 {
2604                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2605                         return "";
2606                 }
2607         }
2608         else
2609         {
2610                 num = -1 - num;
2611 #if 0
2612                 if (num >= (1<<30))
2613                 {
2614                         // special range reserved for tempstrings
2615                         num -= (1<<30);
2616                         if (num < vm_tempstringsbuf.cursize)
2617                                 return (char *)vm_tempstringsbuf.data + num;
2618                         else
2619                         {
2620                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2621                                 return "";
2622                         }
2623                 }
2624                 else
2625 #endif
2626                 if (num < prog->numknownstrings)
2627                 {
2628                         if (!prog->knownstrings[num])
2629                         {
2630                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2631                                 return "";
2632                         }
2633                         return prog->knownstrings[num];
2634                 }
2635                 else
2636                 {
2637                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2638                         return "";
2639                 }
2640         }
2641 }
2642
2643 int PRVM_SetEngineString(const char *s)
2644 {
2645         int i;
2646         if (!s)
2647                 return 0;
2648         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2649                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2650         // if it's in the tempstrings area, use a reserved range
2651         // (otherwise we'd get millions of useless string offsets cluttering the database)
2652         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2653 #if 1
2654                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2655 #else
2656                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2657 #endif
2658         // see if it's a known string address
2659         for (i = 0;i < prog->numknownstrings;i++)
2660                 if (prog->knownstrings[i] == s)
2661                         return -1 - i;
2662         // new unknown engine string
2663         if (developer.integer >= 200)
2664                 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2665         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2666                 if (!prog->knownstrings[i])
2667                         break;
2668         if (i >= prog->numknownstrings)
2669         {
2670                 if (i >= prog->maxknownstrings)
2671                 {
2672                         const char **oldstrings = prog->knownstrings;
2673                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2674                         const char **oldstrings_origin = prog->knownstrings_origin;
2675                         prog->maxknownstrings += 128;
2676                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2677                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2678                         if(prog->leaktest_active)
2679                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2680                         if (prog->numknownstrings)
2681                         {
2682                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2683                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2684                                 if(prog->leaktest_active)
2685                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2686                         }
2687                 }
2688                 prog->numknownstrings++;
2689         }
2690         prog->firstfreeknownstring = i + 1;
2691         prog->knownstrings[i] = s;
2692         prog->knownstrings_freeable[i] = false;
2693         if(prog->leaktest_active)
2694                 prog->knownstrings_origin[i] = NULL;
2695         return -1 - i;
2696 }
2697
2698 // temp string handling
2699
2700 // all tempstrings go into this buffer consecutively, and it is reset
2701 // whenever PRVM_ExecuteProgram returns to the engine
2702 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2703 //  restores it on return, so multiple recursive calls can share the same
2704 //  buffer)
2705 // the buffer size is automatically grown as needed
2706
2707 int PRVM_SetTempString(const char *s)
2708 {
2709         int size;
2710         char *t;
2711         if (!s)
2712                 return 0;
2713         size = (int)strlen(s) + 1;
2714         if (developer.integer >= 300)
2715                 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2716         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2717         {
2718                 sizebuf_t old = vm_tempstringsbuf;
2719                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2720                         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);
2721                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2722                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2723                         vm_tempstringsbuf.maxsize *= 2;
2724                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2725                 {
2726                         if (developer.integer >= 100)
2727                                 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2728                         vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2729                         if (old.cursize)
2730                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2731                         if (old.data)
2732                                 Mem_Free(old.data);
2733                 }
2734         }
2735         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2736         memcpy(t, s, size);
2737         vm_tempstringsbuf.cursize += size;
2738         return PRVM_SetEngineString(t);
2739 }
2740
2741 int PRVM_AllocString(size_t bufferlength, char **pointer)
2742 {
2743         int i;
2744         if (!bufferlength)
2745                 return 0;
2746         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2747                 if (!prog->knownstrings[i])
2748                         break;
2749         if (i >= prog->numknownstrings)
2750         {
2751                 if (i >= prog->maxknownstrings)
2752                 {
2753                         const char **oldstrings = prog->knownstrings;
2754                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2755                         const char **oldstrings_origin = prog->knownstrings_origin;
2756                         prog->maxknownstrings += 128;
2757                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2758                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2759                         if(prog->leaktest_active)
2760                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2761                         if (prog->numknownstrings)
2762                         {
2763                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2764                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2765                                 if(prog->leaktest_active)
2766                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2767                         }
2768                         // TODO why not Mem_Free the old ones?
2769                 }
2770                 prog->numknownstrings++;
2771         }
2772         prog->firstfreeknownstring = i + 1;
2773         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2774         prog->knownstrings_freeable[i] = true;
2775         if(prog->leaktest_active)
2776                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2777         if (pointer)
2778                 *pointer = (char *)(prog->knownstrings[i]);
2779         return -1 - i;
2780 }
2781
2782 void PRVM_FreeString(int num)
2783 {
2784         if (num == 0)
2785                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2786         else if (num >= 0 && num < prog->stringssize)
2787                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2788         else if (num < 0 && num >= -prog->numknownstrings)
2789         {
2790                 num = -1 - num;
2791                 if (!prog->knownstrings[num])
2792                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2793                 if (!prog->knownstrings_freeable[num])
2794                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2795                 PRVM_Free((char *)prog->knownstrings[num]);
2796                 if(prog->leaktest_active)
2797                         if(prog->knownstrings_origin[num])
2798                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
2799                 prog->knownstrings[num] = NULL;
2800                 prog->knownstrings_freeable[num] = false;
2801                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2802         }
2803         else
2804                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2805 }
2806
2807 static qboolean PRVM_IsStringReferenced(string_t string)
2808 {
2809         int i, j;
2810
2811         for (i = 0;i < prog->progs->numglobaldefs;i++)
2812         {
2813                 ddef_t *d = &prog->globaldefs[i];
2814                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2815                         continue;
2816                 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2817                         return true;
2818         }
2819
2820         for(j = 0; j < prog->num_edicts; ++j)
2821         {
2822                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2823                 if (ed->priv.required->free)
2824                         continue;
2825                 for (i=0; i<prog->progs->numfielddefs; ++i)
2826                 {
2827                         ddef_t *d = &prog->fielddefs[i];
2828                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2829                                 continue;
2830                         if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2831                                 return true;
2832                 }
2833         }
2834
2835         return false;
2836 }
2837
2838 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2839 {
2840         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2841                 return true; // world or clients
2842         switch(prog - prog_list)
2843         {
2844                 case PRVM_SERVERPROG:
2845                         {
2846                                 entvars_t *ev = edict->fields.server;
2847                                 if(ev->solid) // can block other stuff, or is a trigger?
2848                                         return true;
2849                                 if(ev->modelindex) // visible ent?
2850                                         return true;
2851                                 if(ev->effects) // particle effect?
2852                                         return true;
2853                                 if(ev->think) // has a think function?
2854                                         if(ev->nextthink > 0) // that actually will eventually run?
2855                                                 return true;
2856                                 if(ev->takedamage)
2857                                         return true;
2858                                 if(*prvm_leaktest_ignore_classnames.string)
2859                                 {
2860                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2861                                                 return true;
2862                                 }
2863                         }
2864                         break;
2865                 case PRVM_CLIENTPROG:
2866                         {
2867                                 // TODO someone add more stuff here
2868                                 cl_entvars_t *ev = edict->fields.client;
2869                                 if(ev->entnum) // csqc networked
2870                                         return true;
2871                                 if(ev->modelindex) // visible ent?
2872                                         return true;
2873                                 if(ev->effects) // particle effect?
2874                                         return true;
2875                                 if(ev->think) // has a think function?
2876                                         if(ev->nextthink > 0) // that actually will eventually run?
2877                                                 return true;
2878                                 if(*prvm_leaktest_ignore_classnames.string)
2879                                 {
2880                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2881                                                 return true;
2882                                 }
2883                         }
2884                         break;
2885                 case PRVM_MENUPROG:
2886                         // menu prog does not have classnames
2887                         break;
2888         }
2889         return false;
2890 }
2891
2892 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2893 {
2894         int i, j;
2895         int edictnum = PRVM_NUM_FOR_EDICT(edict);
2896         const char *targetname = NULL;
2897
2898         switch(prog - prog_list)
2899         {
2900                 case PRVM_SERVERPROG:
2901                         targetname = PRVM_GetString(edict->fields.server->targetname);
2902                         break;
2903         }
2904
2905         if(targetname)
2906                 if(!*targetname) // ""
2907                         targetname = NULL;
2908
2909         for (i = 0;i < prog->progs->numglobaldefs;i++)
2910         {
2911                 ddef_t *d = &prog->globaldefs[i];
2912                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2913                         continue;
2914                 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2915                         return true;
2916         }
2917
2918         for(j = 0; j < prog->num_edicts; ++j)
2919         {
2920                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2921                 if (ed->priv.required->mark < mark)
2922                         continue;
2923                 if(ed == edict)
2924                         continue;
2925                 if(targetname)
2926                 {
2927                         const char *target = PRVM_GetString(ed->fields.server->target);
2928                         if(target)
2929                                 if(!strcmp(target, targetname))
2930                                         return true;
2931                 }
2932                 for (i=0; i<prog->progs->numfielddefs; ++i)
2933                 {
2934                         ddef_t *d = &prog->fielddefs[i];
2935                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2936                                 continue;
2937                         if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2938                                 return true;
2939                 }
2940         }
2941
2942         return false;
2943 }
2944
2945 static void PRVM_MarkReferencedEdicts(void)
2946 {
2947         int j;
2948         qboolean found_new;
2949         int stage;
2950
2951         for(j = 0; j < prog->num_edicts; ++j)
2952         {
2953                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2954                 if(ed->priv.required->free)
2955                         continue;
2956                 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2957         }
2958
2959         stage = 1;
2960         do
2961         {
2962                 found_new = false;
2963                 for(j = 0; j < prog->num_edicts; ++j)
2964                 {
2965                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2966                         if(ed->priv.required->free)
2967                                 continue;
2968                         if(ed->priv.required->mark)
2969                                 continue;
2970                         if(PRVM_IsEdictReferenced(ed, stage))
2971                         {
2972                                 ed->priv.required->mark = stage + 1;
2973                                 found_new = true;
2974                         }
2975                 }
2976                 ++stage;
2977         }
2978         while(found_new);
2979         Con_DPrintf("leak check used %d stages to find all references\n", stage);
2980 }
2981
2982 void PRVM_LeakTest(void)
2983 {
2984         int i, j;
2985         qboolean leaked = false;
2986
2987         if(!prog->leaktest_active)
2988                 return;
2989
2990         // 1. Strings
2991         for (i = 0; i < prog->numknownstrings; ++i)
2992         {
2993                 if(prog->knownstrings[i])
2994                 if(prog->knownstrings_freeable[i])
2995                 if(prog->knownstrings_origin[i])
2996                 if(!PRVM_IsStringReferenced(-1 - i))
2997                 {
2998                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2999                         leaked = true;
3000                 }
3001         }
3002
3003         // 2. Edicts
3004         PRVM_MarkReferencedEdicts();
3005         for(j = 0; j < prog->num_edicts; ++j)
3006         {
3007                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3008                 if(ed->priv.required->free)
3009                         continue;
3010                 if(!ed->priv.required->mark)
3011                 if(ed->priv.required->allocation_origin)
3012                 {
3013                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3014                         PRVM_ED_Print(ed, NULL);
3015                         Con_Print("\n");
3016                         leaked = true;
3017                 }
3018         }
3019
3020         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3021         {
3022                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3023                 if(stringbuffer)
3024                 if(stringbuffer->origin)
3025                 {
3026                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3027                         leaked = true;
3028                 }
3029         }
3030
3031         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3032         {
3033                 if(prog->openfiles[i])
3034                 if(prog->openfiles_origin[i])
3035                 {
3036                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3037                         leaked = true;
3038                 }
3039         }
3040
3041         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3042         {
3043                 if(prog->opensearches[i])
3044                 if(prog->opensearches_origin[i])
3045                 {
3046                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3047                         leaked = true;
3048                 }
3049         }
3050
3051         if(!leaked)
3052                 Con_Printf("Congratulations. No leaks found.\n");
3053 }