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