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