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