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