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