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