Make watchpoints data type aware; now vector watchpoints work too.
[xonotic/darkplaces.git] / prvm_edict.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // AK new vm
21
22 #include "quakedef.h"
23 #include "progsvm.h"
24 #include "csprogs.h"
25
26 prvm_prog_t prvm_prog_list[PRVM_PROG_MAX];
27
28 int             prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
29
30 prvm_eval_t prvm_badvalue; // used only for error returns
31
32 cvar_t prvm_language = {CVAR_SAVE, "prvm_language", "", "when set, loads progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.pot is written from the strings in the progs"};
33 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
34 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
35 // LordHavoc: counts usage of each QuakeC statement
36 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)"};
37 cvar_t prvm_timeprofiling = {0, "prvm_timeprofiling", "0", "counts how long each function has been executed, these counts are displayed in prvm_profile output (if enabled)"};
38 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42 cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"};
43 cvar_t prvm_reuseedicts_startuptime = {0, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
44 cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"};
45
46 static double prvm_reuseedicts_always_allow = 0;
47 qboolean prvm_runawaycheck = true;
48
49 //============================================================================
50 // mempool handling
51
52 /*
53 ===============
54 PRVM_MEM_Alloc
55 ===============
56 */
57 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
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->entityfieldsarea = prog->entityfields * prog->max_edicts;
77         prog->edictsfields = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
78
79         // set edict pointers
80         for(i = 0; i < prog->max_edicts; i++)
81         {
82                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
83                 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
84         }
85 }
86
87 /*
88 ===============
89 PRVM_MEM_IncreaseEdicts
90 ===============
91 */
92 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
93 {
94         int             i;
95
96         if(prog->max_edicts >= prog->limit_edicts)
97                 return;
98
99         prog->begin_increase_edicts(prog);
100
101         // increase edicts
102         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
103
104         prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
105         prog->edictsfields = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields, prog->entityfieldsarea * sizeof(prvm_vec_t));
106         prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
107
108         //set e and v pointers
109         for(i = 0; i < prog->max_edicts; i++)
110         {
111                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
112                 prog->edicts[i].fields.fp = prog->edictsfields + i * prog->entityfields;
113         }
114
115         prog->end_increase_edicts(prog);
116 }
117
118 //============================================================================
119 // normal prvm
120
121 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
122 {
123         ddef_t *d;
124         d = PRVM_ED_FindField(prog, field);
125         if (!d)
126                 return -1;
127         return d->ofs;
128 }
129
130 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
131 {
132         ddef_t *d;
133         d = PRVM_ED_FindGlobal(prog, global);
134         if (!d)
135                 return -1;
136         return d->ofs;
137 }
138
139 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
140 {
141         mfunction_t *f;
142         f = PRVM_ED_FindFunction(prog, function);
143         if (!f)
144                 return 0;
145         return (func_t)(f - prog->functions);
146 }
147
148 /*
149 =================
150 PRVM_ProgFromString
151 =================
152 */
153 prvm_prog_t *PRVM_ProgFromString(const char *str)
154 {
155         if (!strcmp(str, "server"))
156                 return SVVM_prog;
157         if (!strcmp(str, "client"))
158                 return CLVM_prog;
159         if (!strcmp(str, "menu"))
160                 return MVM_prog;
161         return NULL;
162 }
163
164 /*
165 =================
166 PRVM_FriendlyProgFromString
167 =================
168 */
169 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
170 {
171         prvm_prog_t *prog = PRVM_ProgFromString(str);
172         if (!prog)
173         {
174                 Con_Printf("%s: unknown program name\n", str);
175                 return NULL;
176         }
177         if (!prog->loaded)
178         {
179                 Con_Printf("%s: program is not loaded\n", str);
180                 return NULL;
181         }
182         return prog;
183 }
184
185 /*
186 =================
187 PRVM_ED_ClearEdict
188
189 Sets everything to NULL
190 =================
191 */
192 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
193 {
194         memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
195         e->priv.required->free = false;
196
197         // AK: Let the init_edict function determine if something needs to be initialized
198         prog->init_edict(prog, e);
199 }
200
201 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
202 {
203         char *buf = NULL;
204         if(prog->leaktest_active)
205         if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
206         {
207                 buf = (char *)PRVM_Alloc(128);
208                 PRVM_ShortStackTrace(prog, buf, 128);
209         }
210         return buf;
211 }
212
213 /*
214 =================
215 PRVM_ED_CanAlloc
216
217 Returns if this particular edict could get allocated by PRVM_ED_Alloc
218 =================
219 */
220 qboolean PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
221 {
222         if(!e->priv.required->free)
223                 return false;
224         if(prvm_reuseedicts_always_allow == realtime)
225                 return true;
226         if(realtime <= e->priv.required->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
227                 return false; // never allow reuse in same frame (causes networking trouble)
228         if(e->priv.required->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
229                 return true;
230         if(realtime > e->priv.required->freetime + 1)
231                 return true;
232         return false; // entity slot still blocked because the entity was freed less than one second ago
233 }
234
235 /*
236 =================
237 PRVM_ED_Alloc
238
239 Either finds a free edict, or allocates a new one.
240 Try to avoid reusing an entity that was recently freed, because it
241 can cause the client to think the entity morphed into something else
242 instead of being removed and recreated, which can cause interpolated
243 angles and bad trails.
244 =================
245 */
246 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
247 {
248         int i;
249         prvm_edict_t *e;
250
251         // the client qc dont need maxclients
252         // thus it doesnt need to use svs.maxclients
253         // AK:  changed i=svs.maxclients+1
254         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
255         //              although the menu/client has no world
256         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
257         {
258                 e = PRVM_EDICT_NUM(i);
259                 if(PRVM_ED_CanAlloc(prog, e))
260                 {
261                         PRVM_ED_ClearEdict (prog, e);
262                         e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
263                         return e;
264                 }
265         }
266
267         if (i == prog->limit_edicts)
268                 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
269
270         prog->num_edicts++;
271         if (prog->num_edicts >= prog->max_edicts)
272                 PRVM_MEM_IncreaseEdicts(prog);
273
274         e = PRVM_EDICT_NUM(i);
275         PRVM_ED_ClearEdict(prog, e);
276
277         e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
278
279         return e;
280 }
281
282 /*
283 =================
284 PRVM_ED_Free
285
286 Marks the edict as free
287 FIXME: walk all entities and NULL out references to this entity
288 =================
289 */
290 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
291 {
292         // dont delete the null entity (world) or reserved edicts
293         if (ed - prog->edicts <= prog->reserved_edicts)
294                 return;
295
296         prog->free_edict(prog, ed);
297
298         ed->priv.required->free = true;
299         ed->priv.required->freetime = realtime;
300         if(ed->priv.required->allocation_origin)
301         {
302                 Mem_Free((char *)ed->priv.required->allocation_origin);
303                 ed->priv.required->allocation_origin = NULL;
304         }
305 }
306
307 //===========================================================================
308
309 /*
310 ============
311 PRVM_ED_GlobalAtOfs
312 ============
313 */
314 static ddef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, int ofs)
315 {
316         ddef_t          *def;
317         int                     i;
318
319         for (i = 0;i < prog->numglobaldefs;i++)
320         {
321                 def = &prog->globaldefs[i];
322                 if (def->ofs == ofs)
323                         return def;
324         }
325         return NULL;
326 }
327
328 /*
329 ============
330 PRVM_ED_FieldAtOfs
331 ============
332 */
333 ddef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, int ofs)
334 {
335         ddef_t          *def;
336         int                     i;
337
338         for (i = 0;i < prog->numfielddefs;i++)
339         {
340                 def = &prog->fielddefs[i];
341                 if (def->ofs == ofs)
342                         return def;
343         }
344         return NULL;
345 }
346
347 /*
348 ============
349 PRVM_ED_FindField
350 ============
351 */
352 ddef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
353 {
354         ddef_t *def;
355         int i;
356
357         for (i = 0;i < prog->numfielddefs;i++)
358         {
359                 def = &prog->fielddefs[i];
360                 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
361                         return def;
362         }
363         return NULL;
364 }
365
366 /*
367 ============
368 PRVM_ED_FindGlobal
369 ============
370 */
371 ddef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
372 {
373         ddef_t *def;
374         int i;
375
376         for (i = 0;i < prog->numglobaldefs;i++)
377         {
378                 def = &prog->globaldefs[i];
379                 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
380                         return def;
381         }
382         return NULL;
383 }
384
385
386 /*
387 ============
388 PRVM_ED_FindFunction
389 ============
390 */
391 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
392 {
393         mfunction_t             *func;
394         int                             i;
395
396         for (i = 0;i < prog->numfunctions;i++)
397         {
398                 func = &prog->functions[i];
399                 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
400                         return func;
401         }
402         return NULL;
403 }
404
405
406 /*
407 ============
408 PRVM_ValueString
409
410 Returns a string describing *data in a type specific manner
411 =============
412 */
413 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
414 {
415         ddef_t *def;
416         mfunction_t *f;
417         int n;
418
419         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
420
421         switch (type)
422         {
423         case ev_string:
424                 strlcpy (line, PRVM_GetString (prog, val->string), linelength);
425                 break;
426         case ev_entity:
427                 n = val->edict;
428                 if (n < 0 || n >= prog->max_edicts)
429                         dpsnprintf (line, linelength, "entity %i (invalid!)", n);
430                 else
431                         dpsnprintf (line, linelength, "entity %i", n);
432                 break;
433         case ev_function:
434                 f = prog->functions + val->function;
435                 dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
436                 break;
437         case ev_field:
438                 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
439                 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
440                 break;
441         case ev_void:
442                 dpsnprintf (line, linelength, "void");
443                 break;
444         case ev_float:
445                 // LordHavoc: changed from %5.1f to %10.4f
446                 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
447                 break;
448         case ev_vector:
449                 // LordHavoc: changed from %5.1f to %10.4f
450                 dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
451                 break;
452         case ev_pointer:
453                 dpsnprintf (line, linelength, "pointer");
454                 break;
455         default:
456                 dpsnprintf (line, linelength, "bad type %i", (int) type);
457                 break;
458         }
459
460         return line;
461 }
462
463 /*
464 ============
465 PRVM_UglyValueString
466
467 Returns a string describing *data in a type specific manner
468 Easier to parse than PR_ValueString
469 =============
470 */
471 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
472 {
473         int i;
474         const char *s;
475         ddef_t *def;
476         mfunction_t *f;
477
478         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
479
480         switch (type)
481         {
482         case ev_string:
483                 // Parse the string a bit to turn special characters
484                 // (like newline, specifically) into escape codes,
485                 // this fixes saving games from various mods
486                 s = PRVM_GetString (prog, val->string);
487                 for (i = 0;i < (int)linelength - 2 && *s;)
488                 {
489                         if (*s == '\n')
490                         {
491                                 line[i++] = '\\';
492                                 line[i++] = 'n';
493                         }
494                         else if (*s == '\r')
495                         {
496                                 line[i++] = '\\';
497                                 line[i++] = 'r';
498                         }
499                         else if (*s == '\\')
500                         {
501                                 line[i++] = '\\';
502                                 line[i++] = '\\';
503                         }
504                         else if (*s == '"')
505                         {
506                                 line[i++] = '\\';
507                                 line[i++] = '"';
508                         }
509                         else
510                                 line[i++] = *s;
511                         s++;
512                 }
513                 line[i] = '\0';
514                 break;
515         case ev_entity:
516                 dpsnprintf (line, linelength, "%i", val->edict);
517                 break;
518         case ev_function:
519                 f = prog->functions + val->function;
520                 strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
521                 break;
522         case ev_field:
523                 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
524                 dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
525                 break;
526         case ev_void:
527                 dpsnprintf (line, linelength, "void");
528                 break;
529         case ev_float:
530                 dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float);
531                 break;
532         case ev_vector:
533                 dpsnprintf (line, linelength, VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
534                 break;
535         default:
536                 dpsnprintf (line, linelength, "bad type %i", type);
537                 break;
538         }
539
540         return line;
541 }
542
543 /*
544 ============
545 PRVM_GlobalString
546
547 Returns a string with a description and the contents of a global,
548 padded to 20 field width
549 ============
550 */
551 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
552 {
553         char    *s;
554         //size_t        i;
555         ddef_t  *def;
556         prvm_eval_t     *val;
557         char valuebuf[MAX_INPUTLINE];
558
559         val = (prvm_eval_t *)&prog->globals.fp[ofs];
560         def = PRVM_ED_GlobalAtOfs(prog, ofs);
561         if (!def)
562                 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
563         else
564         {
565                 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
566                 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
567         }
568
569         //i = strlen(line);
570         //for ( ; i<20 ; i++)
571         //      strcat (line," ");
572         //strcat (line," ");
573
574         return line;
575 }
576
577 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
578 {
579         //size_t        i;
580         ddef_t  *def;
581
582         def = PRVM_ED_GlobalAtOfs(prog, ofs);
583         if (!def)
584                 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
585         else
586                 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
587
588         //i = strlen(line);
589         //for ( ; i<20 ; i++)
590         //      strcat (line," ");
591         //strcat (line," ");
592
593         return line;
594 }
595
596
597 /*
598 =============
599 PRVM_ED_Print
600
601 For debugging
602 =============
603 */
604 // LordHavoc: optimized this to print out much more quickly (tempstring)
605 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
606 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
607 {
608         size_t  l;
609         ddef_t  *d;
610         prvm_eval_t     *val;
611         int             i, j;
612         const char      *name;
613         int             type;
614         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
615         char    valuebuf[MAX_INPUTLINE];
616
617         if (ed->priv.required->free)
618         {
619                 Con_Printf("%s: FREE\n",prog->name);
620                 return;
621         }
622
623         tempstring[0] = 0;
624         dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
625         for (i = 1;i < prog->numfielddefs;i++)
626         {
627                 d = &prog->fielddefs[i];
628                 name = PRVM_GetString(prog, d->s_name);
629                 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
630                         continue;       // skip _x, _y, _z vars
631
632                 // Check Field Name Wildcard
633                 if(wildcard_fieldname)
634                         if( !matchpattern(name, wildcard_fieldname, 1) )
635                                 // Didn't match; skip
636                                 continue;
637
638                 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
639
640         // if the value is still all 0, skip the field
641                 type = d->type & ~DEF_SAVEGLOBAL;
642
643                 for (j=0 ; j<prvm_type_size[type] ; j++)
644                         if (val->ivector[j])
645                                 break;
646                 if (j == prvm_type_size[type])
647                         continue;
648
649                 if (strlen(name) > sizeof(tempstring2)-4)
650                 {
651                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
652                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
653                         tempstring2[sizeof(tempstring2)-1] = 0;
654                         name = tempstring2;
655                 }
656                 strlcat(tempstring, name, sizeof(tempstring));
657                 for (l = strlen(name);l < 14;l++)
658                         strlcat(tempstring, " ", sizeof(tempstring));
659                 strlcat(tempstring, " ", sizeof(tempstring));
660
661                 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
662                 if (strlen(name) > sizeof(tempstring2)-4)
663                 {
664                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
665                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
666                         tempstring2[sizeof(tempstring2)-1] = 0;
667                         name = tempstring2;
668                 }
669                 strlcat(tempstring, name, sizeof(tempstring));
670                 strlcat(tempstring, "\n", sizeof(tempstring));
671                 if (strlen(tempstring) >= sizeof(tempstring)/2)
672                 {
673                         Con_Print(tempstring);
674                         tempstring[0] = 0;
675                 }
676         }
677         if (tempstring[0])
678                 Con_Print(tempstring);
679 }
680
681 /*
682 =============
683 PRVM_ED_Write
684
685 For savegames
686 =============
687 */
688 extern cvar_t developer_entityparsing;
689 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
690 {
691         ddef_t  *d;
692         prvm_eval_t     *val;
693         int             i, j;
694         const char      *name;
695         int             type;
696         char vabuf[1024];
697         char valuebuf[MAX_INPUTLINE];
698
699         FS_Print(f, "{\n");
700
701         if (ed->priv.required->free)
702         {
703                 FS_Print(f, "}\n");
704                 return;
705         }
706
707         for (i = 1;i < prog->numfielddefs;i++)
708         {
709                 d = &prog->fielddefs[i];
710                 name = PRVM_GetString(prog, d->s_name);
711
712                 if(developer_entityparsing.integer)
713                         Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
714
715                 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
716                 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
717                         continue;       // skip _x, _y, _z vars, and ALSO other _? vars as some mods expect them to be never saved (TODO: a gameplayfix for using the "more precise" condition above?)
718
719                 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
720
721         // if the value is still all 0, skip the field
722                 type = d->type & ~DEF_SAVEGLOBAL;
723                 for (j=0 ; j<prvm_type_size[type] ; j++)
724                         if (val->ivector[j])
725                                 break;
726                 if (j == prvm_type_size[type])
727                         continue;
728
729                 FS_Printf(f,"\"%s\" ",name);
730                 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
731                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
732                 prog->statestring = NULL;
733         }
734
735         FS_Print(f, "}\n");
736 }
737
738 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
739 {
740         PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
741 }
742
743 /*
744 =============
745 PRVM_ED_PrintEdicts_f
746
747 For debugging, prints all the entities in the current server
748 =============
749 */
750 void PRVM_ED_PrintEdicts_f (void)
751 {
752         prvm_prog_t *prog;
753         int             i;
754         const char *wildcard_fieldname;
755
756         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
757         {
758                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
759                 return;
760         }
761
762         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
763                 return;
764
765         if( Cmd_Argc() == 3)
766                 wildcard_fieldname = Cmd_Argv(2);
767         else
768                 wildcard_fieldname = NULL;
769
770         Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
771         for (i=0 ; i<prog->num_edicts ; i++)
772                 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
773 }
774
775 /*
776 =============
777 PRVM_ED_PrintEdict_f
778
779 For debugging, prints a single edict
780 =============
781 */
782 static void PRVM_ED_PrintEdict_f (void)
783 {
784         prvm_prog_t *prog;
785         int             i;
786         const char      *wildcard_fieldname;
787
788         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
789         {
790                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
791                 return;
792         }
793
794         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
795                 return;
796
797         i = atoi (Cmd_Argv(2));
798         if (i >= prog->num_edicts)
799         {
800                 Con_Print("Bad edict number\n");
801                 return;
802         }
803         if( Cmd_Argc() == 4)
804                 // Optional Wildcard Provided
805                 wildcard_fieldname = Cmd_Argv(3);
806         else
807                 // Use All
808                 wildcard_fieldname = NULL;
809         PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
810 }
811
812 /*
813 =============
814 PRVM_ED_Count
815
816 For debugging
817 =============
818 */
819 // 2 possibilities : 1. just displaying the active edict count
820 //                                       2. making a function pointer [x]
821 static void PRVM_ED_Count_f (void)
822 {
823         prvm_prog_t *prog;
824
825         if(Cmd_Argc() != 2)
826         {
827                 Con_Print("prvm_count <program name>\n");
828                 return;
829         }
830
831         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
832                 return;
833
834         prog->count_edicts(prog);
835 }
836
837 /*
838 ==============================================================================
839
840                                         ARCHIVING GLOBALS
841
842 FIXME: need to tag constants, doesn't really work
843 ==============================================================================
844 */
845
846 /*
847 =============
848 PRVM_ED_WriteGlobals
849 =============
850 */
851 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
852 {
853         ddef_t          *def;
854         int                     i;
855         const char              *name;
856         int                     type;
857         char vabuf[1024];
858         char valuebuf[MAX_INPUTLINE];
859
860         FS_Print(f,"{\n");
861         for (i = 0;i < prog->numglobaldefs;i++)
862         {
863                 def = &prog->globaldefs[i];
864                 type = def->type;
865                 if ( !(def->type & DEF_SAVEGLOBAL) )
866                         continue;
867                 type &= ~DEF_SAVEGLOBAL;
868
869                 if (type != ev_string && type != ev_float && type != ev_entity)
870                         continue;
871
872                 name = PRVM_GetString(prog, def->s_name);
873
874                 if(developer_entityparsing.integer)
875                         Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
876
877                 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
878                 FS_Printf(f,"\"%s\" ", name);
879                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
880                 prog->statestring = NULL;
881         }
882         FS_Print(f,"}\n");
883 }
884
885 /*
886 =============
887 PRVM_ED_ParseGlobals
888 =============
889 */
890 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
891 {
892         char keyname[MAX_INPUTLINE];
893         ddef_t *key;
894
895         while (1)
896         {
897                 // parse key
898                 if (!COM_ParseToken_Simple(&data, false, false, true))
899                         prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
900                 if (com_token[0] == '}')
901                         break;
902
903                 if (developer_entityparsing.integer)
904                         Con_Printf("Key: \"%s\"", com_token);
905
906                 strlcpy (keyname, com_token, sizeof(keyname));
907
908                 // parse value
909                 if (!COM_ParseToken_Simple(&data, false, true, true))
910                         prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
911
912                 if (developer_entityparsing.integer)
913                         Con_Printf(" \"%s\"\n", com_token);
914
915                 if (com_token[0] == '}')
916                         prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
917
918                 key = PRVM_ED_FindGlobal (prog, keyname);
919                 if (!key)
920                 {
921                         Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
922                         continue;
923                 }
924
925                 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
926                         prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
927         }
928 }
929
930 //============================================================================
931
932
933 /*
934 =============
935 PRVM_ED_ParseEval
936
937 Can parse either fields or globals
938 returns false if error
939 =============
940 */
941 qboolean PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
942 {
943         int i, l;
944         char *new_p;
945         ddef_t *def;
946         prvm_eval_t *val;
947         mfunction_t *func;
948
949         if (ent)
950                 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
951         else
952                 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
953         switch (key->type & ~DEF_SAVEGLOBAL)
954         {
955         case ev_string:
956                 l = (int)strlen(s) + 1;
957                 val->string = PRVM_AllocString(prog, l, &new_p);
958                 for (i = 0;i < l;i++)
959                 {
960                         if (s[i] == '\\' && s[i+1] && parsebackslash)
961                         {
962                                 i++;
963                                 if (s[i] == 'n')
964                                         *new_p++ = '\n';
965                                 else if (s[i] == 'r')
966                                         *new_p++ = '\r';
967                                 else
968                                         *new_p++ = s[i];
969                         }
970                         else
971                                 *new_p++ = s[i];
972                 }
973                 break;
974
975         case ev_float:
976                 while (*s && ISWHITESPACE(*s))
977                         s++;
978                 val->_float = atof(s);
979                 break;
980
981         case ev_vector:
982                 for (i = 0;i < 3;i++)
983                 {
984                         while (*s && ISWHITESPACE(*s))
985                                 s++;
986                         if (!*s)
987                                 break;
988                         val->vector[i] = atof(s);
989                         while (!ISWHITESPACE(*s))
990                                 s++;
991                         if (!*s)
992                                 break;
993                 }
994                 break;
995
996         case ev_entity:
997                 while (*s && ISWHITESPACE(*s))
998                         s++;
999                 i = atoi(s);
1000                 if (i >= prog->limit_edicts)
1001                         Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, prog->limit_edicts, prog->name);
1002                 while (i >= prog->max_edicts)
1003                         PRVM_MEM_IncreaseEdicts(prog);
1004                 // if IncreaseEdicts was called the base pointer needs to be updated
1005                 if (ent)
1006                         val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1007                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1008                 break;
1009
1010         case ev_field:
1011                 if (*s != '.')
1012                 {
1013                         Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1014                         return false;
1015                 }
1016                 def = PRVM_ED_FindField(prog, s + 1);
1017                 if (!def)
1018                 {
1019                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1020                         return false;
1021                 }
1022                 val->_int = def->ofs;
1023                 break;
1024
1025         case ev_function:
1026                 func = PRVM_ED_FindFunction(prog, s);
1027                 if (!func)
1028                 {
1029                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1030                         return false;
1031                 }
1032                 val->function = func - prog->functions;
1033                 break;
1034
1035         default:
1036                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(prog, key->s_name), prog->name);
1037                 return false;
1038         }
1039         return true;
1040 }
1041
1042 /*
1043 =============
1044 PRVM_GameCommand_f
1045
1046 Console command to send a string to QC function GameCommand of the
1047 indicated progs
1048
1049 Usage:
1050   sv_cmd adminmsg 3 "do not teamkill"
1051   cl_cmd someclientcommand
1052   menu_cmd somemenucommand
1053
1054 All progs can support this extension; sg calls it in server QC, cg in client
1055 QC, mg in menu QC.
1056 =============
1057 */
1058 static void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1059 {
1060         prvm_prog_t *prog;
1061         if(Cmd_Argc() < 1)
1062         {
1063                 Con_Printf("%s text...\n", whichcmd);
1064                 return;
1065         }
1066
1067         if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1068                 return;
1069
1070         if(!PRVM_allfunction(GameCommand))
1071         {
1072                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1073         }
1074         else
1075         {
1076                 int restorevm_tempstringsbuf_cursize;
1077                 const char *s;
1078
1079                 s = Cmd_Args();
1080
1081                 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1082                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "");
1083                 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1084                 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1085         }
1086 }
1087 static void PRVM_GameCommand_Server_f(void)
1088 {
1089         PRVM_GameCommand("server", "sv_cmd");
1090 }
1091 static void PRVM_GameCommand_Client_f(void)
1092 {
1093         PRVM_GameCommand("client", "cl_cmd");
1094 }
1095 static void PRVM_GameCommand_Menu_f(void)
1096 {
1097         PRVM_GameCommand("menu", "menu_cmd");
1098 }
1099
1100 /*
1101 =============
1102 PRVM_ED_EdictGet_f
1103
1104 Console command to load a field of a specified edict
1105 =============
1106 */
1107 static void PRVM_ED_EdictGet_f(void)
1108 {
1109         prvm_prog_t *prog;
1110         prvm_edict_t *ed;
1111         ddef_t *key;
1112         const char *s;
1113         prvm_eval_t *v;
1114         char valuebuf[MAX_INPUTLINE];
1115
1116         if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1117         {
1118                 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1119                 return;
1120         }
1121
1122         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1123                 return;
1124
1125         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1126
1127         if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1128         {
1129                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1130                 goto fail;
1131         }
1132
1133         v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1134         s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1135         if(Cmd_Argc() == 5)
1136         {
1137                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1138                 if (cvar && cvar->flags & CVAR_READONLY)
1139                 {
1140                         Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1141                         goto fail;
1142                 }
1143                 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1144         }
1145         else
1146                 Con_Printf("%s\n", s);
1147
1148 fail:
1149         ;
1150 }
1151
1152 static void PRVM_ED_GlobalGet_f(void)
1153 {
1154         prvm_prog_t *prog;
1155         ddef_t *key;
1156         const char *s;
1157         prvm_eval_t *v;
1158         char valuebuf[MAX_INPUTLINE];
1159
1160         if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1161         {
1162                 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1163                 return;
1164         }
1165
1166         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1167                 return;
1168
1169         key = PRVM_ED_FindGlobal(prog, Cmd_Argv(2));
1170         if(!key)
1171         {
1172                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1173                 goto fail;
1174         }
1175
1176         v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1177         s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1178         if(Cmd_Argc() == 4)
1179         {
1180                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1181                 if (cvar && cvar->flags & CVAR_READONLY)
1182                 {
1183                         Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1184                         goto fail;
1185                 }
1186                 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1187         }
1188         else
1189                 Con_Printf("%s\n", s);
1190
1191 fail:
1192         ;
1193 }
1194
1195 /*
1196 =============
1197 PRVM_ED_EdictSet_f
1198
1199 Console command to set a field of a specified edict
1200 =============
1201 */
1202 static void PRVM_ED_EdictSet_f(void)
1203 {
1204         prvm_prog_t *prog;
1205         prvm_edict_t *ed;
1206         ddef_t *key;
1207
1208         if(Cmd_Argc() != 5)
1209         {
1210                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1211                 return;
1212         }
1213
1214         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
1215                 return;
1216
1217         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1218
1219         if((key = PRVM_ED_FindField(prog, Cmd_Argv(3))) == 0)
1220                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1221         else
1222                 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(4), true);
1223 }
1224
1225 /*
1226 ====================
1227 PRVM_ED_ParseEdict
1228
1229 Parses an edict out of the given string, returning the new position
1230 ed should be a properly initialized empty edict.
1231 Used for initial level load and for savegames.
1232 ====================
1233 */
1234 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1235 {
1236         ddef_t *key;
1237         qboolean anglehack;
1238         qboolean init;
1239         char keyname[256];
1240         size_t n;
1241
1242         init = false;
1243
1244 // go through all the dictionary pairs
1245         while (1)
1246         {
1247         // parse key
1248                 if (!COM_ParseToken_Simple(&data, false, false, true))
1249                         prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1250                 if (developer_entityparsing.integer)
1251                         Con_Printf("Key: \"%s\"", com_token);
1252                 if (com_token[0] == '}')
1253                         break;
1254
1255                 // anglehack is to allow QuakeEd to write single scalar angles
1256                 // and allow them to be turned into vectors. (FIXME...)
1257                 if (!strcmp(com_token, "angle"))
1258                 {
1259                         strlcpy (com_token, "angles", sizeof(com_token));
1260                         anglehack = true;
1261                 }
1262                 else
1263                         anglehack = false;
1264
1265                 // FIXME: change light to _light to get rid of this hack
1266                 if (!strcmp(com_token, "light"))
1267                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1268
1269                 strlcpy (keyname, com_token, sizeof(keyname));
1270
1271                 // another hack to fix keynames with trailing spaces
1272                 n = strlen(keyname);
1273                 while (n && keyname[n-1] == ' ')
1274                 {
1275                         keyname[n-1] = 0;
1276                         n--;
1277                 }
1278
1279         // parse value
1280                 if (!COM_ParseToken_Simple(&data, false, false, true))
1281                         prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1282                 if (developer_entityparsing.integer)
1283                         Con_Printf(" \"%s\"\n", com_token);
1284
1285                 if (com_token[0] == '}')
1286                         prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1287
1288                 init = true;
1289
1290                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1291                 if (!keyname[0])
1292                         continue;
1293
1294 // keynames with a leading underscore are used for utility comments,
1295 // and are immediately discarded by quake
1296                 if (keyname[0] == '_')
1297                         continue;
1298
1299                 key = PRVM_ED_FindField (prog, keyname);
1300                 if (!key)
1301                 {
1302                         Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1303                         continue;
1304                 }
1305
1306                 if (anglehack)
1307                 {
1308                         char    temp[32];
1309                         strlcpy (temp, com_token, sizeof(temp));
1310                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1311                 }
1312
1313                 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1314                         prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1315         }
1316
1317         if (!init)
1318                 ent->priv.required->free = true;
1319
1320         return data;
1321 }
1322
1323
1324 /*
1325 ================
1326 PRVM_ED_LoadFromFile
1327
1328 The entities are directly placed in the array, rather than allocated with
1329 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1330 number references out of order.
1331
1332 Creates a server's entity / program execution context by
1333 parsing textual entity definitions out of an ent file.
1334
1335 Used for both fresh maps and savegame loads.  A fresh map would also need
1336 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1337 ================
1338 */
1339 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1340 {
1341         prvm_edict_t *ent;
1342         int parsed, inhibited, spawned, died;
1343         const char *funcname;
1344         mfunction_t *func;
1345         char vabuf[1024];
1346
1347         parsed = 0;
1348         inhibited = 0;
1349         spawned = 0;
1350         died = 0;
1351
1352         prvm_reuseedicts_always_allow = realtime;
1353
1354 // parse ents
1355         while (1)
1356         {
1357 // parse the opening brace
1358                 if (!COM_ParseToken_Simple(&data, false, false, true))
1359                         break;
1360                 if (com_token[0] != '{')
1361                         prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1362
1363                 // CHANGED: this is not conform to PR_LoadFromFile
1364                 if(prog->loadintoworld)
1365                 {
1366                         prog->loadintoworld = false;
1367                         ent = PRVM_EDICT_NUM(0);
1368                 }
1369                 else
1370                         ent = PRVM_ED_Alloc(prog);
1371
1372                 // clear it
1373                 if (ent != prog->edicts)        // hack
1374                         memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1375
1376                 data = PRVM_ED_ParseEdict (prog, data, ent);
1377                 parsed++;
1378
1379                 // remove the entity ?
1380                 if(!prog->load_edict(prog, ent))
1381                 {
1382                         PRVM_ED_Free(prog, ent);
1383                         inhibited++;
1384                         continue;
1385                 }
1386
1387                 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1388                 {
1389                         // self = ent
1390                         PRVM_serverglobalfloat(time) = sv.time;
1391                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1392                         prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1393                 }
1394
1395                 if(ent->priv.required->free)
1396                 {
1397                         inhibited++;
1398                         continue;
1399                 }
1400
1401 //
1402 // immediately call spawn function, but only if there is a self global and a classname
1403 //
1404                 if(!ent->priv.required->free)
1405                 {
1406                         if (!PRVM_alledictstring(ent, classname))
1407                         {
1408                                 Con_Print("No classname for:\n");
1409                                 PRVM_ED_Print(prog, ent, NULL);
1410                                 PRVM_ED_Free (prog, ent);
1411                                 continue;
1412                         }
1413
1414                         // look for the spawn function
1415                         funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1416                         func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1417                         if(!func)
1418                                 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1419                                         func = PRVM_ED_FindFunction (prog, funcname);
1420
1421                         if (!func)
1422                         {
1423                                 // check for OnEntityNoSpawnFunction
1424                                 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1425                                 {
1426                                         // self = ent
1427                                         PRVM_serverglobalfloat(time) = sv.time;
1428                                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1429                                         prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1430                                 }
1431                                 else
1432                                 {
1433                                         if (developer.integer > 0) // don't confuse non-developers with errors
1434                                         {
1435                                                 Con_Print("No spawn function for:\n");
1436                                                 PRVM_ED_Print(prog, ent, NULL);
1437                                         }
1438                                         PRVM_ED_Free (prog, ent);
1439                                         continue; // not included in "inhibited" count
1440                                 }
1441                         }
1442                         else
1443                         {
1444                                 // self = ent
1445                                 PRVM_serverglobalfloat(time) = sv.time;
1446                                 PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1447                                 prog->ExecuteProgram(prog, func - prog->functions, "");
1448                         }
1449                 }
1450
1451                 if(!ent->priv.required->free)
1452                 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1453                 {
1454                         // self = ent
1455                         PRVM_serverglobalfloat(time) = sv.time;
1456                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1457                         prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1458                 }
1459
1460                 spawned++;
1461                 if (ent->priv.required->free)
1462                         died++;
1463         }
1464
1465         Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", prog->name, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1466
1467         prvm_reuseedicts_always_allow = 0;
1468 }
1469
1470 static void PRVM_FindOffsets(prvm_prog_t *prog)
1471 {
1472         // field and global searches use -1 for NULL
1473         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1474         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1475         // function searches use 0 for NULL
1476         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1477 #define PRVM_DECLARE_serverglobalfloat(x)
1478 #define PRVM_DECLARE_serverglobalvector(x)
1479 #define PRVM_DECLARE_serverglobalstring(x)
1480 #define PRVM_DECLARE_serverglobaledict(x)
1481 #define PRVM_DECLARE_serverglobalfunction(x)
1482 #define PRVM_DECLARE_clientglobalfloat(x)
1483 #define PRVM_DECLARE_clientglobalvector(x)
1484 #define PRVM_DECLARE_clientglobalstring(x)
1485 #define PRVM_DECLARE_clientglobaledict(x)
1486 #define PRVM_DECLARE_clientglobalfunction(x)
1487 #define PRVM_DECLARE_menuglobalfloat(x)
1488 #define PRVM_DECLARE_menuglobalvector(x)
1489 #define PRVM_DECLARE_menuglobalstring(x)
1490 #define PRVM_DECLARE_menuglobaledict(x)
1491 #define PRVM_DECLARE_menuglobalfunction(x)
1492 #define PRVM_DECLARE_serverfieldfloat(x)
1493 #define PRVM_DECLARE_serverfieldvector(x)
1494 #define PRVM_DECLARE_serverfieldstring(x)
1495 #define PRVM_DECLARE_serverfieldedict(x)
1496 #define PRVM_DECLARE_serverfieldfunction(x)
1497 #define PRVM_DECLARE_clientfieldfloat(x)
1498 #define PRVM_DECLARE_clientfieldvector(x)
1499 #define PRVM_DECLARE_clientfieldstring(x)
1500 #define PRVM_DECLARE_clientfieldedict(x)
1501 #define PRVM_DECLARE_clientfieldfunction(x)
1502 #define PRVM_DECLARE_menufieldfloat(x)
1503 #define PRVM_DECLARE_menufieldvector(x)
1504 #define PRVM_DECLARE_menufieldstring(x)
1505 #define PRVM_DECLARE_menufieldedict(x)
1506 #define PRVM_DECLARE_menufieldfunction(x)
1507 #define PRVM_DECLARE_serverfunction(x)
1508 #define PRVM_DECLARE_clientfunction(x)
1509 #define PRVM_DECLARE_menufunction(x)
1510 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1511 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1512 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1513 #include "prvm_offsets.h"
1514 #undef PRVM_DECLARE_serverglobalfloat
1515 #undef PRVM_DECLARE_serverglobalvector
1516 #undef PRVM_DECLARE_serverglobalstring
1517 #undef PRVM_DECLARE_serverglobaledict
1518 #undef PRVM_DECLARE_serverglobalfunction
1519 #undef PRVM_DECLARE_clientglobalfloat
1520 #undef PRVM_DECLARE_clientglobalvector
1521 #undef PRVM_DECLARE_clientglobalstring
1522 #undef PRVM_DECLARE_clientglobaledict
1523 #undef PRVM_DECLARE_clientglobalfunction
1524 #undef PRVM_DECLARE_menuglobalfloat
1525 #undef PRVM_DECLARE_menuglobalvector
1526 #undef PRVM_DECLARE_menuglobalstring
1527 #undef PRVM_DECLARE_menuglobaledict
1528 #undef PRVM_DECLARE_menuglobalfunction
1529 #undef PRVM_DECLARE_serverfieldfloat
1530 #undef PRVM_DECLARE_serverfieldvector
1531 #undef PRVM_DECLARE_serverfieldstring
1532 #undef PRVM_DECLARE_serverfieldedict
1533 #undef PRVM_DECLARE_serverfieldfunction
1534 #undef PRVM_DECLARE_clientfieldfloat
1535 #undef PRVM_DECLARE_clientfieldvector
1536 #undef PRVM_DECLARE_clientfieldstring
1537 #undef PRVM_DECLARE_clientfieldedict
1538 #undef PRVM_DECLARE_clientfieldfunction
1539 #undef PRVM_DECLARE_menufieldfloat
1540 #undef PRVM_DECLARE_menufieldvector
1541 #undef PRVM_DECLARE_menufieldstring
1542 #undef PRVM_DECLARE_menufieldedict
1543 #undef PRVM_DECLARE_menufieldfunction
1544 #undef PRVM_DECLARE_serverfunction
1545 #undef PRVM_DECLARE_clientfunction
1546 #undef PRVM_DECLARE_menufunction
1547 #undef PRVM_DECLARE_field
1548 #undef PRVM_DECLARE_global
1549 #undef PRVM_DECLARE_function
1550 }
1551
1552 // not used
1553 /*
1554 typedef struct dpfield_s
1555 {
1556         int type;
1557         char *string;
1558 }
1559 dpfield_t;
1560
1561 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1562
1563 dpfield_t dpfields[] =
1564 {
1565 };
1566 */
1567
1568 /*
1569 ===============
1570 PRVM_ResetProg
1571 ===============
1572 */
1573
1574 #define PO_HASHSIZE 16384
1575 typedef struct po_string_s
1576 {
1577         char *key, *value;
1578         struct po_string_s *nextonhashchain;
1579 }
1580 po_string_t;
1581 typedef struct po_s
1582 {
1583         po_string_t *hashtable[PO_HASHSIZE];
1584 }
1585 po_t;
1586 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1587 {
1588         for(;;)
1589         {
1590                 switch(*in)
1591                 {
1592                         case 0:
1593                                 *out++ = 0;
1594                                 return;
1595                         case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1596                         case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1597                         case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1598                         case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1599                         case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1600                         case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1601                         case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1602                         default:
1603                                 if(*in >= 0 && *in <= 0x1F)
1604                                 {
1605                                         if(outsize >= 4)
1606                                         {
1607                                                 *out++ = '\\';
1608                                                 *out++ = '0' + ((*in & 0700) >> 6);
1609                                                 *out++ = '0' + ((*in & 0070) >> 3);
1610                                                 *out++ = '0' +  (*in & 0007)      ;
1611                                                 outsize -= 4;
1612                                         }
1613                                 }
1614                                 else
1615                                 {
1616                                         if(outsize >= 1)
1617                                         {
1618                                                 *out++ = *in;
1619                                                 outsize -= 1;
1620                                         }
1621                                 }
1622                                 break;
1623                 }
1624                 ++in;
1625         }
1626 }
1627 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1628 {
1629         for(;;)
1630         {
1631                 switch(*in)
1632                 {
1633                         case 0:
1634                                 *out++ = 0;
1635                                 return;
1636                         case '\\':
1637                                 ++in;
1638                                 switch(*in)
1639                                 {
1640                                         case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1641                                         case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1642                                         case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1643                                         case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1644                                         case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1645                                         case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1646                                         case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1647                                         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1648                                                 if(outsize > 0) 
1649                                                         *out = *in - '0';
1650                                                 ++in;
1651                                                 if(*in >= '0' && *in <= '7')
1652                                                 {
1653                                                         if(outsize > 0)
1654                                                                 *out = (*out << 3) | (*in - '0');
1655                                                         ++in;
1656                                                 }
1657                                                 if(*in >= '0' && *in <= '7')
1658                                                 {
1659                                                         if(outsize > 0)
1660                                                                 *out = (*out << 3) | (*in - '0');
1661                                                         ++in;
1662                                                 }
1663                                                 --in;
1664                                                 if(outsize > 0)
1665                                                 {
1666                                                         ++out;
1667                                                         --outsize;
1668                                                 }
1669                                                 break;
1670                                         default:
1671                                                 if(outsize > 0) { *out++ = *in; --outsize; }
1672                                                 break;
1673                                 }
1674                                 break;
1675                         default:
1676                                 if(outsize > 0)
1677                                 {
1678                                         *out++ = *in;
1679                                         --outsize;
1680                                 }
1681                                 break;
1682                 }
1683                 ++in;
1684         }
1685 }
1686 static po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1687 {
1688         po_t *po;
1689         const char *p, *q;
1690         int mode;
1691         char inbuf[MAX_INPUTLINE];
1692         char decodedbuf[MAX_INPUTLINE];
1693         size_t decodedpos;
1694         int hashindex;
1695         po_string_t thisstr;
1696         const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1697
1698         if(!buf)
1699                 return NULL;
1700
1701         memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1702
1703         po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1704         memset(po, 0, sizeof(*po));
1705
1706         p = buf;
1707         while(*p)
1708         {
1709                 if(*p == '#')
1710                 {
1711                         // skip to newline
1712                         p = strchr(p, '\n');
1713                         if(!p)
1714                                 break;
1715                         ++p;
1716                         continue;
1717                 }
1718                 if(*p == '\r' || *p == '\n')
1719                 {
1720                         ++p;
1721                         continue;
1722                 }
1723                 if(!strncmp(p, "msgid \"", 7))
1724                 {
1725                         mode = 0;
1726                         p += 6;
1727                 }
1728                 else if(!strncmp(p, "msgstr \"", 8))
1729                 {
1730                         mode = 1;
1731                         p += 7;
1732                 }
1733                 else
1734                 {
1735                         p = strchr(p, '\n');
1736                         if(!p)
1737                                 break;
1738                         ++p;
1739                         continue;
1740                 }
1741                 decodedpos = 0;
1742                 while(*p == '"')
1743                 {
1744                         ++p;
1745                         q = strchr(p, '\n');
1746                         if(!q)
1747                                 break;
1748                         if(*(q-1) == '\r')
1749                                 --q;
1750                         if(*(q-1) != '"')
1751                                 break;
1752                         if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1753                                 break;
1754                         strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1755                         PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1756                         decodedpos += strlen(decodedbuf + decodedpos);
1757                         if(*q == '\r')
1758                                 ++q;
1759                         if(*q == '\n')
1760                                 ++q;
1761                         p = q;
1762                 }
1763                 if(mode == 0)
1764                 {
1765                         if(thisstr.key)
1766                                 Mem_Free(thisstr.key);
1767                         thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1768                         memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1769                 }
1770                 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1771                 {
1772                         thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1773                         memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1774                         hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1775                         thisstr.nextonhashchain = po->hashtable[hashindex];
1776                         po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1777                         memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1778                         memset(&thisstr, 0, sizeof(thisstr));
1779                 }
1780         }
1781         
1782         Mem_Free((char *) buf);
1783         return po;
1784 }
1785 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1786 {
1787         int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1788         po_string_t *p = po->hashtable[hashindex];
1789         while(p)
1790         {
1791                 if(!strcmp(str, p->key))
1792                         return p->value;
1793                 p = p->nextonhashchain;
1794         }
1795         return NULL;
1796 }
1797 static void PRVM_PO_Destroy(po_t *po)
1798 {
1799         int i;
1800         for(i = 0; i < PO_HASHSIZE; ++i)
1801         {
1802                 po_string_t *p = po->hashtable[i];
1803                 while(p)
1804                 {
1805                         po_string_t *q = p;
1806                         p = p->nextonhashchain;
1807                         Mem_Free(q->key);
1808                         Mem_Free(q->value);
1809                         Mem_Free(q);
1810                 }
1811         }
1812         Mem_Free(po);
1813 }
1814
1815 void PRVM_LeakTest(prvm_prog_t *prog);
1816 void PRVM_Prog_Reset(prvm_prog_t *prog)
1817 {
1818         if (prog->loaded)
1819         {
1820                 PRVM_LeakTest(prog);
1821                 prog->reset_cmd(prog);
1822                 Mem_FreePool(&prog->progs_mempool);
1823                 if(prog->po)
1824                         PRVM_PO_Destroy((po_t *) prog->po);
1825         }
1826         memset(prog,0,sizeof(prvm_prog_t));
1827         prog->break_statement = -1;
1828         prog->watch_global_type = ev_void;
1829         prog->watch_field_type = ev_void;
1830 }
1831
1832 /*
1833 ===============
1834 PRVM_LoadLNO
1835 ===============
1836 */
1837 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1838         fs_offset_t filesize;
1839         unsigned char *lno;
1840         unsigned int *header;
1841         char filename[512];
1842
1843         FS_StripExtension( progname, filename, sizeof( filename ) );
1844         strlcat( filename, ".lno", sizeof( filename ) );
1845
1846         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1847         if( !lno ) {
1848                 return;
1849         }
1850
1851 /*
1852 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1853 <Spike>    SafeWrite (h, &version, sizeof(int));
1854 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1855 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1856 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1857 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1858 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1859 */
1860         if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1861         {
1862                 Mem_Free(lno);
1863                 return;
1864         }
1865
1866         header = (unsigned int *) lno;
1867         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1868                 LittleLong( header[ 1 ] ) == 1 &&
1869                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1870                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1871                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1872                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1873         {
1874                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1875                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
1876         }
1877         Mem_Free( lno );
1878 }
1879
1880 /*
1881 ===============
1882 PRVM_LoadProgs
1883 ===============
1884 */
1885 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1886 void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * data, fs_offset_t size, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global)
1887 {
1888         int i;
1889         dprograms_t *dprograms;
1890         dstatement_t *instatements;
1891         ddef_t *infielddefs;
1892         ddef_t *inglobaldefs;
1893         int *inglobals;
1894         dfunction_t *infunctions;
1895         char *instrings;
1896         fs_offset_t filesize;
1897         int requiredglobalspace;
1898         opcode_t op;
1899         int a;
1900         int b;
1901         int c;
1902         union
1903         {
1904                 unsigned int i;
1905                 float f;
1906         }
1907         u;
1908         unsigned int d;
1909         char vabuf[1024];
1910
1911         if (prog->loaded)
1912                 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1913
1914         Host_LockSession(); // all progs can use the session cvar
1915         Crypto_LoadKeys(); // all progs might use the keys at init time
1916
1917         if (data)
1918         {
1919                 dprograms = (dprograms_t *) data;
1920                 filesize = size;
1921         }
1922         else
1923                 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1924         if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1925                 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1926         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1927
1928         prog->profiletime = Sys_DirtyTime();
1929         prog->starttime = realtime;
1930
1931         Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1932
1933         requiredglobalspace = 0;
1934         for (i = 0;i < numrequiredglobals;i++)
1935                 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1936
1937         prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1938
1939 // byte swap the header
1940         prog->progs_version = LittleLong(dprograms->version);
1941         prog->progs_crc = LittleLong(dprograms->crc);
1942         if (prog->progs_version != PROG_VERSION)
1943                 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1944         instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1945         prog->progs_numstatements = LittleLong(dprograms->numstatements);
1946         inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1947         prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1948         infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1949         prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1950         infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1951         prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1952         instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1953         prog->progs_numstrings = LittleLong(dprograms->numstrings);
1954         inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1955         prog->progs_numglobals = LittleLong(dprograms->numglobals);
1956         prog->progs_entityfields = LittleLong(dprograms->entityfields);
1957
1958         prog->numstatements = prog->progs_numstatements;
1959         prog->numglobaldefs = prog->progs_numglobaldefs;
1960         prog->numfielddefs = prog->progs_numfielddefs;
1961         prog->numfunctions = prog->progs_numfunctions;
1962         prog->numstrings = prog->progs_numstrings;
1963         prog->numglobals = prog->progs_numglobals;
1964         prog->entityfields = prog->progs_entityfields;
1965
1966         if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1967                 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1968         prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1969         memcpy(prog->strings, instrings, prog->progs_numstrings);
1970         prog->stringssize = prog->progs_numstrings;
1971
1972         prog->numknownstrings = 0;
1973         prog->maxknownstrings = 0;
1974         prog->knownstrings = NULL;
1975         prog->knownstrings_freeable = NULL;
1976
1977         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1978
1979         // we need to expand the globaldefs and fielddefs to include engine defs
1980         prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1981         prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
1982                 // + 2 is because of an otherwise occurring overrun in RETURN instruction
1983                 // when trying to return the last or second-last global
1984                 // (RETURN always returns a vector, there is no RETURN_F instruction)
1985         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
1986         // we need to convert the statements to our memory format
1987         prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
1988         // allocate space for profiling statement usage
1989         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
1990         // functions need to be converted to the memory format
1991         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
1992
1993         for (i = 0;i < prog->progs_numfunctions;i++)
1994         {
1995                 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
1996                 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
1997                 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
1998                 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
1999                 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2000                 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2001                 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2002                 if(prog->functions[i].first_statement >= prog->numstatements)
2003                         prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2004                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2005         }
2006
2007         // copy the globaldefs to the new globaldefs list
2008         for (i=0 ; i<prog->numglobaldefs ; i++)
2009         {
2010                 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2011                 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2012                 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2013                 // TODO bounds check ofs, s_name
2014         }
2015
2016         // append the required globals
2017         for (i = 0;i < numrequiredglobals;i++)
2018         {
2019                 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2020                 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2021                 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2022                 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2023                         prog->numglobals += 3;
2024                 else
2025                         prog->numglobals++;
2026                 prog->numglobaldefs++;
2027         }
2028
2029         // copy the progs fields to the new fields list
2030         for (i = 0;i < prog->numfielddefs;i++)
2031         {
2032                 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2033                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2034                         prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2035                 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2036                 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2037                 // TODO bounds check ofs, s_name
2038         }
2039
2040         // append the required fields
2041         for (i = 0;i < numrequiredfields;i++)
2042         {
2043                 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2044                 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2045                 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2046                 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2047                         prog->entityfields += 3;
2048                 else
2049                         prog->entityfields++;
2050                 prog->numfielddefs++;
2051         }
2052
2053         // LordHavoc: TODO: reorder globals to match engine struct
2054         // LordHavoc: TODO: reorder fields to match engine struct
2055 #define remapglobal(index) (index)
2056 #define remapfield(index) (index)
2057
2058         // copy globals
2059         // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2060         for (i = 0;i < prog->progs_numglobals;i++)
2061         {
2062                 u.i = LittleLong(inglobals[i]);
2063                 // most globals are 0, we only need to deal with the ones that are not
2064                 if (u.i)
2065                 {
2066                         d = u.i & 0xFF800000;
2067                         if ((d == 0xFF800000) || (d == 0))
2068                         {
2069                                 // Looks like an integer (expand to int64)
2070                                 prog->globals.ip[remapglobal(i)] = u.i;
2071                         }
2072                         else
2073                         {
2074                                 // Looks like a float (expand to double)
2075                                 prog->globals.fp[remapglobal(i)] = u.f;
2076                         }
2077                 }
2078         }
2079
2080         // LordHavoc: TODO: support 32bit progs statement formats
2081         // copy, remap globals in statements, bounds check
2082         for (i = 0;i < prog->progs_numstatements;i++)
2083         {
2084                 op = (opcode_t)LittleShort(instatements[i].op);
2085                 a = (unsigned short)LittleShort(instatements[i].a);
2086                 b = (unsigned short)LittleShort(instatements[i].b);
2087                 c = (unsigned short)LittleShort(instatements[i].c);
2088                 switch (op)
2089                 {
2090                 case OP_IF:
2091                 case OP_IFNOT:
2092                         b = (short)b;
2093                         if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2094                                 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2095                         prog->statements[i].op = op;
2096                         prog->statements[i].operand[0] = remapglobal(a);
2097                         prog->statements[i].operand[1] = -1;
2098                         prog->statements[i].operand[2] = -1;
2099                         prog->statements[i].jumpabsolute = i + b;
2100                         break;
2101                 case OP_GOTO:
2102                         a = (short)a;
2103                         if (a + i < 0 || a + i >= prog->progs_numstatements)
2104                                 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2105                         prog->statements[i].op = op;
2106                         prog->statements[i].operand[0] = -1;
2107                         prog->statements[i].operand[1] = -1;
2108                         prog->statements[i].operand[2] = -1;
2109                         prog->statements[i].jumpabsolute = i + a;
2110                         break;
2111                 default:
2112                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2113                 // global global global
2114                 case OP_ADD_F:
2115                 case OP_ADD_V:
2116                 case OP_SUB_F:
2117                 case OP_SUB_V:
2118                 case OP_MUL_F:
2119                 case OP_MUL_V:
2120                 case OP_MUL_FV:
2121                 case OP_MUL_VF:
2122                 case OP_DIV_F:
2123                 case OP_BITAND:
2124                 case OP_BITOR:
2125                 case OP_GE:
2126                 case OP_LE:
2127                 case OP_GT:
2128                 case OP_LT:
2129                 case OP_AND:
2130                 case OP_OR:
2131                 case OP_EQ_F:
2132                 case OP_EQ_V:
2133                 case OP_EQ_S:
2134                 case OP_EQ_E:
2135                 case OP_EQ_FNC:
2136                 case OP_NE_F:
2137                 case OP_NE_V:
2138                 case OP_NE_S:
2139                 case OP_NE_E:
2140                 case OP_NE_FNC:
2141                 case OP_ADDRESS:
2142                 case OP_LOAD_F:
2143                 case OP_LOAD_FLD:
2144                 case OP_LOAD_ENT:
2145                 case OP_LOAD_S:
2146                 case OP_LOAD_FNC:
2147                 case OP_LOAD_V:
2148                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2149                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2150                         prog->statements[i].op = op;
2151                         prog->statements[i].operand[0] = remapglobal(a);
2152                         prog->statements[i].operand[1] = remapglobal(b);
2153                         prog->statements[i].operand[2] = remapglobal(c);
2154                         prog->statements[i].jumpabsolute = -1;
2155                         break;
2156                 // global none global
2157                 case OP_NOT_F:
2158                 case OP_NOT_V:
2159                 case OP_NOT_S:
2160                 case OP_NOT_FNC:
2161                 case OP_NOT_ENT:
2162                         if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2163                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2164                         prog->statements[i].op = op;
2165                         prog->statements[i].operand[0] = remapglobal(a);
2166                         prog->statements[i].operand[1] = -1;
2167                         prog->statements[i].operand[2] = remapglobal(c);
2168                         prog->statements[i].jumpabsolute = -1;
2169                         break;
2170                 // 2 globals
2171                 case OP_STOREP_F:
2172                 case OP_STOREP_ENT:
2173                 case OP_STOREP_FLD:
2174                 case OP_STOREP_S:
2175                 case OP_STOREP_FNC:
2176                 case OP_STORE_F:
2177                 case OP_STORE_ENT:
2178                 case OP_STORE_FLD:
2179                 case OP_STORE_S:
2180                 case OP_STORE_FNC:
2181                 case OP_STATE:
2182                 case OP_STOREP_V:
2183                 case OP_STORE_V:
2184                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2185                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2186                         prog->statements[i].op = op;
2187                         prog->statements[i].operand[0] = remapglobal(a);
2188                         prog->statements[i].operand[1] = remapglobal(b);
2189                         prog->statements[i].operand[2] = -1;
2190                         prog->statements[i].jumpabsolute = -1;
2191                         break;
2192                 // 1 global
2193                 case OP_CALL0:
2194                 case OP_CALL1:
2195                 case OP_CALL2:
2196                 case OP_CALL3:
2197                 case OP_CALL4:
2198                 case OP_CALL5:
2199                 case OP_CALL6:
2200                 case OP_CALL7:
2201                 case OP_CALL8:
2202                 case OP_DONE:
2203                 case OP_RETURN:
2204                         if ( a >= prog->progs_numglobals)
2205                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2206                         prog->statements[i].op = op;
2207                         prog->statements[i].operand[0] = remapglobal(a);
2208                         prog->statements[i].operand[1] = -1;
2209                         prog->statements[i].operand[2] = -1;
2210                         prog->statements[i].jumpabsolute = -1;
2211                         break;
2212                 }
2213         }
2214         if(prog->numstatements < 1)
2215         {
2216                 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2217         }
2218         else switch(prog->statements[prog->numstatements - 1].op)
2219         {
2220                 case OP_RETURN:
2221                 case OP_GOTO:
2222                 case OP_DONE:
2223                         break;
2224                 default:
2225                         prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2226                         break;
2227         }
2228
2229         // we're done with the file now
2230         if(!data)
2231                 Mem_Free(dprograms);
2232         dprograms = NULL;
2233
2234         // check required functions
2235         for(i=0 ; i < numrequiredfunc ; i++)
2236                 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2237                         prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2238
2239         PRVM_LoadLNO(prog, filename);
2240
2241         PRVM_Init_Exec(prog);
2242
2243         if(*prvm_language.string)
2244         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2245         // later idea: include a list of authorized .po file checksums with the csprogs
2246         {
2247                 qboolean deftrans = prog == CLVM_prog;
2248                 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2249                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2250                 {
2251                         for (i=0 ; i<prog->numglobaldefs ; i++)
2252                         {
2253                                 const char *name;
2254                                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2255                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2256                                 if(name && !strncmp(name, "dotranslate_", 12))
2257                                 {
2258                                         deftrans = false;
2259                                         break;
2260                                 }
2261                         }
2262                 }
2263                 if(!strcmp(prvm_language.string, "dump"))
2264                 {
2265                         qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2266                         Con_Printf("Dumping to %s.pot\n", realfilename);
2267                         if(f)
2268                         {
2269                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2270                                 {
2271                                         const char *name;
2272                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2273                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2274                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2275                                         {
2276                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2277                                                 const char *value = PRVM_GetString(prog, val->string);
2278                                                 if(*value)
2279                                                 {
2280                                                         char buf[MAX_INPUTLINE];
2281                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2282                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2283                                                 }
2284                                         }
2285                                 }
2286                                 FS_Close(f);
2287                         }
2288                 }
2289                 else
2290                 {
2291                         po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2292                         if(po)
2293                         {
2294                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2295                                 {
2296                                         const char *name;
2297                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2298                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2299                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2300                                         {
2301                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2302                                                 const char *value = PRVM_GetString(prog, val->string);
2303                                                 if(*value)
2304                                                 {
2305                                                         value = PRVM_PO_Lookup(po, value);
2306                                                         if(value)
2307                                                                 val->string = PRVM_SetEngineString(prog, value);
2308                                                 }
2309                                         }
2310                                 }
2311                         }
2312                 }
2313         }
2314
2315         for (i=0 ; i<prog->numglobaldefs ; i++)
2316         {
2317                 const char *name;
2318                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2319                 //Con_Printf("found var %s\n", name);
2320                 if(name
2321                         && !strncmp(name, "autocvar_", 9)
2322                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2323                 )
2324                 {
2325                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2326                         cvar_t *cvar = Cvar_FindVar(name + 9);
2327                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2328                         if(!cvar)
2329                         {
2330                                 const char *value;
2331                                 char buf[64];
2332                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2333                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2334                                 {
2335                                         case ev_float:
2336                                                 if((float)((int)(val->_float)) == val->_float)
2337                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2338                                                 else
2339                                                         dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2340                                                 value = buf;
2341                                                 break;
2342                                         case ev_vector:
2343                                                 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2344                                                 break;
2345                                         case ev_string:
2346                                                 value = PRVM_GetString(prog, val->string);
2347                                                 break;
2348                                         default:
2349                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2350                                                 goto fail;
2351                                 }
2352                                 cvar = Cvar_Get(name + 9, value, 0, NULL);
2353                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2354                                 {
2355                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2356                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2357                                 }
2358                                 if(!cvar)
2359                                         prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2360                                 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2361                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2362                         }
2363                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2364                         {
2365                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2366                                 int j;
2367                                 const char *s;
2368                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2369                                 {
2370                                         case ev_float:
2371                                                 val->_float = cvar->value;
2372                                                 break;
2373                                         case ev_vector:
2374                                                 s = cvar->string;
2375                                                 VectorClear(val->vector);
2376                                                 for (j = 0;j < 3;j++)
2377                                                 {
2378                                                         while (*s && ISWHITESPACE(*s))
2379                                                                 s++;
2380                                                         if (!*s)
2381                                                                 break;
2382                                                         val->vector[j] = atof(s);
2383                                                         while (!ISWHITESPACE(*s))
2384                                                                 s++;
2385                                                         if (!*s)
2386                                                                 break;
2387                                                 }
2388                                                 break;
2389                                         case ev_string:
2390                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2391                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2392                                                 break;
2393                                         default:
2394                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2395                                                 goto fail;
2396                                 }
2397                                 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2398                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2399                         }
2400                         else
2401                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2402                 }
2403 fail:
2404                 ;
2405         }
2406
2407         prog->loaded = TRUE;
2408
2409         PRVM_UpdateBreakpoints(prog);
2410
2411         // set flags & ddef_ts in prog
2412
2413         prog->flag = 0;
2414
2415         PRVM_FindOffsets(prog);
2416
2417         prog->init_cmd(prog);
2418
2419         // init mempools
2420         PRVM_MEM_Alloc(prog);
2421 }
2422
2423
2424 static void PRVM_Fields_f (void)
2425 {
2426         prvm_prog_t *prog;
2427         int i, j, ednum, used, usedamount;
2428         int *counts;
2429         char tempstring[MAX_INPUTLINE], tempstring2[260];
2430         const char *name;
2431         prvm_edict_t *ed;
2432         ddef_t *d;
2433         prvm_eval_t *val;
2434
2435         // TODO
2436         /*
2437         if (!sv.active)
2438         {
2439                 Con_Print("no progs loaded\n");
2440                 return;
2441         }
2442         */
2443
2444         if(Cmd_Argc() != 2)
2445         {
2446                 Con_Print("prvm_fields <program name>\n");
2447                 return;
2448         }
2449
2450         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2451                 return;
2452
2453         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2454         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2455         {
2456                 ed = PRVM_EDICT_NUM(ednum);
2457                 if (ed->priv.required->free)
2458                         continue;
2459                 for (i = 1;i < prog->numfielddefs;i++)
2460                 {
2461                         d = &prog->fielddefs[i];
2462                         name = PRVM_GetString(prog, d->s_name);
2463                         if (name[strlen(name)-2] == '_')
2464                                 continue;       // skip _x, _y, _z vars
2465                         val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2466                         // if the value is still all 0, skip the field
2467                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2468                         {
2469                                 if (val->ivector[j])
2470                                 {
2471                                         counts[i]++;
2472                                         break;
2473                                 }
2474                         }
2475                 }
2476         }
2477         used = 0;
2478         usedamount = 0;
2479         tempstring[0] = 0;
2480         for (i = 0;i < prog->numfielddefs;i++)
2481         {
2482                 d = &prog->fielddefs[i];
2483                 name = PRVM_GetString(prog, d->s_name);
2484                 if (name[strlen(name)-2] == '_')
2485                         continue;       // skip _x, _y, _z vars
2486                 switch(d->type & ~DEF_SAVEGLOBAL)
2487                 {
2488                 case ev_string:
2489                         strlcat(tempstring, "string   ", sizeof(tempstring));
2490                         break;
2491                 case ev_entity:
2492                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2493                         break;
2494                 case ev_function:
2495                         strlcat(tempstring, "function ", sizeof(tempstring));
2496                         break;
2497                 case ev_field:
2498                         strlcat(tempstring, "field    ", sizeof(tempstring));
2499                         break;
2500                 case ev_void:
2501                         strlcat(tempstring, "void     ", sizeof(tempstring));
2502                         break;
2503                 case ev_float:
2504                         strlcat(tempstring, "float    ", sizeof(tempstring));
2505                         break;
2506                 case ev_vector:
2507                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2508                         break;
2509                 case ev_pointer:
2510                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2511                         break;
2512                 default:
2513                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2514                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2515                         break;
2516                 }
2517                 if (strlen(name) > sizeof(tempstring2)-4)
2518                 {
2519                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2520                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2521                         tempstring2[sizeof(tempstring2)-1] = 0;
2522                         name = tempstring2;
2523                 }
2524                 strlcat(tempstring, name, sizeof(tempstring));
2525                 for (j = (int)strlen(name);j < 25;j++)
2526                         strlcat(tempstring, " ", sizeof(tempstring));
2527                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2528                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2529                 strlcat(tempstring, "\n", sizeof(tempstring));
2530                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2531                 {
2532                         Con_Print(tempstring);
2533                         tempstring[0] = 0;
2534                 }
2535                 if (counts[i])
2536                 {
2537                         used++;
2538                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2539                 }
2540         }
2541         Mem_Free(counts);
2542         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", prog->name, prog->entityfields, used, prog->entityfields * 4, usedamount * 4, prog->max_edicts, prog->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2543 }
2544
2545 static void PRVM_Globals_f (void)
2546 {
2547         prvm_prog_t *prog;
2548         int i;
2549         const char *wildcard;
2550         int numculled;
2551                 numculled = 0;
2552         // TODO
2553         /*if (!sv.active)
2554         {
2555                 Con_Print("no progs loaded\n");
2556                 return;
2557         }*/
2558         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2559         {
2560                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2561                 return;
2562         }
2563
2564         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2565                 return;
2566
2567         if( Cmd_Argc() == 3)
2568                 wildcard = Cmd_Argv(2);
2569         else
2570                 wildcard = NULL;
2571
2572         Con_Printf("%s :", prog->name);
2573
2574         for (i = 0;i < prog->numglobaldefs;i++)
2575         {
2576                 if(wildcard)
2577                         if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2578                         {
2579                                 numculled++;
2580                                 continue;
2581                         }
2582                 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2583         }
2584         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2585 }
2586
2587 /*
2588 ===============
2589 PRVM_Global
2590 ===============
2591 */
2592 static void PRVM_Global_f(void)
2593 {
2594         prvm_prog_t *prog;
2595         ddef_t *global;
2596         char valuebuf[MAX_INPUTLINE];
2597         if( Cmd_Argc() != 3 ) {
2598                 Con_Printf( "prvm_global <program name> <global name>\n" );
2599                 return;
2600         }
2601
2602         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2603                 return;
2604
2605         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2606         if( !global )
2607                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2608         else
2609                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2610 }
2611
2612 /*
2613 ===============
2614 PRVM_GlobalSet
2615 ===============
2616 */
2617 static void PRVM_GlobalSet_f(void)
2618 {
2619         prvm_prog_t *prog;
2620         ddef_t *global;
2621         if( Cmd_Argc() != 4 ) {
2622                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2623                 return;
2624         }
2625
2626         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2627                 return;
2628
2629         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2630         if( !global )
2631                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2632         else
2633                 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2634 }
2635
2636 /*
2637 ======================
2638 Break- and Watchpoints
2639 ======================
2640 */
2641 typedef struct
2642 {
2643         char break_statement[256];
2644         char watch_global[256];
2645         int watch_edict;
2646         char watch_field[256];
2647 }
2648 debug_data_t;
2649 static debug_data_t debug_data[PRVM_PROG_MAX];
2650
2651 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2652 {
2653         char vabuf[1024];
2654         Con_Printf("PRVM_Breakpoint: %s\n", text);
2655         PRVM_PrintState(prog, stack_index);
2656         if (prvm_breakpointdump.integer)
2657                 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2658 }
2659
2660 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2661 {
2662         size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2663         if (memcmp(o, n, sz))
2664         {
2665                 char buf[1024];
2666                 char valuebuf_o[128];
2667                 char valuebuf_n[128];
2668                 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2669                 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2670                 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2671                 PRVM_Breakpoint(prog, stack_index, buf);
2672                 memcpy(o, n, sz);
2673         }
2674 }
2675
2676 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2677 {
2678         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2679         if (!prog->loaded)
2680                 return;
2681         if (debug->break_statement[0])
2682         {
2683                 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2684                 {
2685                         prog->break_statement = atoi(debug->break_statement);
2686                         prog->break_stack_index = 0;
2687                 }
2688                 else
2689                 {
2690                         mfunction_t *func;
2691                         func = PRVM_ED_FindFunction (prog, debug->break_statement);
2692                         if (!func)
2693                         {
2694                                 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2695                                 prog->break_statement = -1;
2696                         }
2697                         else
2698                         {
2699                                 prog->break_statement = func->first_statement;
2700                                 prog->break_stack_index = 1;
2701                         }
2702                 }
2703                 if (prog->break_statement >= -1)
2704                         Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2705         }
2706         else
2707                 prog->break_statement = -1;
2708
2709         if (debug->watch_global[0])
2710         {
2711                 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2712                 if( !global )
2713                 {
2714                         Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2715                         prog->watch_global_type = ev_void;
2716                 }
2717                 else
2718                 {
2719                         size_t sz = sizeof(prvm_vec_t) * ((global->type  & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2720                         prog->watch_global = global->ofs;
2721                         prog->watch_global_type = global->type;
2722                         memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2723                 }
2724                 if (prog->watch_global_type != ev_void)
2725                         Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2726         }
2727         else
2728                 prog->watch_global_type = ev_void;
2729
2730         if (debug->watch_field[0])
2731         {
2732                 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2733                 if( !field )
2734                 {
2735                         Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2736                         prog->watch_field_type = ev_void;
2737                 }
2738                 else
2739                 {
2740                         size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2741                         prog->watch_edict = debug->watch_edict;
2742                         prog->watch_field = field->ofs;
2743                         prog->watch_field_type = field->type;
2744                         if (prog->watch_edict < prog->num_edicts)
2745                                 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2746                         else
2747                                 memset(&prog->watch_edictfield_value, 0, sz);
2748                 }
2749                 if (prog->watch_edict != ev_void)
2750                         Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2751         }
2752         else
2753                 prog->watch_field_type = ev_void;
2754 }
2755
2756 static void PRVM_Breakpoint_f(void)
2757 {
2758         prvm_prog_t *prog;
2759
2760         if( Cmd_Argc() == 2 ) {
2761                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2762                         return;
2763                 {
2764                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2765                         debug->break_statement[0] = 0;
2766                 }
2767                 PRVM_UpdateBreakpoints(prog);
2768                 return;
2769         }
2770         if( Cmd_Argc() != 3 ) {
2771                 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2772                 return;
2773         }
2774
2775         if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2776                 return;
2777
2778         {
2779                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2780                 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2781         }
2782         PRVM_UpdateBreakpoints(prog);
2783 }
2784
2785 static void PRVM_GlobalWatchpoint_f(void)
2786 {
2787         prvm_prog_t *prog;
2788
2789         if( Cmd_Argc() == 2 ) {
2790                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2791                         return;
2792                 {
2793                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2794                         debug->watch_global[0] = 0;
2795                 }
2796                 PRVM_UpdateBreakpoints(prog);
2797                 return;
2798         }
2799         if( Cmd_Argc() != 3 ) {
2800                 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2801                 return;
2802         }
2803
2804         if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2805                 return;
2806
2807         {
2808                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2809                 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2810         }
2811         PRVM_UpdateBreakpoints(prog);
2812 }
2813
2814 static void PRVM_EdictWatchpoint_f(void)
2815 {
2816         prvm_prog_t *prog;
2817
2818         if( Cmd_Argc() == 2 ) {
2819                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2820                         return;
2821                 {
2822                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2823                         debug->watch_field[0] = 0;
2824                 }
2825                 PRVM_UpdateBreakpoints(prog);
2826                 return;
2827         }
2828         if( Cmd_Argc() != 4 ) {
2829                 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2830                 return;
2831         }
2832
2833         if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2834                 return;
2835
2836         {
2837                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2838                 debug->watch_edict = atoi(Cmd_Argv(2));
2839                 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2840         }
2841         PRVM_UpdateBreakpoints(prog);
2842 }
2843
2844 /*
2845 ===============
2846 PRVM_Init
2847 ===============
2848 */
2849 void PRVM_Init (void)
2850 {
2851         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2852         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2853         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2854         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2855         Cmd_AddCommand ("prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2856         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)");
2857         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)");
2858         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2859         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2860         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2861         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)");
2862         Cmd_AddCommand ("prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2863         Cmd_AddCommand ("prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2864         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2865         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2866         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2867         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2868
2869         Cmd_AddCommand ("prvm_breakpoint", PRVM_Breakpoint_f, "marks a statement or function as breakpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear breakpoint");
2870         Cmd_AddCommand ("prvm_globalwatchpoint", PRVM_GlobalWatchpoint_f, "marks a global as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint");
2871         Cmd_AddCommand ("prvm_edictwatchpoint", PRVM_EdictWatchpoint_f, "marks an entity field as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint");
2872
2873         Cvar_RegisterVariable (&prvm_language);
2874         Cvar_RegisterVariable (&prvm_traceqc);
2875         Cvar_RegisterVariable (&prvm_statementprofiling);
2876         Cvar_RegisterVariable (&prvm_timeprofiling);
2877         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2878         Cvar_RegisterVariable (&prvm_leaktest);
2879         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2880         Cvar_RegisterVariable (&prvm_errordump);
2881         Cvar_RegisterVariable (&prvm_breakpointdump);
2882         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2883         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2884
2885         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2886         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2887
2888         //VM_Cmd_Init();
2889 }
2890
2891 /*
2892 ===============
2893 PRVM_InitProg
2894 ===============
2895 */
2896 void PRVM_Prog_Init(prvm_prog_t *prog)
2897 {
2898         PRVM_Prog_Reset(prog);
2899         prog->leaktest_active = prvm_leaktest.integer != 0;
2900 }
2901
2902 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2903 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2904 {
2905         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2906         return 0;
2907 }
2908
2909 #define PRVM_KNOWNSTRINGBASE 0x40000000
2910
2911 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2912 {
2913         if (num < 0)
2914         {
2915                 // invalid
2916                 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2917                 return "";
2918         }
2919         else if (num < prog->stringssize)
2920         {
2921                 // constant string from progs.dat
2922                 return prog->strings + num;
2923         }
2924         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2925         {
2926                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2927                 num -= prog->stringssize;
2928                 if (num < prog->tempstringsbuf.cursize)
2929                         return (char *)prog->tempstringsbuf.data + num;
2930                 else
2931                 {
2932                         VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2933                         return "";
2934                 }
2935         }
2936         else if (num & PRVM_KNOWNSTRINGBASE)
2937         {
2938                 // allocated string
2939                 num = num - PRVM_KNOWNSTRINGBASE;
2940                 if (num >= 0 && num < prog->numknownstrings)
2941                 {
2942                         if (!prog->knownstrings[num])
2943                         {
2944                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2945                                 return "";
2946                         }
2947                         return prog->knownstrings[num];
2948                 }
2949                 else
2950                 {
2951                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2952                         return "";
2953                 }
2954         }
2955         else
2956         {
2957                 // invalid string offset
2958                 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2959                 return "";
2960         }
2961 }
2962
2963 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2964 {
2965         const char *old;
2966         i = i - PRVM_KNOWNSTRINGBASE;
2967         if(i < 0 || i >= prog->numknownstrings)
2968                 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2969         old = prog->knownstrings[i];
2970         prog->knownstrings[i] = s;
2971         return old;
2972 }
2973
2974 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2975 {
2976         int i;
2977         if (!s)
2978                 return 0;
2979         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2980                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
2981         // if it's in the tempstrings area, use a reserved range
2982         // (otherwise we'd get millions of useless string offsets cluttering the database)
2983         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
2984                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
2985         // see if it's a known string address
2986         for (i = 0;i < prog->numknownstrings;i++)
2987                 if (prog->knownstrings[i] == s)
2988                         return PRVM_KNOWNSTRINGBASE + i;
2989         // new unknown engine string
2990         if (developer_insane.integer)
2991                 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2992         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2993                 if (!prog->knownstrings[i])
2994                         break;
2995         if (i >= prog->numknownstrings)
2996         {
2997                 if (i >= prog->maxknownstrings)
2998                 {
2999                         const char **oldstrings = prog->knownstrings;
3000                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3001                         const char **oldstrings_origin = prog->knownstrings_origin;
3002                         prog->maxknownstrings += 128;
3003                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3004                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3005                         if(prog->leaktest_active)
3006                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3007                         if (prog->numknownstrings)
3008                         {
3009                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3010                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3011                                 if(prog->leaktest_active)
3012                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3013                         }
3014                 }
3015                 prog->numknownstrings++;
3016         }
3017         prog->firstfreeknownstring = i + 1;
3018         prog->knownstrings[i] = s;
3019         prog->knownstrings_freeable[i] = false;
3020         if(prog->leaktest_active)
3021                 prog->knownstrings_origin[i] = NULL;
3022         return PRVM_KNOWNSTRINGBASE + i;
3023 }
3024
3025 // temp string handling
3026
3027 // all tempstrings go into this buffer consecutively, and it is reset
3028 // whenever PRVM_ExecuteProgram returns to the engine
3029 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3030 //  restores it on return, so multiple recursive calls can share the same
3031 //  buffer)
3032 // the buffer size is automatically grown as needed
3033
3034 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3035 {
3036         int size;
3037         char *t;
3038         if (!s)
3039                 return 0;
3040         size = (int)strlen(s) + 1;
3041         if (developer_insane.integer)
3042                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3043         if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3044         {
3045                 sizebuf_t old = prog->tempstringsbuf;
3046                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3047                         prog->error_cmd("PRVM_SetTempString: ran out of tempstring memory!  (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", prog->tempstringsbuf.cursize, size);
3048                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3049                 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3050                         prog->tempstringsbuf.maxsize *= 2;
3051                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3052                 {
3053                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3054                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3055                         if (old.cursize)
3056                                 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3057                         if (old.data)
3058                                 Mem_Free(old.data);
3059                 }
3060         }
3061         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3062         memcpy(t, s, size);
3063         prog->tempstringsbuf.cursize += size;
3064         return PRVM_SetEngineString(prog, t);
3065 }
3066
3067 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3068 {
3069         int i;
3070         if (!bufferlength)
3071                 return 0;
3072         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3073                 if (!prog->knownstrings[i])
3074                         break;
3075         if (i >= prog->numknownstrings)
3076         {
3077                 if (i >= prog->maxknownstrings)
3078                 {
3079                         const char **oldstrings = prog->knownstrings;
3080                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3081                         const char **oldstrings_origin = prog->knownstrings_origin;
3082                         prog->maxknownstrings += 128;
3083                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3084                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3085                         if(prog->leaktest_active)
3086                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3087                         if (prog->numknownstrings)
3088                         {
3089                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3090                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3091                                 if(prog->leaktest_active)
3092                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3093                         }
3094                         if (oldstrings)
3095                                 Mem_Free((char **)oldstrings);
3096                         if (oldstrings_freeable)
3097                                 Mem_Free((unsigned char *)oldstrings_freeable);
3098                         if (oldstrings_origin)
3099                                 Mem_Free((char **)oldstrings_origin);
3100                 }
3101                 prog->numknownstrings++;
3102         }
3103         prog->firstfreeknownstring = i + 1;
3104         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3105         prog->knownstrings_freeable[i] = true;
3106         if(prog->leaktest_active)
3107                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3108         if (pointer)
3109                 *pointer = (char *)(prog->knownstrings[i]);
3110         return PRVM_KNOWNSTRINGBASE + i;
3111 }
3112
3113 void PRVM_FreeString(prvm_prog_t *prog, int num)
3114 {
3115         if (num == 0)
3116                 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3117         else if (num >= 0 && num < prog->stringssize)
3118                 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3119         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3120         {
3121                 num = num - PRVM_KNOWNSTRINGBASE;
3122                 if (!prog->knownstrings[num])
3123                         prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3124                 if (!prog->knownstrings_freeable[num])
3125                         prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3126                 PRVM_Free((char *)prog->knownstrings[num]);
3127                 if(prog->leaktest_active)
3128                         if(prog->knownstrings_origin[num])
3129                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3130                 prog->knownstrings[num] = NULL;
3131                 prog->knownstrings_freeable[num] = false;
3132                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3133         }
3134         else
3135                 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3136 }
3137
3138 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3139 {
3140         int i, j;
3141
3142         for (i = 0;i < prog->numglobaldefs;i++)
3143         {
3144                 ddef_t *d = &prog->globaldefs[i];
3145                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3146                         continue;
3147                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3148                         return true;
3149         }
3150
3151         for(j = 0; j < prog->num_edicts; ++j)
3152         {
3153                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3154                 if (ed->priv.required->free)
3155                         continue;
3156                 for (i=0; i<prog->numfielddefs; ++i)
3157                 {
3158                         ddef_t *d = &prog->fielddefs[i];
3159                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3160                                 continue;
3161                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3162                                 return true;
3163                 }
3164         }
3165
3166         return false;
3167 }
3168
3169 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3170 {
3171         char vabuf[1024];
3172         char vabuf2[1024];
3173         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3174                 return true; // world or clients
3175         if (prog == SVVM_prog)
3176         {
3177                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3178                         return true;
3179                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3180                         return true;
3181                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3182                         return true;
3183                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3184                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3185                                 return true;
3186                 if(PRVM_serveredictfloat(edict, takedamage))
3187                         return true;
3188                 if(*prvm_leaktest_ignore_classnames.string)
3189                 {
3190                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3191                                 return true;
3192                 }
3193         }
3194         else if (prog == CLVM_prog)
3195         {
3196                 // TODO someone add more stuff here
3197                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3198                         return true;
3199                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3200                         return true;
3201                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3202                         return true;
3203                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3204                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3205                                 return true;
3206                 if(*prvm_leaktest_ignore_classnames.string)
3207                 {
3208                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3209                                 return true;
3210                 }
3211         }
3212         else
3213         {
3214                 // menu prog does not have classnames
3215         }
3216         return false;
3217 }
3218
3219 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3220 {
3221         int i, j;
3222         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3223         const char *targetname = NULL;
3224
3225         if (prog == SVVM_prog)
3226                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3227
3228         if(targetname)
3229                 if(!*targetname) // ""
3230                         targetname = NULL;
3231
3232         if(mark == 0)
3233         {
3234                 for (i = 0;i < prog->numglobaldefs;i++)
3235                 {
3236                         ddef_t *d = &prog->globaldefs[i];
3237                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3238                                 continue;
3239                         if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3240                                 return true;
3241                 }
3242         }
3243
3244         for(j = 0; j < prog->num_edicts; ++j)
3245         {
3246                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3247                 if (ed->priv.required->mark < mark)
3248                         continue;
3249                 if(ed == edict)
3250                         continue;
3251                 if(targetname)
3252                 {
3253                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3254                         if(target)
3255                                 if(!strcmp(target, targetname))
3256                                         return true;
3257                 }
3258                 for (i=0; i<prog->numfielddefs; ++i)
3259                 {
3260                         ddef_t *d = &prog->fielddefs[i];
3261                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3262                                 continue;
3263                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3264                                 return true;
3265                 }
3266         }
3267
3268         return false;
3269 }
3270
3271 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3272 {
3273         int j;
3274         qboolean found_new;
3275         int stage;
3276
3277         for(j = 0; j < prog->num_edicts; ++j)
3278         {
3279                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3280                 if(ed->priv.required->free)
3281                         continue;
3282                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3283         }
3284
3285         stage = 1;
3286         do
3287         {
3288                 found_new = false;
3289                 for(j = 0; j < prog->num_edicts; ++j)
3290                 {
3291                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3292                         if(ed->priv.required->free)
3293                                 continue;
3294                         if(ed->priv.required->mark)
3295                                 continue;
3296                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3297                         {
3298                                 ed->priv.required->mark = stage + 1;
3299                                 found_new = true;
3300                         }
3301                 }
3302                 ++stage;
3303         }
3304         while(found_new);
3305         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3306 }
3307
3308 void PRVM_LeakTest(prvm_prog_t *prog)
3309 {
3310         int i, j;
3311         qboolean leaked = false;
3312
3313         if(!prog->leaktest_active)
3314                 return;
3315
3316         // 1. Strings
3317         for (i = 0; i < prog->numknownstrings; ++i)
3318         {
3319                 if(prog->knownstrings[i])
3320                 if(prog->knownstrings_freeable[i])
3321                 if(prog->knownstrings_origin[i])
3322                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3323                 {
3324                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3325                         leaked = true;
3326                 }
3327         }
3328
3329         // 2. Edicts
3330         PRVM_MarkReferencedEdicts(prog);
3331         for(j = 0; j < prog->num_edicts; ++j)
3332         {
3333                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3334                 if(ed->priv.required->free)
3335                         continue;
3336                 if(!ed->priv.required->mark)
3337                 if(ed->priv.required->allocation_origin)
3338                 {
3339                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3340                         PRVM_ED_Print(prog, ed, NULL);
3341                         Con_Print("\n");
3342                         leaked = true;
3343                 }
3344
3345                 ed->priv.required->mark = 0; // clear marks again when done
3346         }
3347
3348         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3349         {
3350                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3351                 if(stringbuffer)
3352                 if(stringbuffer->origin)
3353                 {
3354                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3355                         leaked = true;
3356                 }
3357         }
3358
3359         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3360         {
3361                 if(prog->openfiles[i])
3362                 if(prog->openfiles_origin[i])
3363                 {
3364                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3365                         leaked = true;
3366                 }
3367         }
3368
3369         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3370         {
3371                 if(prog->opensearches[i])
3372                 if(prog->opensearches_origin[i])
3373                 {
3374                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3375                         leaked = true;
3376                 }
3377         }
3378
3379         if(!leaked)
3380                 Con_Printf("Congratulations. No leaks found.\n");
3381 }