]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_edict.c
reduce memory usage by several megabytes
[divverent/darkplaces.git] / prvm_edict.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // AK new vm
21
22 #include "quakedef.h"
23 #include "progsvm.h"
24
25 prvm_prog_t *prog;
26
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
28
29 int             prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
33
34 cvar_t prvm_language = {0, "prvm_language", "", "when set, loads progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.dump.po is written from the strings in the progs"};
35 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
36 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
37 // LordHavoc: counts usage of each QuakeC statement
38 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)"};
39 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
40 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
41 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)"};
42 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
43 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)"};
44 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
45
46 static double prvm_reuseedicts_always_allow = 0;
47 qboolean prvm_runawaycheck = true;
48
49 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
50 // enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)
51 qboolean prvm_boundscheck = 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->progs->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->progs->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->progs->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->progs->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->progs->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->progs->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), "%f", val->_float);
556                 break;
557         case ev_vector:
558                 dpsnprintf (line, sizeof (line), "%f %f %f", 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->progs->numfielddefs ; i++)
651         {
652                 d = &prog->fielddefs[i];
653                 name = PRVM_GetString(d->s_name);
654                 if (name[strlen(name)-2] == '_')
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->progs->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 (name[strlen(name)-2] == '_')
739                         continue;       // skip _x, _y, _z vars
740
741                 v = (int *)(ed->fields.vp + d->ofs);
742
743         // if the value is still all 0, skip the field
744                 type = d->type & ~DEF_SAVEGLOBAL;
745                 for (j=0 ; j<prvm_type_size[type] ; j++)
746                         if (v[j])
747                                 break;
748                 if (j == prvm_type_size[type])
749                         continue;
750
751                 FS_Printf(f,"\"%s\" ",name);
752                 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
753                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
754                 prog->statestring = NULL;
755         }
756
757         FS_Print(f, "}\n");
758 }
759
760 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
761 {
762         PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
763 }
764
765 /*
766 =============
767 PRVM_ED_PrintEdicts_f
768
769 For debugging, prints all the entities in the current server
770 =============
771 */
772 void PRVM_ED_PrintEdicts_f (void)
773 {
774         int             i;
775         const char *wildcard_fieldname;
776
777         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
778         {
779                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
780                 return;
781         }
782
783         PRVM_Begin;
784         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
785                 return;
786
787         if( Cmd_Argc() == 3)
788                 wildcard_fieldname = Cmd_Argv(2);
789         else
790                 wildcard_fieldname = NULL;
791
792         Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
793         for (i=0 ; i<prog->num_edicts ; i++)
794                 PRVM_ED_PrintNum (i, wildcard_fieldname);
795
796         PRVM_End;
797 }
798
799 /*
800 =============
801 PRVM_ED_PrintEdict_f
802
803 For debugging, prints a single edict
804 =============
805 */
806 void PRVM_ED_PrintEdict_f (void)
807 {
808         int             i;
809         const char      *wildcard_fieldname;
810
811         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
812         {
813                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
814                 return;
815         }
816
817         PRVM_Begin;
818         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
819                 return;
820
821         i = atoi (Cmd_Argv(2));
822         if (i >= prog->num_edicts)
823         {
824                 Con_Print("Bad edict number\n");
825                 PRVM_End;
826                 return;
827         }
828         if( Cmd_Argc() == 4)
829                 // Optional Wildcard Provided
830                 wildcard_fieldname = Cmd_Argv(3);
831         else
832                 // Use All
833                 wildcard_fieldname = NULL;
834         PRVM_ED_PrintNum (i, wildcard_fieldname);
835
836         PRVM_End;
837 }
838
839 /*
840 =============
841 PRVM_ED_Count
842
843 For debugging
844 =============
845 */
846 // 2 possibilities : 1. just displaying the active edict count
847 //                                       2. making a function pointer [x]
848 void PRVM_ED_Count_f (void)
849 {
850         int             i;
851         prvm_edict_t    *ent;
852         int             active;
853
854         if(Cmd_Argc() != 2)
855         {
856                 Con_Print("prvm_count <program name>\n");
857                 return;
858         }
859
860         PRVM_Begin;
861         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
862                 return;
863
864         if(prog->count_edicts)
865                 prog->count_edicts();
866         else
867         {
868                 active = 0;
869                 for (i=0 ; i<prog->num_edicts ; i++)
870                 {
871                         ent = PRVM_EDICT_NUM(i);
872                         if (ent->priv.required->free)
873                                 continue;
874                         active++;
875                 }
876
877                 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
878                 Con_Printf("active    :%3i\n", active);
879         }
880
881         PRVM_End;
882 }
883
884 /*
885 ==============================================================================
886
887                                         ARCHIVING GLOBALS
888
889 FIXME: need to tag constants, doesn't really work
890 ==============================================================================
891 */
892
893 /*
894 =============
895 PRVM_ED_WriteGlobals
896 =============
897 */
898 void PRVM_ED_WriteGlobals (qfile_t *f)
899 {
900         ddef_t          *def;
901         int                     i;
902         const char              *name;
903         int                     type;
904
905         FS_Print(f,"{\n");
906         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
907         {
908                 def = &prog->globaldefs[i];
909                 type = def->type;
910                 if ( !(def->type & DEF_SAVEGLOBAL) )
911                         continue;
912                 type &= ~DEF_SAVEGLOBAL;
913
914                 if (type != ev_string && type != ev_float && type != ev_entity)
915                         continue;
916
917                 name = PRVM_GetString(def->s_name);
918
919                 if(developer_entityparsing.integer)
920                         Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
921
922                 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
923                 FS_Printf(f,"\"%s\" ", name);
924                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
925                 prog->statestring = NULL;
926         }
927         FS_Print(f,"}\n");
928 }
929
930 /*
931 =============
932 PRVM_ED_ParseGlobals
933 =============
934 */
935 void PRVM_ED_ParseGlobals (const char *data)
936 {
937         char keyname[MAX_INPUTLINE];
938         ddef_t *key;
939
940         while (1)
941         {
942                 // parse key
943                 if (!COM_ParseToken_Simple(&data, false, false))
944                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
945                 if (com_token[0] == '}')
946                         break;
947
948                 if (developer_entityparsing.integer)
949                         Con_Printf("Key: \"%s\"", com_token);
950
951                 strlcpy (keyname, com_token, sizeof(keyname));
952
953                 // parse value
954                 if (!COM_ParseToken_Simple(&data, false, true))
955                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
956
957                 if (developer_entityparsing.integer)
958                         Con_Printf(" \"%s\"\n", com_token);
959
960                 if (com_token[0] == '}')
961                         PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
962
963                 key = PRVM_ED_FindGlobal (keyname);
964                 if (!key)
965                 {
966                         Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
967                         continue;
968                 }
969
970                 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
971                         PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
972         }
973 }
974
975 //============================================================================
976
977
978 /*
979 =============
980 PRVM_ED_ParseEval
981
982 Can parse either fields or globals
983 returns false if error
984 =============
985 */
986 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
987 {
988         int i, l;
989         char *new_p;
990         ddef_t *def;
991         prvm_eval_t *val;
992         mfunction_t *func;
993
994         if (ent)
995                 val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
996         else
997                 val = (prvm_eval_t *)(prog->globals.generic + key->ofs);
998         switch (key->type & ~DEF_SAVEGLOBAL)
999         {
1000         case ev_string:
1001                 l = (int)strlen(s) + 1;
1002                 val->string = PRVM_AllocString(l, &new_p);
1003                 for (i = 0;i < l;i++)
1004                 {
1005                         if (s[i] == '\\' && s[i+1] && parsebackslash)
1006                         {
1007                                 i++;
1008                                 if (s[i] == 'n')
1009                                         *new_p++ = '\n';
1010                                 else if (s[i] == 'r')
1011                                         *new_p++ = '\r';
1012                                 else
1013                                         *new_p++ = s[i];
1014                         }
1015                         else
1016                                 *new_p++ = s[i];
1017                 }
1018                 break;
1019
1020         case ev_float:
1021                 while (*s && ISWHITESPACE(*s))
1022                         s++;
1023                 val->_float = atof(s);
1024                 break;
1025
1026         case ev_vector:
1027                 for (i = 0;i < 3;i++)
1028                 {
1029                         while (*s && ISWHITESPACE(*s))
1030                                 s++;
1031                         if (!*s)
1032                                 break;
1033                         val->vector[i] = atof(s);
1034                         while (!ISWHITESPACE(*s))
1035                                 s++;
1036                         if (!*s)
1037                                 break;
1038                 }
1039                 break;
1040
1041         case ev_entity:
1042                 while (*s && ISWHITESPACE(*s))
1043                         s++;
1044                 i = atoi(s);
1045                 if (i >= prog->limit_edicts)
1046                         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);
1047                 while (i >= prog->max_edicts)
1048                         PRVM_MEM_IncreaseEdicts();
1049                 // if IncreaseEdicts was called the base pointer needs to be updated
1050                 if (ent)
1051                         val = (prvm_eval_t *)(ent->fields.vp + key->ofs);
1052                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1053                 break;
1054
1055         case ev_field:
1056                 if (*s != '.')
1057                 {
1058                         Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1059                         return false;
1060                 }
1061                 def = PRVM_ED_FindField(s + 1);
1062                 if (!def)
1063                 {
1064                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1065                         return false;
1066                 }
1067                 val->_int = def->ofs;
1068                 break;
1069
1070         case ev_function:
1071                 func = PRVM_ED_FindFunction(s);
1072                 if (!func)
1073                 {
1074                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1075                         return false;
1076                 }
1077                 val->function = func - prog->functions;
1078                 break;
1079
1080         default:
1081                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1082                 return false;
1083         }
1084         return true;
1085 }
1086
1087 /*
1088 =============
1089 PRVM_GameCommand_f
1090
1091 Console command to send a string to QC function GameCommand of the
1092 indicated progs
1093
1094 Usage:
1095   sv_cmd adminmsg 3 "do not teamkill"
1096   cl_cmd someclientcommand
1097   menu_cmd somemenucommand
1098
1099 All progs can support this extension; sg calls it in server QC, cg in client
1100 QC, mg in menu QC.
1101 =============
1102 */
1103 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1104 {
1105         if(Cmd_Argc() < 1)
1106         {
1107                 Con_Printf("%s text...\n", whichcmd);
1108                 return;
1109         }
1110
1111         PRVM_Begin;
1112         if(!PRVM_SetProgFromString(whichprogs))
1113         // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1114         // also, it makes printing error messages easier!
1115         {
1116                 Con_Printf("%s program not loaded.\n", whichprogs);
1117                 return;
1118         }
1119
1120         if(!prog->funcoffsets.GameCommand)
1121         {
1122                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1123         }
1124         else
1125         {
1126                 int restorevm_tempstringsbuf_cursize;
1127                 const char *s;
1128
1129                 s = Cmd_Args();
1130
1131                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1132                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1133                 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1134                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1135         }
1136
1137         PRVM_End;
1138 }
1139 void PRVM_GameCommand_Server_f(void)
1140 {
1141         PRVM_GameCommand("server", "sv_cmd");
1142 }
1143 void PRVM_GameCommand_Client_f(void)
1144 {
1145         PRVM_GameCommand("client", "cl_cmd");
1146 }
1147 void PRVM_GameCommand_Menu_f(void)
1148 {
1149         PRVM_GameCommand("menu", "menu_cmd");
1150 }
1151
1152 /*
1153 =============
1154 PRVM_ED_EdictGet_f
1155
1156 Console command to load a field of a specified edict
1157 =============
1158 */
1159 void PRVM_ED_EdictGet_f(void)
1160 {
1161         prvm_edict_t *ed;
1162         ddef_t *key;
1163         const char *s;
1164         prvm_eval_t *v;
1165
1166         if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1167         {
1168                 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1169                 return;
1170         }
1171
1172         PRVM_Begin;
1173         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1174         {
1175                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1176                 return;
1177         }
1178
1179         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1180
1181         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1182         {
1183                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1184                 goto fail;
1185         }
1186
1187         v = (prvm_eval_t *)(ed->fields.vp + key->ofs);
1188         s = PRVM_UglyValueString(key->type, v);
1189         if(Cmd_Argc() == 5)
1190         {
1191                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1192                 if (cvar && cvar->flags & CVAR_READONLY)
1193                 {
1194                         Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1195                         goto fail;
1196                 }
1197                 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1198         }
1199         else
1200                 Con_Printf("%s\n", s);
1201
1202 fail:
1203         PRVM_End;
1204 }
1205
1206 void PRVM_ED_GlobalGet_f(void)
1207 {
1208         ddef_t *key;
1209         const char *s;
1210         prvm_eval_t *v;
1211
1212         if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1213         {
1214                 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1215                 return;
1216         }
1217
1218         PRVM_Begin;
1219         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1220         {
1221                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1222                 return;
1223         }
1224
1225         key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1226         if(!key)
1227         {
1228                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1229                 goto fail;
1230         }
1231
1232         v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1233         s = PRVM_UglyValueString(key->type, v);
1234         if(Cmd_Argc() == 4)
1235         {
1236                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1237                 if (cvar && cvar->flags & CVAR_READONLY)
1238                 {
1239                         Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1240                         goto fail;
1241                 }
1242                 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1243         }
1244         else
1245                 Con_Printf("%s\n", s);
1246
1247 fail:
1248         PRVM_End;
1249 }
1250
1251 /*
1252 =============
1253 PRVM_ED_EdictSet_f
1254
1255 Console command to set a field of a specified edict
1256 =============
1257 */
1258 void PRVM_ED_EdictSet_f(void)
1259 {
1260         prvm_edict_t *ed;
1261         ddef_t *key;
1262
1263         if(Cmd_Argc() != 5)
1264         {
1265                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1266                 return;
1267         }
1268
1269         PRVM_Begin;
1270         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1271         {
1272                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1273                 return;
1274         }
1275
1276         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1277
1278         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1279                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1280         else
1281                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1282
1283         PRVM_End;
1284 }
1285
1286 /*
1287 ====================
1288 PRVM_ED_ParseEdict
1289
1290 Parses an edict out of the given string, returning the new position
1291 ed should be a properly initialized empty edict.
1292 Used for initial level load and for savegames.
1293 ====================
1294 */
1295 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1296 {
1297         ddef_t *key;
1298         qboolean anglehack;
1299         qboolean init;
1300         char keyname[256];
1301         size_t n;
1302
1303         init = false;
1304
1305 // go through all the dictionary pairs
1306         while (1)
1307         {
1308         // parse key
1309                 if (!COM_ParseToken_Simple(&data, false, false))
1310                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1311                 if (developer_entityparsing.integer)
1312                         Con_Printf("Key: \"%s\"", com_token);
1313                 if (com_token[0] == '}')
1314                         break;
1315
1316                 // anglehack is to allow QuakeEd to write single scalar angles
1317                 // and allow them to be turned into vectors. (FIXME...)
1318                 if (!strcmp(com_token, "angle"))
1319                 {
1320                         strlcpy (com_token, "angles", sizeof(com_token));
1321                         anglehack = true;
1322                 }
1323                 else
1324                         anglehack = false;
1325
1326                 // FIXME: change light to _light to get rid of this hack
1327                 if (!strcmp(com_token, "light"))
1328                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1329
1330                 strlcpy (keyname, com_token, sizeof(keyname));
1331
1332                 // another hack to fix keynames with trailing spaces
1333                 n = strlen(keyname);
1334                 while (n && keyname[n-1] == ' ')
1335                 {
1336                         keyname[n-1] = 0;
1337                         n--;
1338                 }
1339
1340         // parse value
1341                 if (!COM_ParseToken_Simple(&data, false, false))
1342                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1343                 if (developer_entityparsing.integer)
1344                         Con_Printf(" \"%s\"\n", com_token);
1345
1346                 if (com_token[0] == '}')
1347                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1348
1349                 init = true;
1350
1351                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1352                 if (!keyname[0])
1353                         continue;
1354
1355 // keynames with a leading underscore are used for utility comments,
1356 // and are immediately discarded by quake
1357                 if (keyname[0] == '_')
1358                         continue;
1359
1360                 key = PRVM_ED_FindField (keyname);
1361                 if (!key)
1362                 {
1363                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1364                         continue;
1365                 }
1366
1367                 if (anglehack)
1368                 {
1369                         char    temp[32];
1370                         strlcpy (temp, com_token, sizeof(temp));
1371                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1372                 }
1373
1374                 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1375                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1376         }
1377
1378         if (!init)
1379                 ent->priv.required->free = true;
1380
1381         return data;
1382 }
1383
1384
1385 /*
1386 ================
1387 PRVM_ED_LoadFromFile
1388
1389 The entities are directly placed in the array, rather than allocated with
1390 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1391 number references out of order.
1392
1393 Creates a server's entity / program execution context by
1394 parsing textual entity definitions out of an ent file.
1395
1396 Used for both fresh maps and savegame loads.  A fresh map would also need
1397 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1398 ================
1399 */
1400 void PRVM_ED_LoadFromFile (const char *data)
1401 {
1402         prvm_edict_t *ent;
1403         int parsed, inhibited, spawned, died;
1404         const char *funcname;
1405         mfunction_t *func;
1406
1407         parsed = 0;
1408         inhibited = 0;
1409         spawned = 0;
1410         died = 0;
1411
1412         prvm_reuseedicts_always_allow = realtime;
1413
1414 // parse ents
1415         while (1)
1416         {
1417 // parse the opening brace
1418                 if (!COM_ParseToken_Simple(&data, false, false))
1419                         break;
1420                 if (com_token[0] != '{')
1421                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1422
1423                 // CHANGED: this is not conform to PR_LoadFromFile
1424                 if(prog->loadintoworld)
1425                 {
1426                         prog->loadintoworld = false;
1427                         ent = PRVM_EDICT_NUM(0);
1428                 }
1429                 else
1430                         ent = PRVM_ED_Alloc();
1431
1432                 // clear it
1433                 if (ent != prog->edicts)        // hack
1434                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1435
1436                 data = PRVM_ED_ParseEdict (data, ent);
1437                 parsed++;
1438
1439                 // remove the entity ?
1440                 if(prog->load_edict && !prog->load_edict(ent))
1441                 {
1442                         PRVM_ED_Free(ent);
1443                         inhibited++;
1444                         continue;
1445                 }
1446
1447                 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1448                 {
1449                         // self = ent
1450                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1451                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1452                 }
1453
1454                 if(ent->priv.required->free)
1455                 {
1456                         inhibited++;
1457                         continue;
1458                 }
1459
1460 //
1461 // immediately call spawn function, but only if there is a self global and a classname
1462 //
1463                 if(!ent->priv.required->free)
1464                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1465                 {
1466                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1467                         if (!handle)
1468                         {
1469                                 Con_Print("No classname for:\n");
1470                                 PRVM_ED_Print(ent, NULL);
1471                                 PRVM_ED_Free (ent);
1472                                 continue;
1473                         }
1474
1475                         // look for the spawn function
1476                         funcname = PRVM_GetString(handle);
1477                         func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1478                         if(!func)
1479                                 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1480                                         func = PRVM_ED_FindFunction (funcname);
1481
1482                         if (!func)
1483                         {
1484                                 // check for OnEntityNoSpawnFunction
1485                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1486                                 {
1487                                         // self = ent
1488                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1489                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1490                                 }
1491                                 else
1492                                 {
1493                                         if (developer.integer) // don't confuse non-developers with errors
1494                                         {
1495                                                 Con_Print("No spawn function for:\n");
1496                                                 PRVM_ED_Print(ent, NULL);
1497                                         }
1498                                         PRVM_ED_Free (ent);
1499                                         continue; // not included in "inhibited" count
1500                                 }
1501                         }
1502                         else
1503                         {
1504                                 // self = ent
1505                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1506                                 PRVM_ExecuteProgram (func - prog->functions, "");
1507                         }
1508                 }
1509
1510                 if(!ent->priv.required->free)
1511                 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1512                 {
1513                         // self = ent
1514                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1515                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1516                 }
1517
1518                 spawned++;
1519                 if (ent->priv.required->free)
1520                         died++;
1521         }
1522
1523         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);
1524
1525         prvm_reuseedicts_always_allow = 0;
1526 }
1527
1528 void PRVM_FindOffsets(void)
1529 {
1530         // field and global searches use -1 for NULL
1531         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1532         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1533         // functions use 0 for NULL
1534         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1535
1536         // server and client qc use a lot of similar fields, so this is combined
1537         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1538         prog->fieldoffsets.SendFlags                      = PRVM_ED_FindFieldOffset("SendFlags");
1539         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1540         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1541         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1542         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1543         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1544         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1545         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1546         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1547         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1548         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1549         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1550         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1551         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1552         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1553         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1554         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1555         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1556         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1557         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1558         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1559         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1560         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1561         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1562         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1563         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1564         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1565         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1566         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1567         prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
1568         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1569         prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
1570         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1571         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1572         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1573         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1574         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1575         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1576         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1577         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1578         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1579         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1580         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1581         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1582         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1583         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1584         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1585         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1586         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1587         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1588         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1589         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1590         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1591         prog->fieldoffsets.frame3                         = PRVM_ED_FindFieldOffset("frame3");
1592         prog->fieldoffsets.frame3time                     = PRVM_ED_FindFieldOffset("frame3time");
1593         prog->fieldoffsets.frame4                         = PRVM_ED_FindFieldOffset("frame4");
1594         prog->fieldoffsets.frame4time                     = PRVM_ED_FindFieldOffset("frame4time");
1595         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1596         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1597         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1598         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1599         prog->fieldoffsets.glowmod                        = PRVM_ED_FindFieldOffset("glowmod");
1600         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1601         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1602         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1603         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1604         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1605         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1606         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1607         prog->fieldoffsets.lerpfrac3                      = PRVM_ED_FindFieldOffset("lerpfrac3");
1608         prog->fieldoffsets.lerpfrac4                      = PRVM_ED_FindFieldOffset("lerpfrac4");
1609         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1610         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1611         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1612         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1613         prog->fieldoffsets.movetypesteplandevent          = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1614         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1615         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1616         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1617         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1618         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1619         prog->fieldoffsets.packetloss                     = PRVM_ED_FindFieldOffset("ping_packetloss");
1620         prog->fieldoffsets.movementloss                   = PRVM_ED_FindFieldOffset("ping_movementloss");
1621         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1622         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1623         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1624         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1625         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1626         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1627         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1628         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1629         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1630         prog->fieldoffsets.shadertime                     = PRVM_ED_FindFieldOffset("shadertime");
1631         prog->fieldoffsets.skeletonindex                  = PRVM_ED_FindFieldOffset("skeletonindex");
1632         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1633         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1634         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1635         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1636         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1637         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1638         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1639         prog->fieldoffsets.bouncefactor                   = PRVM_ED_FindFieldOffset("bouncefactor");
1640         prog->fieldoffsets.bouncestop                     = PRVM_ED_FindFieldOffset("bouncestop");
1641
1642         prog->fieldoffsets.solid                          = PRVM_ED_FindFieldOffset("solid");
1643         prog->fieldoffsets.movetype                       = PRVM_ED_FindFieldOffset("movetype");
1644         prog->fieldoffsets.modelindex                     = PRVM_ED_FindFieldOffset("modelindex");
1645         prog->fieldoffsets.mins                           = PRVM_ED_FindFieldOffset("mins");
1646         prog->fieldoffsets.maxs                           = PRVM_ED_FindFieldOffset("maxs");
1647         prog->fieldoffsets.mass                           = PRVM_ED_FindFieldOffset("mass");
1648         prog->fieldoffsets.origin                         = PRVM_ED_FindFieldOffset("origin");
1649         prog->fieldoffsets.velocity                       = PRVM_ED_FindFieldOffset("velocity");
1650         //prog->fieldoffsets.axis_forward                   = PRVM_ED_FindFieldOffset("axis_forward");
1651         //prog->fieldoffsets.axis_left                      = PRVM_ED_FindFieldOffset("axis_left");
1652         //prog->fieldoffsets.axis_up                        = PRVM_ED_FindFieldOffset("axis_up");
1653         //prog->fieldoffsets.spinvelocity                   = PRVM_ED_FindFieldOffset("spinvelocity");
1654         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1655         prog->fieldoffsets.avelocity                      = PRVM_ED_FindFieldOffset("avelocity");
1656         prog->fieldoffsets.aiment                         = PRVM_ED_FindFieldOffset("aiment");
1657         prog->fieldoffsets.enemy                          = PRVM_ED_FindFieldOffset("enemy");
1658         prog->fieldoffsets.jointtype                      = PRVM_ED_FindFieldOffset("jointtype");
1659         prog->fieldoffsets.movedir                        = PRVM_ED_FindFieldOffset("movedir");
1660
1661         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1662         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1663         prog->funcoffsets.CSQC_Ent_Spawn                  = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1664         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1665         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1666         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1667         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1668         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1669         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1670         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1671         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1672         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1673         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1674         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1675         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1676         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1677         prog->funcoffsets.Gecko_Query                     = PRVM_ED_FindFunctionOffset("Gecko_Query");
1678         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1679         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1680         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1681         prog->funcoffsets.SV_OnEntityPostSpawnFunction    = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1682         prog->funcoffsets.SV_OnEntityPreSpawnFunction     = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1683         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1684         prog->funcoffsets.SV_PausedTic                    = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1685         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1686         prog->funcoffsets.SV_Shutdown                     = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1687         prog->funcoffsets.URI_Get_Callback                = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1688         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1689         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1690         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1691         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1692         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1693         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1694         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1695         prog->globaloffsets.drawfontscale                 = PRVM_ED_FindGlobalOffset("drawfontscale");
1696         prog->globaloffsets.gettaginfo_forward            = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1697         prog->globaloffsets.gettaginfo_name               = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1698         prog->globaloffsets.gettaginfo_offset             = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1699         prog->globaloffsets.gettaginfo_parent             = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1700         prog->globaloffsets.gettaginfo_right              = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1701         prog->globaloffsets.gettaginfo_up                 = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1702         prog->globaloffsets.transparent_offset            = PRVM_ED_FindGlobalOffset("transparent_offset");
1703         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1704         prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1705         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1706         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1707         prog->globaloffsets.serverdeltatime               = PRVM_ED_FindGlobalOffset("serverdeltatime");
1708         prog->globaloffsets.serverprevtime                = PRVM_ED_FindGlobalOffset("serverprevtime");
1709         prog->globaloffsets.servertime                    = PRVM_ED_FindGlobalOffset("servertime");
1710         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1711         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1712         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1713         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1714         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1715         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1716         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1717         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1718         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1719         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1720         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1721         prog->globaloffsets.trace_networkentity           = PRVM_ED_FindGlobalOffset("trace_networkentity");
1722         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1723         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1724         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1725         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1726         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1727         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1728         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1729         prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
1730
1731         // menu qc only uses some functions, nothing else
1732         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1733         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1734         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1735         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1736         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1737         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1738 }
1739
1740 // not used
1741 /*
1742 typedef struct dpfield_s
1743 {
1744         int type;
1745         char *string;
1746 }
1747 dpfield_t;
1748
1749 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1750
1751 dpfield_t dpfields[] =
1752 {
1753 };
1754 */
1755
1756 /*
1757 ===============
1758 PRVM_ResetProg
1759 ===============
1760 */
1761
1762 #define PO_HASHSIZE 16384
1763 typedef struct po_string_s
1764 {
1765         char *key, *value;
1766         struct po_string_s *nextonhashchain;
1767 }
1768 po_string_t;
1769 typedef struct po_s
1770 {
1771         po_string_t *hashtable[PO_HASHSIZE];
1772 }
1773 po_t;
1774 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1775 {
1776         for(;;)
1777         {
1778                 switch(*in)
1779                 {
1780                         case 0:
1781                                 *out++ = 0;
1782                                 return;
1783                         case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1784                         case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1785                         case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1786                         case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1787                         case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1788                         case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1789                         case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1790                         default:
1791                                 if(*in >= 0 && *in <= 0x1F)
1792                                 {
1793                                         if(outsize >= 4)
1794                                         {
1795                                                 *out++ = '\\';
1796                                                 *out++ = '0' + ((*in & 0700) >> 6);
1797                                                 *out++ = '0' + ((*in & 0070) >> 3);
1798                                                 *out++ = '0' + ((*in & 0007));
1799                                                 outsize -= 4;
1800                                         }
1801                                 }
1802                                 else
1803                                 {
1804                                         if(outsize >= 1)
1805                                         {
1806                                                 *out++ = *in;
1807                                                 outsize -= 1;
1808                                         }
1809                                 }
1810                                 break;
1811                 }
1812                 ++in;
1813         }
1814 }
1815 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1816 {
1817         for(;;)
1818         {
1819                 switch(*in)
1820                 {
1821                         case 0:
1822                                 *out++ = 0;
1823                                 return;
1824                         case '\\':
1825                                 ++in;
1826                                 switch(*in)
1827                                 {
1828                                         case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1829                                         case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1830                                         case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1831                                         case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1832                                         case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1833                                         case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1834                                         case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1835                                         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1836                                                 if(outsize > 0) 
1837                                                         *out = *in - '0';
1838                                                 ++in;
1839                                                 if(*in >= '0' && *in <= '7')
1840                                                 {
1841                                                         if(outsize > 0)
1842                                                                 *out = (*out << 3) | (*in - '0');
1843                                                         ++in;
1844                                                 }
1845                                                 if(*in >= '0' && *in <= '7')
1846                                                 {
1847                                                         if(outsize > 0)
1848                                                                 *out = (*out << 3) | (*in - '0');
1849                                                         ++in;
1850                                                 }
1851                                                 --in;
1852                                                 if(outsize > 0)
1853                                                 {
1854                                                         ++out;
1855                                                         --outsize;
1856                                                 }
1857                                                 break;
1858                                         default:
1859                                                 if(outsize > 0) { *out++ = *in; --outsize; }
1860                                                 break;
1861                                 }
1862                                 break;
1863                         default:
1864                                 if(outsize > 0)
1865                                 {
1866                                         *out++ = *in;
1867                                         --outsize;
1868                                 }
1869                                 break;
1870                 }
1871                 ++in;
1872         }
1873 }
1874 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1875 {
1876         po_t *po;
1877         const char *p, *q;
1878         int mode;
1879         char inbuf[MAX_INPUTLINE];
1880         char decodedbuf[MAX_INPUTLINE];
1881         size_t decodedpos;
1882         int hashindex;
1883         po_string_t thisstr;
1884         const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1885
1886         if(!buf)
1887                 return NULL;
1888
1889         po = Mem_Alloc(pool, sizeof(*po));
1890         memset(po, 0, sizeof(*po));
1891
1892         p = buf;
1893         while(*p)
1894         {
1895                 if(*p == '#')
1896                 {
1897                         // skip to newline
1898                         p = strchr(p, '\n');
1899                         if(!p)
1900                                 break;
1901                         ++p;
1902                         continue;
1903                 }
1904                 if(*p == '\r' || *p == '\n')
1905                 {
1906                         ++p;
1907                         continue;
1908                 }
1909                 if(!strncmp(p, "msgid \"", 7))
1910                 {
1911                         mode = 0;
1912                         p += 6;
1913                 }
1914                 else if(!strncmp(p, "msgstr \"", 8))
1915                 {
1916                         mode = 1;
1917                         p += 7;
1918                 }
1919                 else
1920                 {
1921                         p = strchr(p, '\n');
1922                         if(!p)
1923                                 break;
1924                         ++p;
1925                         continue;
1926                 }
1927                 decodedpos = 0;
1928                 while(*p == '"')
1929                 {
1930                         ++p;
1931                         q = strchr(p, '\n');
1932                         if(!q)
1933                                 break;
1934                         if(*(q-1) == '\r')
1935                                 --q;
1936                         if(*(q-1) != '"')
1937                                 break;
1938                         if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1939                                 break;
1940                         strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1941                         PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1942                         decodedpos += strlen(decodedbuf + decodedpos);
1943                         if(*q == '\r')
1944                                 ++q;
1945                         if(*q == '\n')
1946                                 ++q;
1947                         p = q;
1948                 }
1949                 if(mode == 0)
1950                 {
1951                         if(thisstr.key)
1952                                 Mem_Free(thisstr.key);
1953                         thisstr.key = Mem_Alloc(pool, decodedpos + 1);
1954                         memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1955                 }
1956                 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1957                 {
1958                         thisstr.value = Mem_Alloc(pool, decodedpos + 1);
1959                         memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1960                         hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1961                         thisstr.nextonhashchain = po->hashtable[hashindex];
1962                         po->hashtable[hashindex] = Mem_Alloc(pool, sizeof(thisstr));
1963                         memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1964                         memset(&thisstr, 0, sizeof(thisstr));
1965                 }
1966         }
1967         
1968         Mem_Free((char *) buf);
1969         return po;
1970 }
1971 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1972 {
1973         int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1974         po_string_t *p = po->hashtable[hashindex];
1975         while(p)
1976         {
1977                 if(!strcmp(str, p->key))
1978                         return p->value;
1979                 p = p->nextonhashchain;
1980         }
1981         return NULL;
1982 }
1983 void PRVM_PO_Destroy(po_t *po)
1984 {
1985         int i;
1986         for(i = 0; i < PO_HASHSIZE; ++i)
1987         {
1988                 po_string_t *p = po->hashtable[i];
1989                 while(p)
1990                 {
1991                         po_string_t *q = p;
1992                         p = p->nextonhashchain;
1993                         Mem_Free(q->key);
1994                         Mem_Free(q->value);
1995                         Mem_Free(q);
1996                 }
1997         }
1998         Mem_Free(po);
1999 }
2000
2001 void PRVM_LeakTest(void);
2002 void PRVM_ResetProg(void)
2003 {
2004         PRVM_LeakTest();
2005         PRVM_GCALL(reset_cmd)();
2006         Mem_FreePool(&prog->progs_mempool);
2007         if(prog->po)
2008                 PRVM_PO_Destroy((po_t *) prog->po);
2009         memset(prog,0,sizeof(prvm_prog_t));
2010         prog->starttime = Sys_DoubleTime();
2011 }
2012
2013 /*
2014 ===============
2015 PRVM_LoadLNO
2016 ===============
2017 */
2018 void PRVM_LoadLNO( const char *progname ) {
2019         fs_offset_t filesize;
2020         unsigned char *lno;
2021         unsigned int *header;
2022         char filename[512];
2023
2024         FS_StripExtension( progname, filename, sizeof( filename ) );
2025         strlcat( filename, ".lno", sizeof( filename ) );
2026
2027         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2028         if( !lno ) {
2029                 return;
2030         }
2031
2032 /*
2033 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
2034 <Spike>    SafeWrite (h, &version, sizeof(int));
2035 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
2036 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
2037 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
2038 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
2039 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2040 */
2041         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
2042                 Mem_Free(lno);
2043                 return;
2044         }
2045
2046         header = (unsigned int *) lno;
2047         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2048                 LittleLong( header[ 1 ] ) == 1 &&
2049                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
2050                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
2051                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
2052                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
2053         {
2054                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
2055                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
2056         }
2057         Mem_Free( lno );
2058 }
2059
2060 /*
2061 ===============
2062 PRVM_LoadProgs
2063 ===============
2064 */
2065 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
2066 {
2067         int i;
2068         dstatement_t *st;
2069         ddef_t *infielddefs;
2070         dfunction_t *dfunctions;
2071         fs_offset_t filesize;
2072
2073         if( prog->loaded ) {
2074                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2075         }
2076
2077         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2078         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2079                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2080         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2081
2082         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2083
2084         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
2085
2086 // byte swap the header
2087         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
2088                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
2089
2090         if (prog->progs->version != PROG_VERSION)
2091                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
2092         if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
2093                 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);
2094
2095         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
2096         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
2097
2098         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
2099                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2100         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
2101         prog->stringssize = prog->progs->numstrings;
2102
2103         prog->numknownstrings = 0;
2104         prog->maxknownstrings = 0;
2105         prog->knownstrings = NULL;
2106         prog->knownstrings_freeable = NULL;
2107
2108         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2109
2110         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
2111
2112         // we need to expand the fielddefs list to include all the engine fields,
2113         // so allocate a new place for it
2114         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
2115         //                                                                                              ( + DPFIELDS                       )
2116         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
2117
2118         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
2119
2120         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
2121
2122         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
2123         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
2124
2125 // byte swap the lumps
2126         for (i=0 ; i<prog->progs->numstatements ; i++)
2127         {
2128                 prog->statements[i].op = LittleShort(prog->statements[i].op);
2129                 prog->statements[i].a = LittleShort(prog->statements[i].a);
2130                 prog->statements[i].b = LittleShort(prog->statements[i].b);
2131                 prog->statements[i].c = LittleShort(prog->statements[i].c);
2132         }
2133
2134         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
2135         for (i = 0;i < prog->progs->numfunctions;i++)
2136         {
2137                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
2138                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
2139                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
2140                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
2141                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
2142                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
2143                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
2144                 if(prog->functions[i].first_statement >= prog->progs->numstatements)
2145                         PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2146                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2147         }
2148
2149         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2150         {
2151                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
2152                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
2153                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
2154                 // TODO bounds check ofs, s_name
2155         }
2156
2157         // copy the progs fields to the new fields list
2158         for (i = 0;i < prog->progs->numfielddefs;i++)
2159         {
2160                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
2161                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2162                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2163                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
2164                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
2165                 // TODO bounds check ofs, s_name
2166         }
2167
2168         // append the required fields
2169         for (i = 0;i < (int) numrequiredfields;i++)
2170         {
2171                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
2172                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
2173                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2174                 // TODO bounds check ofs, s_name
2175                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
2176                         prog->progs->entityfields += 3;
2177                 else
2178                         prog->progs->entityfields++;
2179                 prog->progs->numfielddefs++;
2180         }
2181         prog->entityfields = prog->progs->entityfields;
2182
2183         // check required functions
2184         for(i=0 ; i < numrequiredfunc ; i++)
2185                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2186                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2187
2188         // check required globals
2189         for(i=0 ; i < numrequiredglobals ; i++)
2190                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
2191                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
2192
2193         for (i=0 ; i<prog->progs->numglobals ; i++)
2194                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
2195
2196         // LordHavoc: bounds check anything static
2197         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
2198         {
2199                 switch (st->op)
2200                 {
2201                 case OP_IF:
2202                 case OP_IFNOT:
2203                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
2204                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2205                         break;
2206                 case OP_GOTO:
2207                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
2208                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2209                         break;
2210                 // global global global
2211                 case OP_ADD_F:
2212                 case OP_ADD_V:
2213                 case OP_SUB_F:
2214                 case OP_SUB_V:
2215                 case OP_MUL_F:
2216                 case OP_MUL_V:
2217                 case OP_MUL_FV:
2218                 case OP_MUL_VF:
2219                 case OP_DIV_F:
2220                 case OP_BITAND:
2221                 case OP_BITOR:
2222                 case OP_GE:
2223                 case OP_LE:
2224                 case OP_GT:
2225                 case OP_LT:
2226                 case OP_AND:
2227                 case OP_OR:
2228                 case OP_EQ_F:
2229                 case OP_EQ_V:
2230                 case OP_EQ_S:
2231                 case OP_EQ_E:
2232                 case OP_EQ_FNC:
2233                 case OP_NE_F:
2234                 case OP_NE_V:
2235                 case OP_NE_S:
2236                 case OP_NE_E:
2237                 case OP_NE_FNC:
2238                 case OP_ADDRESS:
2239                 case OP_LOAD_F:
2240                 case OP_LOAD_FLD:
2241                 case OP_LOAD_ENT:
2242                 case OP_LOAD_S:
2243                 case OP_LOAD_FNC:
2244                 case OP_LOAD_V:
2245                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2246                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2247                         break;
2248                 // global none global
2249                 case OP_NOT_F:
2250                 case OP_NOT_V:
2251                 case OP_NOT_S:
2252                 case OP_NOT_FNC:
2253                 case OP_NOT_ENT:
2254                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
2255                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2256                         break;
2257                 // 2 globals
2258                 case OP_STOREP_F:
2259                 case OP_STOREP_ENT:
2260                 case OP_STOREP_FLD:
2261                 case OP_STOREP_S:
2262                 case OP_STOREP_FNC:
2263                 case OP_STORE_F:
2264                 case OP_STORE_ENT:
2265                 case OP_STORE_FLD:
2266                 case OP_STORE_S:
2267                 case OP_STORE_FNC:
2268                 case OP_STATE:
2269                 case OP_STOREP_V:
2270                 case OP_STORE_V:
2271                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2272                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2273                         break;
2274                 // 1 global
2275                 case OP_CALL0:
2276                 case OP_CALL1:
2277                 case OP_CALL2:
2278                 case OP_CALL3:
2279                 case OP_CALL4:
2280                 case OP_CALL5:
2281                 case OP_CALL6:
2282                 case OP_CALL7:
2283                 case OP_CALL8:
2284                 case OP_DONE:
2285                 case OP_RETURN:
2286                         if ((unsigned short) st->a >= prog->progs->numglobals)
2287                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2288                         break;
2289                 default:
2290                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2291                         break;
2292                 }
2293         }
2294         if(prog->progs->numstatements < 1)
2295         {
2296                 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2297         }
2298         else switch(prog->statements[prog->progs->numstatements - 1].op)
2299         {
2300                 case OP_RETURN:
2301                 case OP_GOTO:
2302                 case OP_DONE:
2303                         break;
2304                 default:
2305                         PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2306                         break;
2307         }
2308
2309         PRVM_LoadLNO(filename);
2310
2311         PRVM_Init_Exec();
2312
2313         if(*prvm_language.string)
2314         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2315         // later idea: include a list of authorized .po file checksums with the csprogs
2316         {
2317                 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2318                 if(!strcmp(prvm_language.string, "dump"))
2319                 {
2320                         qfile_t *f = FS_OpenRealFile(va("%s.%s.po", filename, prvm_language.string), "w", false);
2321                         Con_Printf("Dumping to %s.%s.po\n", filename, prvm_language.string);
2322                         if(f)
2323                         {
2324                                 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2325                                 {
2326                                         const char *name;
2327                                         name = PRVM_GetString(prog->globaldefs[i].s_name);
2328                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2329                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2330                                         {
2331                                                 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2332                                                 const char *value = PRVM_GetString(val->string);
2333                                                 if(*value)
2334                                                 {
2335                                                         char buf[MAX_INPUTLINE];
2336                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2337                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2338                                                 }
2339                                         }
2340                                 }
2341                                 FS_Close(f);
2342                         }
2343                 }
2344                 else
2345                 {
2346                         po_t *po = PRVM_PO_Load(va("%s.%s.po", filename, prvm_language.string), prog->progs_mempool);
2347                         if(po)
2348                         {
2349                                 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2350                                 {
2351                                         const char *name;
2352                                         name = PRVM_GetString(prog->globaldefs[i].s_name);
2353                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2354                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2355                                         {
2356                                                 prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2357                                                 const char *value = PRVM_GetString(val->string);
2358                                                 if(*value)
2359                                                 {
2360                                                         value = PRVM_PO_Lookup(po, value);
2361                                                         if(value)
2362                                                                 val->string = PRVM_SetEngineString(value);
2363                                                 }
2364                                         }
2365                                 }
2366                         }
2367                 }
2368         }
2369
2370         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
2371         {
2372                 const char *name;
2373                 name = PRVM_GetString(prog->globaldefs[i].s_name);
2374                 //Con_Printf("found var %s\n", name);
2375                 if(name
2376                         && !strncmp(name, "autocvar_", 9)
2377                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2378                 )
2379                 {
2380                         prvm_eval_t *val = (prvm_eval_t *)(prog->globals.generic + prog->globaldefs[i].ofs);
2381                         cvar_t *cvar = Cvar_FindVar(name + 9);
2382                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2383                         if(!cvar)
2384                         {
2385                                 const char *value;
2386                                 char buf[64];
2387                                 Con_Printf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2388                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2389                                 {
2390                                         case ev_float:
2391                                                 if((float)((int)(val->_float)) == val->_float)
2392                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2393                                                 else
2394                                                         dpsnprintf(buf, sizeof(buf), "%f", val->_float);
2395                                                 value = buf;
2396                                                 break;
2397                                         case ev_vector:
2398                                                 dpsnprintf(buf, sizeof(buf), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2399                                                 break;
2400                                         case ev_string:
2401                                                 value = PRVM_GetString(val->string);
2402                                                 break;
2403                                         default:
2404                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2405                                                 goto fail;
2406                                 }
2407                                 cvar = Cvar_Get(name + 9, value, 0, NULL);
2408                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2409                                 {
2410                                         val->string = PRVM_SetEngineString(cvar->string);
2411                                         cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2412                                 }
2413                                 if(!cvar)
2414                                         PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2415                                 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2416                                 cvar->globaldefindex[prog - prog_list] = i;
2417                         }
2418                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2419                         {
2420                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2421                                 int j;
2422                                 const char *s;
2423                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2424                                 {
2425                                         case ev_float:
2426                                                 val->_float = cvar->value;
2427                                                 break;
2428                                         case ev_vector:
2429                                                 s = cvar->string;
2430                                                 VectorClear(val->vector);
2431                                                 for (j = 0;j < 3;j++)
2432                                                 {
2433                                                         while (*s && ISWHITESPACE(*s))
2434                                                                 s++;
2435                                                         if (!*s)
2436                                                                 break;
2437                                                         val->vector[j] = atof(s);
2438                                                         while (!ISWHITESPACE(*s))
2439                                                                 s++;
2440                                                         if (!*s)
2441                                                                 break;
2442                                                 }
2443                                                 break;
2444                                         case ev_string:
2445                                                 val->string = PRVM_SetEngineString(cvar->string);
2446                                                 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2447                                                 break;
2448                                         default:
2449                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2450                                                 goto fail;
2451                                 }
2452                                 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2453                                 cvar->globaldefindex[prog - prog_list] = i;
2454                         }
2455                         else
2456                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2457                 }
2458 fail:
2459                 ;
2460         }
2461
2462         prog->loaded = TRUE;
2463
2464         // set flags & ddef_ts in prog
2465
2466         prog->flag = 0;
2467
2468         PRVM_FindOffsets();
2469
2470         PRVM_GCALL(init_cmd)();
2471
2472         // init mempools
2473         PRVM_MEM_Alloc();
2474 }
2475
2476
2477 void PRVM_Fields_f (void)
2478 {
2479         int i, j, ednum, used, usedamount;
2480         int *counts;
2481         char tempstring[MAX_INPUTLINE], tempstring2[260];
2482         const char *name;
2483         prvm_edict_t *ed;
2484         ddef_t *d;
2485         int *v;
2486
2487         // TODO
2488         /*
2489         if (!sv.active)
2490         {
2491                 Con_Print("no progs loaded\n");
2492                 return;
2493         }
2494         */
2495
2496         if(Cmd_Argc() != 2)
2497         {
2498                 Con_Print("prvm_fields <program name>\n");
2499                 return;
2500         }
2501
2502         PRVM_Begin;
2503         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2504                 return;
2505
2506         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2507         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2508         {
2509                 ed = PRVM_EDICT_NUM(ednum);
2510                 if (ed->priv.required->free)
2511                         continue;
2512                 for (i = 1;i < prog->progs->numfielddefs;i++)
2513                 {
2514                         d = &prog->fielddefs[i];
2515                         name = PRVM_GetString(d->s_name);
2516                         if (name[strlen(name)-2] == '_')
2517                                 continue;       // skip _x, _y, _z vars
2518                         v = (int *)(ed->fields.vp + d->ofs);
2519                         // if the value is still all 0, skip the field
2520                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2521                         {
2522                                 if (v[j])
2523                                 {
2524                                         counts[i]++;
2525                                         break;
2526                                 }
2527                         }
2528                 }
2529         }
2530         used = 0;
2531         usedamount = 0;
2532         tempstring[0] = 0;
2533         for (i = 0;i < prog->progs->numfielddefs;i++)
2534         {
2535                 d = &prog->fielddefs[i];
2536                 name = PRVM_GetString(d->s_name);
2537                 if (name[strlen(name)-2] == '_')
2538                         continue;       // skip _x, _y, _z vars
2539                 switch(d->type & ~DEF_SAVEGLOBAL)
2540                 {
2541                 case ev_string:
2542                         strlcat(tempstring, "string   ", sizeof(tempstring));
2543                         break;
2544                 case ev_entity:
2545                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2546                         break;
2547                 case ev_function:
2548                         strlcat(tempstring, "function ", sizeof(tempstring));
2549                         break;
2550                 case ev_field:
2551                         strlcat(tempstring, "field    ", sizeof(tempstring));
2552                         break;
2553                 case ev_void:
2554                         strlcat(tempstring, "void     ", sizeof(tempstring));
2555                         break;
2556                 case ev_float:
2557                         strlcat(tempstring, "float    ", sizeof(tempstring));
2558                         break;
2559                 case ev_vector:
2560                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2561                         break;
2562                 case ev_pointer:
2563                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2564                         break;
2565                 default:
2566                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2567                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2568                         break;
2569                 }
2570                 if (strlen(name) > sizeof(tempstring2)-4)
2571                 {
2572                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2573                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2574                         tempstring2[sizeof(tempstring2)-1] = 0;
2575                         name = tempstring2;
2576                 }
2577                 strlcat(tempstring, name, sizeof(tempstring));
2578                 for (j = (int)strlen(name);j < 25;j++)
2579                         strlcat(tempstring, " ", sizeof(tempstring));
2580                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2581                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2582                 strlcat(tempstring, "\n", sizeof(tempstring));
2583                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2584                 {
2585                         Con_Print(tempstring);
2586                         tempstring[0] = 0;
2587                 }
2588                 if (counts[i])
2589                 {
2590                         used++;
2591                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2592                 }
2593         }
2594         Mem_Free(counts);
2595         Con_Printf("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2596
2597         PRVM_End;
2598 }
2599
2600 void PRVM_Globals_f (void)
2601 {
2602         int i;
2603         const char *wildcard;
2604         int numculled;
2605                 numculled = 0;
2606         // TODO
2607         /*if (!sv.active)
2608         {
2609                 Con_Print("no progs loaded\n");
2610                 return;
2611         }*/
2612         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2613         {
2614                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2615                 return;
2616         }
2617
2618         PRVM_Begin;
2619         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2620                 return;
2621
2622         if( Cmd_Argc() == 3)
2623                 wildcard = Cmd_Argv(2);
2624         else
2625                 wildcard = NULL;
2626
2627         Con_Printf("%s :", PRVM_NAME);
2628
2629         for (i = 0;i < prog->progs->numglobaldefs;i++)
2630         {
2631                 if(wildcard)
2632                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2633                         {
2634                                 numculled++;
2635                                 continue;
2636                         }
2637                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2638         }
2639         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2640
2641         PRVM_End;
2642 }
2643
2644 /*
2645 ===============
2646 PRVM_Global
2647 ===============
2648 */
2649 void PRVM_Global_f(void)
2650 {
2651         ddef_t *global;
2652         if( Cmd_Argc() != 3 ) {
2653                 Con_Printf( "prvm_global <program name> <global name>\n" );
2654                 return;
2655         }
2656
2657         PRVM_Begin;
2658         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2659                 return;
2660
2661         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2662         if( !global )
2663                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2664         else
2665                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2666         PRVM_End;
2667 }
2668
2669 /*
2670 ===============
2671 PRVM_GlobalSet
2672 ===============
2673 */
2674 void PRVM_GlobalSet_f(void)
2675 {
2676         ddef_t *global;
2677         if( Cmd_Argc() != 4 ) {
2678                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2679                 return;
2680         }
2681
2682         PRVM_Begin;
2683         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2684                 return;
2685
2686         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2687         if( !global )
2688                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2689         else
2690                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2691         PRVM_End;
2692 }
2693
2694 /*
2695 ===============
2696 PRVM_Init
2697 ===============
2698 */
2699 void PRVM_Init (void)
2700 {
2701         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2702         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2703         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2704         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2705         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");
2706         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)");
2707         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)");
2708         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2709         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2710         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2711         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)");
2712         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");
2713         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");
2714         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2715         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2716         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2717         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2718
2719         // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2720         prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2721
2722         Cvar_RegisterVariable (&prvm_language);
2723         Cvar_RegisterVariable (&prvm_traceqc);
2724         Cvar_RegisterVariable (&prvm_statementprofiling);
2725         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2726         Cvar_RegisterVariable (&prvm_leaktest);
2727         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2728         Cvar_RegisterVariable (&prvm_errordump);
2729         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2730         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2731
2732         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2733         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2734
2735         //VM_Cmd_Init();
2736 }
2737
2738 /*
2739 ===============
2740 PRVM_InitProg
2741 ===============
2742 */
2743 void PRVM_InitProg(int prognr)
2744 {
2745         static unsigned int progid = 0;
2746
2747         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2748                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2749
2750         prog = &prog_list[prognr];
2751
2752         if(prog->loaded)
2753                 PRVM_ResetProg();
2754
2755         memset(prog, 0, sizeof(prvm_prog_t));
2756         prog->starttime = Sys_DoubleTime();
2757         prog->id = ++progid;
2758
2759         prog->error_cmd = Host_Error;
2760         prog->leaktest_active = prvm_leaktest.integer != 0;
2761 }
2762
2763 int PRVM_GetProgNr(void)
2764 {
2765         return prog - prog_list;
2766 }
2767
2768 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2769 {
2770         return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2771 }
2772
2773 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2774 {
2775         _Mem_Free(buffer, filename, fileline);
2776 }
2777
2778 void _PRVM_FreeAll(const char *filename, int fileline)
2779 {
2780         prog->progs = NULL;
2781         prog->fielddefs = NULL;
2782         prog->functions = NULL;
2783         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2784 }
2785
2786 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2787 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2788 {
2789         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2790         return 0;
2791 }
2792
2793 sizebuf_t vm_tempstringsbuf;
2794
2795 const char *PRVM_GetString(int num)
2796 {
2797         if (num >= 0)
2798         {
2799                 if (num < prog->stringssize)
2800                         return prog->strings + num;
2801                 else
2802 #if 1
2803                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2804                 {
2805                         num -= prog->stringssize;
2806                         if (num < vm_tempstringsbuf.cursize)
2807                                 return (char *)vm_tempstringsbuf.data + num;
2808                         else
2809                         {
2810                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2811                                 return "";
2812                         }
2813                 }
2814                 else
2815 #endif
2816                 {
2817                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2818                         return "";
2819                 }
2820         }
2821         else
2822         {
2823                 num = -1 - num;
2824 #if 0
2825                 if (num >= (1<<30))
2826                 {
2827                         // special range reserved for tempstrings
2828                         num -= (1<<30);
2829                         if (num < vm_tempstringsbuf.cursize)
2830                                 return (char *)vm_tempstringsbuf.data + num;
2831                         else
2832                         {
2833                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2834                                 return "";
2835                         }
2836                 }
2837                 else
2838 #endif
2839                 if (num < prog->numknownstrings)
2840                 {
2841                         if (!prog->knownstrings[num])
2842                         {
2843                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2844                                 return "";
2845                         }
2846                         return prog->knownstrings[num];
2847                 }
2848                 else
2849                 {
2850                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2851                         return "";
2852                 }
2853         }
2854 }
2855
2856 const char *PRVM_ChangeEngineString(int i, const char *s)
2857 {
2858         const char *old;
2859         i = -1 - i;
2860         if(i < 0 || i >= prog->numknownstrings)
2861                 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2862         old = prog->knownstrings[i];
2863         prog->knownstrings[i] = s;
2864         return old;
2865 }
2866
2867 int PRVM_SetEngineString(const char *s)
2868 {
2869         int i;
2870         if (!s)
2871                 return 0;
2872         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2873                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2874         // if it's in the tempstrings area, use a reserved range
2875         // (otherwise we'd get millions of useless string offsets cluttering the database)
2876         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2877 #if 1
2878                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2879 #else
2880                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2881 #endif
2882         // see if it's a known string address
2883         for (i = 0;i < prog->numknownstrings;i++)
2884                 if (prog->knownstrings[i] == s)
2885                         return -1 - i;
2886         // new unknown engine string
2887         if (developer_insane.integer)
2888                 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2889         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2890                 if (!prog->knownstrings[i])
2891                         break;
2892         if (i >= prog->numknownstrings)
2893         {
2894                 if (i >= prog->maxknownstrings)
2895                 {
2896                         const char **oldstrings = prog->knownstrings;
2897                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2898                         const char **oldstrings_origin = prog->knownstrings_origin;
2899                         prog->maxknownstrings += 128;
2900                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2901                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2902                         if(prog->leaktest_active)
2903                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2904                         if (prog->numknownstrings)
2905                         {
2906                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2907                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2908                                 if(prog->leaktest_active)
2909                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2910                         }
2911                 }
2912                 prog->numknownstrings++;
2913         }
2914         prog->firstfreeknownstring = i + 1;
2915         prog->knownstrings[i] = s;
2916         prog->knownstrings_freeable[i] = false;
2917         if(prog->leaktest_active)
2918                 prog->knownstrings_origin[i] = NULL;
2919         return -1 - i;
2920 }
2921
2922 // temp string handling
2923
2924 // all tempstrings go into this buffer consecutively, and it is reset
2925 // whenever PRVM_ExecuteProgram returns to the engine
2926 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2927 //  restores it on return, so multiple recursive calls can share the same
2928 //  buffer)
2929 // the buffer size is automatically grown as needed
2930
2931 int PRVM_SetTempString(const char *s)
2932 {
2933         int size;
2934         char *t;
2935         if (!s)
2936                 return 0;
2937         size = (int)strlen(s) + 1;
2938         if (developer_insane.integer)
2939                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2940         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2941         {
2942                 sizebuf_t old = vm_tempstringsbuf;
2943                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2944                         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);
2945                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2946                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2947                         vm_tempstringsbuf.maxsize *= 2;
2948                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2949                 {
2950                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2951                         vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2952                         if (old.cursize)
2953                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2954                         if (old.data)
2955                                 Mem_Free(old.data);
2956                 }
2957         }
2958         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2959         memcpy(t, s, size);
2960         vm_tempstringsbuf.cursize += size;
2961         return PRVM_SetEngineString(t);
2962 }
2963
2964 int PRVM_AllocString(size_t bufferlength, char **pointer)
2965 {
2966         int i;
2967         if (!bufferlength)
2968                 return 0;
2969         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2970                 if (!prog->knownstrings[i])
2971                         break;
2972         if (i >= prog->numknownstrings)
2973         {
2974                 if (i >= prog->maxknownstrings)
2975                 {
2976                         const char **oldstrings = prog->knownstrings;
2977                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2978                         const char **oldstrings_origin = prog->knownstrings_origin;
2979                         prog->maxknownstrings += 128;
2980                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2981                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2982                         if(prog->leaktest_active)
2983                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2984                         if (prog->numknownstrings)
2985                         {
2986                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2987                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2988                                 if(prog->leaktest_active)
2989                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2990                         }
2991                         // TODO why not Mem_Free the old ones?
2992                 }
2993                 prog->numknownstrings++;
2994         }
2995         prog->firstfreeknownstring = i + 1;
2996         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2997         prog->knownstrings_freeable[i] = true;
2998         if(prog->leaktest_active)
2999                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3000         if (pointer)
3001                 *pointer = (char *)(prog->knownstrings[i]);
3002         return -1 - i;
3003 }
3004
3005 void PRVM_FreeString(int num)
3006 {
3007         if (num == 0)
3008                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3009         else if (num >= 0 && num < prog->stringssize)
3010                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3011         else if (num < 0 && num >= -prog->numknownstrings)
3012         {
3013                 num = -1 - num;
3014                 if (!prog->knownstrings[num])
3015                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3016                 if (!prog->knownstrings_freeable[num])
3017                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3018                 PRVM_Free((char *)prog->knownstrings[num]);
3019                 if(prog->leaktest_active)
3020                         if(prog->knownstrings_origin[num])
3021                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3022                 prog->knownstrings[num] = NULL;
3023                 prog->knownstrings_freeable[num] = false;
3024                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3025         }
3026         else
3027                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3028 }
3029
3030 static qboolean PRVM_IsStringReferenced(string_t string)
3031 {
3032         int i, j;
3033
3034         for (i = 0;i < prog->progs->numglobaldefs;i++)
3035         {
3036                 ddef_t *d = &prog->globaldefs[i];
3037                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3038                         continue;
3039                 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
3040                         return true;
3041         }
3042
3043         for(j = 0; j < prog->num_edicts; ++j)
3044         {
3045                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3046                 if (ed->priv.required->free)
3047                         continue;
3048                 for (i=0; i<prog->progs->numfielddefs; ++i)
3049                 {
3050                         ddef_t *d = &prog->fielddefs[i];
3051                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3052                                 continue;
3053                         if(string == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->string)
3054                                 return true;
3055                 }
3056         }
3057
3058         return false;
3059 }
3060
3061 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3062 {
3063         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3064                 return true; // world or clients
3065         switch(prog - prog_list)
3066         {
3067                 case PRVM_SERVERPROG:
3068                         {
3069                                 entvars_t *ev = edict->fields.server;
3070                                 if(ev->solid) // can block other stuff, or is a trigger?
3071                                         return true;
3072                                 if(ev->modelindex) // visible ent?
3073                                         return true;
3074                                 if(ev->effects) // particle effect?
3075                                         return true;
3076                                 if(ev->think) // has a think function?
3077                                         if(ev->nextthink > 0) // that actually will eventually run?
3078                                                 return true;
3079                                 if(ev->takedamage)
3080                                         return true;
3081                                 if(*prvm_leaktest_ignore_classnames.string)
3082                                 {
3083                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3084                                                 return true;
3085                                 }
3086                         }
3087                         break;
3088                 case PRVM_CLIENTPROG:
3089                         {
3090                                 // TODO someone add more stuff here
3091                                 cl_entvars_t *ev = edict->fields.client;
3092                                 if(ev->entnum) // csqc networked
3093                                         return true;
3094                                 if(ev->modelindex) // visible ent?
3095                                         return true;
3096                                 if(ev->effects) // particle effect?
3097                                         return true;
3098                                 if(ev->think) // has a think function?
3099                                         if(ev->nextthink > 0) // that actually will eventually run?
3100                                                 return true;
3101                                 if(*prvm_leaktest_ignore_classnames.string)
3102                                 {
3103                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3104                                                 return true;
3105                                 }
3106                         }
3107                         break;
3108                 case PRVM_MENUPROG:
3109                         // menu prog does not have classnames
3110                         break;
3111         }
3112         return false;
3113 }
3114
3115 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3116 {
3117         int i, j;
3118         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3119         const char *targetname = NULL;
3120
3121         switch(prog - prog_list)
3122         {
3123                 case PRVM_SERVERPROG:
3124                         targetname = PRVM_GetString(edict->fields.server->targetname);
3125                         break;
3126         }
3127
3128         if(targetname)
3129                 if(!*targetname) // ""
3130                         targetname = NULL;
3131
3132         for (i = 0;i < prog->progs->numglobaldefs;i++)
3133         {
3134                 ddef_t *d = &prog->globaldefs[i];
3135                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3136                         continue;
3137                 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
3138                         return true;
3139         }
3140
3141         for(j = 0; j < prog->num_edicts; ++j)
3142         {
3143                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3144                 if (ed->priv.required->mark < mark)
3145                         continue;
3146                 if(ed == edict)
3147                         continue;
3148                 if(targetname)
3149                 {
3150                         const char *target = PRVM_GetString(ed->fields.server->target);
3151                         if(target)
3152                                 if(!strcmp(target, targetname))
3153                                         return true;
3154                 }
3155                 for (i=0; i<prog->progs->numfielddefs; ++i)
3156                 {
3157                         ddef_t *d = &prog->fielddefs[i];
3158                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3159                                 continue;
3160                         if(edictnum == ((prvm_eval_t *) &ed->fields.vp[d->ofs])->edict)
3161                                 return true;
3162                 }
3163         }
3164
3165         return false;
3166 }
3167
3168 static void PRVM_MarkReferencedEdicts(void)
3169 {
3170         int j;
3171         qboolean found_new;
3172         int stage;
3173
3174         for(j = 0; j < prog->num_edicts; ++j)
3175         {
3176                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3177                 if(ed->priv.required->free)
3178                         continue;
3179                 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3180         }
3181
3182         stage = 1;
3183         do
3184         {
3185                 found_new = false;
3186                 for(j = 0; j < prog->num_edicts; ++j)
3187                 {
3188                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3189                         if(ed->priv.required->free)
3190                                 continue;
3191                         if(ed->priv.required->mark)
3192                                 continue;
3193                         if(PRVM_IsEdictReferenced(ed, stage))
3194                         {
3195                                 ed->priv.required->mark = stage + 1;
3196                                 found_new = true;
3197                         }
3198                 }
3199                 ++stage;
3200         }
3201         while(found_new);
3202         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3203 }
3204
3205 void PRVM_LeakTest(void)
3206 {
3207         int i, j;
3208         qboolean leaked = false;
3209
3210         if(!prog->leaktest_active)
3211                 return;
3212
3213         // 1. Strings
3214         for (i = 0; i < prog->numknownstrings; ++i)
3215         {
3216                 if(prog->knownstrings[i])
3217                 if(prog->knownstrings_freeable[i])
3218                 if(prog->knownstrings_origin[i])
3219                 if(!PRVM_IsStringReferenced(-1 - i))
3220                 {
3221                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3222                         leaked = true;
3223                 }
3224         }
3225
3226         // 2. Edicts
3227         PRVM_MarkReferencedEdicts();
3228         for(j = 0; j < prog->num_edicts; ++j)
3229         {
3230                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3231                 if(ed->priv.required->free)
3232                         continue;
3233                 if(!ed->priv.required->mark)
3234                 if(ed->priv.required->allocation_origin)
3235                 {
3236                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3237                         PRVM_ED_Print(ed, NULL);
3238                         Con_Print("\n");
3239                         leaked = true;
3240                 }
3241         }
3242
3243         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3244         {
3245                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3246                 if(stringbuffer)
3247                 if(stringbuffer->origin)
3248                 {
3249                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3250                         leaked = true;
3251                 }
3252         }
3253
3254         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3255         {
3256                 if(prog->openfiles[i])
3257                 if(prog->openfiles_origin[i])
3258                 {
3259                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3260                         leaked = true;
3261                 }
3262         }
3263
3264         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3265         {
3266                 if(prog->opensearches[i])
3267                 if(prog->opensearches_origin[i])
3268                 {
3269                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3270                         leaked = true;
3271                 }
3272         }
3273
3274         if(!leaked)
3275                 Con_Printf("Congratulations. No leaks found.\n");
3276 }