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