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