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