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