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