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;
214 e->freetime = host.realtime;
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;
247 if(prvm_reuseedicts_always_allow == host.realtime)
248 return true;
249 if(host.realtime <= e->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
250 return false; // never allow reuse in same frame (causes networking trouble)
251 if(e->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
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
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
762 if(developer_entityparsing.integer)
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
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;
918
919 if (type != ev_string && type != ev_float && type != ev_entity)
920 continue;
921
922 name = PRVM_GetString(prog, def->s_name);
923
924 if(developer_entityparsing.integer)
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
953 if (developer_entityparsing.integer)
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
962 if (developer_entityparsing.integer)
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);
1057 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
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) == 4) {
1256 // Baker: Turn into prvm_edictget when no value(s) provided
1258 return;
1259 }
1260
1261 if (Cmd_Argc(cmd) != 5)
1262 {
1263 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1264 return;
1265 }
1266
1267 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1268 return;
1269
1270 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1271
1272 if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1273 Con_Printf("Key %s not found!\n", Cmd_Argv(cmd, 3));
1274 else
1275 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1276}
1277
1278/*
1279====================
1280PRVM_ED_ParseEdict
1281
1282Parses an edict out of the given string, returning the new position
1283ed should be a properly initialized empty edict.
1284Used for initial level load and for savegames.
1285====================
1286*/
1287const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent, qbool saveload)
1288{
1289 mdef_t *key;
1290 qbool anglehack;
1291 qbool init = false;
1292 qbool parsebackslash = true;
1293 char keyname[256];
1294 size_t n;
1295
1296// go through all the dictionary pairs
1297 while (1)
1298 {
1299 // parse key
1300 if (!COM_ParseToken_Simple(&data, false, false, true))
1301 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1302 if (developer_entityparsing.integer)
1303 Con_Printf("Key: \"%s\"", com_token);
1304 if (com_token[0] == '}')
1305 break;
1306
1307 // anglehack is to allow QuakeEd to write single scalar angles
1308 // and allow them to be turned into vectors. (FIXME...)
1309 if (!strcmp(com_token, "angle"))
1310 {
1311 dp_strlcpy (com_token, "angles", sizeof(com_token));
1312 anglehack = true;
1313 }
1314 else
1315 anglehack = false;
1316
1317 // FIXME: change light to _light to get rid of this hack
1318 if (!strcmp(com_token, "light"))
1319 dp_strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1320
1321 n = dp_strlcpy (keyname, com_token, sizeof(keyname));
1322
1323 // another hack to fix keynames with trailing spaces
1324 while (n && keyname[n-1] == ' ')
1325 {
1326 keyname[n-1] = 0;
1327 n--;
1328 }
1329
1330 // Check if escape parsing is disabled for this key (see cvar description).
1331 // Escapes are always used in savegames and DP_QC_ENTITYSTRING for compatibility.
1332 if (!saveload)
1333 {
1334 const char *cvarpos = sv_entfields_noescapes.string;
1335
1336 while (COM_ParseToken_Console(&cvarpos))
1337 {
1338 if (strcmp(com_token, keyname) == 0)
1339 {
1340 parsebackslash = false;
1341 break;
1342 }
1343 }
1344 }
1345
1346 // parse value
1347 // If loading a save, unescape characters (they're escaped when saving).
1348 // Otherwise, load them as they are (BSP compilers don't support escaping).
1349 if (!COM_ParseToken_Simple(&data, false, parsebackslash, true))
1350 prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1351 if (developer_entityparsing.integer)
1352 Con_Printf(" \"%s\"\n", com_token);
1353
1354 if (com_token[0] == '}')
1355 prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1356
1357 init = true;
1358
1359 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1360 if (!keyname[0])
1361 continue;
1362
1363// keynames with a leading underscore are used for utility comments,
1364// and are immediately discarded by quake
1365 if (keyname[0] == '_')
1366 continue;
1367
1368 key = PRVM_ED_FindField (prog, keyname);
1369 if (!key)
1370 {
1371 Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1372 continue;
1373 }
1374
1375 if (anglehack)
1376 {
1377 char temp[32];
1378 dp_strlcpy (temp, com_token, sizeof(temp));
1379 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1380 }
1381
1382 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, false))
1383 prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1384 }
1385
1386 if (!init)
1387 {
1388 ent->free = true;
1389 ent->freetime = host.realtime;
1390 }
1391
1392 return data;
1393}
1394
1396{
1397 if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1398 {
1399 // self = ent
1402 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1403 }
1404}
1405
1406qbool PRVM_ED_CallSpawnFunction(prvm_prog_t *prog, prvm_edict_t *ent, const char *data, const char *start)
1407{
1408 const char *funcname;
1409 mfunction_t *func;
1410 prvm_eval_t *fulldata = NULL;
1411 char vabuf[1024];
1412
1413//
1414// immediately call spawn function, but only if there is a self global and a classname
1415//
1416 if (!ent->free)
1417 {
1418 if (!PRVM_alledictstring(ent, classname))
1419 {
1420 Con_Print("No classname for:\n");
1421 PRVM_ED_Print(prog, ent, NULL);
1422 PRVM_ED_Free (prog, ent);
1423 return false;
1424 }
1425 /*
1426 * This is required for FTE compatibility (FreeCS).
1427 * It copies the key/value pairs themselves into a
1428 * global for QC to parse on its own.
1429 */
1430 else if (data && start)
1431 {
1432 if((fulldata = PRVM_ED_FindGlobalEval(prog, "__fullspawndata")))
1433 {
1434 const char *in;
1435 char *spawndata;
1436 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1437 for(in = start; in < data; )
1438 {
1439 char c = *in++;
1440 if(c == '\n')
1441 *spawndata++ = '\t';
1442 else
1443 *spawndata++ = c;
1444 }
1445 *spawndata = 0;
1446 }
1447 }
1448
1449 // look for the spawn function
1450 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1451 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1452 if(!func)
1453 if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1454 func = PRVM_ED_FindFunction (prog, funcname);
1455
1456 if (!func)
1457 {
1458 // check for OnEntityNoSpawnFunction
1459 if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1460 {
1461 // self = ent
1464 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1465 }
1466 else
1467 {
1468
1469 Con_DPrint("No spawn function for:\n");
1470 if (developer.integer > 0) // don't confuse non-developers with errors
1471 PRVM_ED_Print(prog, ent, NULL);
1472
1473 PRVM_ED_Free (prog, ent);
1474 return false; // not included in "inhibited" count
1475 }
1476 }
1477 else
1478 {
1479 // self = ent
1482 prog->ExecuteProgram(prog, func - prog->functions, "");
1483 }
1484 return true;
1485 }
1486 PRVM_ED_Free(prog, ent);
1487 return false;
1488}
1489
1491{
1492 if(!ent->free)
1493 if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1494 {
1495 // self = ent
1498 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1499 }
1500}
1501
1502/*
1503================
1504PRVM_ED_LoadFromFile
1505
1506The entities are directly placed in the array, rather than allocated with
1507PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1508number references out of order.
1509
1510Creates a server's entity / program execution context by
1511parsing textual entity definitions out of an ent file.
1512
1513Used for both fresh maps and savegame loads. A fresh map would also need
1514to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1515================
1516*/
1517void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1518{
1519 prvm_edict_t *ent;
1520 const char *start;
1521 int parsed, inhibited, spawned, died;
1522
1523 parsed = 0;
1524 inhibited = 0;
1525 spawned = 0;
1526 died = 0;
1527
1529
1530 // parse ents
1531 while (1)
1532 {
1533 start = data;
1534
1535 // parse the opening brace
1536 if (!COM_ParseToken_Simple(&data, false, false, true))
1537 break;
1538 if (com_token[0] != '{')
1539 prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1540
1541 // CHANGED: this is not conform to PR_LoadFromFile
1542 if(prog->loadintoworld)
1543 {
1544 prog->loadintoworld = false;
1545 ent = PRVM_EDICT_NUM(0);
1546 }
1547 else
1548 ent = PRVM_ED_Alloc(prog);
1549
1550 // clear it
1551 if (ent != prog->edicts) // hack
1552 memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1553
1554 data = PRVM_ED_ParseEdict (prog, data, ent, false);
1555 parsed++;
1556
1557 // remove the entity ?
1558 if(!prog->load_edict(prog, ent))
1559 {
1560 PRVM_ED_Free(prog, ent);
1561 inhibited++;
1562 continue;
1563 }
1564
1566
1567 if(ent->free)
1568 {
1569 inhibited++;
1570 continue;
1571 }
1572
1573 SV_LinkEdict(ent);
1574
1575 if(!PRVM_ED_CallSpawnFunction(prog, ent, data, start))
1576 continue;
1577
1579
1580 spawned++;
1581 if (ent->free)
1582 died++;
1583 }
1584
1585 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);
1586
1588}
1589
1591{
1592 // field and global searches use -1 for NULL
1593 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1594 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1595 // function searches use 0 for NULL
1596 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1597#define PRVM_DECLARE_serverglobalfloat(x)
1598#define PRVM_DECLARE_serverglobalvector(x)
1599#define PRVM_DECLARE_serverglobalstring(x)
1600#define PRVM_DECLARE_serverglobaledict(x)
1601#define PRVM_DECLARE_serverglobalfunction(x)
1602#define PRVM_DECLARE_clientglobalfloat(x)
1603#define PRVM_DECLARE_clientglobalvector(x)
1604#define PRVM_DECLARE_clientglobalstring(x)
1605#define PRVM_DECLARE_clientglobaledict(x)
1606#define PRVM_DECLARE_clientglobalfunction(x)
1607#define PRVM_DECLARE_menuglobalfloat(x)
1608#define PRVM_DECLARE_menuglobalvector(x)
1609#define PRVM_DECLARE_menuglobalstring(x)
1610#define PRVM_DECLARE_menuglobaledict(x)
1611#define PRVM_DECLARE_menuglobalfunction(x)
1612#define PRVM_DECLARE_serverfieldfloat(x)
1613#define PRVM_DECLARE_serverfieldvector(x)
1614#define PRVM_DECLARE_serverfieldstring(x)
1615#define PRVM_DECLARE_serverfieldedict(x)
1616#define PRVM_DECLARE_serverfieldfunction(x)
1617#define PRVM_DECLARE_clientfieldfloat(x)
1618#define PRVM_DECLARE_clientfieldvector(x)
1619#define PRVM_DECLARE_clientfieldstring(x)
1620#define PRVM_DECLARE_clientfieldedict(x)
1621#define PRVM_DECLARE_clientfieldfunction(x)
1622#define PRVM_DECLARE_menufieldfloat(x)
1623#define PRVM_DECLARE_menufieldvector(x)
1624#define PRVM_DECLARE_menufieldstring(x)
1625#define PRVM_DECLARE_menufieldedict(x)
1626#define PRVM_DECLARE_menufieldfunction(x)
1627#define PRVM_DECLARE_serverfunction(x)
1628#define PRVM_DECLARE_clientfunction(x)
1629#define PRVM_DECLARE_menufunction(x)
1630#define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1631#define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1632#define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1633#include "prvm_offsets.h"
1634#undef PRVM_DECLARE_serverglobalfloat
1635#undef PRVM_DECLARE_serverglobalvector
1636#undef PRVM_DECLARE_serverglobalstring
1637#undef PRVM_DECLARE_serverglobaledict
1638#undef PRVM_DECLARE_serverglobalfunction
1639#undef PRVM_DECLARE_clientglobalfloat
1640#undef PRVM_DECLARE_clientglobalvector
1641#undef PRVM_DECLARE_clientglobalstring
1642#undef PRVM_DECLARE_clientglobaledict
1643#undef PRVM_DECLARE_clientglobalfunction
1644#undef PRVM_DECLARE_menuglobalfloat
1645#undef PRVM_DECLARE_menuglobalvector
1646#undef PRVM_DECLARE_menuglobalstring
1647#undef PRVM_DECLARE_menuglobaledict
1648#undef PRVM_DECLARE_menuglobalfunction
1649#undef PRVM_DECLARE_serverfieldfloat
1650#undef PRVM_DECLARE_serverfieldvector
1651#undef PRVM_DECLARE_serverfieldstring
1652#undef PRVM_DECLARE_serverfieldedict
1653#undef PRVM_DECLARE_serverfieldfunction
1654#undef PRVM_DECLARE_clientfieldfloat
1655#undef PRVM_DECLARE_clientfieldvector
1656#undef PRVM_DECLARE_clientfieldstring
1657#undef PRVM_DECLARE_clientfieldedict
1658#undef PRVM_DECLARE_clientfieldfunction
1659#undef PRVM_DECLARE_menufieldfloat
1660#undef PRVM_DECLARE_menufieldvector
1661#undef PRVM_DECLARE_menufieldstring
1662#undef PRVM_DECLARE_menufieldedict
1663#undef PRVM_DECLARE_menufieldfunction
1664#undef PRVM_DECLARE_serverfunction
1665#undef PRVM_DECLARE_clientfunction
1666#undef PRVM_DECLARE_menufunction
1667#undef PRVM_DECLARE_field
1668#undef PRVM_DECLARE_global
1669#undef PRVM_DECLARE_function
1670}
1671
1672// not used
1673/*
1674typedef struct dpfield_s
1675{
1676 int type;
1677 char *string;
1678}
1679dpfield_t;
1680
1681#define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1682
1683dpfield_t dpfields[] =
1684{
1685};
1686*/
1687
1688/*
1689===============
1690PRVM_ResetProg
1691===============
1692*/
1693
1694#define PO_HASHSIZE 16384
1695typedef struct po_string_s
1696{
1697 char *key, *value;
1698 struct po_string_s *nextonhashchain;
1699}
1701typedef struct po_s
1702{
1704}
1705po_t;
1706static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1707{
1708 for(;;)
1709 {
1710 switch(*in)
1711 {
1712 case 0:
1713 *out++ = 0;
1714 return;
1715 case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1716 case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1717 case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1718 case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1719 case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1720 case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1721 case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1722 default:
1723 if(*in >= 0 && *in <= 0x1F)
1724 {
1725 if(outsize >= 4)
1726 {
1727 *out++ = '\\';
1728 *out++ = '0' + ((*in & 0700) >> 6);
1729 *out++ = '0' + ((*in & 0070) >> 3);
1730 *out++ = '0' + (*in & 0007) ;
1731 outsize -= 4;
1732 }
1733 }
1734 else
1735 {
1736 if(outsize >= 1)
1737 {
1738 *out++ = *in;
1739 outsize -= 1;
1740 }
1741 }
1742 break;
1743 }
1744 ++in;
1745 }
1746}
1747static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1748{
1749 for(;;)
1750 {
1751 switch(*in)
1752 {
1753 case 0:
1754 *out++ = 0;
1755 return;
1756 case '\\':
1757 ++in;
1758 switch(*in)
1759 {
1760 case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1761 case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1762 case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1763 case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1764 case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1765 case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1766 case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1767 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1768 if(outsize > 0)
1769 *out = *in - '0';
1770 ++in;
1771 if(*in >= '0' && *in <= '7')
1772 {
1773 if(outsize > 0)
1774 *out = (*out << 3) | (*in - '0');
1775 ++in;
1776 }
1777 if(*in >= '0' && *in <= '7')
1778 {
1779 if(outsize > 0)
1780 *out = (*out << 3) | (*in - '0');
1781 ++in;
1782 }
1783 --in;
1784 if(outsize > 0)
1785 {
1786 ++out;
1787 --outsize;
1788 }
1789 break;
1790 default:
1791 if(outsize > 0) { *out++ = *in; --outsize; }
1792 break;
1793 }
1794 break;
1795 default:
1796 if(outsize > 0)
1797 {
1798 *out++ = *in;
1799 --outsize;
1800 }
1801 break;
1802 }
1803 ++in;
1804 }
1805}
1806static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1807{
1808 po_t *po = NULL;
1809 const char *p, *q;
1810 int mode;
1811 char inbuf[MAX_INPUTLINE];
1812 char decodedbuf[MAX_INPUTLINE];
1813 size_t decodedpos;
1814 int hashindex;
1815 po_string_t thisstr;
1816 int i;
1817
1818 for (i = 0; i < 2; ++i)
1819 {
1820 const char *buf = (const char *)
1821 FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1822 // first read filename2, then read filename
1823 // so that progs.dat.de.po wins over common.de.po
1824 // and within file, last item wins
1825
1826 if(!buf)
1827 continue;
1828
1829 if (!po)
1830 {
1831 po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1832 memset(po, 0, sizeof(*po));
1833 }
1834
1835 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1836
1837 p = buf;
1838 while(*p)
1839 {
1840 if(*p == '#')
1841 {
1842 // skip to newline
1843 p = strchr(p, '\n');
1844 if(!p)
1845 break;
1846 ++p;
1847 continue;
1848 }
1849 if(*p == '\r' || *p == '\n')
1850 {
1851 ++p;
1852 continue;
1853 }
1854 if(!strncmp(p, "msgid \"", 7))
1855 {
1856 mode = 0;
1857 p += 6;
1858 }
1859 else if(!strncmp(p, "msgstr \"", 8))
1860 {
1861 mode = 1;
1862 p += 7;
1863 }
1864 else
1865 {
1866 p = strchr(p, '\n');
1867 if(!p)
1868 break;
1869 ++p;
1870 continue;
1871 }
1872 decodedpos = 0;
1873 while(*p == '"')
1874 {
1875 ++p;
1876 q = strchr(p, '\n');
1877 if(!q)
1878 break;
1879 if(*(q-1) == '\r')
1880 --q;
1881 if(*(q-1) != '"')
1882 break;
1883 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1884 break;
1885 memcpy(inbuf, p, q - p - 1);
1886 inbuf[q - p - 1] = '\0';
1887 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1888 decodedpos += strlen(decodedbuf + decodedpos);
1889 if(*q == '\r')
1890 ++q;
1891 if(*q == '\n')
1892 ++q;
1893 p = q;
1894 }
1895 if(mode == 0)
1896 {
1897 if(thisstr.key)
1898 Mem_Free(thisstr.key);
1899 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1900 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1901 }
1902 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1903 {
1904 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1905 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1906 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1907 thisstr.nextonhashchain = po->hashtable[hashindex];
1908 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1909 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1910 memset(&thisstr, 0, sizeof(thisstr));
1911 }
1912 }
1913
1914 Mem_Free((char *) buf);
1915 }
1916
1917 return po;
1918}
1919static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1920{
1921 int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1922 po_string_t *p = po->hashtable[hashindex];
1923 while(p)
1924 {
1925 if(!strcmp(str, p->key))
1926 return p->value;
1927 p = p->nextonhashchain;
1928 }
1929 return NULL;
1930}
1931static void PRVM_PO_Destroy(po_t *po)
1932{
1933 int i;
1934 for(i = 0; i < PO_HASHSIZE; ++i)
1935 {
1936 po_string_t *p = po->hashtable[i];
1937 while(p)
1938 {
1939 po_string_t *q = p;
1940 p = p->nextonhashchain;
1941 Mem_Free(q->key);
1942 Mem_Free(q->value);
1943 Mem_Free(q);
1944 }
1945 }
1946 Mem_Free(po);
1947}
1948
1949void PRVM_LeakTest(prvm_prog_t *prog);
1951{
1952 if (prog->loaded)
1953 {
1954 if(prog->tempstringsbuf.cursize)
1956 prog->tempstringsbuf.cursize = 0;
1957 PRVM_LeakTest(prog);
1958 prog->reset_cmd(prog);
1960 if(prog->po)
1961 PRVM_PO_Destroy((po_t *) prog->po);
1962 }
1963 memset(prog,0,sizeof(prvm_prog_t));
1964 prog->break_statement = -1;
1965 prog->watch_global_type = ev_void;
1966 prog->watch_field_type = ev_void;
1967}
1968
1969/*
1970===============
1971PRVM_LoadLNO
1972===============
1973*/
1974static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1975 fs_offset_t filesize;
1976 unsigned char *lno;
1977 unsigned int *header;
1978 char filename[512];
1979
1980 FS_StripExtension( progname, filename, sizeof( filename ) );
1981 dp_strlcat( filename, ".lno", sizeof( filename ) );
1982
1983 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1984 if( !lno ) {
1985 return;
1986 }
1987
1988/*
1989<Spike> SafeWrite (h, &lnotype, sizeof(int));
1990<Spike> SafeWrite (h, &version, sizeof(int));
1991<Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1992<Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1993<Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1994<Spike> SafeWrite (h, &numstatements, sizeof(int));
1995<Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1996*/
1997 if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1998 {
1999 Mem_Free(lno);
2000 return;
2001 }
2002
2003 header = (unsigned int *) lno;
2004 if (memcmp(lno, "LNOF", 4) == 0
2005 && LittleLong( header[ 1 ] ) == 1
2006 && (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs
2007 && (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals
2008 && (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs
2009 && (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements)
2010 {
2011 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
2012 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
2013
2014 /* gmqcc suports columnums */
2015 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
2016 {
2017 prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
2018 memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
2019 }
2020 }
2021 Mem_Free( lno );
2022}
2023
2024/*
2025===============
2026PRVM_Prog_Load
2027===============
2028*/
2029static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
2030void 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)
2031{
2032 int i;
2033 dprograms_t *dprograms;
2034
2035 dstatement16_t *instatements16;
2036 dstatement32_t *instatements32;
2037 ddef16_t *infielddefs16;
2038 ddef32_t *infielddefs32;
2039 ddef16_t *inglobaldefs16;
2040 ddef32_t *inglobaldefs32;
2041
2042 int *inglobals;
2043 dfunction_t *infunctions;
2044 char *instrings;
2045 fs_offset_t filesize;
2046 int requiredglobalspace;
2047 opcode_t op;
2048 int a;
2049 int b;
2050 int c;
2051 union
2052 {
2053 unsigned int i;
2054 float f;
2055 }
2056 u;
2057 unsigned int d;
2058 char vabuf[1024];
2059 char vabuf2[1024];
2060 cvar_t *cvar;
2061 int structtype = 0;
2062 int max_safe_edicts;
2063 bool gaddress_warned = false;
2064
2065 if (prog->loaded)
2066 prog->error_cmd("%s: there is already a %s program loaded!", __func__, prog->name);
2067
2068 Host_LockSession(); // all progs can use the session cvar
2069 Crypto_LoadKeys(); // all progs might use the keys at init time
2070
2071 if (data)
2072 {
2073 dprograms = (dprograms_t *) data;
2074 filesize = size;
2075 }
2076 else
2077 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2078 if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2079 prog->error_cmd("%s: couldn't load \"%s\" for %s", __func__, filename, prog->name);
2080 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2081
2082 prog->profiletime = Sys_DirtyTime();
2083 prog->starttime = host.realtime;
2084
2085 requiredglobalspace = 0;
2086 for (i = 0;i < numrequiredglobals;i++)
2087 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2088
2089 prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2090
2091// byte swap the header
2092 prog->progs_version = LittleLong(dprograms->version);
2093 prog->progs_crc = LittleLong(dprograms->crc);
2094 if (prog->progs_version == 7)
2095 {
2096 dprograms_v7_t *v7 = (dprograms_v7_t*)dprograms;
2097 structtype = LittleLong(v7->secondaryversion);
2098 if (structtype == PROG_SECONDARYVERSION16 ||
2099 structtype == PROG_SECONDARYVERSION32) // barely supported
2100 Con_Printf(CON_WARN "WARNING: %s: %s targets FTEQW, for which support is incomplete. Proceed at your own risk.\n", prog->name, filename);
2101 else
2102 prog->error_cmd("%s: %s targets unknown engine", prog->name, filename);
2103
2104 if (v7->numbodylessfuncs != 0 || v7->numtypes != 0 || v7->blockscompressed != 0)
2105 prog->error_cmd("%s: %s uses unsupported features.", prog->name, filename);
2106 }
2107 else if (prog->progs_version != PROG_VERSION)
2108 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2109 instatements16 = (dstatement16_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2110 instatements32 = (dstatement32_t *)instatements16;
2111 prog->progs_numstatements = LittleLong(dprograms->numstatements);
2112 inglobaldefs16 = (ddef16_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2113 inglobaldefs32 = (ddef32_t *)inglobaldefs16;
2114 prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2115 infielddefs16 = (ddef16_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2116 infielddefs32 = (ddef32_t *)infielddefs16;
2117 prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2118 infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2119 prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2120 instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2121 prog->progs_numstrings = LittleLong(dprograms->numstrings);
2122 inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2123 prog->progs_numglobals = LittleLong(dprograms->numglobals);
2124 prog->progs_entityfields = LittleLong(dprograms->entityfields);
2125
2126 prog->numstatements = prog->progs_numstatements;
2127 prog->numglobaldefs = prog->progs_numglobaldefs;
2128 prog->numfielddefs = prog->progs_numfielddefs;
2129 prog->numfunctions = prog->progs_numfunctions;
2130 prog->numstrings = prog->progs_numstrings;
2131 prog->numglobals = prog->progs_numglobals;
2132 prog->entityfields = prog->progs_entityfields;
2133
2134 if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2135 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2136 prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2137 memcpy(prog->strings, instrings, prog->progs_numstrings);
2138 prog->stringssize = prog->progs_numstrings;
2139
2140 prog->numknownstrings = 0;
2141 prog->maxknownstrings = 0;
2142 prog->knownstrings = NULL;
2143 prog->knownstrings_flags = NULL;
2144
2146
2147 // we need to expand the globaldefs and fielddefs to include engine defs
2148 prog->globaldefs = (mdef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(mdef_t));
2149 prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2150 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2151 // when trying to return the last or second-last global
2152 // (RETURN always returns a vector, there is no RETURN_F instruction)
2153 prog->fielddefs = (mdef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(mdef_t));
2154 // we need to convert the statements to our memory format
2156 // allocate space for profiling statement usage
2157 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2158 prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2159 // functions need to be converted to the memory format
2161
2162 for (i = 0;i < prog->progs_numfunctions;i++)
2163 {
2164 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2165 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2166 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2167 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2168 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2169 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2170 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2171 if(prog->functions[i].first_statement >= prog->numstatements)
2172 prog->error_cmd("%s: out of bounds function statement (function %d) in %s", __func__, i, prog->name);
2173 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2174 }
2175
2176 // copy the globaldefs to the new globaldefs list
2177 switch(structtype)
2178 {
2180 for (i=0 ; i<prog->numglobaldefs ; i++)
2181 {
2182 prog->globaldefs[i].type = LittleLong(inglobaldefs32[i].type);
2183 prog->globaldefs[i].ofs = LittleLong(inglobaldefs32[i].ofs);
2184 prog->globaldefs[i].s_name = LittleLong(inglobaldefs32[i].s_name);
2185 // TODO bounds check ofs, s_name
2186 }
2187 break;
2188 default:
2189 for (i=0 ; i<prog->numglobaldefs ; i++)
2190 {
2191 prog->globaldefs[i].type = (unsigned short)LittleShort(inglobaldefs16[i].type);
2192 prog->globaldefs[i].ofs = (unsigned short)LittleShort(inglobaldefs16[i].ofs);
2193 prog->globaldefs[i].s_name = LittleLong(inglobaldefs16[i].s_name);
2194 // TODO bounds check ofs, s_name
2195 }
2196 break;
2197 }
2198
2199 // append the required globals
2200 for (i = 0;i < numrequiredglobals;i++)
2201 {
2202 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2203 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2204 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2205 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2206 prog->numglobals += 3;
2207 else
2208 prog->numglobals++;
2209 prog->numglobaldefs++;
2210 }
2211
2212 // copy the progs fields to the new fields list
2213 switch(structtype)
2214 {
2216 for (i = 0;i < prog->numfielddefs;i++)
2217 {
2218 prog->fielddefs[i].type = LittleLong(infielddefs32[i].type);
2219 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2220 prog->error_cmd("%s: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", __func__, prog->name);
2221 prog->fielddefs[i].ofs = LittleLong(infielddefs32[i].ofs);
2222 prog->fielddefs[i].s_name = LittleLong(infielddefs32[i].s_name);
2223 // TODO bounds check ofs, s_name
2224 }
2225 break;
2226 default:
2227 for (i = 0;i < prog->numfielddefs;i++)
2228 {
2229 prog->fielddefs[i].type = (unsigned short)LittleShort(infielddefs16[i].type);
2230 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2231 prog->error_cmd("%s: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", __func__, prog->name);
2232 prog->fielddefs[i].ofs = (unsigned short)LittleShort(infielddefs16[i].ofs);
2233 prog->fielddefs[i].s_name = LittleLong(infielddefs16[i].s_name);
2234 // TODO bounds check ofs, s_name
2235 }
2236 break;
2237 }
2238
2239 // append the required fields
2240 for (i = 0;i < numrequiredfields;i++)
2241 {
2242 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2243 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2244 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2245 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2246 prog->entityfields += 3;
2247 else
2248 prog->entityfields++;
2249 prog->numfielddefs++;
2250 }
2251
2252 // LadyHavoc: TODO: reorder globals to match engine struct
2253 // LadyHavoc: TODO: reorder fields to match engine struct
2254 // divVerent: note that when ever doing any remapping, globaldefs with pre-initialized global pointers need to also be identified and remapped
2255#define remapglobal(index) (index)
2256#define remapfield(index) (index)
2257
2258 // copy globals
2259 // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2260 for (i = 0;i < prog->progs_numglobals;i++)
2261 {
2262 u.i = LittleLong(inglobals[i]);
2263 // most globals are 0, we only need to deal with the ones that are not
2264 if (u.i)
2265 {
2266 d = u.i & 0xFF800000;
2267 if ((d == 0xFF800000) || (d == 0))
2268 {
2269 // Looks like an integer (expand to int64)
2270 prog->globals.ip[remapglobal(i)] = u.i;
2271 }
2272 else
2273 {
2274 // Looks like a float (expand to double)
2275 prog->globals.fp[remapglobal(i)] = u.f;
2276 }
2277 }
2278 }
2279
2280 // copy, remap globals in statements, bounds check
2281 for (i = 0;i < prog->progs_numstatements;i++)
2282 {
2283 switch(structtype)
2284 {
2286 op = (opcode_t)LittleLong(instatements32[i].op);
2287 a = (unsigned int)LittleLong(instatements32[i].a);
2288 b = (unsigned int)LittleLong(instatements32[i].b);
2289 c = (unsigned int)LittleLong(instatements32[i].c);
2290 break;
2291 default:
2292 op = (opcode_t)LittleShort(instatements16[i].op);
2293 a = (unsigned short)LittleShort(instatements16[i].a);
2294 b = (unsigned short)LittleShort(instatements16[i].b);
2295 c = (unsigned short)LittleShort(instatements16[i].c);
2296 break;
2297 }
2298 switch (op)
2299 {
2300 case OP_IF:
2301 case OP_IFNOT:
2302 b = (short)b;
2303 if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2304 prog->error_cmd("%s: out of bounds IF/IFNOT (statement %d) in %s", __func__, i, prog->name);
2305 if (c)
2306 Con_DPrintf("%s: unexpected offset on binary opcode in %s\n", __func__, prog->name);
2307 prog->statements[i].op = op;
2308 prog->statements[i].operand[0] = remapglobal(a);
2309 prog->statements[i].operand[1] = b;
2310 prog->statements[i].operand[2] = -1;
2311 break;
2312 case OP_GOTO:
2313 a = (short)a;
2314 if (a + i < 0 || a + i >= prog->progs_numstatements)
2315 prog->error_cmd("%s: out of bounds GOTO (statement %d) in %s", __func__, i, prog->name);
2316 if (b || c)
2317 Con_DPrintf("%s: unexpected offset on unary opcode in %s\n", __func__, prog->name);
2318 prog->statements[i].op = op;
2319 prog->statements[i].operand[0] = a;
2320 prog->statements[i].operand[1] = -1;
2321 prog->statements[i].operand[2] = -1;
2322 break;
2323 default:
2324 Con_DPrintf("%s: unknown opcode %d at statement %d in %s\n", __func__, (int)op, i, prog->name);
2325
2326 //make sure its something well defined.
2327 prog->statements[i].op = OP_BOUNDCHECK;
2328 prog->statements[i].operand[0] = 0;
2329 prog->statements[i].operand[1] =
2330 prog->statements[i].operand[2] = op;
2331 break;
2332 // global global global
2333 case OP_ADD_I:
2334 case OP_ADD_FI:
2335 case OP_ADD_IF:
2336 case OP_SUB_I:
2337 case OP_SUB_FI:
2338 case OP_SUB_IF:
2339 case OP_CONV_ITOF:
2340 case OP_CONV_FTOI:
2341 case OP_LOAD_I:
2342 case OP_BITAND_I:
2343 case OP_BITOR_I:
2344 case OP_MUL_I:
2345 case OP_DIV_I:
2346 case OP_EQ_I:
2347 case OP_NE_I:
2348 case OP_NOT_I:
2349 case OP_DIV_VF:
2350 case OP_LE_I:
2351 case OP_GE_I:
2352 case OP_LT_I:
2353 case OP_GT_I:
2354 case OP_LE_IF:
2355 case OP_GE_IF:
2356 case OP_LT_IF:
2357 case OP_GT_IF:
2358 case OP_LE_FI:
2359 case OP_GE_FI:
2360 case OP_LT_FI:
2361 case OP_GT_FI:
2362 case OP_EQ_IF:
2363 case OP_EQ_FI:
2364 case OP_MUL_IF:
2365 case OP_MUL_FI:
2366 case OP_MUL_VI:
2367 case OP_DIV_IF:
2368 case OP_DIV_FI:
2369 case OP_BITAND_IF:
2370 case OP_BITOR_IF:
2371 case OP_BITAND_FI:
2372 case OP_BITOR_FI:
2373 case OP_AND_I:
2374 case OP_OR_I:
2375 case OP_AND_IF:
2376 case OP_OR_IF:
2377 case OP_AND_FI:
2378 case OP_OR_FI:
2379 case OP_NE_IF:
2380 case OP_NE_FI:
2381 case OP_GSTOREP_I:
2382 case OP_GSTOREP_F:
2383 case OP_GSTOREP_ENT:
2384 case OP_GSTOREP_FLD:
2385 case OP_GSTOREP_S:
2386 case OP_GSTOREP_FNC:
2387 case OP_GSTOREP_V:
2388// case OP_GADDRESS:
2389 case OP_GLOAD_I:
2390 case OP_GLOAD_F:
2391 case OP_GLOAD_FLD:
2392 case OP_GLOAD_ENT:
2393 case OP_GLOAD_S:
2394 case OP_GLOAD_FNC:
2395 case OP_BOUNDCHECK:
2396 case OP_GLOAD_V:
2397 case OP_ADD_F:
2398 case OP_ADD_V:
2399 case OP_SUB_F:
2400 case OP_SUB_V:
2401 case OP_MUL_F:
2402 case OP_MUL_V:
2403 case OP_MUL_FV:
2404 case OP_MUL_VF:
2405 case OP_DIV_F:
2406 case OP_BITAND_F:
2407 case OP_BITOR_F:
2408 case OP_GE_F:
2409 case OP_LE_F:
2410 case OP_GT_F:
2411 case OP_LT_F:
2412 case OP_AND_F:
2413 case OP_OR_F:
2414 case OP_EQ_F:
2415 case OP_EQ_V:
2416 case OP_EQ_S:
2417 case OP_EQ_E:
2418 case OP_EQ_FNC:
2419 case OP_NE_F:
2420 case OP_NE_V:
2421 case OP_NE_S:
2422 case OP_NE_E:
2423 case OP_NE_FNC:
2424 case OP_ADDRESS:
2425 case OP_LOAD_F:
2426 case OP_LOAD_FLD:
2427 case OP_LOAD_ENT:
2428 case OP_LOAD_S:
2429 case OP_LOAD_FNC:
2430 case OP_LOAD_V:
2431 case OP_LOAD_P:
2432 case OP_ADD_PIW:
2433 case OP_GLOBALADDRESS:
2434 case OP_LOADA_F:
2435 case OP_LOADA_V:
2436 case OP_LOADA_S:
2437 case OP_LOADA_ENT:
2438 case OP_LOADA_FLD:
2439 case OP_LOADA_FNC:
2440 case OP_LOADA_I:
2441 case OP_LOADP_F:
2442 case OP_LOADP_V:
2443 case OP_LOADP_S:
2444 case OP_LOADP_ENT:
2445 case OP_LOADP_FLD:
2446 case OP_LOADP_FNC:
2447 case OP_LOADP_I:
2448 case OP_STOREP_F:
2449 case OP_STOREP_ENT:
2450 case OP_STOREP_FLD:
2451 case OP_STOREP_S:
2452 case OP_STOREP_FNC:
2453 case OP_STOREP_V:
2454 case OP_STOREP_I:
2455 case OP_RSHIFT_I:
2456 case OP_LSHIFT_I:
2457 case OP_LE_U:
2458 case OP_LT_U:
2459 case OP_DIV_U:
2460 case OP_RSHIFT_U:
2461 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2462 prog->error_cmd("%s: out of bounds global index (statement %d)", __func__, i);
2463 prog->statements[i].op = op;
2464 prog->statements[i].operand[0] = remapglobal(a);
2465 prog->statements[i].operand[1] = remapglobal(b);
2466 prog->statements[i].operand[2] = remapglobal(c);
2467 break;
2468 // global none global
2469 case OP_NOT_F:
2470 case OP_NOT_V:
2471 case OP_NOT_S:
2472 case OP_NOT_FNC:
2473 case OP_NOT_ENT:
2474 if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2475 prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
2476 if (b)
2477 Con_DPrintf("%s: unexpected offset on binary opcode in %s\n", __func__, prog->name);
2478 prog->statements[i].op = op;
2479 prog->statements[i].operand[0] = remapglobal(a);
2480 prog->statements[i].operand[1] = -1;
2481 prog->statements[i].operand[2] = remapglobal(c);
2482 break;
2483 // global global none
2484 case OP_STORE_F:
2485 case OP_STORE_ENT:
2486 case OP_STORE_FLD:
2487 case OP_STORE_S:
2488 case OP_STORE_FNC:
2489 case OP_STORE_V:
2490 case OP_STORE_I:
2491 case OP_STORE_P:
2492 case OP_STATE:
2493 if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2494 prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
2495 if (c)
2496 Con_DPrintf("%s: unexpected offset on binary opcode in %s\n", __func__, prog->name);
2497 prog->statements[i].op = op;
2498 prog->statements[i].operand[0] = remapglobal(a);
2499 prog->statements[i].operand[1] = remapglobal(b);
2500 prog->statements[i].operand[2] = -1;
2501 break;
2502 // 1 global
2503 case OP_CALL0:
2504 if ( a < prog->progs_numglobals)
2505 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2506 if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2507 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2509 case OP_CALL1:
2510 case OP_CALL2:
2511 case OP_CALL3:
2512 case OP_CALL4:
2513 case OP_CALL5:
2514 case OP_CALL6:
2515 case OP_CALL7:
2516 case OP_CALL8:
2517 case OP_DONE:
2518 case OP_RETURN:
2519 if ( a >= prog->progs_numglobals)
2520 prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
2521 if (b || c) //Spike -- added this check just as a diagnostic...
2522 Con_DPrintf("%s: unexpected offset on call opcode in %s. Hexen2 format is not supported\n", __func__, prog->name);
2523 prog->statements[i].op = op;
2524 prog->statements[i].operand[0] = remapglobal(a);
2525 prog->statements[i].operand[1] = -1;
2526 prog->statements[i].operand[2] = -1;
2527 break;
2528 }
2529
2530 // Warn about deprecated instructions.
2531 switch (op)
2532 {
2533 case OP_GSTOREP_I:
2534 case OP_GSTOREP_F:
2535 case OP_GSTOREP_ENT:
2536 case OP_GSTOREP_FLD:
2537 case OP_GSTOREP_S:
2538 case OP_GSTOREP_FNC:
2539 case OP_GSTOREP_V:
2540 //case OP_GADDRESS:
2541 case OP_GLOAD_I:
2542 case OP_GLOAD_F:
2543 case OP_GLOAD_FLD:
2544 case OP_GLOAD_ENT:
2545 case OP_GLOAD_S:
2546 case OP_GLOAD_FNC:
2547 case OP_GLOAD_V:
2548 if (!gaddress_warned) {
2549 Con_Printf(CON_WARN "WARNING: %s: %s uses deprecated OP_G* instructions, which are unlikely to be used correctly. Proceed at your own risk.\n", prog->name, filename);
2550 gaddress_warned = true;
2551 }
2552 break;
2553 default:
2554 break;
2555 }
2556 }
2557 if(prog->numstatements < 1)
2558 {
2559 prog->error_cmd("%s: empty program in %s", __func__, prog->name);
2560 }
2561 else switch(prog->statements[prog->numstatements - 1].op)
2562 {
2563 case OP_RETURN:
2564 case OP_GOTO:
2565 case OP_DONE:
2566 break;
2567 default:
2568 prog->error_cmd("%s: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", __func__, prog->name);
2569 break;
2570 }
2571
2572 // we're done with the file now
2573 if(!data)
2574 Mem_Free(dprograms);
2575 dprograms = NULL;
2576
2577 prog->flag = 0;
2578 // expected to not return (call prog->error_cmd) if checks fail
2579 CheckRequiredFuncs(prog, filename);
2580
2581 PRVM_LoadLNO(prog, filename);
2582
2583 PRVM_Init_Exec(prog);
2584
2585 if(*prvm_language.string)
2586 // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2587 // later idea: include a list of authorized .po file checksums with the csprogs
2588 {
2589 qbool deftrans = prog == CLVM_prog;
2590 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2591 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2592 {
2593 for (i=0 ; i<prog->numglobaldefs ; i++)
2594 {
2595 const char *name;
2596 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2597 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2598 if(name && !strncmp(name, "dotranslate_", 12))
2599 {
2600 deftrans = false;
2601 break;
2602 }
2603 }
2604 }
2605 if(!strcmp(prvm_language.string, "dump"))
2606 {
2607 qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2608 Con_Printf("Dumping to %s.pot\n", realfilename);
2609 if(f)
2610 {
2611 for (i=0 ; i<prog->numglobaldefs ; i++)
2612 {
2613 const char *name;
2614 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2615 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2616 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2617 {
2619 const char *value = PRVM_GetString(prog, val->string);
2620 if(*value)
2621 {
2622 char buf[MAX_INPUTLINE];
2624 FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2625 }
2626 }
2627 }
2628 FS_Close(f);
2629 }
2630 }
2631 else
2632 {
2633 po_t *po = PRVM_PO_Load(
2634 va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2635 va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2636 prog->progs_mempool);
2637 if(po)
2638 {
2639 for (i=0 ; i<prog->numglobaldefs ; i++)
2640 {
2641 const char *name;
2642 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2643 if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2644 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2645 {
2647 const char *value = PRVM_GetString(prog, val->string);
2648 if(*value)
2649 {
2650 value = PRVM_PO_Lookup(po, value);
2651 if(value)
2652 val->string = PRVM_SetEngineString(prog, value);
2653 }
2654 }
2655 }
2656 }
2657 }
2658 }
2659
2660 for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2661 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2662
2663 for (i=0 ; i<prog->numglobaldefs ; i++)
2664 {
2665 const char *name;
2666 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2667 //Con_Printf("found var %s\n", name);
2668 if(name
2669 && !strncmp(name, "autocvar_", 9)
2670 && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2671 )
2672 {
2674 cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2675 //Con_Printf("%s: autocvar global %s in %s, processing...\n", __func__, name, prog->name);
2676 if(!cvar)
2677 {
2678 const char *value;
2679 char buf[128];
2680 int prec[3];
2681 float f;
2682 Con_DPrintf("%s: no cvar for autocvar global %s in %s, creating...\n", __func__, name, prog->name);
2683 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2684 {
2685 case ev_float:
2686 if((float)((int)(val->_float)) == val->_float)
2687 dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2688 else
2689 {
2690 // ftos_slow
2691 f = val->_float;
2692 for (int precision = 7; precision <= 9; ++precision) {
2693 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2694 if ((float)atof(buf) == f) {
2695 break;
2696 }
2697 }
2698 }
2699 value = buf;
2700 break;
2701 case ev_vector:
2702 for (i = 0; i < 3; ++i)
2703 {
2704 prec[i] = 9;
2705 f = val->vector[i];
2706 for (int precision = 7; precision <= 9; ++precision) {
2707 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2708 if ((float)atof(buf) == f) {
2709 prec[i] = precision;
2710 break;
2711 }
2712 }
2713 }
2714 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2715 value = buf;
2716 break;
2717 case ev_string:
2718 value = PRVM_GetString(prog, val->string);
2719 break;
2720 default:
2721 Con_Printf("%s: invalid type of autocvar global %s in %s\n", __func__, name, prog->name);
2722 goto fail;
2723 }
2724 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2725 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2726 {
2727 val->string = PRVM_SetEngineString(prog, cvar->string);
2728 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2729 }
2730 if(!cvar)
2731 prog->error_cmd("%s: could not create cvar for autocvar global %s in %s", __func__, name, prog->name);
2732 cvar->globaldefindex[prog - prvm_prog_list] = i;
2733 }
2734 else if((cvar->flags & CF_PRIVATE) == 0)
2735 {
2736 // MUST BE SYNCED WITH cvar.c Cvar_Set
2737 int j;
2738 const char *s;
2739 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2740 {
2741 case ev_float:
2742 val->_float = cvar->value;
2743 break;
2744 case ev_vector:
2745 s = cvar->string;
2746 VectorClear(val->vector);
2747 for (j = 0;j < 3;j++)
2748 {
2749 while (*s && ISWHITESPACE(*s))
2750 s++;
2751 if (!*s)
2752 break;
2753 val->vector[j] = atof(s);
2754 while (!ISWHITESPACE(*s))
2755 s++;
2756 if (!*s)
2757 break;
2758 }
2759 break;
2760 case ev_string:
2761 val->string = PRVM_SetEngineString(prog, cvar->string);
2762 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2763 break;
2764 default:
2765 Con_Printf("%s: invalid type of autocvar global %s in %s\n", __func__, name, prog->name);
2766 goto fail;
2767 }
2768 cvar->globaldefindex[prog - prvm_prog_list] = i;
2769 }
2770 else
2771 Con_Printf("%s: private cvar for autocvar global %s in %s\n", __func__, name, prog->name);
2772 }
2773fail:
2774 ;
2775 }
2776
2777 prog->loaded = true;
2778
2780
2781 // set flags & mdef_ts in prog
2782
2783 PRVM_FindOffsets(prog);
2784
2785 // Do not allow more than 2^31 total entityfields. Achieve this by limiting maximum edict count.
2786 // TODO: For PRVM_64, this can be relaxes. May require changing some types away from int.
2787 max_safe_edicts = ((1 << 31) - prog->numglobals) / prog->entityfields;
2788 if (prog->limit_edicts > max_safe_edicts)
2789 {
2790 Con_Printf("%s: reducing maximum entity count to %d to avoid address overflow in %s\n", __func__, max_safe_edicts, prog->name);
2791 prog->limit_edicts = max_safe_edicts;
2792 }
2793
2794 prog->init_cmd(prog);
2795
2796 // init mempools
2797 PRVM_MEM_Alloc(prog);
2798
2799 Con_Printf("%s: program loaded (crc %i, size %iK)%s\n", prog->name, prog->filecrc, (int)(filesize/1024),
2800 prog == CLVM_prog ? (prog->flag & PRVM_CSQC_SIMPLE ? " CSQC_SIMPLE" : " EXT_CSQC") : "");
2801
2802 // Inittime is at least the time when this function finished. However,
2803 // later events may bump it.
2804 prog->inittime = host.realtime;
2805}
2806
2807
2809{
2810 prvm_prog_t *prog;
2811 int i, j, ednum, used, usedamount;
2812 int *counts;
2813 char tempstring[MAX_INPUTLINE], tempstring2[260];
2814 const char *name;
2815 prvm_edict_t *ed;
2816 mdef_t *d;
2817 prvm_eval_t *val;
2818
2819 // TODO
2820 /*
2821 if (!sv.active)
2822 {
2823 Con_Print("no progs loaded\n");
2824 return;
2825 }
2826 */
2827
2828 if(Cmd_Argc(cmd) != 2)
2829 {
2830 Con_Print("prvm_fields <program name>\n");
2831 return;
2832 }
2833
2834 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2835 return;
2836
2837 counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2838 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2839 {
2840 ed = PRVM_EDICT_NUM(ednum);
2841 if (ed->free)
2842 continue;
2843 for (i = 1;i < prog->numfielddefs;i++)
2844 {
2845 d = &prog->fielddefs[i];
2846 name = PRVM_GetString(prog, d->s_name);
2847 if (name[strlen(name)-2] == '_')
2848 continue; // skip _x, _y, _z vars
2849 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2850 // if the value is still all 0, skip the field
2851 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2852 {
2853 if (val->ivector[j])
2854 {
2855 counts[i]++;
2856 break;
2857 }
2858 }
2859 }
2860 }
2861 used = 0;
2862 usedamount = 0;
2863 tempstring[0] = 0;
2864 for (i = 0;i < prog->numfielddefs;i++)
2865 {
2866 d = &prog->fielddefs[i];
2867 name = PRVM_GetString(prog, d->s_name);
2868 if (name[strlen(name)-2] == '_')
2869 continue; // skip _x, _y, _z vars
2870 switch(d->type & ~DEF_SAVEGLOBAL)
2871 {
2872 case ev_string:
2873 dp_strlcat(tempstring, "string ", sizeof(tempstring));
2874 break;
2875 case ev_entity:
2876 dp_strlcat(tempstring, "entity ", sizeof(tempstring));
2877 break;
2878 case ev_function:
2879 dp_strlcat(tempstring, "function ", sizeof(tempstring));
2880 break;
2881 case ev_field:
2882 dp_strlcat(tempstring, "field ", sizeof(tempstring));
2883 break;
2884 case ev_void:
2885 dp_strlcat(tempstring, "void ", sizeof(tempstring));
2886 break;
2887 case ev_float:
2888 dp_strlcat(tempstring, "float ", sizeof(tempstring));
2889 break;
2890 case ev_vector:
2891 dp_strlcat(tempstring, "vector ", sizeof(tempstring));
2892 break;
2893 case ev_pointer:
2894 dp_strlcat(tempstring, "pointer ", sizeof(tempstring));
2895 break;
2896 default:
2897 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2898 dp_strlcat(tempstring, tempstring2, sizeof(tempstring));
2899 break;
2900 }
2901 if (strlen(name) > sizeof(tempstring2)-4)
2902 {
2903 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2904 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2905 tempstring2[sizeof(tempstring2)-1] = 0;
2906 name = tempstring2;
2907 }
2908 dp_strlcat(tempstring, name, sizeof(tempstring));
2909 for (j = (int)strlen(name);j < 25;j++)
2910 dp_strlcat(tempstring, " ", sizeof(tempstring));
2911 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2912 dp_strlcat(tempstring, tempstring2, sizeof(tempstring));
2913 dp_strlcat(tempstring, "\n", sizeof(tempstring));
2914 if (strlen(tempstring) >= sizeof(tempstring)/2)
2915 {
2916 Con_Print(tempstring);
2917 tempstring[0] = 0;
2918 }
2919 if (counts[i])
2920 {
2921 used++;
2922 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2923 }
2924 }
2925 Mem_Free(counts);
2926 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);
2927}
2928
2930{
2931 prvm_prog_t *prog;
2932 int i;
2933 const char *wildcard;
2934 int numculled;
2935 numculled = 0;
2936 // TODO
2937 /*if (!sv.active)
2938 {
2939 Con_Print("no progs loaded\n");
2940 return;
2941 }*/
2942 if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2943 {
2944 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2945 return;
2946 }
2947
2948 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2949 return;
2950
2951 if( Cmd_Argc(cmd) == 3)
2952 wildcard = Cmd_Argv(cmd, 2);
2953 else
2954 wildcard = NULL;
2955
2956 Con_Printf("%s :", prog->name);
2957
2958 for (i = 0;i < prog->numglobaldefs;i++)
2959 {
2960 if(wildcard)
2961 if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2962 {
2963 numculled++;
2964 continue;
2965 }
2966 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2967 }
2968 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2969}
2970
2971/*
2972===============
2973PRVM_Global
2974===============
2975*/
2977{
2978 prvm_prog_t *prog;
2979 mdef_t *global;
2980 char valuebuf[MAX_INPUTLINE];
2981 if( Cmd_Argc(cmd) != 3 ) {
2982 Con_Printf( "prvm_global <program name> <global name>\n" );
2983 return;
2984 }
2985
2986 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2987 return;
2988
2989 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2990 if( !global )
2991 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2992 else
2993 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2994}
2995
2996/*
2997===============
2998PRVM_GlobalSet
2999===============
3000*/
3002{
3003 prvm_prog_t *prog;
3004 mdef_t *global;
3005 if( Cmd_Argc(cmd) != 4 ) {
3006 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
3007 return;
3008 }
3009
3010 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3011 return;
3012
3013 global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
3014 if( !global )
3015 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
3016 else
3017 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
3018}
3019
3020/*
3021======================
3022Break- and Watchpoints
3023======================
3024*/
3025typedef struct
3026{
3028 char watch_global[256];
3030 char watch_field[256];
3031}
3034
3035void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
3036{
3037 char vabuf[1024];
3038 Con_Printf("PRVM_Breakpoint: %s\n", text);
3039 PRVM_PrintState(prog, stack_index);
3040 if (prvm_breakpointdump.integer)
3041 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
3042}
3043
3044void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
3045{
3046 size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
3047 if (memcmp(o, n, sz))
3048 {
3049 char buf[1024];
3050 char valuebuf_o[128];
3051 char valuebuf_n[128];
3052 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
3053 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
3054 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
3055 PRVM_Breakpoint(prog, stack_index, buf);
3056 memcpy(o, n, sz);
3057 }
3058}
3059
3061{
3062 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3063 if (!prog->loaded)
3064 return;
3065 if (debug->break_statement[0])
3066 {
3067 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
3068 {
3069 prog->break_statement = atoi(debug->break_statement);
3070 prog->break_stack_index = 0;
3071 }
3072 else
3073 {
3074 mfunction_t *func;
3075 func = PRVM_ED_FindFunction (prog, debug->break_statement);
3076 if (!func)
3077 {
3078 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
3079 prog->break_statement = -1;
3080 }
3081 else
3082 {
3083 prog->break_statement = func->first_statement;
3084 prog->break_stack_index = 1;
3085 }
3086 }
3087 if (prog->break_statement >= -1)
3088 Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
3089 }
3090 else
3091 prog->break_statement = -1;
3092
3093 if (debug->watch_global[0])
3094 {
3095 mdef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
3096 if( !global )
3097 {
3098 Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
3099 prog->watch_global_type = ev_void;
3100 }
3101 else
3102 {
3103 size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
3104 prog->watch_global = global->ofs;
3105 prog->watch_global_type = (etype_t)global->type;
3106 memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
3107 }
3108 if (prog->watch_global_type != ev_void)
3109 Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
3110 }
3111 else
3112 prog->watch_global_type = ev_void;
3113
3114 if (debug->watch_field[0])
3115 {
3116 mdef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
3117 if( !field )
3118 {
3119 Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
3120 prog->watch_field_type = ev_void;
3121 }
3122 else
3123 {
3124 size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
3125 prog->watch_edict = debug->watch_edict;
3126 prog->watch_field = field->ofs;
3127 prog->watch_field_type = (etype_t)field->type;
3128 if (prog->watch_edict < prog->num_edicts)
3130 else
3131 memset(&prog->watch_edictfield_value, 0, sz);
3132 }
3133 if (prog->watch_edict != ev_void)
3134 Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
3135 }
3136 else
3137 prog->watch_field_type = ev_void;
3138}
3139
3141{
3142 prvm_prog_t *prog;
3143
3144 if( Cmd_Argc(cmd) == 2 ) {
3145 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3146 return;
3147 {
3148 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3149 debug->break_statement[0] = 0;
3150 }
3152 return;
3153 }
3154 if( Cmd_Argc(cmd) != 3 ) {
3155 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
3156 return;
3157 }
3158
3159 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
3160 return;
3161
3162 {
3163 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3164 dp_strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
3165 }
3167}
3168
3170{
3171 prvm_prog_t *prog;
3172
3173 if( Cmd_Argc(cmd) == 2 ) {
3174 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3175 return;
3176 {
3177 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3178 debug->watch_global[0] = 0;
3179 }
3181 return;
3182 }
3183 if( Cmd_Argc(cmd) != 3 ) {
3184 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
3185 return;
3186 }
3187
3188 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
3189 return;
3190
3191 {
3192 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3193 dp_strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
3194 }
3196}
3197
3199{
3200 prvm_prog_t *prog;
3201
3202 if( Cmd_Argc(cmd) == 2 ) {
3203 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3204 return;
3205 {
3206 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3207 debug->watch_field[0] = 0;
3208 }
3210 return;
3211 }
3212 if( Cmd_Argc(cmd) != 4 ) {
3213 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
3214 return;
3215 }
3216
3217 if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
3218 return;
3219
3220 {
3221 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3222 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
3223 dp_strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
3224 }
3226}
3227
3228/*
3229===============
3230PRVM_Init
3231===============
3232*/
3233void PRVM_Init (void)
3234{
3235 unsigned int i;
3236
3237 Cmd_AddCommand(CF_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
3238 Cmd_AddCommand(CF_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
3239 Cmd_AddCommand(CF_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
3240 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)");
3241 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");
3242 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)");
3243 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)");
3244 Cmd_AddCommand(CF_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
3245 Cmd_AddCommand(CF_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
3246 Cmd_AddCommand(CF_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
3247 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)");
3248 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");
3249 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");
3250 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)");
3251 Cmd_AddCommand(CF_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
3252 Cmd_AddCommand(CF_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
3253 Cmd_AddCommand(CF_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
3254 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");
3255 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");
3256 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");
3257
3278
3279 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3280 prvm_runawaycheck = !Sys_CheckParm("-norunaway");
3281
3282 //VM_Cmd_Init();
3283
3284 // LadyHavoc: report supported extensions
3285 Con_DPrintf("\nQuakeC extensions for server and client:");
3286 for (i = 0; vm_sv_extensions[i]; i++)
3287 Con_DPrintf(" %s", vm_sv_extensions[i]);
3288 Con_DPrintf("\n");
3289#ifdef CONFIG_MENU
3290 Con_DPrintf("\nQuakeC extensions for menu:");
3291 for (i = 0; vm_m_extensions[i]; i++)
3292 Con_DPrintf(" %s", vm_m_extensions[i]);
3293 Con_DPrintf("\n");
3294#endif
3295}
3296
3297/*
3298===============
3299PRVM_InitProg
3300===============
3301*/
3303{
3304 PRVM_Prog_Reset(prog);
3305 prog->leaktest_active = prvm_leaktest.integer != 0;
3306 prog->console_cmd = cmd;
3307}
3308
3309// LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3310unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3311{
3312 prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3313 return 0;
3314}
3315
3316#define PRVM_KNOWNSTRINGBASE 0x40000000
3317
3318const char *PRVM_GetString(prvm_prog_t *prog, int num)
3319{
3320 if (num < 0)
3321 {
3322 // invalid
3323 if (prvm_stringdebug.integer)
3324 VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3325 return "";
3326 }
3327 else if (num < prog->stringssize)
3328 {
3329 // constant string from progs.dat
3330 return prog->strings + num;
3331 }
3332 else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3333 {
3334 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3335 num -= prog->stringssize;
3336 if (num < prog->tempstringsbuf.cursize)
3337 return (char *)prog->tempstringsbuf.data + num;
3338 else
3339 {
3340 if (prvm_stringdebug.integer)
3341 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3342 return "";
3343 }
3344 }
3345 else if (num & PRVM_KNOWNSTRINGBASE)
3346 {
3347 // allocated string
3348 num = num - PRVM_KNOWNSTRINGBASE;
3349 if (num >= 0 && num < prog->numknownstrings)
3350 {
3351 if (!prog->knownstrings[num])
3352 {
3353 if (prvm_stringdebug.integer)
3354 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3355 return "";
3356 }
3357 // refresh the garbage collection on the string - this guards
3358 // against a certain sort of repeated migration to earlier
3359 // points in the scan that could otherwise result in the string
3360 // being freed for being unused
3362 return prog->knownstrings[num];
3363 }
3364 else
3365 {
3366 if (prvm_stringdebug.integer)
3367 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3368 return "";
3369 }
3370 }
3371 else
3372 {
3373 // invalid string offset
3374 if (prvm_stringdebug.integer)
3375 VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3376 return "";
3377 }
3378}
3379
3380const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3381{
3382 const char *old;
3383 i = i - PRVM_KNOWNSTRINGBASE;
3384 if (i < 0 || i >= prog->numknownstrings)
3385 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3386 else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3387 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3388 old = prog->knownstrings[i];
3389 prog->knownstrings[i] = s;
3390 return old;
3391}
3392
3393static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3394{
3395 if (i >= prog->numknownstrings)
3396 {
3397 if (i >= prog->maxknownstrings)
3398 {
3399 const char **oldstrings = prog->knownstrings;
3400 const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3401 const char **oldstrings_origin = prog->knownstrings_origin;
3402 prog->maxknownstrings += 128;
3403 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3404 prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3405 if (prog->leaktest_active)
3406 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3407 if (prog->numknownstrings)
3408 {
3409 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3410 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3411 if (prog->leaktest_active)
3412 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3413 }
3414 }
3415 prog->numknownstrings++;
3416 }
3417 prog->firstfreeknownstring = i + 1;
3418 prog->knownstrings[i] = s;
3419 // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3420 prog->knownstrings_flags[i] = flags;
3421 if (prog->leaktest_active)
3422 prog->knownstrings_origin[i] = NULL;
3423}
3424
3425int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3426{
3427 int i;
3428 if (!s)
3429 return 0;
3430 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3431 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3432 // if it's in the tempstrings area, use a reserved range
3433 // (otherwise we'd get millions of useless string offsets cluttering the database)
3434 if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3435 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3436 // see if it's a known string address
3437 for (i = 0;i < prog->numknownstrings;i++)
3438 if (prog->knownstrings[i] == s)
3439 return PRVM_KNOWNSTRINGBASE + i;
3440 // new unknown engine string
3441 if (developer_insane.integer)
3442 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3443 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3444 if (!prog->knownstrings[i])
3445 break;
3447 return PRVM_KNOWNSTRINGBASE + i;
3448}
3449
3450// temp string handling
3451
3452// all tempstrings go into this buffer consecutively, and it is reset
3453// whenever PRVM_ExecuteProgram returns to the engine
3454// (technically each PRVM_ExecuteProgram call saves the cursize value and
3455// restores it on return, so multiple recursive calls can share the same
3456// buffer)
3457// the buffer size is automatically grown as needed
3458int PRVM_SetTempString(prvm_prog_t *prog, const char *s, size_t slen)
3459{
3460 size_t size;
3461 char *t;
3462
3463 if (!s || slen >= VM_TEMPSTRING_MAXSIZE)
3464 return 0;
3465 size = slen + 1;
3466 if (developer_insane.integer)
3467 Con_DPrintf("PRVM_SetTempString %s: cursize %i, new tempstring size %lu\n", prog->name, prog->tempstringsbuf.cursize, (unsigned long)size);
3468 if ((size_t)prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3469 {
3470 sizebuf_t old = prog->tempstringsbuf;
3471 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3472 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);
3473 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3474 while ((size_t)prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3475 prog->tempstringsbuf.maxsize *= 2;
3476 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3477 {
3478 Con_DPrintf("PRVM_SetTempString %s: enlarging tempstrings buffer (%iKB -> %iKB)\n", prog->name, old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3479 prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3480 if (old.data)
3481 {
3482 if (old.cursize)
3483 memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3484 Mem_Free(old.data);
3485 }
3486 }
3487 }
3488 t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3489 memcpy(t, s, size);
3490 prog->tempstringsbuf.cursize += size;
3491 return PRVM_SetEngineString(prog, t);
3492}
3493
3494int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3495{
3496 int i;
3497 char *s;
3498 if (!bufferlength)
3499 {
3500 if (pointer)
3501 *pointer = NULL;
3502 return 0;
3503 }
3504 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3505 if (!prog->knownstrings[i])
3506 break;
3507 s = (char *)PRVM_Alloc(bufferlength);
3509 if(prog->leaktest_active)
3511 if (pointer)
3512 *pointer = (char *)(prog->knownstrings[i]);
3513 return PRVM_KNOWNSTRINGBASE + i;
3514}
3515
3516void PRVM_FreeString(prvm_prog_t *prog, int num)
3517{
3518 if (num == 0)
3519 prog->error_cmd("PRVM_FreeString %s: attempt to free a NULL string", prog->name);
3520 else if (num >= 0 && num < prog->stringssize)
3521 prog->error_cmd("PRVM_FreeString %s: attempt to free a constant string", prog->name);
3522 else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3523 {
3524 num = num - PRVM_KNOWNSTRINGBASE;
3525 if (!prog->knownstrings[num])
3526 prog->error_cmd("PRVM_FreeString %s: attempt to free a non-existent or already freed string", prog->name);
3527 if (!prog->knownstrings_flags[num])
3528 prog->error_cmd("PRVM_FreeString %s: attempt to free a string owned by the engine", prog->name);
3529 PRVM_Free((char *)prog->knownstrings[num]);
3530 if(prog->leaktest_active)
3531 if(prog->knownstrings_origin[num])
3532 PRVM_Free((char *)prog->knownstrings_origin[num]);
3533 prog->knownstrings[num] = NULL;
3534 prog->knownstrings_flags[num] = 0;
3535 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3536 }
3537 else
3538 prog->error_cmd("PRVM_FreeString %s: invalid string offset %i", prog->name, num);
3539}
3540
3542{
3543 int i, j;
3544
3545 for (i = 0;i < prog->numglobaldefs;i++)
3546 {
3547 mdef_t *d = &prog->globaldefs[i];
3548 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3549 continue;
3550 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3551 return true;
3552 }
3553
3554 for(j = 0; j < prog->num_edicts; ++j)
3555 {
3557 if (ed->free)
3558 continue;
3559 for (i=0; i<prog->numfielddefs; ++i)
3560 {
3561 mdef_t *d = &prog->fielddefs[i];
3562 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3563 continue;
3564 if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3565 return true;
3566 }
3567 }
3568
3569 return false;
3570}
3571
3573{
3574 char vabuf[1024];
3575 char vabuf2[1024];
3576 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3577 return true; // world or clients
3578 if (edict->freetime <= prog->inittime)
3579 return true; // created during startup
3580 if (prog == SVVM_prog)
3581 {
3582 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3583 return true;
3584 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3585 return true;
3586 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3587 return true;
3588 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3589 if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3590 return true;
3592 return true;
3594 {
3595 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3596 return true;
3597 }
3598 }
3599 else if (prog == CLVM_prog)
3600 {
3601 // TODO someone add more stuff here
3602 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3603 return true;
3604 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3605 return true;
3606 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3607 return true;
3608 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3609 if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3610 return true;
3612 {
3613 if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3614 return true;
3615 }
3616 }
3617 else
3618 {
3619 // menu prog does not have classnames
3620 }
3621 return false;
3622}
3623
3625{
3626 int i, j;
3627 int edictnum = PRVM_NUM_FOR_EDICT(edict);
3628 const char *targetname = NULL;
3629
3630 if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3632
3633 if(targetname)
3634 if(!*targetname) // ""
3635 targetname = NULL;
3636
3637 for(j = 0; j < prog->num_edicts; ++j)
3638 {
3640 if (ed->priv.required->mark < mark)
3641 continue;
3642 if(ed == edict)
3643 continue;
3644 if(targetname)
3645 {
3646 const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3647 if(target)
3648 if(!strcmp(target, targetname))
3649 return true;
3650 }
3651 for (i=0; i<prog->numfielddefs; ++i)
3652 {
3653 mdef_t *d = &prog->fielddefs[i];
3654 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3655 continue;
3656 if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3657 return true;
3658 }
3659 }
3660
3661 return false;
3662}
3663
3665{
3666 int i, j;
3667 qbool found_new;
3668 int stage;
3669
3670 // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3671 stage = 1;
3672 for(j = 0; j < prog->num_edicts; ++j)
3673 {
3675 if(ed->free)
3676 continue;
3677 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3678 }
3679 for (i = 0;i < prog->numglobaldefs;i++)
3680 {
3681 mdef_t *d = &prog->globaldefs[i];
3682 prvm_edict_t *ed;
3683 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3684 continue;
3685 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3686 if (i < 0 || j >= prog->max_edicts) {
3687 Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3688 continue;
3689 }
3690 ed = PRVM_EDICT_NUM(j);;
3691 ed->priv.required->mark = stage;
3692 }
3693
3694 // Future stages: all entities that are referenced by an entity of the previous stage.
3695 do
3696 {
3697 found_new = false;
3698 for(j = 0; j < prog->num_edicts; ++j)
3699 {
3701 if(ed->free)
3702 continue;
3703 if(ed->priv.required->mark)
3704 continue;
3705 if(PRVM_IsEdictReferenced(prog, ed, stage))
3706 {
3707 ed->priv.required->mark = stage + 1;
3708 found_new = true;
3709 }
3710 }
3711 ++stage;
3712 }
3713 while(found_new);
3714 Con_DPrintf("leak check used %d stages to find all references\n", stage);
3715}
3716
3718{
3719 int i, j;
3720 qbool leaked = false;
3721
3722 if(!prog->leaktest_active)
3723 return;
3724
3725 // 1. Strings
3726 for (i = 0; i < prog->numknownstrings; ++i)
3727 {
3728 if(prog->knownstrings[i])
3729 if(prog->knownstrings_flags[i])
3730 if(prog->knownstrings_origin[i])
3732 {
3733 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3734 leaked = true;
3735 }
3736 }
3737
3738 // 2. Edicts
3740 for(j = 0; j < prog->num_edicts; ++j)
3741 {
3743 if(ed->free)
3744 continue;
3745 if(!ed->priv.required->mark)
3747 {
3748 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
3749 PRVM_ED_Print(prog, ed, NULL);
3750 Con_Print("\n");
3751 leaked = true;
3752 }
3753
3754 ed->priv.required->mark = 0; // clear marks again when done
3755 }
3756
3757 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3758 {
3760 if(stringbuffer)
3761 if(stringbuffer->origin)
3762 {
3763 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
3764 leaked = true;
3765 }
3766 }
3767
3768 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3769 {
3770 if(prog->openfiles[i])
3771 if(prog->openfiles_origin[i])
3772 {
3773 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
3774 leaked = true;
3775 }
3776 }
3777
3778 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3779 {
3780 if(prog->opensearches[i])
3781 if(prog->opensearches_origin[i])
3782 {
3783 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
3784 leaked = true;
3785 }
3786 }
3787
3788 if(!leaked)
3789 Con_Printf("Congratulations. No leaks found.\n");
3790}
3791
3793{
3794 int limit = prvm_garbagecollection_scan_limit.integer * (prog == SVVM_prog ? sv.frametime : cl.realframetime);
3796 if (!prvm_garbagecollection_enable.integer)
3797 return;
3798 // philosophy:
3799 // we like to limit how much scanning we do so it doesn't put a significant
3800 // burden on the cpu, so each of these are not complete scans, we also like
3801 // to have consistent cpu usage so we do a bit of work on each category of
3802 // leaked object every frame
3803 switch (gc->stage)
3804 {
3805 case PRVM_GC_START:
3806 gc->stage++;
3807 break;
3809 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3810 {
3811 mdef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3812 switch (d->type)
3813 {
3814 case ev_string:
3815 {
3816 prvm_int_t s = prog->globals.ip[d->ofs];
3817 if (s & PRVM_KNOWNSTRINGBASE)
3818 {
3820 if (!prog->knownstrings[num])
3821 {
3822 // invalid
3823 Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3824 prog->globals.ip[d->ofs] = 0;
3825 continue;
3826 }
3828 }
3829 }
3830 break;
3831 default:
3832 break;
3833 }
3834 }
3835 if (gc->globals_mark_progress >= prog->numglobaldefs)
3836 gc->stage++;
3837 break;
3839 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3840 {
3841 mdef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3842 switch (d->type)
3843 {
3844 case ev_string:
3845 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3846 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3847 {
3848 int entityindex = gc->fields_mark_progress_entity;
3849 prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3850 if (s & PRVM_KNOWNSTRINGBASE)
3851 {
3853 if (!prog->knownstrings[num])
3854 {
3855 // invalid
3856 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));
3857 prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3858 continue;
3859 }
3861 }
3862 }
3863 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3864 {
3867 }
3868 break;
3869 default:
3872 break;
3873 }
3874 }
3875 if (gc->fields_mark_progress >= prog->numfielddefs)
3876 gc->stage++;
3877 break;
3879 // free any strzone'd strings that are not marked
3880 if (!prvm_garbagecollection_strings.integer)
3881 {
3882 gc->stage++;
3883 break;
3884 }
3885 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3886 {
3887 int num = gc->knownstrings_sweep_progress;
3888 if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3889 {
3891 {
3892 // string has been marked for pruning two passes in a row
3894 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3895 Mem_Free((char *)prog->knownstrings[num]);
3896 prog->knownstrings[num] = NULL;
3897 prog->knownstrings_flags[num] = 0;
3898 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3899 }
3900 else
3901 {
3902 // mark it for pruning next pass
3904 }
3905 }
3906 }
3908 gc->stage++;
3909 break;
3910 case PRVM_GC_RESET:
3911 default:
3912 memset(gc, 0, sizeof(*gc));
3913// 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);
3914 }
3915}
client_state_t cl
Definition cl_main.c:117
cvar_t csqc_progname
Definition cl_main.c:35
qbool prvm_runawaycheck
Definition prvm_edict.c:60
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:3541
qfile_t * FS_OpenRealFile(const char *filepath, const char *mode, qbool quiet)
Definition fs.c:2902
static int(ZEXPORT *qz_inflate)(z_stream *strm
void FS_StripExtension(const char *in, char *out, size_t size_out)
Definition fs.c:3612
int FS_Close(qfile_t *file)
Definition fs.c:2971
int FS_Printf(qfile_t *file, const char *format,...)
Definition fs.c:3274
int FS_Print(qfile_t *file, const char *msg)
Definition fs.c:3262
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:331
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:425
unsigned int func_t
Definition pr_comp.h:26
#define DEF_SAVEGLOBAL
Definition pr_comp.h:370
opcode_t
Definition pr_comp.h:46
@ OP_SUB_FI
Definition pr_comp.h:168
@ OP_STOREP_S
Definition pr_comp.h:87
@ OP_NE_E
Definition pr_comp.h:66
@ OP_LOADA_ENT
Definition pr_comp.h:196
@ OP_AND_I
Definition pr_comp.h:237
@ OP_LE_FI
Definition pr_comp.h:217
@ OP_ADD_I
Definition pr_comp.h:164
@ OP_GSTOREP_FLD
Definition pr_comp.h:248
@ OP_STORE_V
Definition pr_comp.h:80
@ OP_GSTOREP_FNC
Definition pr_comp.h:250
@ OP_RSHIFT_U
Definition pr_comp.h:276
@ OP_SUB_F
Definition pr_comp.h:56
@ OP_LOAD_ENT
Definition pr_comp.h:75
@ OP_LOAD_F
Definition pr_comp.h:72
@ OP_LT_FI
Definition pr_comp.h:219
@ OP_GE_I
Definition pr_comp.h:210
@ OP_DIV_I
Definition pr_comp.h:181
@ OP_LT_I
Definition pr_comp.h:211
@ OP_IF
Definition pr_comp.h:97
@ OP_LOAD_FLD
Definition pr_comp.h:76
@ OP_MUL_VI
Definition pr_comp.h:229
@ OP_BITOR_F
Definition pr_comp.h:113
@ OP_STOREP_V
Definition pr_comp.h:86
@ OP_GLOAD_ENT
Definition pr_comp.h:256
@ OP_GLOAD_F
Definition pr_comp.h:254
@ OP_EQ_FI
Definition pr_comp.h:222
@ OP_GSTOREP_I
Definition pr_comp.h:245
@ OP_MUL_VF
Definition pr_comp.h:52
@ OP_ADD_IF
Definition pr_comp.h:166
@ OP_GLOAD_FLD
Definition pr_comp.h:255
@ OP_NOT_I
Definition pr_comp.h:186
@ OP_LOADA_I
Definition pr_comp.h:199
@ OP_GSTOREP_F
Definition pr_comp.h:246
@ OP_CALL8
Definition pr_comp.h:107
@ OP_CONV_ITOF
Definition pr_comp.h:170
@ OP_DIV_F
Definition pr_comp.h:53
@ OP_GT_F
Definition pr_comp.h:71
@ OP_DIV_IF
Definition pr_comp.h:231
@ OP_MUL_I
Definition pr_comp.h:180
@ OP_OR_FI
Definition pr_comp.h:242
@ OP_CALL6
Definition pr_comp.h:105
@ OP_ADD_V
Definition pr_comp.h:55
@ OP_STOREP_ENT
Definition pr_comp.h:88
@ OP_STOREP_FNC
Definition pr_comp.h:90
@ OP_AND_IF
Definition pr_comp.h:239
@ OP_CALL5
Definition pr_comp.h:104
@ OP_LOADP_V
Definition pr_comp.h:203
@ OP_NE_FNC
Definition pr_comp.h:67
@ OP_NE_S
Definition pr_comp.h:65
@ OP_BITOR_FI
Definition pr_comp.h:236
@ OP_LT_IF
Definition pr_comp.h:215
@ OP_STORE_P
Definition pr_comp.h:200
@ OP_CONV_FTOI
Definition pr_comp.h:171
@ OP_LOADA_FNC
Definition pr_comp.h:198
@ OP_LOADP_ENT
Definition pr_comp.h:205
@ OP_STORE_I
Definition pr_comp.h:161
@ OP_SUB_V
Definition pr_comp.h:57
@ OP_BITAND_FI
Definition pr_comp.h:235
@ OP_NOT_ENT
Definition pr_comp.h:95
@ OP_GSTOREP_S
Definition pr_comp.h:249
@ OP_GLOAD_I
Definition pr_comp.h:253
@ OP_OR_IF
Definition pr_comp.h:240
@ OP_STOREP_I
Definition pr_comp.h:175
@ OP_DIV_FI
Definition pr_comp.h:232
@ OP_EQ_FNC
Definition pr_comp.h:62
@ OP_LE_U
Definition pr_comp.h:273
@ OP_STOREP_FLD
Definition pr_comp.h:89
@ OP_MUL_FV
Definition pr_comp.h:51
@ OP_LOAD_P
Definition pr_comp.h:201
@ OP_NE_V
Definition pr_comp.h:64
@ OP_STORE_FLD
Definition pr_comp.h:83
@ OP_GLOBALADDRESS
Definition pr_comp.h:191
@ OP_ADD_PIW
Definition pr_comp.h:192
@ OP_CALL1
Definition pr_comp.h:100
@ OP_EQ_I
Definition pr_comp.h:182
@ OP_EQ_V
Definition pr_comp.h:59
@ OP_STORE_ENT
Definition pr_comp.h:82
@ OP_MUL_IF
Definition pr_comp.h:227
@ OP_GSTOREP_ENT
Definition pr_comp.h:247
@ OP_GT_IF
Definition pr_comp.h:216
@ OP_NE_IF
Definition pr_comp.h:243
@ OP_ADD_F
Definition pr_comp.h:54
@ OP_LT_F
Definition pr_comp.h:70
@ OP_RSHIFT_I
Definition pr_comp.h:189
@ OP_LOADP_S
Definition pr_comp.h:204
@ OP_MUL_FI
Definition pr_comp.h:228
@ OP_LOADA_F
Definition pr_comp.h:193
@ OP_GE_F
Definition pr_comp.h:69
@ OP_GE_IF
Definition pr_comp.h:214
@ OP_DIV_U
Definition pr_comp.h:275
@ OP_LE_I
Definition pr_comp.h:209
@ OP_LE_F
Definition pr_comp.h:68
@ OP_LOADP_I
Definition pr_comp.h:208
@ OP_BITAND_F
Definition pr_comp.h:112
@ OP_LE_IF
Definition pr_comp.h:213
@ OP_ADD_FI
Definition pr_comp.h:165
@ OP_GE_FI
Definition pr_comp.h:218
@ OP_AND_F
Definition pr_comp.h:110
@ OP_CALL0
Definition pr_comp.h:99
@ OP_LOADP_FLD
Definition pr_comp.h:206
@ OP_NE_F
Definition pr_comp.h:63
@ OP_NOT_F
Definition pr_comp.h:92
@ OP_STORE_S
Definition pr_comp.h:81
@ OP_LOAD_S
Definition pr_comp.h:74
@ OP_OR_I
Definition pr_comp.h:238
@ OP_CALL4
Definition pr_comp.h:103
@ OP_EQ_E
Definition pr_comp.h:61
@ OP_DIV_VF
Definition pr_comp.h:187
@ OP_STOREP_F
Definition pr_comp.h:85
@ OP_SUB_IF
Definition pr_comp.h:169
@ OP_LSHIFT_I
Definition pr_comp.h:190
@ OP_CALL2
Definition pr_comp.h:101
@ OP_GLOAD_V
Definition pr_comp.h:264
@ OP_MUL_V
Definition pr_comp.h:50
@ OP_GLOAD_S
Definition pr_comp.h:257
@ OP_LOADA_FLD
Definition pr_comp.h:197
@ OP_SUB_I
Definition pr_comp.h:167
@ OP_NE_FI
Definition pr_comp.h:244
@ OP_LOADP_F
Definition pr_comp.h:202
@ OP_STATE
Definition pr_comp.h:108
@ OP_GT_I
Definition pr_comp.h:212
@ OP_LOAD_I
Definition pr_comp.h:174
@ OP_LOADA_V
Definition pr_comp.h:194
@ OP_LOADA_S
Definition pr_comp.h:195
@ OP_CALL3
Definition pr_comp.h:102
@ OP_RETURN
Definition pr_comp.h:91
@ OP_OR_F
Definition pr_comp.h:111
@ OP_ADDRESS
Definition pr_comp.h:78
@ OP_EQ_IF
Definition pr_comp.h:221
@ OP_BITAND_I
Definition pr_comp.h:178
@ OP_EQ_F
Definition pr_comp.h:58
@ OP_DONE
Definition pr_comp.h:48
@ OP_NOT_V
Definition pr_comp.h:93
@ OP_BOUNDCHECK
Definition pr_comp.h:259
@ OP_AND_FI
Definition pr_comp.h:241
@ OP_GSTOREP_V
Definition pr_comp.h:251
@ OP_EQ_S
Definition pr_comp.h:60
@ OP_BITOR_IF
Definition pr_comp.h:234
@ OP_LOAD_FNC
Definition pr_comp.h:77
@ OP_NE_I
Definition pr_comp.h:183
@ OP_GT_FI
Definition pr_comp.h:220
@ OP_NOT_FNC
Definition pr_comp.h:96
@ OP_LOAD_V
Definition pr_comp.h:73
@ OP_GOTO
Definition pr_comp.h:109
@ OP_GLOAD_FNC
Definition pr_comp.h:258
@ OP_BITAND_IF
Definition pr_comp.h:233
@ OP_CALL7
Definition pr_comp.h:106
@ OP_STORE_FNC
Definition pr_comp.h:84
@ OP_LT_U
Definition pr_comp.h:274
@ OP_BITOR_I
Definition pr_comp.h:179
@ OP_MUL_F
Definition pr_comp.h:49
@ OP_LOADP_FNC
Definition pr_comp.h:207
@ OP_NOT_S
Definition pr_comp.h:94
@ OP_STORE_F
Definition pr_comp.h:79
@ OP_IFNOT
Definition pr_comp.h:98
#define PROG_SECONDARYVERSION32
Definition pr_comp.h:470
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:469
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
void PRVM_ED_PrintNum(prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
Definition prvm_edict.c:788
#define PRVM_Alloc(buffersize)
Definition progsvm.h:818
#define PRVM_EDICT_NUM(n)
Definition progsvm.h:867
const char * PRVM_GetString(prvm_prog_t *prog, int num)
#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
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
#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
void PRVM_ED_PrintEdicts_f(struct cmd_state_s *cmd)
#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
prvm_eval_t prvm_badvalue
Definition prvm_edict.c:31
#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
prvm_prog_t prvm_prog_list[PRVM_PROG_MAX]
Definition prvm_edict.c:27
#define PRVM_G_INT(o)
Definition progsvm.h:883
cvar_t prvm_coverage
Definition prvm_edict.c:39
cvar_t prvm_backtraceforwarnings
Definition prvm_edict.c:40
#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)
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
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)
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
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
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
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
#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
command interpreter state - the tokenizing and execution of commands, as well as pointers to which cv...
Definition cmd.h:127
Definition cvar.h:66
char break_statement[256]
char watch_global[256]
char watch_field[256]
uint8_t parm_size[MAX_PARMS]
Definition pr_comp.h:387
uint32_t numstrings
Definition pr_comp.h:445
uint32_t numstatements
Definition pr_comp.h:433
uint32_t numfielddefs
Definition pr_comp.h:439
int32_t crc
Definition pr_comp.h:429
uint32_t numglobals
Definition pr_comp.h:448
uint32_t numglobaldefs
Definition pr_comp.h:436
uint32_t ofs_strings
Definition pr_comp.h:444
uint32_t ofs_globals
Definition pr_comp.h:447
uint32_t ofs_statements
Definition pr_comp.h:432
int32_t version
Definition pr_comp.h:428
uint32_t ofs_fielddefs
Definition pr_comp.h:438
uint32_t ofs_functions
Definition pr_comp.h:441
uint32_t entityfields
Definition pr_comp.h:450
uint32_t ofs_globaldefs
Definition pr_comp.h:435
uint32_t numfunctions
Definition pr_comp.h:442
uint32_t numbodylessfuncs
Definition pr_comp.h:462
uint32_t blockscompressed
Definition pr_comp.h:466
uint32_t numtypes
Definition pr_comp.h:465
int32_t secondaryversion
Definition pr_comp.h:468
uint32_t ofs
Definition pr_comp.h:365
int32_t s_name
Definition pr_comp.h:366
uint32_t type
Definition pr_comp.h:363
int32_t first_statement
Definition pr_comp.h:393
int32_t s_file
Definition pr_comp.h:410
uint8_t parm_size[MAX_PARMS]
Definition pr_comp.h:413
int32_t numparms
Definition pr_comp.h:412
int32_t s_name
Definition pr_comp.h:409
int32_t locals
Definition pr_comp.h:395
int32_t parm_start
Definition pr_comp.h:394
opcode_t op
Definition pr_comp.h:419
int operand[3]
Definition pr_comp.h:420
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
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::@123106147040330276163271355201011305114267000315 priv
union prvm_edict_t::@130370055076073023136252053272156300303324036224 fields
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
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
union prvm_prog_t::@154363101014374065374200170016167302241050246021 globals
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
union prvm_prog_t::@327125214102202150166162221173336172250075227076 edictsfields
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
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
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