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