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