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