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