]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
speed up leaktest a bit
[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 = (vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(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.vp = 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 = (vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(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.vp = 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.vp, 0, prog->entityfields * 4);
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         void    *val;
556         char valuebuf[MAX_INPUTLINE];
557
558         val = (void *)&prog->globals.generic[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, (prvm_eval_t *)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         int             *v;
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                 v = (int *)(ed->fields.vp + 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 (v[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, (prvm_eval_t *)v, 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         int             *v;
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                 v = (int *)(ed->fields.vp + 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 (v[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, (prvm_eval_t *)v, 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.generic[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.vp + key->ofs);
950         else
951                 val = (prvm_eval_t *)(prog->globals.generic + 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.vp + 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.vp + 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.generic[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.vp, 0, prog->entityfields * 4);
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         float *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         char vabuf[1024];
1895
1896         if (prog->loaded)
1897                 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1898
1899         dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1900         if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1901                 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1902         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1903
1904         prog->profiletime = Sys_DirtyTime();
1905         prog->starttime = realtime;
1906
1907         Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1908
1909         requiredglobalspace = 0;
1910         for (i = 0;i < numrequiredglobals;i++)
1911                 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1912
1913         prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1914
1915 // byte swap the header
1916         prog->progs_version = LittleLong(dprograms->version);
1917         prog->progs_crc = LittleLong(dprograms->crc);
1918         if (prog->progs_version != PROG_VERSION)
1919                 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1920         instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1921         prog->progs_numstatements = LittleLong(dprograms->numstatements);
1922         inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1923         prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1924         infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1925         prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1926         infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1927         prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1928         instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1929         prog->progs_numstrings = LittleLong(dprograms->numstrings);
1930         inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1931         prog->progs_numglobals = LittleLong(dprograms->numglobals);
1932         prog->progs_entityfields = LittleLong(dprograms->entityfields);
1933
1934         prog->numstatements = prog->progs_numstatements;
1935         prog->numglobaldefs = prog->progs_numglobaldefs;
1936         prog->numfielddefs = prog->progs_numfielddefs;
1937         prog->numfunctions = prog->progs_numfunctions;
1938         prog->numstrings = prog->progs_numstrings;
1939         prog->numglobals = prog->progs_numglobals;
1940         prog->entityfields = prog->progs_entityfields;
1941
1942         if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
1943                 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1944         prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1945         memcpy(prog->strings, instrings, prog->progs_numstrings);
1946         prog->stringssize = prog->progs_numstrings;
1947
1948         prog->numknownstrings = 0;
1949         prog->maxknownstrings = 0;
1950         prog->knownstrings = NULL;
1951         prog->knownstrings_freeable = NULL;
1952
1953         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1954
1955         // we need to expand the globaldefs and fielddefs to include engine defs
1956         prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1957         prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
1958         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1959         // we need to convert the statements to our memory format
1960         prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1961         // allocate space for profiling statement usage
1962         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1963         // functions need to be converted to the memory format
1964         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1965
1966         for (i = 0;i < prog->progs_numfunctions;i++)
1967         {
1968                 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1969                 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1970                 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1971                 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1972                 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
1973                 prog->functions[i].locals = LittleLong(infunctions[i].locals);
1974                 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
1975                 if(prog->functions[i].first_statement >= prog->numstatements)
1976                         prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
1977                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1978         }
1979
1980         // copy the globaldefs to the new globaldefs list
1981         for (i=0 ; i<prog->numglobaldefs ; i++)
1982         {
1983                 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
1984                 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
1985                 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
1986                 // TODO bounds check ofs, s_name
1987         }
1988
1989         // append the required globals
1990         for (i = 0;i < numrequiredglobals;i++)
1991         {
1992                 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
1993                 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
1994                 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
1995                 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
1996                         prog->numglobals += 3;
1997                 else
1998                         prog->numglobals++;
1999                 prog->numglobaldefs++;
2000         }
2001
2002         // copy the progs fields to the new fields list
2003         for (i = 0;i < prog->numfielddefs;i++)
2004         {
2005                 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2006                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2007                         prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2008                 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2009                 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2010                 // TODO bounds check ofs, s_name
2011         }
2012
2013         // append the required fields
2014         for (i = 0;i < numrequiredfields;i++)
2015         {
2016                 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2017                 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2018                 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2019                 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2020                         prog->entityfields += 3;
2021                 else
2022                         prog->entityfields++;
2023                 prog->numfielddefs++;
2024         }
2025
2026         // LordHavoc: TODO: reorder globals to match engine struct
2027         // LordHavoc: TODO: reorder fields to match engine struct
2028 #define remapglobal(index) (index)
2029 #define remapfield(index) (index)
2030
2031         // copy globals
2032         for (i = 0;i < prog->progs_numglobals;i++)
2033                 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2034
2035         // LordHavoc: TODO: support 32bit progs statement formats
2036         // copy, remap globals in statements, bounds check
2037         for (i = 0;i < prog->progs_numstatements;i++)
2038         {
2039                 op = (opcode_t)LittleShort(instatements[i].op);
2040                 a = (unsigned short)LittleShort(instatements[i].a);
2041                 b = (unsigned short)LittleShort(instatements[i].b);
2042                 c = (unsigned short)LittleShort(instatements[i].c);
2043                 switch (op)
2044                 {
2045                 case OP_IF:
2046                 case OP_IFNOT:
2047                         b = (short)b;
2048                         if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2049                                 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2050                         prog->statements[i].op = op;
2051                         prog->statements[i].operand[0] = remapglobal(a);
2052                         prog->statements[i].operand[1] = -1;
2053                         prog->statements[i].operand[2] = -1;
2054                         prog->statements[i].jumpabsolute = i + b;
2055                         break;
2056                 case OP_GOTO:
2057                         a = (short)a;
2058                         if (a + i < 0 || a + i >= prog->progs_numstatements)
2059                                 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2060                         prog->statements[i].op = op;
2061                         prog->statements[i].operand[0] = -1;
2062                         prog->statements[i].operand[1] = -1;
2063                         prog->statements[i].operand[2] = -1;
2064                         prog->statements[i].jumpabsolute = i + a;
2065                         break;
2066                 default:
2067                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2068                 // global global global
2069                 case OP_ADD_F:
2070                 case OP_ADD_V:
2071                 case OP_SUB_F:
2072                 case OP_SUB_V:
2073                 case OP_MUL_F:
2074                 case OP_MUL_V:
2075                 case OP_MUL_FV:
2076                 case OP_MUL_VF:
2077                 case OP_DIV_F:
2078                 case OP_BITAND:
2079                 case OP_BITOR:
2080                 case OP_GE:
2081                 case OP_LE:
2082                 case OP_GT:
2083                 case OP_LT:
2084                 case OP_AND:
2085                 case OP_OR:
2086                 case OP_EQ_F:
2087                 case OP_EQ_V:
2088                 case OP_EQ_S:
2089                 case OP_EQ_E:
2090                 case OP_EQ_FNC:
2091                 case OP_NE_F:
2092                 case OP_NE_V:
2093                 case OP_NE_S:
2094                 case OP_NE_E:
2095                 case OP_NE_FNC:
2096                 case OP_ADDRESS:
2097                 case OP_LOAD_F:
2098                 case OP_LOAD_FLD:
2099                 case OP_LOAD_ENT:
2100                 case OP_LOAD_S:
2101                 case OP_LOAD_FNC:
2102                 case OP_LOAD_V:
2103                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2104                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2105                         prog->statements[i].op = op;
2106                         prog->statements[i].operand[0] = remapglobal(a);
2107                         prog->statements[i].operand[1] = remapglobal(b);
2108                         prog->statements[i].operand[2] = remapglobal(c);
2109                         prog->statements[i].jumpabsolute = -1;
2110                         break;
2111                 // global none global
2112                 case OP_NOT_F:
2113                 case OP_NOT_V:
2114                 case OP_NOT_S:
2115                 case OP_NOT_FNC:
2116                 case OP_NOT_ENT:
2117                         if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2118                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2119                         prog->statements[i].op = op;
2120                         prog->statements[i].operand[0] = remapglobal(a);
2121                         prog->statements[i].operand[1] = -1;
2122                         prog->statements[i].operand[2] = remapglobal(c);
2123                         prog->statements[i].jumpabsolute = -1;
2124                         break;
2125                 // 2 globals
2126                 case OP_STOREP_F:
2127                 case OP_STOREP_ENT:
2128                 case OP_STOREP_FLD:
2129                 case OP_STOREP_S:
2130                 case OP_STOREP_FNC:
2131                 case OP_STORE_F:
2132                 case OP_STORE_ENT:
2133                 case OP_STORE_FLD:
2134                 case OP_STORE_S:
2135                 case OP_STORE_FNC:
2136                 case OP_STATE:
2137                 case OP_STOREP_V:
2138                 case OP_STORE_V:
2139                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2140                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2141                         prog->statements[i].op = op;
2142                         prog->statements[i].operand[0] = remapglobal(a);
2143                         prog->statements[i].operand[1] = remapglobal(b);
2144                         prog->statements[i].operand[2] = -1;
2145                         prog->statements[i].jumpabsolute = -1;
2146                         break;
2147                 // 1 global
2148                 case OP_CALL0:
2149                 case OP_CALL1:
2150                 case OP_CALL2:
2151                 case OP_CALL3:
2152                 case OP_CALL4:
2153                 case OP_CALL5:
2154                 case OP_CALL6:
2155                 case OP_CALL7:
2156                 case OP_CALL8:
2157                 case OP_DONE:
2158                 case OP_RETURN:
2159                         if ( a >= prog->progs_numglobals)
2160                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2161                         prog->statements[i].op = op;
2162                         prog->statements[i].operand[0] = remapglobal(a);
2163                         prog->statements[i].operand[1] = -1;
2164                         prog->statements[i].operand[2] = -1;
2165                         prog->statements[i].jumpabsolute = -1;
2166                         break;
2167                 }
2168         }
2169         if(prog->numstatements < 1)
2170         {
2171                 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2172         }
2173         else switch(prog->statements[prog->numstatements - 1].op)
2174         {
2175                 case OP_RETURN:
2176                 case OP_GOTO:
2177                 case OP_DONE:
2178                         break;
2179                 default:
2180                         prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2181                         break;
2182         }
2183
2184         // we're done with the file now
2185         Mem_Free(dprograms);
2186         dprograms = NULL;
2187
2188         // check required functions
2189         for(i=0 ; i < numrequiredfunc ; i++)
2190                 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2191                         prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2192
2193         PRVM_LoadLNO(prog, filename);
2194
2195         PRVM_Init_Exec(prog);
2196
2197         if(*prvm_language.string)
2198         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2199         // later idea: include a list of authorized .po file checksums with the csprogs
2200         {
2201                 qboolean deftrans = prog == CLVM_prog;
2202                 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2203                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2204                 {
2205                         for (i=0 ; i<prog->numglobaldefs ; i++)
2206                         {
2207                                 const char *name;
2208                                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2209                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2210                                 if(name && !strncmp(name, "dotranslate_", 12))
2211                                 {
2212                                         deftrans = false;
2213                                         break;
2214                                 }
2215                         }
2216                 }
2217                 if(!strcmp(prvm_language.string, "dump"))
2218                 {
2219                         qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2220                         Con_Printf("Dumping to %s.pot\n", realfilename);
2221                         if(f)
2222                         {
2223                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2224                                 {
2225                                         const char *name;
2226                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2227                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2228                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2229                                         {
2230                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2231                                                 const char *value = PRVM_GetString(prog, val->string);
2232                                                 if(*value)
2233                                                 {
2234                                                         char buf[MAX_INPUTLINE];
2235                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2236                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2237                                                 }
2238                                         }
2239                                 }
2240                                 FS_Close(f);
2241                         }
2242                 }
2243                 else
2244                 {
2245                         po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2246                         if(po)
2247                         {
2248                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2249                                 {
2250                                         const char *name;
2251                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2252                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2253                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2254                                         {
2255                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2256                                                 const char *value = PRVM_GetString(prog, val->string);
2257                                                 if(*value)
2258                                                 {
2259                                                         value = PRVM_PO_Lookup(po, value);
2260                                                         if(value)
2261                                                                 val->string = PRVM_SetEngineString(prog, value);
2262                                                 }
2263                                         }
2264                                 }
2265                         }
2266                 }
2267         }
2268
2269         for (i=0 ; i<prog->numglobaldefs ; i++)
2270         {
2271                 const char *name;
2272                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2273                 //Con_Printf("found var %s\n", name);
2274                 if(name
2275                         && !strncmp(name, "autocvar_", 9)
2276                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2277                 )
2278                 {
2279                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2280                         cvar_t *cvar = Cvar_FindVar(name + 9);
2281                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2282                         if(!cvar)
2283                         {
2284                                 const char *value;
2285                                 char buf[64];
2286                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2287                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2288                                 {
2289                                         case ev_float:
2290                                                 if((float)((int)(val->_float)) == val->_float)
2291                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2292                                                 else
2293                                                         dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2294                                                 value = buf;
2295                                                 break;
2296                                         case ev_vector:
2297                                                 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2298                                                 break;
2299                                         case ev_string:
2300                                                 value = PRVM_GetString(prog, val->string);
2301                                                 break;
2302                                         default:
2303                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2304                                                 goto fail;
2305                                 }
2306                                 cvar = Cvar_Get(name + 9, value, 0, NULL);
2307                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2308                                 {
2309                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2310                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2311                                 }
2312                                 if(!cvar)
2313                                         prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2314                                 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2315                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2316                         }
2317                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2318                         {
2319                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2320                                 int j;
2321                                 const char *s;
2322                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2323                                 {
2324                                         case ev_float:
2325                                                 val->_float = cvar->value;
2326                                                 break;
2327                                         case ev_vector:
2328                                                 s = cvar->string;
2329                                                 VectorClear(val->vector);
2330                                                 for (j = 0;j < 3;j++)
2331                                                 {
2332                                                         while (*s && ISWHITESPACE(*s))
2333                                                                 s++;
2334                                                         if (!*s)
2335                                                                 break;
2336                                                         val->vector[j] = atof(s);
2337                                                         while (!ISWHITESPACE(*s))
2338                                                                 s++;
2339                                                         if (!*s)
2340                                                                 break;
2341                                                 }
2342                                                 break;
2343                                         case ev_string:
2344                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2345                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2346                                                 break;
2347                                         default:
2348                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2349                                                 goto fail;
2350                                 }
2351                                 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2352                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2353                         }
2354                         else
2355                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2356                 }
2357 fail:
2358                 ;
2359         }
2360
2361         prog->loaded = TRUE;
2362
2363         // set flags & ddef_ts in prog
2364
2365         prog->flag = 0;
2366
2367         PRVM_FindOffsets(prog);
2368
2369         prog->init_cmd(prog);
2370
2371         // init mempools
2372         PRVM_MEM_Alloc(prog);
2373 }
2374
2375
2376 static void PRVM_Fields_f (void)
2377 {
2378         prvm_prog_t *prog;
2379         int i, j, ednum, used, usedamount;
2380         int *counts;
2381         char tempstring[MAX_INPUTLINE], tempstring2[260];
2382         const char *name;
2383         prvm_edict_t *ed;
2384         ddef_t *d;
2385         int *v;
2386
2387         // TODO
2388         /*
2389         if (!sv.active)
2390         {
2391                 Con_Print("no progs loaded\n");
2392                 return;
2393         }
2394         */
2395
2396         if(Cmd_Argc() != 2)
2397         {
2398                 Con_Print("prvm_fields <program name>\n");
2399                 return;
2400         }
2401
2402         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2403                 return;
2404
2405         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2406         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2407         {
2408                 ed = PRVM_EDICT_NUM(ednum);
2409                 if (ed->priv.required->free)
2410                         continue;
2411                 for (i = 1;i < prog->numfielddefs;i++)
2412                 {
2413                         d = &prog->fielddefs[i];
2414                         name = PRVM_GetString(prog, d->s_name);
2415                         if (name[strlen(name)-2] == '_')
2416                                 continue;       // skip _x, _y, _z vars
2417                         v = (int *)(ed->fields.vp + d->ofs);
2418                         // if the value is still all 0, skip the field
2419                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2420                         {
2421                                 if (v[j])
2422                                 {
2423                                         counts[i]++;
2424                                         break;
2425                                 }
2426                         }
2427                 }
2428         }
2429         used = 0;
2430         usedamount = 0;
2431         tempstring[0] = 0;
2432         for (i = 0;i < prog->numfielddefs;i++)
2433         {
2434                 d = &prog->fielddefs[i];
2435                 name = PRVM_GetString(prog, d->s_name);
2436                 if (name[strlen(name)-2] == '_')
2437                         continue;       // skip _x, _y, _z vars
2438                 switch(d->type & ~DEF_SAVEGLOBAL)
2439                 {
2440                 case ev_string:
2441                         strlcat(tempstring, "string   ", sizeof(tempstring));
2442                         break;
2443                 case ev_entity:
2444                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2445                         break;
2446                 case ev_function:
2447                         strlcat(tempstring, "function ", sizeof(tempstring));
2448                         break;
2449                 case ev_field:
2450                         strlcat(tempstring, "field    ", sizeof(tempstring));
2451                         break;
2452                 case ev_void:
2453                         strlcat(tempstring, "void     ", sizeof(tempstring));
2454                         break;
2455                 case ev_float:
2456                         strlcat(tempstring, "float    ", sizeof(tempstring));
2457                         break;
2458                 case ev_vector:
2459                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2460                         break;
2461                 case ev_pointer:
2462                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2463                         break;
2464                 default:
2465                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2466                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2467                         break;
2468                 }
2469                 if (strlen(name) > sizeof(tempstring2)-4)
2470                 {
2471                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2472                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2473                         tempstring2[sizeof(tempstring2)-1] = 0;
2474                         name = tempstring2;
2475                 }
2476                 strlcat(tempstring, name, sizeof(tempstring));
2477                 for (j = (int)strlen(name);j < 25;j++)
2478                         strlcat(tempstring, " ", sizeof(tempstring));
2479                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2480                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2481                 strlcat(tempstring, "\n", sizeof(tempstring));
2482                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2483                 {
2484                         Con_Print(tempstring);
2485                         tempstring[0] = 0;
2486                 }
2487                 if (counts[i])
2488                 {
2489                         used++;
2490                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2491                 }
2492         }
2493         Mem_Free(counts);
2494         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);
2495 }
2496
2497 static void PRVM_Globals_f (void)
2498 {
2499         prvm_prog_t *prog;
2500         int i;
2501         const char *wildcard;
2502         int numculled;
2503                 numculled = 0;
2504         // TODO
2505         /*if (!sv.active)
2506         {
2507                 Con_Print("no progs loaded\n");
2508                 return;
2509         }*/
2510         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2511         {
2512                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2513                 return;
2514         }
2515
2516         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2517                 return;
2518
2519         if( Cmd_Argc() == 3)
2520                 wildcard = Cmd_Argv(2);
2521         else
2522                 wildcard = NULL;
2523
2524         Con_Printf("%s :", prog->name);
2525
2526         for (i = 0;i < prog->numglobaldefs;i++)
2527         {
2528                 if(wildcard)
2529                         if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2530                         {
2531                                 numculled++;
2532                                 continue;
2533                         }
2534                 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2535         }
2536         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2537 }
2538
2539 /*
2540 ===============
2541 PRVM_Global
2542 ===============
2543 */
2544 static void PRVM_Global_f(void)
2545 {
2546         prvm_prog_t *prog;
2547         ddef_t *global;
2548         char valuebuf[MAX_INPUTLINE];
2549         if( Cmd_Argc() != 3 ) {
2550                 Con_Printf( "prvm_global <program name> <global name>\n" );
2551                 return;
2552         }
2553
2554         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2555                 return;
2556
2557         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2558         if( !global )
2559                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2560         else
2561                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2562 }
2563
2564 /*
2565 ===============
2566 PRVM_GlobalSet
2567 ===============
2568 */
2569 static void PRVM_GlobalSet_f(void)
2570 {
2571         prvm_prog_t *prog;
2572         ddef_t *global;
2573         if( Cmd_Argc() != 4 ) {
2574                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2575                 return;
2576         }
2577
2578         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2579                 return;
2580
2581         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2582         if( !global )
2583                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2584         else
2585                 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2586 }
2587
2588 /*
2589 ===============
2590 PRVM_Init
2591 ===============
2592 */
2593 void PRVM_Init (void)
2594 {
2595         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2596         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2597         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2598         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2599         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");
2600         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)");
2601         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)");
2602         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2603         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2604         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2605         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)");
2606         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");
2607         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");
2608         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2609         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2610         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2611         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2612
2613         Cvar_RegisterVariable (&prvm_language);
2614         Cvar_RegisterVariable (&prvm_traceqc);
2615         Cvar_RegisterVariable (&prvm_statementprofiling);
2616         Cvar_RegisterVariable (&prvm_timeprofiling);
2617         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2618         Cvar_RegisterVariable (&prvm_leaktest);
2619         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2620         Cvar_RegisterVariable (&prvm_errordump);
2621         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2622         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2623
2624         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2625         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2626
2627         //VM_Cmd_Init();
2628 }
2629
2630 /*
2631 ===============
2632 PRVM_InitProg
2633 ===============
2634 */
2635 void PRVM_Prog_Init(prvm_prog_t *prog)
2636 {
2637         if (prog->loaded)
2638                 PRVM_Prog_Reset(prog);
2639
2640         memset(prog, 0, sizeof(prvm_prog_t));
2641         prog->leaktest_active = prvm_leaktest.integer != 0;
2642 }
2643
2644 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2645 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2646 {
2647         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2648         return 0;
2649 }
2650
2651 #define PRVM_KNOWNSTRINGBASE 0x40000000
2652
2653 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2654 {
2655         if (num < 0)
2656         {
2657                 // invalid
2658                 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2659                 return "";
2660         }
2661         else if (num < prog->stringssize)
2662         {
2663                 // constant string from progs.dat
2664                 return prog->strings + num;
2665         }
2666         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2667         {
2668                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2669                 num -= prog->stringssize;
2670                 if (num < prog->tempstringsbuf.cursize)
2671                         return (char *)prog->tempstringsbuf.data + num;
2672                 else
2673                 {
2674                         VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2675                         return "";
2676                 }
2677         }
2678         else if (num & PRVM_KNOWNSTRINGBASE)
2679         {
2680                 // allocated string
2681                 num = num - PRVM_KNOWNSTRINGBASE;
2682                 if (num >= 0 && num < prog->numknownstrings)
2683                 {
2684                         if (!prog->knownstrings[num])
2685                         {
2686                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2687                                 return "";
2688                         }
2689                         return prog->knownstrings[num];
2690                 }
2691                 else
2692                 {
2693                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2694                         return "";
2695                 }
2696         }
2697         else
2698         {
2699                 // invalid string offset
2700                 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2701                 return "";
2702         }
2703 }
2704
2705 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2706 {
2707         const char *old;
2708         i = i - PRVM_KNOWNSTRINGBASE;
2709         if(i < 0 || i >= prog->numknownstrings)
2710                 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2711         old = prog->knownstrings[i];
2712         prog->knownstrings[i] = s;
2713         return old;
2714 }
2715
2716 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2717 {
2718         int i;
2719         if (!s)
2720                 return 0;
2721         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2722                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2723         // if it's in the tempstrings area, use a reserved range
2724         // (otherwise we'd get millions of useless string offsets cluttering the database)
2725         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2726 #if 1
2727                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2728 #endif
2729         // see if it's a known string address
2730         for (i = 0;i < prog->numknownstrings;i++)
2731                 if (prog->knownstrings[i] == s)
2732                         return PRVM_KNOWNSTRINGBASE + i;
2733         // new unknown engine string
2734         if (developer_insane.integer)
2735                 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2736         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2737                 if (!prog->knownstrings[i])
2738                         break;
2739         if (i >= prog->numknownstrings)
2740         {
2741                 if (i >= prog->maxknownstrings)
2742                 {
2743                         const char **oldstrings = prog->knownstrings;
2744                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2745                         const char **oldstrings_origin = prog->knownstrings_origin;
2746                         prog->maxknownstrings += 128;
2747                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2748                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2749                         if(prog->leaktest_active)
2750                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2751                         if (prog->numknownstrings)
2752                         {
2753                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2754                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2755                                 if(prog->leaktest_active)
2756                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2757                         }
2758                 }
2759                 prog->numknownstrings++;
2760         }
2761         prog->firstfreeknownstring = i + 1;
2762         prog->knownstrings[i] = s;
2763         prog->knownstrings_freeable[i] = false;
2764         if(prog->leaktest_active)
2765                 prog->knownstrings_origin[i] = NULL;
2766         return PRVM_KNOWNSTRINGBASE + i;
2767 }
2768
2769 // temp string handling
2770
2771 // all tempstrings go into this buffer consecutively, and it is reset
2772 // whenever PRVM_ExecuteProgram returns to the engine
2773 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2774 //  restores it on return, so multiple recursive calls can share the same
2775 //  buffer)
2776 // the buffer size is automatically grown as needed
2777
2778 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
2779 {
2780         int size;
2781         char *t;
2782         if (!s)
2783                 return 0;
2784         size = (int)strlen(s) + 1;
2785         if (developer_insane.integer)
2786                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
2787         if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2788         {
2789                 sizebuf_t old = prog->tempstringsbuf;
2790                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
2791                         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);
2792                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
2793                 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
2794                         prog->tempstringsbuf.maxsize *= 2;
2795                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
2796                 {
2797                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
2798                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
2799                         if (old.cursize)
2800                                 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
2801                         if (old.data)
2802                                 Mem_Free(old.data);
2803                 }
2804         }
2805         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
2806         memcpy(t, s, size);
2807         prog->tempstringsbuf.cursize += size;
2808         return PRVM_SetEngineString(prog, t);
2809 }
2810
2811 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
2812 {
2813         int i;
2814         if (!bufferlength)
2815                 return 0;
2816         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2817                 if (!prog->knownstrings[i])
2818                         break;
2819         if (i >= prog->numknownstrings)
2820         {
2821                 if (i >= prog->maxknownstrings)
2822                 {
2823                         const char **oldstrings = prog->knownstrings;
2824                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2825                         const char **oldstrings_origin = prog->knownstrings_origin;
2826                         prog->maxknownstrings += 128;
2827                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2828                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2829                         if(prog->leaktest_active)
2830                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2831                         if (prog->numknownstrings)
2832                         {
2833                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2834                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2835                                 if(prog->leaktest_active)
2836                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2837                         }
2838                         if (oldstrings)
2839                                 Mem_Free((char **)oldstrings);
2840                         if (oldstrings_freeable)
2841                                 Mem_Free((unsigned char *)oldstrings_freeable);
2842                         if (oldstrings_origin)
2843                                 Mem_Free((char **)oldstrings_origin);
2844                 }
2845                 prog->numknownstrings++;
2846         }
2847         prog->firstfreeknownstring = i + 1;
2848         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2849         prog->knownstrings_freeable[i] = true;
2850         if(prog->leaktest_active)
2851                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
2852         if (pointer)
2853                 *pointer = (char *)(prog->knownstrings[i]);
2854         return PRVM_KNOWNSTRINGBASE + i;
2855 }
2856
2857 void PRVM_FreeString(prvm_prog_t *prog, int num)
2858 {
2859         if (num == 0)
2860                 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
2861         else if (num >= 0 && num < prog->stringssize)
2862                 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
2863         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
2864         {
2865                 num = num - PRVM_KNOWNSTRINGBASE;
2866                 if (!prog->knownstrings[num])
2867                         prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
2868                 if (!prog->knownstrings_freeable[num])
2869                         prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
2870                 PRVM_Free((char *)prog->knownstrings[num]);
2871                 if(prog->leaktest_active)
2872                         if(prog->knownstrings_origin[num])
2873                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
2874                 prog->knownstrings[num] = NULL;
2875                 prog->knownstrings_freeable[num] = false;
2876                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2877         }
2878         else
2879                 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
2880 }
2881
2882 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
2883 {
2884         int i, j;
2885
2886         for (i = 0;i < prog->numglobaldefs;i++)
2887         {
2888                 ddef_t *d = &prog->globaldefs[i];
2889                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2890                         continue;
2891                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
2892                         return true;
2893         }
2894
2895         for(j = 0; j < prog->num_edicts; ++j)
2896         {
2897                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2898                 if (ed->priv.required->free)
2899                         continue;
2900                 for (i=0; i<prog->numfielddefs; ++i)
2901                 {
2902                         ddef_t *d = &prog->fielddefs[i];
2903                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2904                                 continue;
2905                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
2906                                 return true;
2907                 }
2908         }
2909
2910         return false;
2911 }
2912
2913 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
2914 {
2915         char vabuf[1024];
2916         char vabuf2[1024];
2917         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2918                 return true; // world or clients
2919         if (prog == SVVM_prog)
2920         {
2921                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
2922                         return true;
2923                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
2924                         return true;
2925                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
2926                         return true;
2927                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
2928                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
2929                                 return true;
2930                 if(PRVM_serveredictfloat(edict, takedamage))
2931                         return true;
2932                 if(*prvm_leaktest_ignore_classnames.string)
2933                 {
2934                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
2935                                 return true;
2936                 }
2937         }
2938         else if (prog == CLVM_prog)
2939         {
2940                 // TODO someone add more stuff here
2941                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
2942                         return true;
2943                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
2944                         return true;
2945                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
2946                         return true;
2947                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
2948                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
2949                                 return true;
2950                 if(*prvm_leaktest_ignore_classnames.string)
2951                 {
2952                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
2953                                 return true;
2954                 }
2955         }
2956         else
2957         {
2958                 // menu prog does not have classnames
2959         }
2960         return false;
2961 }
2962
2963 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
2964 {
2965         int i, j;
2966         int edictnum = PRVM_NUM_FOR_EDICT(edict);
2967         const char *targetname = NULL;
2968
2969         if (prog == SVVM_prog)
2970                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
2971
2972         if(targetname)
2973                 if(!*targetname) // ""
2974                         targetname = NULL;
2975
2976         if(mark == 0)
2977         {
2978                 for (i = 0;i < prog->numglobaldefs;i++)
2979                 {
2980                         ddef_t *d = &prog->globaldefs[i];
2981                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2982                                 continue;
2983                         if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
2984                                 return true;
2985                 }
2986         }
2987
2988         for(j = 0; j < prog->num_edicts; ++j)
2989         {
2990                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2991                 if (ed->priv.required->mark < mark)
2992                         continue;
2993                 if(ed == edict)
2994                         continue;
2995                 if(targetname)
2996                 {
2997                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
2998                         if(target)
2999                                 if(!strcmp(target, targetname))
3000                                         return true;
3001                 }
3002                 for (i=0; i<prog->numfielddefs; ++i)
3003                 {
3004                         ddef_t *d = &prog->fielddefs[i];
3005                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3006                                 continue;
3007                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3008                                 return true;
3009                 }
3010         }
3011
3012         return false;
3013 }
3014
3015 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3016 {
3017         int j;
3018         qboolean found_new;
3019         int stage;
3020
3021         for(j = 0; j < prog->num_edicts; ++j)
3022         {
3023                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3024                 if(ed->priv.required->free)
3025                         continue;
3026                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3027         }
3028
3029         stage = 1;
3030         do
3031         {
3032                 found_new = false;
3033                 for(j = 0; j < prog->num_edicts; ++j)
3034                 {
3035                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3036                         if(ed->priv.required->free)
3037                                 continue;
3038                         if(ed->priv.required->mark)
3039                                 continue;
3040                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3041                         {
3042                                 ed->priv.required->mark = stage + 1;
3043                                 found_new = true;
3044                         }
3045                 }
3046                 ++stage;
3047         }
3048         while(found_new);
3049         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3050 }
3051
3052 void PRVM_LeakTest(prvm_prog_t *prog)
3053 {
3054         int i, j;
3055         qboolean leaked = false;
3056
3057         if(!prog->leaktest_active)
3058                 return;
3059
3060         // 1. Strings
3061         for (i = 0; i < prog->numknownstrings; ++i)
3062         {
3063                 if(prog->knownstrings[i])
3064                 if(prog->knownstrings_freeable[i])
3065                 if(prog->knownstrings_origin[i])
3066                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3067                 {
3068                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3069                         leaked = true;
3070                 }
3071         }
3072
3073         // 2. Edicts
3074         PRVM_MarkReferencedEdicts(prog);
3075         for(j = 0; j < prog->num_edicts; ++j)
3076         {
3077                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3078                 if(ed->priv.required->free)
3079                         continue;
3080                 if(!ed->priv.required->mark)
3081                 if(ed->priv.required->allocation_origin)
3082                 {
3083                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3084                         PRVM_ED_Print(prog, ed, NULL);
3085                         Con_Print("\n");
3086                         leaked = true;
3087                 }
3088
3089                 ed->priv.required->mark = 0; // clear marks again when done
3090         }
3091
3092         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3093         {
3094                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3095                 if(stringbuffer)
3096                 if(stringbuffer->origin)
3097                 {
3098                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3099                         leaked = true;
3100                 }
3101         }
3102
3103         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3104         {
3105                 if(prog->openfiles[i])
3106                 if(prog->openfiles_origin[i])
3107                 {
3108                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3109                         leaked = true;
3110                 }
3111         }
3112
3113         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3114         {
3115                 if(prog->opensearches[i])
3116                 if(prog->opensearches_origin[i])
3117                 {
3118                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3119                         leaked = true;
3120                 }
3121         }
3122
3123         if(!leaked)
3124                 Con_Printf("Congratulations. No leaks found.\n");
3125 }