]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
brighten up the averaged texture value by factor 2 to make it look right
[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
25 prvm_prog_t *prog;
26
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
28
29 int             prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
33
34 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
35 #ifdef PRVM_BOUNDSCHECK_CVAR
36 cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)"};
37 #endif
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_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
43 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
44 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)"};
45 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
46
47 extern sizebuf_t vm_tempstringsbuf;
48
49 //============================================================================
50 // mempool handling
51
52 /*
53 ===============
54 PRVM_MEM_Alloc
55 ===============
56 */
57 void PRVM_MEM_Alloc(void)
58 {
59         int i;
60
61         // reserve space for the null entity aka world
62         // check bound of max_edicts
63         prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
64         prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
65
66         // edictprivate_size has to be min as big prvm_edict_private_t
67         prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
68
69         // alloc edicts
70         prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
71
72         // alloc edict private space
73         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
74
75         // alloc edict fields
76         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
77
78         // set edict pointers
79         for(i = 0; i < prog->max_edicts; i++)
80         {
81                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
82                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
83         }
84 }
85
86 /*
87 ===============
88 PRVM_MEM_IncreaseEdicts
89 ===============
90 */
91 void PRVM_MEM_IncreaseEdicts(void)
92 {
93         int             i;
94         int             oldmaxedicts = prog->max_edicts;
95         void    *oldedictsfields = prog->edictsfields;
96         void    *oldedictprivate = prog->edictprivate;
97
98         if(prog->max_edicts >= prog->limit_edicts)
99                 return;
100
101         PRVM_GCALL(begin_increase_edicts)();
102
103         // increase edicts
104         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
105
106         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
107         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
108
109         memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
110         memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
111
112         //set e and v pointers
113         for(i = 0; i < prog->max_edicts; i++)
114         {
115                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
116                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
117         }
118
119         PRVM_GCALL(end_increase_edicts)();
120
121         Mem_Free(oldedictsfields);
122         Mem_Free(oldedictprivate);
123 }
124
125 //============================================================================
126 // normal prvm
127
128 int PRVM_ED_FindFieldOffset(const char *field)
129 {
130         ddef_t *d;
131         d = PRVM_ED_FindField(field);
132         if (!d)
133                 return -1;
134         return d->ofs;
135 }
136
137 int PRVM_ED_FindGlobalOffset(const char *global)
138 {
139         ddef_t *d;
140         d = PRVM_ED_FindGlobal(global);
141         if (!d)
142                 return -1;
143         return d->ofs;
144 }
145
146 func_t PRVM_ED_FindFunctionOffset(const char *function)
147 {
148         mfunction_t *f;
149         f = PRVM_ED_FindFunction(function);
150         if (!f)
151                 return 0;
152         return (func_t)(f - prog->functions);
153 }
154
155 qboolean PRVM_ProgLoaded(int prognr)
156 {
157         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
158                 return FALSE;
159
160         return (prog_list[prognr].loaded ? TRUE : FALSE);
161 }
162
163 /*
164 =================
165 PRVM_SetProgFromString
166 =================
167 */
168 // perhaps add a return value when the str doesnt exist
169 qboolean PRVM_SetProgFromString(const char *str)
170 {
171         int i = 0;
172         for(; i < PRVM_MAXPROGS ; i++)
173                 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
174                 {
175                         if(prog_list[i].loaded)
176                         {
177                                 prog = &prog_list[i];
178                                 return TRUE;
179                         }
180                         else
181                         {
182                                 Con_Printf("%s not loaded !\n",PRVM_NAME);
183                                 return FALSE;
184                         }
185                 }
186
187         Con_Printf("Invalid program name %s !\n", str);
188         return FALSE;
189 }
190
191 /*
192 =================
193 PRVM_SetProg
194 =================
195 */
196 void PRVM_SetProg(int prognr)
197 {
198         if(0 <= prognr && prognr < PRVM_MAXPROGS)
199         {
200                 if(prog_list[prognr].loaded)
201                         prog = &prog_list[prognr];
202                 else
203                         PRVM_ERROR("%i not loaded !", prognr);
204                 return;
205         }
206         PRVM_ERROR("Invalid program number %i", prognr);
207 }
208
209 /*
210 =================
211 PRVM_ED_ClearEdict
212
213 Sets everything to NULL
214 =================
215 */
216 void PRVM_ED_ClearEdict (prvm_edict_t *e)
217 {
218         memset (e->fields.vp, 0, prog->progs->entityfields * 4);
219         e->priv.required->free = false;
220
221         // AK: Let the init_edict function determine if something needs to be initialized
222         PRVM_GCALL(init_edict)(e);
223 }
224
225 const char *PRVM_AllocationOrigin()
226 {
227         char *buf = NULL;
228         if(prog->leaktest_active)
229         if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
230         {
231                 buf = (char *)PRVM_Alloc(128);
232                 PRVM_ShortStackTrace(buf, 128);
233         }
234         return buf;
235 }
236
237 /*
238 =================
239 PRVM_ED_Alloc
240
241 Either finds a free edict, or allocates a new one.
242 Try to avoid reusing an entity that was recently freed, because it
243 can cause the client to think the entity morphed into something else
244 instead of being removed and recreated, which can cause interpolated
245 angles and bad trails.
246 =================
247 */
248 prvm_edict_t *PRVM_ED_Alloc (void)
249 {
250         int                     i;
251         prvm_edict_t            *e;
252
253         // the client qc dont need maxclients
254         // thus it doesnt need to use svs.maxclients
255         // AK:  changed i=svs.maxclients+1
256         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
257         //              although the menu/client has no world
258         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
259         {
260                 e = PRVM_EDICT_NUM(i);
261                 // the first couple seconds of server time can involve a lot of
262                 // freeing and allocating, so relax the replacement policy
263                 if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
264                 {
265                         PRVM_ED_ClearEdict (e);
266                         e->priv.required->allocation_origin = PRVM_AllocationOrigin();
267                         return e;
268                 }
269         }
270
271         if (i == prog->limit_edicts)
272                 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
273
274         prog->num_edicts++;
275         if (prog->num_edicts >= prog->max_edicts)
276                 PRVM_MEM_IncreaseEdicts();
277
278         e = PRVM_EDICT_NUM(i);
279         PRVM_ED_ClearEdict (e);
280
281         e->priv.required->allocation_origin = PRVM_AllocationOrigin();
282
283         return e;
284 }
285
286 /*
287 =================
288 PRVM_ED_Free
289
290 Marks the edict as free
291 FIXME: walk all entities and NULL out references to this entity
292 =================
293 */
294 void PRVM_ED_Free (prvm_edict_t *ed)
295 {
296         // dont delete the null entity (world) or reserved edicts
297         if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
298                 return;
299
300         PRVM_GCALL(free_edict)(ed);
301
302         ed->priv.required->free = true;
303         ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
304         if(ed->priv.required->allocation_origin)
305         {
306                 PRVM_Free((char *)ed->priv.required->allocation_origin);
307                 ed->priv.required->allocation_origin = NULL;
308         }
309 }
310
311 //===========================================================================
312
313 /*
314 ============
315 PRVM_ED_GlobalAtOfs
316 ============
317 */
318 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
319 {
320         ddef_t          *def;
321         int                     i;
322
323         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
324         {
325                 def = &prog->globaldefs[i];
326                 if (def->ofs == ofs)
327                         return def;
328         }
329         return NULL;
330 }
331
332 /*
333 ============
334 PRVM_ED_FieldAtOfs
335 ============
336 */
337 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
338 {
339         ddef_t          *def;
340         int                     i;
341
342         for (i=0 ; i<prog->progs->numfielddefs ; i++)
343         {
344                 def = &prog->fielddefs[i];
345                 if (def->ofs == ofs)
346                         return def;
347         }
348         return NULL;
349 }
350
351 /*
352 ============
353 PRVM_ED_FindField
354 ============
355 */
356 ddef_t *PRVM_ED_FindField (const char *name)
357 {
358         ddef_t *def;
359         int i;
360
361         for (i=0 ; i<prog->progs->numfielddefs ; i++)
362         {
363                 def = &prog->fielddefs[i];
364                 if (!strcmp(PRVM_GetString(def->s_name), name))
365                         return def;
366         }
367         return NULL;
368 }
369
370 /*
371 ============
372 PRVM_ED_FindGlobal
373 ============
374 */
375 ddef_t *PRVM_ED_FindGlobal (const char *name)
376 {
377         ddef_t *def;
378         int i;
379
380         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
381         {
382                 def = &prog->globaldefs[i];
383                 if (!strcmp(PRVM_GetString(def->s_name), name))
384                         return def;
385         }
386         return NULL;
387 }
388
389
390 /*
391 ============
392 PRVM_ED_FindFunction
393 ============
394 */
395 mfunction_t *PRVM_ED_FindFunction (const char *name)
396 {
397         mfunction_t             *func;
398         int                             i;
399
400         for (i=0 ; i<prog->progs->numfunctions ; i++)
401         {
402                 func = &prog->functions[i];
403                 if (!strcmp(PRVM_GetString(func->s_name), name))
404                         return func;
405         }
406         return NULL;
407 }
408
409
410 /*
411 ============
412 PRVM_ValueString
413
414 Returns a string describing *data in a type specific manner
415 =============
416 */
417 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
418 {
419         static char line[MAX_INPUTLINE];
420         ddef_t *def;
421         mfunction_t *f;
422         int n;
423
424         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
425
426         switch (type)
427         {
428         case ev_string:
429                 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
430                 break;
431         case ev_entity:
432                 n = val->edict;
433                 if (n < 0 || n >= prog->limit_edicts)
434                         dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
435                 else
436                         dpsnprintf (line, sizeof(line), "entity %i", n);
437                 break;
438         case ev_function:
439                 f = prog->functions + val->function;
440                 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
441                 break;
442         case ev_field:
443                 def = PRVM_ED_FieldAtOfs ( val->_int );
444                 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
445                 break;
446         case ev_void:
447                 dpsnprintf (line, sizeof(line), "void");
448                 break;
449         case ev_float:
450                 // LordHavoc: changed from %5.1f to %10.4f
451                 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
452                 break;
453         case ev_vector:
454                 // LordHavoc: changed from %5.1f to %10.4f
455                 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
456                 break;
457         case ev_pointer:
458                 dpsnprintf (line, sizeof(line), "pointer");
459                 break;
460         default:
461                 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
462                 break;
463         }
464
465         return line;
466 }
467
468 /*
469 ============
470 PRVM_UglyValueString
471
472 Returns a string describing *data in a type specific manner
473 Easier to parse than PR_ValueString
474 =============
475 */
476 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
477 {
478         static char line[MAX_INPUTLINE];
479         int i;
480         const char *s;
481         ddef_t *def;
482         mfunction_t *f;
483
484         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
485
486         switch (type)
487         {
488         case ev_string:
489                 // Parse the string a bit to turn special characters
490                 // (like newline, specifically) into escape codes,
491                 // this fixes saving games from various mods
492                 s = PRVM_GetString (val->string);
493                 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
494                 {
495                         if (*s == '\n')
496                         {
497                                 line[i++] = '\\';
498                                 line[i++] = 'n';
499                         }
500                         else if (*s == '\r')
501                         {
502                                 line[i++] = '\\';
503                                 line[i++] = 'r';
504                         }
505                         else if (*s == '\\')
506                         {
507                                 line[i++] = '\\';
508                                 line[i++] = '\\';
509                         }
510                         else
511                                 line[i++] = *s;
512                         s++;
513                 }
514                 line[i] = '\0';
515                 break;
516         case ev_entity:
517                 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
518                 break;
519         case ev_function:
520                 f = prog->functions + val->function;
521                 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
522                 break;
523         case ev_field:
524                 def = PRVM_ED_FieldAtOfs ( val->_int );
525                 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
526                 break;
527         case ev_void:
528                 dpsnprintf (line, sizeof (line), "void");
529                 break;
530         case ev_float:
531                 dpsnprintf (line, sizeof (line), "%f", val->_float);
532                 break;
533         case ev_vector:
534                 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
535                 break;
536         default:
537                 dpsnprintf (line, sizeof (line), "bad type %i", type);
538                 break;
539         }
540
541         return line;
542 }
543
544 /*
545 ============
546 PRVM_GlobalString
547
548 Returns a string with a description and the contents of a global,
549 padded to 20 field width
550 ============
551 */
552 char *PRVM_GlobalString (int ofs)
553 {
554         char    *s;
555         //size_t        i;
556         ddef_t  *def;
557         void    *val;
558         static char     line[128];
559
560         val = (void *)&prog->globals.generic[ofs];
561         def = PRVM_ED_GlobalAtOfs(ofs);
562         if (!def)
563                 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
564         else
565         {
566                 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
567                 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
568         }
569
570         //i = strlen(line);
571         //for ( ; i<20 ; i++)
572         //      strcat (line," ");
573         //strcat (line," ");
574
575         return line;
576 }
577
578 char *PRVM_GlobalStringNoContents (int ofs)
579 {
580         //size_t        i;
581         ddef_t  *def;
582         static char     line[128];
583
584         def = PRVM_ED_GlobalAtOfs(ofs);
585         if (!def)
586                 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
587         else
588                 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
589
590         //i = strlen(line);
591         //for ( ; i<20 ; i++)
592         //      strcat (line," ");
593         //strcat (line," ");
594
595         return line;
596 }
597
598
599 /*
600 =============
601 PRVM_ED_Print
602
603 For debugging
604 =============
605 */
606 // LordHavoc: optimized this to print out much more quickly (tempstring)
607 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
608 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
609 {
610         size_t  l;
611         ddef_t  *d;
612         int             *v;
613         int             i, j;
614         const char      *name;
615         int             type;
616         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
617
618         if (ed->priv.required->free)
619         {
620                 Con_Printf("%s: FREE\n",PRVM_NAME);
621                 return;
622         }
623
624         tempstring[0] = 0;
625         dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
626         for (i=1 ; i<prog->progs->numfielddefs ; i++)
627         {
628                 d = &prog->fielddefs[i];
629                 name = PRVM_GetString(d->s_name);
630                 if (name[strlen(name)-2] == '_')
631                         continue;       // skip _x, _y, _z vars
632
633                 // Check Field Name Wildcard
634                 if(wildcard_fieldname)
635                         if( !matchpattern(name, wildcard_fieldname, 1) )
636                                 // Didn't match; skip
637                                 continue;
638
639                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
640
641         // if the value is still all 0, skip the field
642                 type = d->type & ~DEF_SAVEGLOBAL;
643
644                 for (j=0 ; j<prvm_type_size[type] ; j++)
645                         if (v[j])
646                                 break;
647                 if (j == prvm_type_size[type])
648                         continue;
649
650                 if (strlen(name) > sizeof(tempstring2)-4)
651                 {
652                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
653                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
654                         tempstring2[sizeof(tempstring2)-1] = 0;
655                         name = tempstring2;
656                 }
657                 strlcat(tempstring, name, sizeof(tempstring));
658                 for (l = strlen(name);l < 14;l++)
659                         strlcat(tempstring, " ", sizeof(tempstring));
660                 strlcat(tempstring, " ", sizeof(tempstring));
661
662                 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
663                 if (strlen(name) > sizeof(tempstring2)-4)
664                 {
665                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
666                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
667                         tempstring2[sizeof(tempstring2)-1] = 0;
668                         name = tempstring2;
669                 }
670                 strlcat(tempstring, name, sizeof(tempstring));
671                 strlcat(tempstring, "\n", sizeof(tempstring));
672                 if (strlen(tempstring) >= sizeof(tempstring)/2)
673                 {
674                         Con_Print(tempstring);
675                         tempstring[0] = 0;
676                 }
677         }
678         if (tempstring[0])
679                 Con_Print(tempstring);
680 }
681
682 /*
683 =============
684 PRVM_ED_Write
685
686 For savegames
687 =============
688 */
689 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
690 {
691         ddef_t  *d;
692         int             *v;
693         int             i, j;
694         const char      *name;
695         int             type;
696
697         FS_Print(f, "{\n");
698
699         if (ed->priv.required->free)
700         {
701                 FS_Print(f, "}\n");
702                 return;
703         }
704
705         for (i=1 ; i<prog->progs->numfielddefs ; i++)
706         {
707                 d = &prog->fielddefs[i];
708                 name = PRVM_GetString(d->s_name);
709                 if (name[strlen(name)-2] == '_')
710                         continue;       // skip _x, _y, _z vars
711
712                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
713
714         // if the value is still all 0, skip the field
715                 type = d->type & ~DEF_SAVEGLOBAL;
716                 for (j=0 ; j<prvm_type_size[type] ; j++)
717                         if (v[j])
718                                 break;
719                 if (j == prvm_type_size[type])
720                         continue;
721
722                 FS_Printf(f,"\"%s\" ",name);
723                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
724         }
725
726         FS_Print(f, "}\n");
727 }
728
729 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
730 {
731         PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
732 }
733
734 /*
735 =============
736 PRVM_ED_PrintEdicts_f
737
738 For debugging, prints all the entities in the current server
739 =============
740 */
741 void PRVM_ED_PrintEdicts_f (void)
742 {
743         int             i;
744         const char *wildcard_fieldname;
745
746         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
747         {
748                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
749                 return;
750         }
751
752         PRVM_Begin;
753         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
754                 return;
755
756         if( Cmd_Argc() == 3)
757                 wildcard_fieldname = Cmd_Argv(2);
758         else
759                 wildcard_fieldname = NULL;
760
761         Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
762         for (i=0 ; i<prog->num_edicts ; i++)
763                 PRVM_ED_PrintNum (i, wildcard_fieldname);
764
765         PRVM_End;
766 }
767
768 /*
769 =============
770 PRVM_ED_PrintEdict_f
771
772 For debugging, prints a single edict
773 =============
774 */
775 void PRVM_ED_PrintEdict_f (void)
776 {
777         int             i;
778         const char      *wildcard_fieldname;
779
780         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
781         {
782                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
783                 return;
784         }
785
786         PRVM_Begin;
787         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
788                 return;
789
790         i = atoi (Cmd_Argv(2));
791         if (i >= prog->num_edicts)
792         {
793                 Con_Print("Bad edict number\n");
794                 PRVM_End;
795                 return;
796         }
797         if( Cmd_Argc() == 4)
798                 // Optional Wildcard Provided
799                 wildcard_fieldname = Cmd_Argv(3);
800         else
801                 // Use All
802                 wildcard_fieldname = NULL;
803         PRVM_ED_PrintNum (i, wildcard_fieldname);
804
805         PRVM_End;
806 }
807
808 /*
809 =============
810 PRVM_ED_Count
811
812 For debugging
813 =============
814 */
815 // 2 possibilities : 1. just displaying the active edict count
816 //                                       2. making a function pointer [x]
817 void PRVM_ED_Count_f (void)
818 {
819         int             i;
820         prvm_edict_t    *ent;
821         int             active;
822
823         if(Cmd_Argc() != 2)
824         {
825                 Con_Print("prvm_count <program name>\n");
826                 return;
827         }
828
829         PRVM_Begin;
830         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
831                 return;
832
833         if(prog->count_edicts)
834                 prog->count_edicts();
835         else
836         {
837                 active = 0;
838                 for (i=0 ; i<prog->num_edicts ; i++)
839                 {
840                         ent = PRVM_EDICT_NUM(i);
841                         if (ent->priv.required->free)
842                                 continue;
843                         active++;
844                 }
845
846                 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
847                 Con_Printf("active    :%3i\n", active);
848         }
849
850         PRVM_End;
851 }
852
853 /*
854 ==============================================================================
855
856                                         ARCHIVING GLOBALS
857
858 FIXME: need to tag constants, doesn't really work
859 ==============================================================================
860 */
861
862 /*
863 =============
864 PRVM_ED_WriteGlobals
865 =============
866 */
867 void PRVM_ED_WriteGlobals (qfile_t *f)
868 {
869         ddef_t          *def;
870         int                     i;
871         const char              *name;
872         int                     type;
873
874         FS_Print(f,"{\n");
875         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
876         {
877                 def = &prog->globaldefs[i];
878                 type = def->type;
879                 if ( !(def->type & DEF_SAVEGLOBAL) )
880                         continue;
881                 type &= ~DEF_SAVEGLOBAL;
882
883                 if (type != ev_string && type != ev_float && type != ev_entity)
884                         continue;
885
886                 name = PRVM_GetString(def->s_name);
887                 FS_Printf(f,"\"%s\" ", name);
888                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
889         }
890         FS_Print(f,"}\n");
891 }
892
893 /*
894 =============
895 PRVM_ED_ParseGlobals
896 =============
897 */
898 void PRVM_ED_ParseGlobals (const char *data)
899 {
900         char keyname[MAX_INPUTLINE];
901         ddef_t *key;
902
903         while (1)
904         {
905                 // parse key
906                 if (!COM_ParseToken_Simple(&data, false, false))
907                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
908                 if (com_token[0] == '}')
909                         break;
910
911                 strlcpy (keyname, com_token, sizeof(keyname));
912
913                 // parse value
914                 if (!COM_ParseToken_Simple(&data, false, true))
915                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
916
917                 if (com_token[0] == '}')
918                         PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
919
920                 key = PRVM_ED_FindGlobal (keyname);
921                 if (!key)
922                 {
923                         Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
924                         continue;
925                 }
926
927                 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
928                         PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
929         }
930 }
931
932 //============================================================================
933
934
935 /*
936 =============
937 PRVM_ED_ParseEval
938
939 Can parse either fields or globals
940 returns false if error
941 =============
942 */
943 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
944 {
945         int i, l;
946         char *new_p;
947         ddef_t *def;
948         prvm_eval_t *val;
949         mfunction_t *func;
950
951         if (ent)
952                 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
953         else
954                 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
955         switch (key->type & ~DEF_SAVEGLOBAL)
956         {
957         case ev_string:
958                 l = (int)strlen(s) + 1;
959                 val->string = PRVM_AllocString(l, &new_p);
960                 for (i = 0;i < l;i++)
961                 {
962                         if (s[i] == '\\' && s[i+1] && parsebackslash)
963                         {
964                                 i++;
965                                 if (s[i] == 'n')
966                                         *new_p++ = '\n';
967                                 else if (s[i] == 'r')
968                                         *new_p++ = '\r';
969                                 else
970                                         *new_p++ = s[i];
971                         }
972                         else
973                                 *new_p++ = s[i];
974                 }
975                 break;
976
977         case ev_float:
978                 while (*s && *s <= ' ')
979                         s++;
980                 val->_float = atof(s);
981                 break;
982
983         case ev_vector:
984                 for (i = 0;i < 3;i++)
985                 {
986                         while (*s && *s <= ' ')
987                                 s++;
988                         if (!*s)
989                                 break;
990                         val->vector[i] = atof(s);
991                         while (*s > ' ')
992                                 s++;
993                         if (!*s)
994                                 break;
995                 }
996                 break;
997
998         case ev_entity:
999                 while (*s && *s <= ' ')
1000                         s++;
1001                 i = atoi(s);
1002                 if (i >= prog->limit_edicts)
1003                         Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME);
1004                 while (i >= prog->max_edicts)
1005                         PRVM_MEM_IncreaseEdicts();
1006                 // if IncreaseEdicts was called the base pointer needs to be updated
1007                 if (ent)
1008                         val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1009                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1010                 break;
1011
1012         case ev_field:
1013                 if (*s != '.')
1014                 {
1015                         Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1016                         return false;
1017                 }
1018                 def = PRVM_ED_FindField(s + 1);
1019                 if (!def)
1020                 {
1021                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1022                         return false;
1023                 }
1024                 val->_int = def->ofs;
1025                 break;
1026
1027         case ev_function:
1028                 func = PRVM_ED_FindFunction(s);
1029                 if (!func)
1030                 {
1031                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1032                         return false;
1033                 }
1034                 val->function = func - prog->functions;
1035                 break;
1036
1037         default:
1038                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1039                 return false;
1040         }
1041         return true;
1042 }
1043
1044 /*
1045 =============
1046 PRVM_GameCommand_f
1047
1048 Console command to send a string to QC function GameCommand of the
1049 indicated progs
1050
1051 Usage:
1052   sv_cmd adminmsg 3 "do not teamkill"
1053   cl_cmd someclientcommand
1054   menu_cmd somemenucommand
1055
1056 All progs can support this extension; sg calls it in server QC, cg in client
1057 QC, mg in menu QC.
1058 =============
1059 */
1060 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1061 {
1062         if(Cmd_Argc() < 1)
1063         {
1064                 Con_Printf("%s text...\n", whichcmd);
1065                 return;
1066         }
1067
1068         PRVM_Begin;
1069         if(!PRVM_SetProgFromString(whichprogs))
1070         // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1071         // also, it makes printing error messages easier!
1072         {
1073                 Con_Printf("%s program not loaded.\n", whichprogs);
1074                 return;
1075         }
1076
1077         if(!prog->funcoffsets.GameCommand)
1078         {
1079                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1080         }
1081         else
1082         {
1083                 int restorevm_tempstringsbuf_cursize;
1084                 const char *s;
1085
1086                 s = Cmd_Args();
1087
1088                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1089                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1090                 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1091                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1092         }
1093
1094         PRVM_End;
1095 }
1096 void PRVM_GameCommand_Server_f(void)
1097 {
1098         PRVM_GameCommand("server", "sv_cmd");
1099 }
1100 void PRVM_GameCommand_Client_f(void)
1101 {
1102         PRVM_GameCommand("client", "cl_cmd");
1103 }
1104 void PRVM_GameCommand_Menu_f(void)
1105 {
1106         PRVM_GameCommand("menu", "menu_cmd");
1107 }
1108
1109 /*
1110 =============
1111 PRVM_ED_EdictSet_f
1112
1113 Console command to set a field of a specified edict
1114 =============
1115 */
1116 void PRVM_ED_EdictSet_f(void)
1117 {
1118         prvm_edict_t *ed;
1119         ddef_t *key;
1120
1121         if(Cmd_Argc() != 5)
1122         {
1123                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1124                 return;
1125         }
1126
1127         PRVM_Begin;
1128         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1129         {
1130                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1131                 return;
1132         }
1133
1134         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1135
1136         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1137                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1138         else
1139                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1140
1141         PRVM_End;
1142 }
1143
1144 /*
1145 ====================
1146 PRVM_ED_ParseEdict
1147
1148 Parses an edict out of the given string, returning the new position
1149 ed should be a properly initialized empty edict.
1150 Used for initial level load and for savegames.
1151 ====================
1152 */
1153 extern cvar_t developer_entityparsing;
1154 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1155 {
1156         ddef_t *key;
1157         qboolean anglehack;
1158         qboolean init;
1159         char keyname[256];
1160         size_t n;
1161
1162         init = false;
1163
1164 // go through all the dictionary pairs
1165         while (1)
1166         {
1167         // parse key
1168                 if (!COM_ParseToken_Simple(&data, false, false))
1169                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1170                 if (developer_entityparsing.integer)
1171                         Con_Printf("Key: \"%s\"", com_token);
1172                 if (com_token[0] == '}')
1173                         break;
1174
1175                 // anglehack is to allow QuakeEd to write single scalar angles
1176                 // and allow them to be turned into vectors. (FIXME...)
1177                 if (!strcmp(com_token, "angle"))
1178                 {
1179                         strlcpy (com_token, "angles", sizeof(com_token));
1180                         anglehack = true;
1181                 }
1182                 else
1183                         anglehack = false;
1184
1185                 // FIXME: change light to _light to get rid of this hack
1186                 if (!strcmp(com_token, "light"))
1187                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1188
1189                 strlcpy (keyname, com_token, sizeof(keyname));
1190
1191                 // another hack to fix keynames with trailing spaces
1192                 n = strlen(keyname);
1193                 while (n && keyname[n-1] == ' ')
1194                 {
1195                         keyname[n-1] = 0;
1196                         n--;
1197                 }
1198
1199         // parse value
1200                 if (!COM_ParseToken_Simple(&data, false, false))
1201                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1202                 if (developer_entityparsing.integer)
1203                         Con_Printf(" \"%s\"\n", com_token);
1204
1205                 if (com_token[0] == '}')
1206                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1207
1208                 init = true;
1209
1210                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1211                 if (!keyname[0])
1212                         continue;
1213
1214 // keynames with a leading underscore are used for utility comments,
1215 // and are immediately discarded by quake
1216                 if (keyname[0] == '_')
1217                         continue;
1218
1219                 key = PRVM_ED_FindField (keyname);
1220                 if (!key)
1221                 {
1222                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1223                         continue;
1224                 }
1225
1226                 if (anglehack)
1227                 {
1228                         char    temp[32];
1229                         strlcpy (temp, com_token, sizeof(temp));
1230                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1231                 }
1232
1233                 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1234                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1235         }
1236
1237         if (!init)
1238                 ent->priv.required->free = true;
1239
1240         return data;
1241 }
1242
1243
1244 /*
1245 ================
1246 PRVM_ED_LoadFromFile
1247
1248 The entities are directly placed in the array, rather than allocated with
1249 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1250 number references out of order.
1251
1252 Creates a server's entity / program execution context by
1253 parsing textual entity definitions out of an ent file.
1254
1255 Used for both fresh maps and savegame loads.  A fresh map would also need
1256 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1257 ================
1258 */
1259 void PRVM_ED_LoadFromFile (const char *data)
1260 {
1261         prvm_edict_t *ent;
1262         int parsed, inhibited, spawned, died;
1263         const char *funcname;
1264         mfunction_t *func;
1265
1266         parsed = 0;
1267         inhibited = 0;
1268         spawned = 0;
1269         died = 0;
1270
1271
1272 // parse ents
1273         while (1)
1274         {
1275 // parse the opening brace
1276                 if (!COM_ParseToken_Simple(&data, false, false))
1277                         break;
1278                 if (com_token[0] != '{')
1279                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1280
1281                 // CHANGED: this is not conform to PR_LoadFromFile
1282                 if(prog->loadintoworld)
1283                 {
1284                         prog->loadintoworld = false;
1285                         ent = PRVM_EDICT_NUM(0);
1286                 }
1287                 else
1288                         ent = PRVM_ED_Alloc();
1289
1290                 // clear it
1291                 if (ent != prog->edicts)        // hack
1292                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1293
1294                 data = PRVM_ED_ParseEdict (data, ent);
1295                 parsed++;
1296
1297                 // remove the entity ?
1298                 if(prog->load_edict && !prog->load_edict(ent))
1299                 {
1300                         PRVM_ED_Free(ent);
1301                         inhibited++;
1302                         continue;
1303                 }
1304
1305 //
1306 // immediately call spawn function, but only if there is a self global and a classname
1307 //
1308                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1309                 {
1310                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1311                         if (!handle)
1312                         {
1313                                 Con_Print("No classname for:\n");
1314                                 PRVM_ED_Print(ent, NULL);
1315                                 PRVM_ED_Free (ent);
1316                                 continue;
1317                         }
1318
1319                         // look for the spawn function
1320                         funcname = PRVM_GetString(handle);
1321                         func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1322                         if(!func)
1323                                 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1324                                         func = PRVM_ED_FindFunction (funcname);
1325
1326                         if (!func)
1327                         {
1328                                 // check for OnEntityNoSpawnFunction
1329                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1330                                 {
1331                                         // self = ent
1332                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1333                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1334                                 }
1335                                 else
1336                                 {
1337                                         if (developer.integer) // don't confuse non-developers with errors
1338                                         {
1339                                                 Con_Print("No spawn function for:\n");
1340                                                 PRVM_ED_Print(ent, NULL);
1341                                         }
1342                                         PRVM_ED_Free (ent);
1343                                         continue;
1344                                 }
1345                         }
1346                         else
1347                         {
1348                                 // self = ent
1349                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1350                                 PRVM_ExecuteProgram (func - prog->functions, "");
1351                         }
1352                 }
1353
1354                 spawned++;
1355                 if (ent->priv.required->free)
1356                         died++;
1357         }
1358
1359         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);
1360 }
1361
1362 void PRVM_FindOffsets(void)
1363 {
1364         // field and global searches use -1 for NULL
1365         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1366         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1367         // functions use 0 for NULL
1368         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1369
1370         // server and client qc use a lot of similar fields, so this is combined
1371         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1372         prog->fieldoffsets.SendFlags                      = PRVM_ED_FindFieldOffset("SendFlags");
1373         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1374         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1375         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1376         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1377         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1378         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1379         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1380         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1381         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1382         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1383         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1384         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1385         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1386         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1387         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1388         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1389         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1390         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1391         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1392         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1393         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1394         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1395         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1396         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1397         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1398         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1399         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1400         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1401         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1402         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1403         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1404         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1405         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1406         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1407         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1408         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1409         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1410         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1411         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1412         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1413         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1414         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1415         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1416         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1417         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1418         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1419         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1420         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1421         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1422         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1423         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1424         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1425         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1426         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1427         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1428         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1429         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1430         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1431         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1432         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1433         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1434         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1435         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1436         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1437         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1438         prog->fieldoffsets.movetypesteplandevent          = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1439         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1440         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1441         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1442         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1443         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1444         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1445         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1446         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1447         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1448         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1449         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1450         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1451         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1452         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1453         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1454         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1455         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1456         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1457         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1458         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1459         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1460         prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
1461         prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
1462         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1463         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1464         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1465         prog->funcoffsets.CSQC_Ent_Spawn                  = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1466         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1467         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1468         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1469         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1470         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1471         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1472         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1473         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1474         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1475         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1476         prog->funcoffsets.Gecko_Query                     = PRVM_ED_FindFunctionOffset("Gecko_Query");
1477         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1478         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1479         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1480         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1481         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1482         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1483         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1484         prog->funcoffsets.SV_Shutdown                     = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1485         prog->funcoffsets.URI_Get_Callback                = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1486         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1487         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1488         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1489         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1490         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1491         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1492         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1493         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1494         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1495         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1496         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1497         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1498         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1499         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1500         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1501         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1502         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1503         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1504         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1505         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1506         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1507         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1508         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1509         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1510         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1511         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1512         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1513         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1514         prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1515         prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
1516
1517         // menu qc only uses some functions, nothing else
1518         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1519         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1520         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1521         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1522         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1523         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1524 }
1525
1526 // not used
1527 /*
1528 typedef struct dpfield_s
1529 {
1530         int type;
1531         char *string;
1532 }
1533 dpfield_t;
1534
1535 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1536
1537 dpfield_t dpfields[] =
1538 {
1539 };
1540 */
1541
1542 /*
1543 ===============
1544 PRVM_ResetProg
1545 ===============
1546 */
1547
1548 void PRVM_LeakTest();
1549 void PRVM_ResetProg()
1550 {
1551         PRVM_LeakTest();
1552         PRVM_GCALL(reset_cmd)();
1553         Mem_FreePool(&prog->progs_mempool);
1554         memset(prog,0,sizeof(prvm_prog_t));
1555         prog->starttime = Sys_DoubleTime();
1556 }
1557
1558 /*
1559 ===============
1560 PRVM_LoadLNO
1561 ===============
1562 */
1563 void PRVM_LoadLNO( const char *progname ) {
1564         fs_offset_t filesize;
1565         unsigned char *lno;
1566         unsigned int *header;
1567         char filename[512];
1568
1569         FS_StripExtension( progname, filename, sizeof( filename ) );
1570         strlcat( filename, ".lno", sizeof( filename ) );
1571
1572         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1573         if( !lno ) {
1574                 return;
1575         }
1576
1577 /*
1578 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1579 <Spike>    SafeWrite (h, &version, sizeof(int));
1580 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1581 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1582 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1583 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1584 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1585 */
1586         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1587                 Mem_Free(lno);
1588                 return;
1589         }
1590
1591         header = (unsigned int *) lno;
1592         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1593                 LittleLong( header[ 1 ] ) == 1 &&
1594                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1595                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1596                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1597                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1598         {
1599                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1600                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1601         }
1602         Mem_Free( lno );
1603 }
1604
1605 /*
1606 ===============
1607 PRVM_LoadProgs
1608 ===============
1609 */
1610 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1611 {
1612         int i;
1613         dstatement_t *st;
1614         ddef_t *infielddefs;
1615         dfunction_t *dfunctions;
1616         fs_offset_t filesize;
1617
1618         if( prog->loaded ) {
1619                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1620         }
1621
1622         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1623         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1624                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1625
1626         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1627
1628         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1629
1630 // byte swap the header
1631         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1632                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1633
1634         if (prog->progs->version != PROG_VERSION)
1635                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1636         if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1637                 PRVM_ERROR ("%s: %s system vars have been modified (CRC of progs.dat systemvars %i != engine %i), progdefs.h is out of date", PRVM_NAME, filename, prog->progs->crc, prog->headercrc);
1638
1639         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1640         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1641
1642         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1643                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1644         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1645         prog->stringssize = prog->progs->numstrings;
1646
1647         prog->numknownstrings = 0;
1648         prog->maxknownstrings = 0;
1649         prog->knownstrings = NULL;
1650         prog->knownstrings_freeable = NULL;
1651
1652         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1653
1654         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1655
1656         // we need to expand the fielddefs list to include all the engine fields,
1657         // so allocate a new place for it
1658         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1659         //                                                                                              ( + DPFIELDS                       )
1660         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1661
1662         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1663
1664         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1665
1666         // moved edict_size calculation down below field adding code
1667
1668         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1669         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1670
1671 // byte swap the lumps
1672         for (i=0 ; i<prog->progs->numstatements ; i++)
1673         {
1674                 prog->statements[i].op = LittleShort(prog->statements[i].op);
1675                 prog->statements[i].a = LittleShort(prog->statements[i].a);
1676                 prog->statements[i].b = LittleShort(prog->statements[i].b);
1677                 prog->statements[i].c = LittleShort(prog->statements[i].c);
1678         }
1679
1680         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1681         for (i = 0;i < prog->progs->numfunctions;i++)
1682         {
1683                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1684                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1685                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1686                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1687                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1688                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1689                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1690         }
1691
1692         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1693         {
1694                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1695                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1696                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1697         }
1698
1699         // copy the progs fields to the new fields list
1700         for (i = 0;i < prog->progs->numfielddefs;i++)
1701         {
1702                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1703                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1704                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1705                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1706                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1707         }
1708
1709         // append the required fields
1710         for (i = 0;i < (int) numrequiredfields;i++)
1711         {
1712                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1713                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1714                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1715                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1716                         prog->progs->entityfields += 3;
1717                 else
1718                         prog->progs->entityfields++;
1719                 prog->progs->numfielddefs++;
1720         }
1721
1722         // check required functions
1723         for(i=0 ; i < numrequiredfunc ; i++)
1724                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1725                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1726
1727         // check required globals
1728         for(i=0 ; i < numrequiredglobals ; i++)
1729                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1730                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1731
1732         for (i=0 ; i<prog->progs->numglobals ; i++)
1733                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1734
1735         // moved edict_size calculation down here, below field adding code
1736         // LordHavoc: this no longer includes the prvm_edict_t header
1737         prog->edict_size = prog->progs->entityfields * 4;
1738         prog->edictareasize = prog->edict_size * prog->limit_edicts;
1739
1740         // LordHavoc: bounds check anything static
1741         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1742         {
1743                 switch (st->op)
1744                 {
1745                 case OP_IF:
1746                 case OP_IFNOT:
1747                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1748                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1749                         break;
1750                 case OP_GOTO:
1751                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1752                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1753                         break;
1754                 // global global global
1755                 case OP_ADD_F:
1756                 case OP_ADD_V:
1757                 case OP_SUB_F:
1758                 case OP_SUB_V:
1759                 case OP_MUL_F:
1760                 case OP_MUL_V:
1761                 case OP_MUL_FV:
1762                 case OP_MUL_VF:
1763                 case OP_DIV_F:
1764                 case OP_BITAND:
1765                 case OP_BITOR:
1766                 case OP_GE:
1767                 case OP_LE:
1768                 case OP_GT:
1769                 case OP_LT:
1770                 case OP_AND:
1771                 case OP_OR:
1772                 case OP_EQ_F:
1773                 case OP_EQ_V:
1774                 case OP_EQ_S:
1775                 case OP_EQ_E:
1776                 case OP_EQ_FNC:
1777                 case OP_NE_F:
1778                 case OP_NE_V:
1779                 case OP_NE_S:
1780                 case OP_NE_E:
1781                 case OP_NE_FNC:
1782                 case OP_ADDRESS:
1783                 case OP_LOAD_F:
1784                 case OP_LOAD_FLD:
1785                 case OP_LOAD_ENT:
1786                 case OP_LOAD_S:
1787                 case OP_LOAD_FNC:
1788                 case OP_LOAD_V:
1789                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1790                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1791                         break;
1792                 // global none global
1793                 case OP_NOT_F:
1794                 case OP_NOT_V:
1795                 case OP_NOT_S:
1796                 case OP_NOT_FNC:
1797                 case OP_NOT_ENT:
1798                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1799                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1800                         break;
1801                 // 2 globals
1802                 case OP_STOREP_F:
1803                 case OP_STOREP_ENT:
1804                 case OP_STOREP_FLD:
1805                 case OP_STOREP_S:
1806                 case OP_STOREP_FNC:
1807                 case OP_STORE_F:
1808                 case OP_STORE_ENT:
1809                 case OP_STORE_FLD:
1810                 case OP_STORE_S:
1811                 case OP_STORE_FNC:
1812                 case OP_STATE:
1813                 case OP_STOREP_V:
1814                 case OP_STORE_V:
1815                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1816                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1817                         break;
1818                 // 1 global
1819                 case OP_CALL0:
1820                 case OP_CALL1:
1821                 case OP_CALL2:
1822                 case OP_CALL3:
1823                 case OP_CALL4:
1824                 case OP_CALL5:
1825                 case OP_CALL6:
1826                 case OP_CALL7:
1827                 case OP_CALL8:
1828                 case OP_DONE:
1829                 case OP_RETURN:
1830                         if ((unsigned short) st->a >= prog->progs->numglobals)
1831                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1832                         break;
1833                 default:
1834                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1835                         break;
1836                 }
1837         }
1838
1839         PRVM_LoadLNO(filename);
1840
1841         PRVM_Init_Exec();
1842
1843         prog->loaded = TRUE;
1844
1845         // set flags & ddef_ts in prog
1846
1847         prog->flag = 0;
1848
1849         PRVM_FindOffsets();
1850
1851         PRVM_GCALL(init_cmd)();
1852
1853         // init mempools
1854         PRVM_MEM_Alloc();
1855 }
1856
1857
1858 void PRVM_Fields_f (void)
1859 {
1860         int i, j, ednum, used, usedamount;
1861         int *counts;
1862         char tempstring[MAX_INPUTLINE], tempstring2[260];
1863         const char *name;
1864         prvm_edict_t *ed;
1865         ddef_t *d;
1866         int *v;
1867
1868         // TODO
1869         /*
1870         if (!sv.active)
1871         {
1872                 Con_Print("no progs loaded\n");
1873                 return;
1874         }
1875         */
1876
1877         if(Cmd_Argc() != 2)
1878         {
1879                 Con_Print("prvm_fields <program name>\n");
1880                 return;
1881         }
1882
1883         PRVM_Begin;
1884         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1885                 return;
1886
1887         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1888         for (ednum = 0;ednum < prog->max_edicts;ednum++)
1889         {
1890                 ed = PRVM_EDICT_NUM(ednum);
1891                 if (ed->priv.required->free)
1892                         continue;
1893                 for (i = 1;i < prog->progs->numfielddefs;i++)
1894                 {
1895                         d = &prog->fielddefs[i];
1896                         name = PRVM_GetString(d->s_name);
1897                         if (name[strlen(name)-2] == '_')
1898                                 continue;       // skip _x, _y, _z vars
1899                         v = (int *)((char *)ed->fields.vp + d->ofs*4);
1900                         // if the value is still all 0, skip the field
1901                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1902                         {
1903                                 if (v[j])
1904                                 {
1905                                         counts[i]++;
1906                                         break;
1907                                 }
1908                         }
1909                 }
1910         }
1911         used = 0;
1912         usedamount = 0;
1913         tempstring[0] = 0;
1914         for (i = 0;i < prog->progs->numfielddefs;i++)
1915         {
1916                 d = &prog->fielddefs[i];
1917                 name = PRVM_GetString(d->s_name);
1918                 if (name[strlen(name)-2] == '_')
1919                         continue;       // skip _x, _y, _z vars
1920                 switch(d->type & ~DEF_SAVEGLOBAL)
1921                 {
1922                 case ev_string:
1923                         strlcat(tempstring, "string   ", sizeof(tempstring));
1924                         break;
1925                 case ev_entity:
1926                         strlcat(tempstring, "entity   ", sizeof(tempstring));
1927                         break;
1928                 case ev_function:
1929                         strlcat(tempstring, "function ", sizeof(tempstring));
1930                         break;
1931                 case ev_field:
1932                         strlcat(tempstring, "field    ", sizeof(tempstring));
1933                         break;
1934                 case ev_void:
1935                         strlcat(tempstring, "void     ", sizeof(tempstring));
1936                         break;
1937                 case ev_float:
1938                         strlcat(tempstring, "float    ", sizeof(tempstring));
1939                         break;
1940                 case ev_vector:
1941                         strlcat(tempstring, "vector   ", sizeof(tempstring));
1942                         break;
1943                 case ev_pointer:
1944                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
1945                         break;
1946                 default:
1947                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1948                         strlcat(tempstring, tempstring2, sizeof(tempstring));
1949                         break;
1950                 }
1951                 if (strlen(name) > sizeof(tempstring2)-4)
1952                 {
1953                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
1954                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1955                         tempstring2[sizeof(tempstring2)-1] = 0;
1956                         name = tempstring2;
1957                 }
1958                 strlcat(tempstring, name, sizeof(tempstring));
1959                 for (j = (int)strlen(name);j < 25;j++)
1960                         strlcat(tempstring, " ", sizeof(tempstring));
1961                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
1962                 strlcat(tempstring, tempstring2, sizeof(tempstring));
1963                 strlcat(tempstring, "\n", sizeof(tempstring));
1964                 if (strlen(tempstring) >= sizeof(tempstring)/2)
1965                 {
1966                         Con_Print(tempstring);
1967                         tempstring[0] = 0;
1968                 }
1969                 if (counts[i])
1970                 {
1971                         used++;
1972                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1973                 }
1974         }
1975         Mem_Free(counts);
1976         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->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
1977
1978         PRVM_End;
1979 }
1980
1981 void PRVM_Globals_f (void)
1982 {
1983         int i;
1984         const char *wildcard;
1985         int numculled;
1986                 numculled = 0;
1987         // TODO
1988         /*if (!sv.active)
1989         {
1990                 Con_Print("no progs loaded\n");
1991                 return;
1992         }*/
1993         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1994         {
1995                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
1996                 return;
1997         }
1998
1999         PRVM_Begin;
2000         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2001                 return;
2002
2003         if( Cmd_Argc() == 3)
2004                 wildcard = Cmd_Argv(2);
2005         else
2006                 wildcard = NULL;
2007
2008         Con_Printf("%s :", PRVM_NAME);
2009
2010         for (i = 0;i < prog->progs->numglobaldefs;i++)
2011         {
2012                 if(wildcard)
2013                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2014                         {
2015                                 numculled++;
2016                                 continue;
2017                         }
2018                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2019         }
2020         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2021
2022         PRVM_End;
2023 }
2024
2025 /*
2026 ===============
2027 PRVM_Global
2028 ===============
2029 */
2030 void PRVM_Global_f(void)
2031 {
2032         ddef_t *global;
2033         if( Cmd_Argc() != 3 ) {
2034                 Con_Printf( "prvm_global <program name> <global name>\n" );
2035                 return;
2036         }
2037
2038         PRVM_Begin;
2039         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2040                 return;
2041
2042         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2043         if( !global )
2044                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2045         else
2046                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2047         PRVM_End;
2048 }
2049
2050 /*
2051 ===============
2052 PRVM_GlobalSet
2053 ===============
2054 */
2055 void PRVM_GlobalSet_f(void)
2056 {
2057         ddef_t *global;
2058         if( Cmd_Argc() != 4 ) {
2059                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2060                 return;
2061         }
2062
2063         PRVM_Begin;
2064         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2065                 return;
2066
2067         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2068         if( !global )
2069                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2070         else
2071                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2072         PRVM_End;
2073 }
2074
2075 /*
2076 ===============
2077 PRVM_Init
2078 ===============
2079 */
2080 void PRVM_Init (void)
2081 {
2082         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2083         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2084         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2085         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2086         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)");
2087         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)");
2088         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2089         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2090         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2091         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)");
2092         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2093         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2094         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2095         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2096         // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2097 #ifdef PRVM_BOUNDSCHECK_CVAR
2098         Cvar_RegisterVariable (&prvm_boundscheck);
2099 #endif
2100         Cvar_RegisterVariable (&prvm_traceqc);
2101         Cvar_RegisterVariable (&prvm_statementprofiling);
2102         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2103         Cvar_RegisterVariable (&prvm_leaktest);
2104         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2105         Cvar_RegisterVariable (&prvm_errordump);
2106
2107         //VM_Cmd_Init();
2108 }
2109
2110 /*
2111 ===============
2112 PRVM_InitProg
2113 ===============
2114 */
2115 void PRVM_InitProg(int prognr)
2116 {
2117         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2118                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2119
2120         prog = &prog_list[prognr];
2121
2122         if(prog->loaded)
2123                 PRVM_ResetProg();
2124
2125         memset(prog, 0, sizeof(prvm_prog_t));
2126         prog->starttime = Sys_DoubleTime();
2127
2128         prog->error_cmd = Host_Error;
2129         prog->leaktest_active = prvm_leaktest.integer;
2130 }
2131
2132 int PRVM_GetProgNr()
2133 {
2134         return prog - prog_list;
2135 }
2136
2137 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2138 {
2139         return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2140 }
2141
2142 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2143 {
2144         _Mem_Free(buffer, filename, fileline);
2145 }
2146
2147 void _PRVM_FreeAll(const char *filename, int fileline)
2148 {
2149         prog->progs = NULL;
2150         prog->fielddefs = NULL;
2151         prog->functions = NULL;
2152         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2153 }
2154
2155 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2156 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2157 {
2158         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2159         return 0;
2160 }
2161
2162 /*
2163 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2164 {
2165         PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2166         return 0;
2167 }
2168
2169 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2170 {
2171         int n;
2172         n = e - prog->edicts;
2173         if ((unsigned int)n >= prog->limit_edicts)
2174                 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2175         return n;
2176 }
2177
2178 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2179 //{
2180 //      return e - prog->edicts;
2181 //}
2182
2183 //#define       PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2184 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2185 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2186 {
2187         int n;
2188         n = e - prog->edicts;
2189         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2190                 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2191         return n;// EXPERIMENTAL
2192         //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2193 }
2194 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2195 {
2196         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2197                 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2198         return prog->edicts + n; // EXPERIMENTAL
2199         //return prog->edicts + ((n) / (progs->entityfields * 4));
2200 }
2201 */
2202
2203
2204 sizebuf_t vm_tempstringsbuf;
2205
2206 const char *PRVM_GetString(int num)
2207 {
2208         if (num >= 0)
2209         {
2210                 if (num < prog->stringssize)
2211                         return prog->strings + num;
2212                 else
2213 #if 1
2214                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2215                 {
2216                         num -= prog->stringssize;
2217                         if (num < vm_tempstringsbuf.cursize)
2218                                 return (char *)vm_tempstringsbuf.data + num;
2219                         else
2220                         {
2221                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2222                                 return "";
2223                         }
2224                 }
2225                 else
2226 #endif
2227                 {
2228                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2229                         return "";
2230                 }
2231         }
2232         else
2233         {
2234                 num = -1 - num;
2235 #if 0
2236                 if (num >= (1<<30))
2237                 {
2238                         // special range reserved for tempstrings
2239                         num -= (1<<30);
2240                         if (num < vm_tempstringsbuf.cursize)
2241                                 return (char *)vm_tempstringsbuf.data + num;
2242                         else
2243                         {
2244                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2245                                 return "";
2246                         }
2247                 }
2248                 else
2249 #endif
2250                 if (num < prog->numknownstrings)
2251                 {
2252                         if (!prog->knownstrings[num])
2253                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2254                         return prog->knownstrings[num];
2255                 }
2256                 else
2257                 {
2258                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2259                         return "";
2260                 }
2261         }
2262 }
2263
2264 int PRVM_SetEngineString(const char *s)
2265 {
2266         int i;
2267         if (!s)
2268                 return 0;
2269         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2270                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2271         // if it's in the tempstrings area, use a reserved range
2272         // (otherwise we'd get millions of useless string offsets cluttering the database)
2273         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2274 #if 1
2275                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2276 #else
2277                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2278 #endif
2279         // see if it's a known string address
2280         for (i = 0;i < prog->numknownstrings;i++)
2281                 if (prog->knownstrings[i] == s)
2282                         return -1 - i;
2283         // new unknown engine string
2284         if (developer.integer >= 200)
2285                 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2286         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2287                 if (!prog->knownstrings[i])
2288                         break;
2289         if (i >= prog->numknownstrings)
2290         {
2291                 if (i >= prog->maxknownstrings)
2292                 {
2293                         const char **oldstrings = prog->knownstrings;
2294                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2295                         const char **oldstrings_origin = prog->knownstrings_origin;
2296                         prog->maxknownstrings += 128;
2297                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2298                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2299                         if(prog->leaktest_active)
2300                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2301                         if (prog->numknownstrings)
2302                         {
2303                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2304                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2305                                 if(prog->leaktest_active)
2306                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2307                         }
2308                 }
2309                 prog->numknownstrings++;
2310         }
2311         prog->firstfreeknownstring = i + 1;
2312         prog->knownstrings[i] = s;
2313         prog->knownstrings_freeable[i] = false;
2314         if(prog->leaktest_active)
2315                 prog->knownstrings_origin[i] = NULL;
2316         return -1 - i;
2317 }
2318
2319 // temp string handling
2320
2321 // all tempstrings go into this buffer consecutively, and it is reset
2322 // whenever PRVM_ExecuteProgram returns to the engine
2323 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2324 //  restores it on return, so multiple recursive calls can share the same
2325 //  buffer)
2326 // the buffer size is automatically grown as needed
2327
2328 int PRVM_SetTempString(const char *s)
2329 {
2330         int size;
2331         char *t;
2332         if (!s)
2333                 return 0;
2334         size = (int)strlen(s) + 1;
2335         if (developer.integer >= 300)
2336                 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2337         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2338         {
2339                 sizebuf_t old = vm_tempstringsbuf;
2340                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2341                         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);
2342                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2343                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2344                         vm_tempstringsbuf.maxsize *= 2;
2345                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2346                 {
2347                         if (developer.integer >= 100)
2348                                 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2349                         vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2350                         if (old.cursize)
2351                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2352                         if (old.data)
2353                                 Mem_Free(old.data);
2354                 }
2355         }
2356         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2357         memcpy(t, s, size);
2358         vm_tempstringsbuf.cursize += size;
2359         return PRVM_SetEngineString(t);
2360 }
2361
2362 int PRVM_AllocString(size_t bufferlength, char **pointer)
2363 {
2364         int i;
2365         if (!bufferlength)
2366                 return 0;
2367         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2368                 if (!prog->knownstrings[i])
2369                         break;
2370         if (i >= prog->numknownstrings)
2371         {
2372                 if (i >= prog->maxknownstrings)
2373                 {
2374                         const char **oldstrings = prog->knownstrings;
2375                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2376                         const char **oldstrings_origin = prog->knownstrings_origin;
2377                         prog->maxknownstrings += 128;
2378                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2379                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2380                         if(prog->leaktest_active)
2381                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2382                         if (prog->numknownstrings)
2383                         {
2384                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2385                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2386                                 if(prog->leaktest_active)
2387                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2388                         }
2389                         // TODO why not Mem_Free the old ones?
2390                 }
2391                 prog->numknownstrings++;
2392         }
2393         prog->firstfreeknownstring = i + 1;
2394         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2395         prog->knownstrings_freeable[i] = true;
2396         if(prog->leaktest_active)
2397                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2398         if (pointer)
2399                 *pointer = (char *)(prog->knownstrings[i]);
2400         return -1 - i;
2401 }
2402
2403 void PRVM_FreeString(int num)
2404 {
2405         if (num == 0)
2406                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2407         else if (num >= 0 && num < prog->stringssize)
2408                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2409         else if (num < 0 && num >= -prog->numknownstrings)
2410         {
2411                 num = -1 - num;
2412                 if (!prog->knownstrings[num])
2413                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2414                 if (!prog->knownstrings_freeable[num])
2415                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2416                 PRVM_Free((char *)prog->knownstrings[num]);
2417                 if(prog->leaktest_active)
2418                         if(prog->knownstrings_origin[num])
2419                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
2420                 prog->knownstrings[num] = NULL;
2421                 prog->knownstrings_freeable[num] = false;
2422                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2423         }
2424         else
2425                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2426 }
2427
2428 static qboolean PRVM_IsStringReferenced(string_t string)
2429 {
2430         int i, j;
2431
2432         for (i = 0;i < prog->progs->numglobaldefs;i++)
2433         {
2434                 ddef_t *d = &prog->globaldefs[i];
2435                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2436                         continue;
2437                 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2438                         return true;
2439         }
2440
2441         for(j = 0; j < prog->num_edicts; ++j)
2442         {
2443                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2444                 if (ed->priv.required->free)
2445                         continue;
2446                 for (i=0; i<prog->progs->numfielddefs; ++i)
2447                 {
2448                         ddef_t *d = &prog->fielddefs[i];
2449                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2450                                 continue;
2451                         if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2452                                 return true;
2453                 }
2454         }
2455
2456         return false;
2457 }
2458
2459 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2460 {
2461         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2462                 return true; // world or clients
2463         switch(prog - prog_list)
2464         {
2465                 case PRVM_SERVERPROG:
2466                         {
2467                                 entvars_t *ev = edict->fields.server;
2468                                 if(ev->solid) // can block other stuff, or is a trigger?
2469                                         return true;
2470                                 if(ev->modelindex) // visible ent?
2471                                         return true;
2472                                 if(ev->effects) // particle effect?
2473                                         return true;
2474                                 if(ev->think) // has a think function?
2475                                         if(ev->nextthink > 0) // that actually will eventually run?
2476                                                 return true;
2477                                 if(ev->takedamage)
2478                                         return true;
2479                                 if(*prvm_leaktest_ignore_classnames.string)
2480                                 {
2481                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2482                                                 return true;
2483                                 }
2484                         }
2485                         break;
2486                 case PRVM_CLIENTPROG:
2487                         {
2488                                 // TODO someone add more stuff here
2489                                 cl_entvars_t *ev = edict->fields.client;
2490                                 if(ev->entnum) // csqc networked
2491                                         return true;
2492                                 if(ev->modelindex) // visible ent?
2493                                         return true;
2494                                 if(ev->effects) // particle effect?
2495                                         return true;
2496                                 if(ev->think) // has a think function?
2497                                         if(ev->nextthink > 0) // that actually will eventually run?
2498                                                 return true;
2499                                 if(*prvm_leaktest_ignore_classnames.string)
2500                                 {
2501                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2502                                                 return true;
2503                                 }
2504                         }
2505                         break;
2506                 case PRVM_MENUPROG:
2507                         // menu prog does not have classnames
2508                         break;
2509         }
2510         return false;
2511 }
2512
2513 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2514 {
2515         int i, j;
2516         int edictnum = PRVM_NUM_FOR_EDICT(edict);
2517         const char *targetname = NULL;
2518
2519         switch(prog - prog_list)
2520         {
2521                 case PRVM_SERVERPROG:
2522                         targetname = PRVM_GetString(edict->fields.server->targetname);
2523                         break;
2524         }
2525
2526         if(targetname)
2527                 if(!*targetname) // ""
2528                         targetname = NULL;
2529
2530         for (i = 0;i < prog->progs->numglobaldefs;i++)
2531         {
2532                 ddef_t *d = &prog->globaldefs[i];
2533                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2534                         continue;
2535                 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2536                         return true;
2537         }
2538
2539         for(j = 0; j < prog->num_edicts; ++j)
2540         {
2541                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2542                 if (ed->priv.required->mark < mark)
2543                         continue;
2544                 if(ed == edict)
2545                         continue;
2546                 if(targetname)
2547                 {
2548                         const char *target = PRVM_GetString(ed->fields.server->target);
2549                         if(target)
2550                                 if(!strcmp(target, targetname))
2551                                         return true;
2552                 }
2553                 for (i=0; i<prog->progs->numfielddefs; ++i)
2554                 {
2555                         ddef_t *d = &prog->fielddefs[i];
2556                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2557                                 continue;
2558                         if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2559                                 return true;
2560                 }
2561         }
2562
2563         return false;
2564 }
2565
2566 static void PRVM_MarkReferencedEdicts()
2567 {
2568         int j;
2569         qboolean found_new;
2570         int stage;
2571
2572         for(j = 0; j < prog->num_edicts; ++j)
2573         {
2574                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2575                 if(ed->priv.required->free)
2576                         continue;
2577                 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2578         }
2579
2580         stage = 1;
2581         do
2582         {
2583                 found_new = false;
2584                 for(j = 0; j < prog->num_edicts; ++j)
2585                 {
2586                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2587                         if(ed->priv.required->free)
2588                                 continue;
2589                         if(ed->priv.required->mark)
2590                                 continue;
2591                         if(PRVM_IsEdictReferenced(ed, stage))
2592                         {
2593                                 ed->priv.required->mark = stage + 1;
2594                                 found_new = true;
2595                         }
2596                 }
2597                 ++stage;
2598         }
2599         while(found_new);
2600         Con_DPrintf("leak check used %d stages to find all references\n", stage);
2601 }
2602
2603 void PRVM_LeakTest()
2604 {
2605         int i, j;
2606         qboolean leaked = false;
2607
2608         if(!prog->leaktest_active)
2609                 return;
2610
2611         // 1. Strings
2612         for (i = 0; i < prog->numknownstrings; ++i)
2613         {
2614                 if(prog->knownstrings[i])
2615                 if(prog->knownstrings_freeable[i])
2616                 if(prog->knownstrings_origin[i])
2617                 if(!PRVM_IsStringReferenced(-1 - i))
2618                 {
2619                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2620                         leaked = true;
2621                 }
2622         }
2623
2624         // 2. Edicts
2625         PRVM_MarkReferencedEdicts();
2626         for(j = 0; j < prog->num_edicts; ++j)
2627         {
2628                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2629                 if(ed->priv.required->free)
2630                         continue;
2631                 if(!ed->priv.required->mark)
2632                 if(ed->priv.required->allocation_origin)
2633                 {
2634                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
2635                         PRVM_ED_Print(ed, NULL);
2636                         Con_Print("\n");
2637                         leaked = true;
2638                 }
2639         }
2640
2641         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2642         {
2643                 prvm_stringbuffer_t *stringbuffer = Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2644                 if(stringbuffer)
2645                 if(stringbuffer->origin)
2646                 {
2647                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
2648                         leaked = true;
2649                 }
2650         }
2651
2652         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2653         {
2654                 if(prog->openfiles[i])
2655                 if(prog->openfiles_origin[i])
2656                 {
2657                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
2658                         leaked = true;
2659                 }
2660         }
2661
2662         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2663         {
2664                 if(prog->opensearches[i])
2665                 if(prog->opensearches_origin[i])
2666                 {
2667                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
2668                         leaked = true;
2669                 }
2670         }
2671
2672         if(!leaked)
2673                 Con_Printf("Congratulations. No leaks found.\n");
2674 }