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