]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
Disable r_water_hideplayer while in chase camera view
[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, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
1891         }
1892         Mem_Free( lno );
1893 }
1894
1895 /*
1896 ===============
1897 PRVM_LoadProgs
1898 ===============
1899 */
1900 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1901 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)
1902 {
1903         int i;
1904         dprograms_t *dprograms;
1905         dstatement_t *instatements;
1906         ddef_t *infielddefs;
1907         ddef_t *inglobaldefs;
1908         int *inglobals;
1909         dfunction_t *infunctions;
1910         char *instrings;
1911         fs_offset_t filesize;
1912         int requiredglobalspace;
1913         opcode_t op;
1914         int a;
1915         int b;
1916         int c;
1917         union
1918         {
1919                 unsigned int i;
1920                 float f;
1921         }
1922         u;
1923         unsigned int d;
1924         char vabuf[1024];
1925         char vabuf2[1024];
1926
1927         if (prog->loaded)
1928                 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
1929
1930         Host_LockSession(); // all progs can use the session cvar
1931         Crypto_LoadKeys(); // all progs might use the keys at init time
1932
1933         if (data)
1934         {
1935                 dprograms = (dprograms_t *) data;
1936                 filesize = size;
1937         }
1938         else
1939                 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1940         if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1941                 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
1942         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1943
1944         prog->profiletime = Sys_DirtyTime();
1945         prog->starttime = realtime;
1946
1947         Con_DPrintf("%s programs occupy %iK.\n", prog->name, (int)(filesize/1024));
1948
1949         requiredglobalspace = 0;
1950         for (i = 0;i < numrequiredglobals;i++)
1951                 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
1952
1953         prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
1954
1955 // byte swap the header
1956         prog->progs_version = LittleLong(dprograms->version);
1957         prog->progs_crc = LittleLong(dprograms->crc);
1958         if (prog->progs_version != PROG_VERSION)
1959                 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
1960         instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
1961         prog->progs_numstatements = LittleLong(dprograms->numstatements);
1962         inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
1963         prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
1964         infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
1965         prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
1966         infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
1967         prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
1968         instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
1969         prog->progs_numstrings = LittleLong(dprograms->numstrings);
1970         inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
1971         prog->progs_numglobals = LittleLong(dprograms->numglobals);
1972         prog->progs_entityfields = LittleLong(dprograms->entityfields);
1973
1974         prog->numstatements = prog->progs_numstatements;
1975         prog->numglobaldefs = prog->progs_numglobaldefs;
1976         prog->numfielddefs = prog->progs_numfielddefs;
1977         prog->numfunctions = prog->progs_numfunctions;
1978         prog->numstrings = prog->progs_numstrings;
1979         prog->numglobals = prog->progs_numglobals;
1980         prog->entityfields = prog->progs_entityfields;
1981
1982         if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
1983                 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
1984         prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
1985         memcpy(prog->strings, instrings, prog->progs_numstrings);
1986         prog->stringssize = prog->progs_numstrings;
1987
1988         prog->numknownstrings = 0;
1989         prog->maxknownstrings = 0;
1990         prog->knownstrings = NULL;
1991         prog->knownstrings_freeable = NULL;
1992
1993         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1994
1995         // we need to expand the globaldefs and fielddefs to include engine defs
1996         prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
1997         prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
1998                 // + 2 is because of an otherwise occurring overrun in RETURN instruction
1999                 // when trying to return the last or second-last global
2000                 // (RETURN always returns a vector, there is no RETURN_F instruction)
2001         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2002         // we need to convert the statements to our memory format
2003         prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2004         // allocate space for profiling statement usage
2005         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2006         // functions need to be converted to the memory format
2007         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2008
2009         for (i = 0;i < prog->progs_numfunctions;i++)
2010         {
2011                 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2012                 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2013                 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2014                 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2015                 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2016                 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2017                 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2018                 if(prog->functions[i].first_statement >= prog->numstatements)
2019                         prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2020                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2021         }
2022
2023         // copy the globaldefs to the new globaldefs list
2024         for (i=0 ; i<prog->numglobaldefs ; i++)
2025         {
2026                 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2027                 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2028                 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2029                 // TODO bounds check ofs, s_name
2030         }
2031
2032         // append the required globals
2033         for (i = 0;i < numrequiredglobals;i++)
2034         {
2035                 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2036                 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2037                 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2038                 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2039                         prog->numglobals += 3;
2040                 else
2041                         prog->numglobals++;
2042                 prog->numglobaldefs++;
2043         }
2044
2045         // copy the progs fields to the new fields list
2046         for (i = 0;i < prog->numfielddefs;i++)
2047         {
2048                 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2049                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2050                         prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2051                 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2052                 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2053                 // TODO bounds check ofs, s_name
2054         }
2055
2056         // append the required fields
2057         for (i = 0;i < numrequiredfields;i++)
2058         {
2059                 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2060                 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2061                 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2062                 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2063                         prog->entityfields += 3;
2064                 else
2065                         prog->entityfields++;
2066                 prog->numfielddefs++;
2067         }
2068
2069         // LordHavoc: TODO: reorder globals to match engine struct
2070         // LordHavoc: TODO: reorder fields to match engine struct
2071 #define remapglobal(index) (index)
2072 #define remapfield(index) (index)
2073
2074         // copy globals
2075         // FIXME: LordHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2076         for (i = 0;i < prog->progs_numglobals;i++)
2077         {
2078                 u.i = LittleLong(inglobals[i]);
2079                 // most globals are 0, we only need to deal with the ones that are not
2080                 if (u.i)
2081                 {
2082                         d = u.i & 0xFF800000;
2083                         if ((d == 0xFF800000) || (d == 0))
2084                         {
2085                                 // Looks like an integer (expand to int64)
2086                                 prog->globals.ip[remapglobal(i)] = u.i;
2087                         }
2088                         else
2089                         {
2090                                 // Looks like a float (expand to double)
2091                                 prog->globals.fp[remapglobal(i)] = u.f;
2092                         }
2093                 }
2094         }
2095
2096         // LordHavoc: TODO: support 32bit progs statement formats
2097         // copy, remap globals in statements, bounds check
2098         for (i = 0;i < prog->progs_numstatements;i++)
2099         {
2100                 op = (opcode_t)LittleShort(instatements[i].op);
2101                 a = (unsigned short)LittleShort(instatements[i].a);
2102                 b = (unsigned short)LittleShort(instatements[i].b);
2103                 c = (unsigned short)LittleShort(instatements[i].c);
2104                 switch (op)
2105                 {
2106                 case OP_IF:
2107                 case OP_IFNOT:
2108                         b = (short)b;
2109                         if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2110                                 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2111                         prog->statements[i].op = op;
2112                         prog->statements[i].operand[0] = remapglobal(a);
2113                         prog->statements[i].operand[1] = -1;
2114                         prog->statements[i].operand[2] = -1;
2115                         prog->statements[i].jumpabsolute = i + b;
2116                         break;
2117                 case OP_GOTO:
2118                         a = (short)a;
2119                         if (a + i < 0 || a + i >= prog->progs_numstatements)
2120                                 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2121                         prog->statements[i].op = op;
2122                         prog->statements[i].operand[0] = -1;
2123                         prog->statements[i].operand[1] = -1;
2124                         prog->statements[i].operand[2] = -1;
2125                         prog->statements[i].jumpabsolute = i + a;
2126                         break;
2127                 default:
2128                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2129                         break;
2130                 // global global global
2131                 case OP_ADD_F:
2132                 case OP_ADD_V:
2133                 case OP_SUB_F:
2134                 case OP_SUB_V:
2135                 case OP_MUL_F:
2136                 case OP_MUL_V:
2137                 case OP_MUL_FV:
2138                 case OP_MUL_VF:
2139                 case OP_DIV_F:
2140                 case OP_BITAND:
2141                 case OP_BITOR:
2142                 case OP_GE:
2143                 case OP_LE:
2144                 case OP_GT:
2145                 case OP_LT:
2146                 case OP_AND:
2147                 case OP_OR:
2148                 case OP_EQ_F:
2149                 case OP_EQ_V:
2150                 case OP_EQ_S:
2151                 case OP_EQ_E:
2152                 case OP_EQ_FNC:
2153                 case OP_NE_F:
2154                 case OP_NE_V:
2155                 case OP_NE_S:
2156                 case OP_NE_E:
2157                 case OP_NE_FNC:
2158                 case OP_ADDRESS:
2159                 case OP_LOAD_F:
2160                 case OP_LOAD_FLD:
2161                 case OP_LOAD_ENT:
2162                 case OP_LOAD_S:
2163                 case OP_LOAD_FNC:
2164                 case OP_LOAD_V:
2165                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2166                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2167                         prog->statements[i].op = op;
2168                         prog->statements[i].operand[0] = remapglobal(a);
2169                         prog->statements[i].operand[1] = remapglobal(b);
2170                         prog->statements[i].operand[2] = remapglobal(c);
2171                         prog->statements[i].jumpabsolute = -1;
2172                         break;
2173                 // global none global
2174                 case OP_NOT_F:
2175                 case OP_NOT_V:
2176                 case OP_NOT_S:
2177                 case OP_NOT_FNC:
2178                 case OP_NOT_ENT:
2179                         if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2180                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2181                         prog->statements[i].op = op;
2182                         prog->statements[i].operand[0] = remapglobal(a);
2183                         prog->statements[i].operand[1] = -1;
2184                         prog->statements[i].operand[2] = remapglobal(c);
2185                         prog->statements[i].jumpabsolute = -1;
2186                         break;
2187                 // 2 globals
2188                 case OP_STOREP_F:
2189                 case OP_STOREP_ENT:
2190                 case OP_STOREP_FLD:
2191                 case OP_STOREP_S:
2192                 case OP_STOREP_FNC:
2193                 case OP_STORE_F:
2194                 case OP_STORE_ENT:
2195                 case OP_STORE_FLD:
2196                 case OP_STORE_S:
2197                 case OP_STORE_FNC:
2198                 case OP_STATE:
2199                 case OP_STOREP_V:
2200                 case OP_STORE_V:
2201                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2202                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2203                         prog->statements[i].op = op;
2204                         prog->statements[i].operand[0] = remapglobal(a);
2205                         prog->statements[i].operand[1] = remapglobal(b);
2206                         prog->statements[i].operand[2] = -1;
2207                         prog->statements[i].jumpabsolute = -1;
2208                         break;
2209                 // 1 global
2210                 case OP_CALL0:
2211                 case OP_CALL1:
2212                 case OP_CALL2:
2213                 case OP_CALL3:
2214                 case OP_CALL4:
2215                 case OP_CALL5:
2216                 case OP_CALL6:
2217                 case OP_CALL7:
2218                 case OP_CALL8:
2219                 case OP_DONE:
2220                 case OP_RETURN:
2221                         if ( a >= prog->progs_numglobals)
2222                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2223                         prog->statements[i].op = op;
2224                         prog->statements[i].operand[0] = remapglobal(a);
2225                         prog->statements[i].operand[1] = -1;
2226                         prog->statements[i].operand[2] = -1;
2227                         prog->statements[i].jumpabsolute = -1;
2228                         break;
2229                 }
2230         }
2231         if(prog->numstatements < 1)
2232         {
2233                 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2234         }
2235         else switch(prog->statements[prog->numstatements - 1].op)
2236         {
2237                 case OP_RETURN:
2238                 case OP_GOTO:
2239                 case OP_DONE:
2240                         break;
2241                 default:
2242                         prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2243                         break;
2244         }
2245
2246         // we're done with the file now
2247         if(!data)
2248                 Mem_Free(dprograms);
2249         dprograms = NULL;
2250
2251         // check required functions
2252         for(i=0 ; i < numrequiredfunc ; i++)
2253                 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2254                         prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2255
2256         PRVM_LoadLNO(prog, filename);
2257
2258         PRVM_Init_Exec(prog);
2259
2260         if(*prvm_language.string)
2261         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2262         // later idea: include a list of authorized .po file checksums with the csprogs
2263         {
2264                 qboolean deftrans = prog == CLVM_prog;
2265                 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2266                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2267                 {
2268                         for (i=0 ; i<prog->numglobaldefs ; i++)
2269                         {
2270                                 const char *name;
2271                                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2272                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2273                                 if(name && !strncmp(name, "dotranslate_", 12))
2274                                 {
2275                                         deftrans = false;
2276                                         break;
2277                                 }
2278                         }
2279                 }
2280                 if(!strcmp(prvm_language.string, "dump"))
2281                 {
2282                         qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2283                         Con_Printf("Dumping to %s.pot\n", realfilename);
2284                         if(f)
2285                         {
2286                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2287                                 {
2288                                         const char *name;
2289                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2290                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2291                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2292                                         {
2293                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2294                                                 const char *value = PRVM_GetString(prog, val->string);
2295                                                 if(*value)
2296                                                 {
2297                                                         char buf[MAX_INPUTLINE];
2298                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2299                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2300                                                 }
2301                                         }
2302                                 }
2303                                 FS_Close(f);
2304                         }
2305                 }
2306                 else
2307                 {
2308                         po_t *po = PRVM_PO_Load(
2309                                         va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2310                                         va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2311                                         prog->progs_mempool);
2312                         if(po)
2313                         {
2314                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2315                                 {
2316                                         const char *name;
2317                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2318                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2319                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2320                                         {
2321                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2322                                                 const char *value = PRVM_GetString(prog, val->string);
2323                                                 if(*value)
2324                                                 {
2325                                                         value = PRVM_PO_Lookup(po, value);
2326                                                         if(value)
2327                                                                 val->string = PRVM_SetEngineString(prog, value);
2328                                                 }
2329                                         }
2330                                 }
2331                         }
2332                 }
2333         }
2334
2335         for (i=0 ; i<prog->numglobaldefs ; i++)
2336         {
2337                 const char *name;
2338                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2339                 //Con_Printf("found var %s\n", name);
2340                 if(name
2341                         && !strncmp(name, "autocvar_", 9)
2342                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2343                 )
2344                 {
2345                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2346                         cvar_t *cvar = Cvar_FindVar(name + 9);
2347                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2348                         if(!cvar)
2349                         {
2350                                 const char *value;
2351                                 char buf[64];
2352                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2353                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2354                                 {
2355                                         case ev_float:
2356                                                 if((float)((int)(val->_float)) == val->_float)
2357                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2358                                                 else
2359                                                         dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2360                                                 value = buf;
2361                                                 break;
2362                                         case ev_vector:
2363                                                 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2364                                                 break;
2365                                         case ev_string:
2366                                                 value = PRVM_GetString(prog, val->string);
2367                                                 break;
2368                                         default:
2369                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2370                                                 goto fail;
2371                                 }
2372                                 cvar = Cvar_Get(name + 9, value, 0, NULL);
2373                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2374                                 {
2375                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2376                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2377                                 }
2378                                 if(!cvar)
2379                                         prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2380                                 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2381                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2382                         }
2383                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2384                         {
2385                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2386                                 int j;
2387                                 const char *s;
2388                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2389                                 {
2390                                         case ev_float:
2391                                                 val->_float = cvar->value;
2392                                                 break;
2393                                         case ev_vector:
2394                                                 s = cvar->string;
2395                                                 VectorClear(val->vector);
2396                                                 for (j = 0;j < 3;j++)
2397                                                 {
2398                                                         while (*s && ISWHITESPACE(*s))
2399                                                                 s++;
2400                                                         if (!*s)
2401                                                                 break;
2402                                                         val->vector[j] = atof(s);
2403                                                         while (!ISWHITESPACE(*s))
2404                                                                 s++;
2405                                                         if (!*s)
2406                                                                 break;
2407                                                 }
2408                                                 break;
2409                                         case ev_string:
2410                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2411                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2412                                                 break;
2413                                         default:
2414                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2415                                                 goto fail;
2416                                 }
2417                                 cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
2418                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2419                         }
2420                         else
2421                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2422                 }
2423 fail:
2424                 ;
2425         }
2426
2427         prog->loaded = TRUE;
2428
2429         PRVM_UpdateBreakpoints(prog);
2430
2431         // set flags & ddef_ts in prog
2432
2433         prog->flag = 0;
2434
2435         PRVM_FindOffsets(prog);
2436
2437         prog->init_cmd(prog);
2438
2439         // init mempools
2440         PRVM_MEM_Alloc(prog);
2441 }
2442
2443
2444 static void PRVM_Fields_f (void)
2445 {
2446         prvm_prog_t *prog;
2447         int i, j, ednum, used, usedamount;
2448         int *counts;
2449         char tempstring[MAX_INPUTLINE], tempstring2[260];
2450         const char *name;
2451         prvm_edict_t *ed;
2452         ddef_t *d;
2453         prvm_eval_t *val;
2454
2455         // TODO
2456         /*
2457         if (!sv.active)
2458         {
2459                 Con_Print("no progs loaded\n");
2460                 return;
2461         }
2462         */
2463
2464         if(Cmd_Argc() != 2)
2465         {
2466                 Con_Print("prvm_fields <program name>\n");
2467                 return;
2468         }
2469
2470         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2471                 return;
2472
2473         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2474         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2475         {
2476                 ed = PRVM_EDICT_NUM(ednum);
2477                 if (ed->priv.required->free)
2478                         continue;
2479                 for (i = 1;i < prog->numfielddefs;i++)
2480                 {
2481                         d = &prog->fielddefs[i];
2482                         name = PRVM_GetString(prog, d->s_name);
2483                         if (name[strlen(name)-2] == '_')
2484                                 continue;       // skip _x, _y, _z vars
2485                         val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2486                         // if the value is still all 0, skip the field
2487                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2488                         {
2489                                 if (val->ivector[j])
2490                                 {
2491                                         counts[i]++;
2492                                         break;
2493                                 }
2494                         }
2495                 }
2496         }
2497         used = 0;
2498         usedamount = 0;
2499         tempstring[0] = 0;
2500         for (i = 0;i < prog->numfielddefs;i++)
2501         {
2502                 d = &prog->fielddefs[i];
2503                 name = PRVM_GetString(prog, d->s_name);
2504                 if (name[strlen(name)-2] == '_')
2505                         continue;       // skip _x, _y, _z vars
2506                 switch(d->type & ~DEF_SAVEGLOBAL)
2507                 {
2508                 case ev_string:
2509                         strlcat(tempstring, "string   ", sizeof(tempstring));
2510                         break;
2511                 case ev_entity:
2512                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2513                         break;
2514                 case ev_function:
2515                         strlcat(tempstring, "function ", sizeof(tempstring));
2516                         break;
2517                 case ev_field:
2518                         strlcat(tempstring, "field    ", sizeof(tempstring));
2519                         break;
2520                 case ev_void:
2521                         strlcat(tempstring, "void     ", sizeof(tempstring));
2522                         break;
2523                 case ev_float:
2524                         strlcat(tempstring, "float    ", sizeof(tempstring));
2525                         break;
2526                 case ev_vector:
2527                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2528                         break;
2529                 case ev_pointer:
2530                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2531                         break;
2532                 default:
2533                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2534                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2535                         break;
2536                 }
2537                 if (strlen(name) > sizeof(tempstring2)-4)
2538                 {
2539                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2540                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2541                         tempstring2[sizeof(tempstring2)-1] = 0;
2542                         name = tempstring2;
2543                 }
2544                 strlcat(tempstring, name, sizeof(tempstring));
2545                 for (j = (int)strlen(name);j < 25;j++)
2546                         strlcat(tempstring, " ", sizeof(tempstring));
2547                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2548                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2549                 strlcat(tempstring, "\n", sizeof(tempstring));
2550                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2551                 {
2552                         Con_Print(tempstring);
2553                         tempstring[0] = 0;
2554                 }
2555                 if (counts[i])
2556                 {
2557                         used++;
2558                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2559                 }
2560         }
2561         Mem_Free(counts);
2562         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);
2563 }
2564
2565 static void PRVM_Globals_f (void)
2566 {
2567         prvm_prog_t *prog;
2568         int i;
2569         const char *wildcard;
2570         int numculled;
2571                 numculled = 0;
2572         // TODO
2573         /*if (!sv.active)
2574         {
2575                 Con_Print("no progs loaded\n");
2576                 return;
2577         }*/
2578         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2579         {
2580                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2581                 return;
2582         }
2583
2584         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2585                 return;
2586
2587         if( Cmd_Argc() == 3)
2588                 wildcard = Cmd_Argv(2);
2589         else
2590                 wildcard = NULL;
2591
2592         Con_Printf("%s :", prog->name);
2593
2594         for (i = 0;i < prog->numglobaldefs;i++)
2595         {
2596                 if(wildcard)
2597                         if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2598                         {
2599                                 numculled++;
2600                                 continue;
2601                         }
2602                 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2603         }
2604         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2605 }
2606
2607 /*
2608 ===============
2609 PRVM_Global
2610 ===============
2611 */
2612 static void PRVM_Global_f(void)
2613 {
2614         prvm_prog_t *prog;
2615         ddef_t *global;
2616         char valuebuf[MAX_INPUTLINE];
2617         if( Cmd_Argc() != 3 ) {
2618                 Con_Printf( "prvm_global <program name> <global name>\n" );
2619                 return;
2620         }
2621
2622         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2623                 return;
2624
2625         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2626         if( !global )
2627                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2628         else
2629                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2630 }
2631
2632 /*
2633 ===============
2634 PRVM_GlobalSet
2635 ===============
2636 */
2637 static void PRVM_GlobalSet_f(void)
2638 {
2639         prvm_prog_t *prog;
2640         ddef_t *global;
2641         if( Cmd_Argc() != 4 ) {
2642                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2643                 return;
2644         }
2645
2646         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2647                 return;
2648
2649         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(2) );
2650         if( !global )
2651                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2652         else
2653                 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true );
2654 }
2655
2656 /*
2657 ======================
2658 Break- and Watchpoints
2659 ======================
2660 */
2661 typedef struct
2662 {
2663         char break_statement[256];
2664         char watch_global[256];
2665         int watch_edict;
2666         char watch_field[256];
2667 }
2668 debug_data_t;
2669 static debug_data_t debug_data[PRVM_PROG_MAX];
2670
2671 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2672 {
2673         char vabuf[1024];
2674         Con_Printf("PRVM_Breakpoint: %s\n", text);
2675         PRVM_PrintState(prog, stack_index);
2676         if (prvm_breakpointdump.integer)
2677                 Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2678 }
2679
2680 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2681 {
2682         size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2683         if (memcmp(o, n, sz))
2684         {
2685                 char buf[1024];
2686                 char valuebuf_o[128];
2687                 char valuebuf_n[128];
2688                 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2689                 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2690                 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2691                 PRVM_Breakpoint(prog, stack_index, buf);
2692                 memcpy(o, n, sz);
2693         }
2694 }
2695
2696 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2697 {
2698         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2699         if (!prog->loaded)
2700                 return;
2701         if (debug->break_statement[0])
2702         {
2703                 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2704                 {
2705                         prog->break_statement = atoi(debug->break_statement);
2706                         prog->break_stack_index = 0;
2707                 }
2708                 else
2709                 {
2710                         mfunction_t *func;
2711                         func = PRVM_ED_FindFunction (prog, debug->break_statement);
2712                         if (!func)
2713                         {
2714                                 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2715                                 prog->break_statement = -1;
2716                         }
2717                         else
2718                         {
2719                                 prog->break_statement = func->first_statement;
2720                                 prog->break_stack_index = 1;
2721                         }
2722                 }
2723                 if (prog->break_statement >= -1)
2724                         Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2725         }
2726         else
2727                 prog->break_statement = -1;
2728
2729         if (debug->watch_global[0])
2730         {
2731                 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2732                 if( !global )
2733                 {
2734                         Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2735                         prog->watch_global_type = ev_void;
2736                 }
2737                 else
2738                 {
2739                         size_t sz = sizeof(prvm_vec_t) * ((global->type  & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2740                         prog->watch_global = global->ofs;
2741                         prog->watch_global_type = (etype_t)global->type;
2742                         memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2743                 }
2744                 if (prog->watch_global_type != ev_void)
2745                         Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2746         }
2747         else
2748                 prog->watch_global_type = ev_void;
2749
2750         if (debug->watch_field[0])
2751         {
2752                 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2753                 if( !field )
2754                 {
2755                         Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2756                         prog->watch_field_type = ev_void;
2757                 }
2758                 else
2759                 {
2760                         size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2761                         prog->watch_edict = debug->watch_edict;
2762                         prog->watch_field = field->ofs;
2763                         prog->watch_field_type = (etype_t)field->type;
2764                         if (prog->watch_edict < prog->num_edicts)
2765                                 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2766                         else
2767                                 memset(&prog->watch_edictfield_value, 0, sz);
2768                 }
2769                 if (prog->watch_edict != ev_void)
2770                         Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2771         }
2772         else
2773                 prog->watch_field_type = ev_void;
2774 }
2775
2776 static void PRVM_Breakpoint_f(void)
2777 {
2778         prvm_prog_t *prog;
2779
2780         if( Cmd_Argc() == 2 ) {
2781                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2782                         return;
2783                 {
2784                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2785                         debug->break_statement[0] = 0;
2786                 }
2787                 PRVM_UpdateBreakpoints(prog);
2788                 return;
2789         }
2790         if( Cmd_Argc() != 3 ) {
2791                 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2792                 return;
2793         }
2794
2795         if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2796                 return;
2797
2798         {
2799                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2800                 strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement));
2801         }
2802         PRVM_UpdateBreakpoints(prog);
2803 }
2804
2805 static void PRVM_GlobalWatchpoint_f(void)
2806 {
2807         prvm_prog_t *prog;
2808
2809         if( Cmd_Argc() == 2 ) {
2810                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2811                         return;
2812                 {
2813                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2814                         debug->watch_global[0] = 0;
2815                 }
2816                 PRVM_UpdateBreakpoints(prog);
2817                 return;
2818         }
2819         if( Cmd_Argc() != 3 ) {
2820                 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2821                 return;
2822         }
2823
2824         if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2825                 return;
2826
2827         {
2828                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2829                 strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global));
2830         }
2831         PRVM_UpdateBreakpoints(prog);
2832 }
2833
2834 static void PRVM_EdictWatchpoint_f(void)
2835 {
2836         prvm_prog_t *prog;
2837
2838         if( Cmd_Argc() == 2 ) {
2839                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1))))
2840                         return;
2841                 {
2842                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2843                         debug->watch_field[0] = 0;
2844                 }
2845                 PRVM_UpdateBreakpoints(prog);
2846                 return;
2847         }
2848         if( Cmd_Argc() != 4 ) {
2849                 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2850                 return;
2851         }
2852
2853         if (!(prog = PRVM_ProgFromString(Cmd_Argv(1))))
2854                 return;
2855
2856         {
2857                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2858                 debug->watch_edict = atoi(Cmd_Argv(2));
2859                 strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field));
2860         }
2861         PRVM_UpdateBreakpoints(prog);
2862 }
2863
2864 /*
2865 ===============
2866 PRVM_Init
2867 ===============
2868 */
2869 void PRVM_Init (void)
2870 {
2871         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2872         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2873         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2874         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2875         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");
2876         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)");
2877         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)");
2878         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2879         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2880         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2881         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)");
2882         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");
2883         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");
2884         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2885         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2886         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2887         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2888
2889         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");
2890         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");
2891         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");
2892
2893         Cvar_RegisterVariable (&prvm_language);
2894         Cvar_RegisterVariable (&prvm_traceqc);
2895         Cvar_RegisterVariable (&prvm_statementprofiling);
2896         Cvar_RegisterVariable (&prvm_timeprofiling);
2897         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2898         Cvar_RegisterVariable (&prvm_leaktest);
2899         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2900         Cvar_RegisterVariable (&prvm_errordump);
2901         Cvar_RegisterVariable (&prvm_breakpointdump);
2902         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2903         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2904
2905         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2906         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2907
2908         //VM_Cmd_Init();
2909 }
2910
2911 /*
2912 ===============
2913 PRVM_InitProg
2914 ===============
2915 */
2916 void PRVM_Prog_Init(prvm_prog_t *prog)
2917 {
2918         PRVM_Prog_Reset(prog);
2919         prog->leaktest_active = prvm_leaktest.integer != 0;
2920 }
2921
2922 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2923 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2924 {
2925         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2926         return 0;
2927 }
2928
2929 #define PRVM_KNOWNSTRINGBASE 0x40000000
2930
2931 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2932 {
2933         if (num < 0)
2934         {
2935                 // invalid
2936                 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2937                 return "";
2938         }
2939         else if (num < prog->stringssize)
2940         {
2941                 // constant string from progs.dat
2942                 return prog->strings + num;
2943         }
2944         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2945         {
2946                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2947                 num -= prog->stringssize;
2948                 if (num < prog->tempstringsbuf.cursize)
2949                         return (char *)prog->tempstringsbuf.data + num;
2950                 else
2951                 {
2952                         VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
2953                         return "";
2954                 }
2955         }
2956         else if (num & PRVM_KNOWNSTRINGBASE)
2957         {
2958                 // allocated string
2959                 num = num - PRVM_KNOWNSTRINGBASE;
2960                 if (num >= 0 && num < prog->numknownstrings)
2961                 {
2962                         if (!prog->knownstrings[num])
2963                         {
2964                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2965                                 return "";
2966                         }
2967                         return prog->knownstrings[num];
2968                 }
2969                 else
2970                 {
2971                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2972                         return "";
2973                 }
2974         }
2975         else
2976         {
2977                 // invalid string offset
2978                 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2979                 return "";
2980         }
2981 }
2982
2983 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
2984 {
2985         const char *old;
2986         i = i - PRVM_KNOWNSTRINGBASE;
2987         if(i < 0 || i >= prog->numknownstrings)
2988                 prog->error_cmd("PRVM_ChangeEngineString: s is not an engine string");
2989         old = prog->knownstrings[i];
2990         prog->knownstrings[i] = s;
2991         return old;
2992 }
2993
2994 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
2995 {
2996         int i;
2997         if (!s)
2998                 return 0;
2999         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3000                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3001         // if it's in the tempstrings area, use a reserved range
3002         // (otherwise we'd get millions of useless string offsets cluttering the database)
3003         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3004                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3005         // see if it's a known string address
3006         for (i = 0;i < prog->numknownstrings;i++)
3007                 if (prog->knownstrings[i] == s)
3008                         return PRVM_KNOWNSTRINGBASE + i;
3009         // new unknown engine string
3010         if (developer_insane.integer)
3011                 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3012         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3013                 if (!prog->knownstrings[i])
3014                         break;
3015         if (i >= prog->numknownstrings)
3016         {
3017                 if (i >= prog->maxknownstrings)
3018                 {
3019                         const char **oldstrings = prog->knownstrings;
3020                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3021                         const char **oldstrings_origin = prog->knownstrings_origin;
3022                         prog->maxknownstrings += 128;
3023                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3024                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3025                         if(prog->leaktest_active)
3026                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3027                         if (prog->numknownstrings)
3028                         {
3029                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3030                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3031                                 if(prog->leaktest_active)
3032                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3033                         }
3034                 }
3035                 prog->numknownstrings++;
3036         }
3037         prog->firstfreeknownstring = i + 1;
3038         prog->knownstrings[i] = s;
3039         prog->knownstrings_freeable[i] = false;
3040         if(prog->leaktest_active)
3041                 prog->knownstrings_origin[i] = NULL;
3042         return PRVM_KNOWNSTRINGBASE + i;
3043 }
3044
3045 // temp string handling
3046
3047 // all tempstrings go into this buffer consecutively, and it is reset
3048 // whenever PRVM_ExecuteProgram returns to the engine
3049 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3050 //  restores it on return, so multiple recursive calls can share the same
3051 //  buffer)
3052 // the buffer size is automatically grown as needed
3053
3054 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3055 {
3056         int size;
3057         char *t;
3058         if (!s)
3059                 return 0;
3060         size = (int)strlen(s) + 1;
3061         if (developer_insane.integer)
3062                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3063         if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3064         {
3065                 sizebuf_t old = prog->tempstringsbuf;
3066                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3067                         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);
3068                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3069                 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3070                         prog->tempstringsbuf.maxsize *= 2;
3071                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3072                 {
3073                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3074                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3075                         if (old.cursize)
3076                                 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3077                         if (old.data)
3078                                 Mem_Free(old.data);
3079                 }
3080         }
3081         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3082         memcpy(t, s, size);
3083         prog->tempstringsbuf.cursize += size;
3084         return PRVM_SetEngineString(prog, t);
3085 }
3086
3087 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3088 {
3089         int i;
3090         if (!bufferlength)
3091                 return 0;
3092         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3093                 if (!prog->knownstrings[i])
3094                         break;
3095         if (i >= prog->numknownstrings)
3096         {
3097                 if (i >= prog->maxknownstrings)
3098                 {
3099                         const char **oldstrings = prog->knownstrings;
3100                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3101                         const char **oldstrings_origin = prog->knownstrings_origin;
3102                         prog->maxknownstrings += 128;
3103                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3104                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3105                         if(prog->leaktest_active)
3106                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3107                         if (prog->numknownstrings)
3108                         {
3109                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3110                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3111                                 if(prog->leaktest_active)
3112                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3113                         }
3114                         if (oldstrings)
3115                                 Mem_Free((char **)oldstrings);
3116                         if (oldstrings_freeable)
3117                                 Mem_Free((unsigned char *)oldstrings_freeable);
3118                         if (oldstrings_origin)
3119                                 Mem_Free((char **)oldstrings_origin);
3120                 }
3121                 prog->numknownstrings++;
3122         }
3123         prog->firstfreeknownstring = i + 1;
3124         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3125         prog->knownstrings_freeable[i] = true;
3126         if(prog->leaktest_active)
3127                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3128         if (pointer)
3129                 *pointer = (char *)(prog->knownstrings[i]);
3130         return PRVM_KNOWNSTRINGBASE + i;
3131 }
3132
3133 void PRVM_FreeString(prvm_prog_t *prog, int num)
3134 {
3135         if (num == 0)
3136                 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3137         else if (num >= 0 && num < prog->stringssize)
3138                 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3139         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3140         {
3141                 num = num - PRVM_KNOWNSTRINGBASE;
3142                 if (!prog->knownstrings[num])
3143                         prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3144                 if (!prog->knownstrings_freeable[num])
3145                         prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3146                 PRVM_Free((char *)prog->knownstrings[num]);
3147                 if(prog->leaktest_active)
3148                         if(prog->knownstrings_origin[num])
3149                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3150                 prog->knownstrings[num] = NULL;
3151                 prog->knownstrings_freeable[num] = false;
3152                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3153         }
3154         else
3155                 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3156 }
3157
3158 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3159 {
3160         int i, j;
3161
3162         for (i = 0;i < prog->numglobaldefs;i++)
3163         {
3164                 ddef_t *d = &prog->globaldefs[i];
3165                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3166                         continue;
3167                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3168                         return true;
3169         }
3170
3171         for(j = 0; j < prog->num_edicts; ++j)
3172         {
3173                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3174                 if (ed->priv.required->free)
3175                         continue;
3176                 for (i=0; i<prog->numfielddefs; ++i)
3177                 {
3178                         ddef_t *d = &prog->fielddefs[i];
3179                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3180                                 continue;
3181                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3182                                 return true;
3183                 }
3184         }
3185
3186         return false;
3187 }
3188
3189 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3190 {
3191         char vabuf[1024];
3192         char vabuf2[1024];
3193         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3194                 return true; // world or clients
3195         if (prog == SVVM_prog)
3196         {
3197                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3198                         return true;
3199                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3200                         return true;
3201                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3202                         return true;
3203                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3204                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3205                                 return true;
3206                 if(PRVM_serveredictfloat(edict, takedamage))
3207                         return true;
3208                 if(*prvm_leaktest_ignore_classnames.string)
3209                 {
3210                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3211                                 return true;
3212                 }
3213         }
3214         else if (prog == CLVM_prog)
3215         {
3216                 // TODO someone add more stuff here
3217                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3218                         return true;
3219                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3220                         return true;
3221                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3222                         return true;
3223                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3224                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3225                                 return true;
3226                 if(*prvm_leaktest_ignore_classnames.string)
3227                 {
3228                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3229                                 return true;
3230                 }
3231         }
3232         else
3233         {
3234                 // menu prog does not have classnames
3235         }
3236         return false;
3237 }
3238
3239 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3240 {
3241         int i, j;
3242         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3243         const char *targetname = NULL;
3244
3245         if (prog == SVVM_prog)
3246                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3247
3248         if(targetname)
3249                 if(!*targetname) // ""
3250                         targetname = NULL;
3251
3252         if(mark == 0)
3253         {
3254                 for (i = 0;i < prog->numglobaldefs;i++)
3255                 {
3256                         ddef_t *d = &prog->globaldefs[i];
3257                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3258                                 continue;
3259                         if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3260                                 return true;
3261                 }
3262         }
3263
3264         for(j = 0; j < prog->num_edicts; ++j)
3265         {
3266                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3267                 if (ed->priv.required->mark < mark)
3268                         continue;
3269                 if(ed == edict)
3270                         continue;
3271                 if(targetname)
3272                 {
3273                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3274                         if(target)
3275                                 if(!strcmp(target, targetname))
3276                                         return true;
3277                 }
3278                 for (i=0; i<prog->numfielddefs; ++i)
3279                 {
3280                         ddef_t *d = &prog->fielddefs[i];
3281                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3282                                 continue;
3283                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3284                                 return true;
3285                 }
3286         }
3287
3288         return false;
3289 }
3290
3291 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3292 {
3293         int j;
3294         qboolean found_new;
3295         int stage;
3296
3297         for(j = 0; j < prog->num_edicts; ++j)
3298         {
3299                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3300                 if(ed->priv.required->free)
3301                         continue;
3302                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
3303         }
3304
3305         stage = 1;
3306         do
3307         {
3308                 found_new = false;
3309                 for(j = 0; j < prog->num_edicts; ++j)
3310                 {
3311                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3312                         if(ed->priv.required->free)
3313                                 continue;
3314                         if(ed->priv.required->mark)
3315                                 continue;
3316                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3317                         {
3318                                 ed->priv.required->mark = stage + 1;
3319                                 found_new = true;
3320                         }
3321                 }
3322                 ++stage;
3323         }
3324         while(found_new);
3325         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3326 }
3327
3328 void PRVM_LeakTest(prvm_prog_t *prog)
3329 {
3330         int i, j;
3331         qboolean leaked = false;
3332
3333         if(!prog->leaktest_active)
3334                 return;
3335
3336         // 1. Strings
3337         for (i = 0; i < prog->numknownstrings; ++i)
3338         {
3339                 if(prog->knownstrings[i])
3340                 if(prog->knownstrings_freeable[i])
3341                 if(prog->knownstrings_origin[i])
3342                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3343                 {
3344                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3345                         leaked = true;
3346                 }
3347         }
3348
3349         // 2. Edicts
3350         PRVM_MarkReferencedEdicts(prog);
3351         for(j = 0; j < prog->num_edicts; ++j)
3352         {
3353                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3354                 if(ed->priv.required->free)
3355                         continue;
3356                 if(!ed->priv.required->mark)
3357                 if(ed->priv.required->allocation_origin)
3358                 {
3359                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3360                         PRVM_ED_Print(prog, ed, NULL);
3361                         Con_Print("\n");
3362                         leaked = true;
3363                 }
3364
3365                 ed->priv.required->mark = 0; // clear marks again when done
3366         }
3367
3368         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3369         {
3370                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3371                 if(stringbuffer)
3372                 if(stringbuffer->origin)
3373                 {
3374                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3375                         leaked = true;
3376                 }
3377         }
3378
3379         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3380         {
3381                 if(prog->openfiles[i])
3382                 if(prog->openfiles_origin[i])
3383                 {
3384                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3385                         leaked = true;
3386                 }
3387         }
3388
3389         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3390         {
3391                 if(prog->opensearches[i])
3392                 if(prog->opensearches_origin[i])
3393                 {
3394                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3395                         leaked = true;
3396                 }
3397         }
3398
3399         if(!leaked)
3400                 Con_Printf("Congratulations. No leaks found.\n");
3401 }