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