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