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