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