]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.cpp
17f52f62e5efcb35c673233e93aff702974ff0ff
[xonotic/gmqcc.git] / exec.cpp
1 #ifndef QCVM_LOOP
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6
7 #include "gmqcc.h"
8
9 static void loaderror(const char *fmt, ...)
10 {
11     int     err = errno;
12     va_list ap;
13     va_start(ap, fmt);
14     vprintf(fmt, ap);
15     va_end(ap);
16     printf(": %s\n", util_strerror(err));
17 }
18
19 static void qcvmerror(qc_program_t *prog, const char *fmt, ...)
20 {
21     va_list ap;
22
23     prog->vmerror++;
24
25     va_start(ap, fmt);
26     vprintf(fmt, ap);
27     va_end(ap);
28     putchar('\n');
29 }
30
31 qc_program::qc_program(const char *name, uint16_t crc, size_t entfields)
32     : filename(name)
33     , crc16(crc)
34     , entityfields(entfields)
35 {}
36
37 qc_program_t* prog_load(const char *filename, bool skipversion)
38 {
39     prog_header_t header;
40     qc_program_t *prog;
41     FILE *file = fopen(filename, "rb");
42
43     /* we need all those in order to support INSTR_STATE: */
44     bool            has_self      = false,
45                     has_time      = false,
46                     has_think     = false,
47                     has_nextthink = false,
48                     has_frame     = false;
49
50     if (!file)
51         return nullptr;
52
53     if (fread(&header, sizeof(header), 1, file) != 1) {
54         loaderror("failed to read header from '%s'", filename);
55         fclose(file);
56         return nullptr;
57     }
58
59     util_swap_header(header);
60
61     if (!skipversion && header.version != 6) {
62         loaderror("header says this is a version %i progs, we need version 6\n", header.version);
63         fclose(file);
64         return nullptr;
65     }
66
67     prog = new qc_program(filename, header.crc16, header.entfield);
68
69 #define read_data(hdrvar, progvar, reserved)                           \
70     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {            \
71         loaderror("seek failed");                                      \
72         goto error;                                                    \
73     }                                                                  \
74     prog->progvar.resize(header.hdrvar.length + reserved);             \
75     if (fread(                                                         \
76             &prog->progvar[0],                                         \
77             sizeof(prog->progvar[0]),                                  \
78             header.hdrvar.length,                                      \
79             file                                                       \
80         )!= header.hdrvar.length                                       \
81     ) {                                                                \
82         loaderror("read failed");                                      \
83         goto error;                                                    \
84     }
85 #define read_data1(x)    read_data(x, x, 0)
86 #define read_data2(x, y) read_data(x, x, y)
87
88     read_data (statements, code, 0);
89     read_data1(defs);
90     read_data1(fields);
91     read_data1(functions);
92     read_data1(strings);
93     read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */
94
95     util_swap_statements(prog->code);
96     util_swap_defs_fields(prog->defs);
97     util_swap_defs_fields(prog->fields);
98     util_swap_functions(prog->functions);
99     util_swap_globals(prog->globals);
100
101     fclose(file);
102
103     /* profile counters */
104     prog->profile.resize(prog->code.size());
105     memset(&prog->profile[0], 0, sizeof(prog->profile[0]) * prog->profile.size());
106
107     /* Add tempstring area */
108     prog->tempstring_start = prog->strings.size();
109     prog->tempstring_at = prog->strings.size();
110
111     prog->strings.resize(prog->strings.size() + 16*1024, '\0');
112
113     /* spawn the world entity */
114     prog->entitypool.emplace_back(true);
115     prog->entitydata.resize(prog->entityfields);
116     memset(prog->entitydata.data(), 0, sizeof(prog->entitydata[0]) * prog->entityfields);
117     prog->entities = 1;
118
119     /* cache some globals and fields from names */
120     for (auto &it : prog->defs) {
121         const char *name = prog_getstring(prog, it.name);
122         if (!strcmp(name, "self")) {
123             prog->cached_globals.self = it.offset;
124             has_self = true;
125         }
126         else if (!strcmp(name, "time")) {
127             prog->cached_globals.time = it.offset;
128             has_time = true;
129         }
130     }
131     for (auto &it : prog->fields) {
132         const char *name = prog_getstring(prog, it.name);
133         if (!strcmp(name, "think")) {
134             prog->cached_fields.think = it.offset;
135             has_think = true;
136         }
137         else if (!strcmp(name, "nextthink")) {
138             prog->cached_fields.nextthink = it.offset;
139             has_nextthink = true;
140         }
141         else if (!strcmp(name, "frame")) {
142             prog->cached_fields.frame  = it.offset;
143             has_frame = true;
144         }
145     }
146     if (has_self && has_time && has_think && has_nextthink && has_frame)
147         prog->supports_state = true;
148
149     return prog;
150
151 error:
152     delete prog;
153
154     fclose(file);
155     return nullptr;
156 }
157
158 void prog_delete(qc_program_t *prog)
159 {
160     delete prog;
161 }
162
163 /***********************************************************************
164  * VM code
165  */
166
167 const char* prog_getstring(qc_program_t *prog, qcint_t str) {
168     /* cast for return required for C++ */
169     if (str < 0 || str >= (qcint_t)prog->strings.size())
170         return  "<<<invalid string>>>";
171
172     return &prog->strings[0] + str;
173 }
174
175 prog_section_def_t* prog_entfield(qc_program_t *prog, qcint_t off) {
176     for (auto &it : prog->fields)
177         if (it.offset == off)
178             return &it;
179     return nullptr;
180 }
181
182 prog_section_def_t* prog_getdef(qc_program_t *prog, qcint_t off)
183 {
184     for (auto &it : prog->defs)
185         if (it.offset == off)
186             return &it;
187     return nullptr;
188 }
189
190 qcany_t* prog_getedict(qc_program_t *prog, qcint_t e) {
191     if (e >= (qcint_t)prog->entitypool.size()) {
192         prog->vmerror++;
193         fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e);
194         e = 0;
195     }
196     return (qcany_t*)&prog->entitydata[prog->entityfields * e];
197 }
198
199 static qcint_t prog_spawn_entity(qc_program_t *prog) {
200     char  *data;
201     qcint_t  e;
202     for (e = 0; e < (qcint_t)prog->entitypool.size(); ++e) {
203         if (!prog->entitypool[e]) {
204             data = (char*)&prog->entitydata[prog->entityfields * e];
205             memset(data, 0, prog->entityfields * sizeof(qcint_t));
206             return e;
207         }
208     }
209     prog->entitypool.emplace_back(true);
210     prog->entities++;
211     size_t sz = prog->entitydata.size();
212     prog->entitydata.resize(sz + prog->entityfields);
213     data = (char*)&prog->entitydata[sz];
214     memset(data, 0, sz * sizeof(qcint_t));
215
216     return e;
217 }
218
219 static void prog_free_entity(qc_program_t *prog, qcint_t e) {
220     if (!e) {
221         prog->vmerror++;
222         fprintf(stderr, "Trying to free world entity\n");
223         return;
224     }
225     if (e >= (qcint_t)prog->entitypool.size()) {
226         prog->vmerror++;
227         fprintf(stderr, "Trying to free out of bounds entity\n");
228         return;
229     }
230     if (!prog->entitypool[e]) {
231         prog->vmerror++;
232         fprintf(stderr, "Double free on entity\n");
233         return;
234     }
235     prog->entitypool[e] = false;
236 }
237
238 qcint_t prog_tempstring(qc_program_t *prog, const char *str) {
239     size_t len = strlen(str);
240     size_t at = prog->tempstring_at;
241
242     /* when we reach the end we start over */
243     if (at + len >= prog->strings.size())
244         at = prog->tempstring_start;
245
246     /* when it doesn't fit, reallocate */
247     if (at + len >= prog->strings.size())
248     {
249         prog->strings.resize(prog->strings.size() + len+1);
250         memcpy(&prog->strings[0] + at, str, len+1);
251         return at;
252     }
253
254     /* when it fits, just copy */
255     memcpy(&prog->strings[0] + at, str, len+1);
256     prog->tempstring_at += len+1;
257     return at;
258 }
259
260 static size_t print_escaped_string(const char *str, size_t maxlen) {
261     size_t len = 2;
262     putchar('"');
263     --maxlen; /* because we're lazy and have escape sequences */
264     while (*str) {
265         if (len >= maxlen) {
266             putchar('.');
267             putchar('.');
268             putchar('.');
269             len += 3;
270             break;
271         }
272         switch (*str) {
273             case '\a': len += 2; putchar('\\'); putchar('a'); break;
274             case '\b': len += 2; putchar('\\'); putchar('b'); break;
275             case '\r': len += 2; putchar('\\'); putchar('r'); break;
276             case '\n': len += 2; putchar('\\'); putchar('n'); break;
277             case '\t': len += 2; putchar('\\'); putchar('t'); break;
278             case '\f': len += 2; putchar('\\'); putchar('f'); break;
279             case '\v': len += 2; putchar('\\'); putchar('v'); break;
280             case '\\': len += 2; putchar('\\'); putchar('\\'); break;
281             case '"':  len += 2; putchar('\\'); putchar('"'); break;
282             default:
283                 ++len;
284                 putchar(*str);
285                 break;
286         }
287         ++str;
288     }
289     putchar('"');
290     return len;
291 }
292
293 static void trace_print_global(qc_program_t *prog, unsigned int glob, int vtype) {
294     static char spaces[28+1] = "                            ";
295     prog_section_def_t *def;
296     qcany_t    *value;
297     int       len;
298
299     if (!glob) {
300         if ((len = printf("<null>,")) == -1)
301             len = 0;
302
303         goto done;
304     }
305
306     def = prog_getdef(prog, glob);
307     value = (qcany_t*)(&prog->globals[glob]);
308
309     len = printf("[@%u] ", glob);
310     if (def) {
311         const char *name = prog_getstring(prog, def->name);
312         if (name[0] == '#')
313             len += printf("$");
314         else
315             len += printf("%s ", name);
316         vtype = def->type & DEF_TYPEMASK;
317     }
318
319     switch (vtype) {
320         case TYPE_VOID:
321         case TYPE_ENTITY:
322         case TYPE_FIELD:
323         case TYPE_FUNCTION:
324         case TYPE_POINTER:
325             len += printf("(%i),", value->_int);
326             break;
327         case TYPE_VECTOR:
328             len += printf("'%g %g %g',", value->vector[0],
329                                          value->vector[1],
330                                          value->vector[2]);
331             break;
332         case TYPE_STRING:
333             if (value->string)
334                 len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5);
335             else
336                 len += printf("(null)");
337             len += printf(",");
338             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
339             break;
340         case TYPE_FLOAT:
341         default:
342             len += printf("%g,", value->_float);
343             break;
344     }
345 done:
346     if (len < (int)sizeof(spaces)-1) {
347         spaces[sizeof(spaces)-1-len] = 0;
348         fputs(spaces, stdout);
349         spaces[sizeof(spaces)-1-len] = ' ';
350     }
351 }
352
353 static void prog_print_statement(qc_program_t *prog, prog_section_statement_t *st) {
354     if (st->opcode >= VINSTR_END) {
355         printf("<illegal instruction %d>\n", st->opcode);
356         return;
357     }
358     if ((prog->xflags & VMXF_TRACE) && !prog->function_stack.empty()) {
359         size_t i;
360         for (i = 0; i < prog->function_stack.size(); ++i)
361             printf("->");
362         printf("%s:", prog->function_stack.back());
363     }
364     printf(" <> %-12s", util_instr_str[st->opcode]);
365     if (st->opcode >= INSTR_IF &&
366         st->opcode <= INSTR_IFNOT)
367     {
368         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
369         printf("%d\n", st->o2.s1);
370     }
371     else if (st->opcode >= INSTR_CALL0 &&
372              st->opcode <= INSTR_CALL8)
373     {
374         trace_print_global(prog, st->o1.u1, TYPE_FUNCTION);
375         printf("\n");
376     }
377     else if (st->opcode == INSTR_GOTO)
378     {
379         printf("%i\n", st->o1.s1);
380     }
381     else
382     {
383         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
384         switch (st->opcode)
385         {
386             case INSTR_MUL_FV:
387                 t[1] = t[2] = TYPE_VECTOR;
388                 break;
389             case INSTR_MUL_VF:
390                 t[0] = t[2] = TYPE_VECTOR;
391                 break;
392             case INSTR_MUL_V:
393                 t[0] = t[1] = TYPE_VECTOR;
394                 break;
395             case INSTR_ADD_V:
396             case INSTR_SUB_V:
397             case INSTR_EQ_V:
398             case INSTR_NE_V:
399                 t[0] = t[1] = t[2] = TYPE_VECTOR;
400                 break;
401             case INSTR_EQ_S:
402             case INSTR_NE_S:
403                 t[0] = t[1] = TYPE_STRING;
404                 break;
405             case INSTR_STORE_F:
406             case INSTR_STOREP_F:
407                 t[2] = -1;
408                 break;
409             case INSTR_STORE_V:
410                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
411                 break;
412             case INSTR_STORE_S:
413                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
414                 break;
415             case INSTR_STORE_ENT:
416                 t[0] = t[1] = TYPE_ENTITY; t[2] = -1;
417                 break;
418             case INSTR_STORE_FLD:
419                 t[0] = t[1] = TYPE_FIELD; t[2] = -1;
420                 break;
421             case INSTR_STORE_FNC:
422                 t[0] = t[1] = TYPE_FUNCTION; t[2] = -1;
423                 break;
424             case INSTR_STOREP_V:
425                 t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1;
426                 break;
427             case INSTR_STOREP_S:
428                 t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1;
429                 break;
430             case INSTR_STOREP_ENT:
431                 t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1;
432                 break;
433             case INSTR_STOREP_FLD:
434                 t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1;
435                 break;
436             case INSTR_STOREP_FNC:
437                 t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1;
438                 break;
439         }
440         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
441         else           printf("(none),          ");
442         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
443         else           printf("(none),          ");
444         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
445         else           printf("(none)");
446         printf("\n");
447     }
448 }
449
450 static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *func) {
451     qc_exec_stack_t st;
452     size_t  parampos;
453     int32_t p;
454
455     /* back up locals */
456     st.localsp  = prog->localstack.size();
457     st.stmt     = prog->statement;
458     st.function = func;
459
460     if (prog->xflags & VMXF_TRACE) {
461         const char *str = prog_getstring(prog, func->name);
462         prog->function_stack.emplace_back(str);
463     }
464
465 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
466     if (prog->stack.size())
467     {
468         prog_section_function_t *cur;
469         cur = prog->stack.back().function;
470         if (cur)
471         {
472             qcint_t *globals = &prog->globals[0] + cur->firstlocal;
473             prog->localstack.insert(prog->localstack.end(), globals, globals + cur->locals);
474         }
475     }
476 #else
477     {
478         qcint_t *globals = &prog->globals[0] + func->firstlocal;
479         prog->localstack.insert(prog->localstack.end(), globals, globals + func->locals);
480     }
481 #endif
482
483     /* copy parameters */
484     parampos = func->firstlocal;
485     for (p = 0; p < func->nargs; ++p)
486     {
487         size_t s;
488         for (s = 0; s < func->argsize[p]; ++s) {
489             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
490             ++parampos;
491         }
492     }
493
494     prog->stack.emplace_back(st);
495
496     return func->entry;
497 }
498
499 static qcint_t prog_leavefunction(qc_program_t *prog) {
500     prog_section_function_t *prev = nullptr;
501     size_t oldsp;
502
503     qc_exec_stack_t st = prog->stack.back();
504
505     if (prog->xflags & VMXF_TRACE) {
506         if (!prog->function_stack.empty())
507             prog->function_stack.pop_back();
508     }
509
510 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
511     if (prog->stack.size() > 1) {
512         prev  = prog->stack[prog->stack.size()-2].function;
513         oldsp = prog->stack[prog->stack.size()-2].localsp;
514     }
515 #else
516     prev  = prog->stack[prog->stack.size()-1].function;
517     oldsp = prog->stack[prog->stack.size()-1].localsp;
518 #endif
519     if (prev) {
520         qcint_t *globals = &prog->globals[0] + prev->firstlocal;
521         memcpy(globals, &prog->localstack[oldsp], prev->locals * sizeof(prog->localstack[0]));
522         prog->localstack.resize(oldsp);
523     }
524
525     prog->stack.pop_back();
526
527     return st.stmt - 1; /* offset the ++st */
528 }
529
530 bool prog_exec(qc_program_t *prog, prog_section_function_t *func, size_t flags, long maxjumps) {
531     long jumpcount = 0;
532     size_t oldxflags = prog->xflags;
533     prog_section_statement_t *st;
534
535     prog->vmerror = 0;
536     prog->xflags = flags;
537
538     st = &prog->code[0] + prog_enterfunction(prog, func);
539     --st;
540     switch (flags)
541     {
542         default:
543         case 0:
544         {
545 #define QCVM_LOOP    1
546 #define QCVM_PROFILE 0
547 #define QCVM_TRACE   0
548 #           include __FILE__
549         }
550         case (VMXF_TRACE):
551         {
552 #define QCVM_PROFILE 0
553 #define QCVM_TRACE   1
554 #           include __FILE__
555         }
556         case (VMXF_PROFILE):
557         {
558 #define QCVM_PROFILE 1
559 #define QCVM_TRACE   0
560 #           include __FILE__
561         }
562         case (VMXF_TRACE|VMXF_PROFILE):
563         {
564 #define QCVM_PROFILE 1
565 #define QCVM_TRACE   1
566 #           include __FILE__
567         }
568     };
569
570 cleanup:
571     prog->xflags = oldxflags;
572     prog->localstack.clear();
573     prog->stack.clear();
574     if (prog->vmerror)
575         return false;
576     return true;
577 }
578
579 /***********************************************************************
580  * main for when building the standalone executor
581  */
582
583 #include <math.h>
584
585 const char *type_name[TYPE_COUNT] = {
586     "void",
587     "string",
588     "float",
589     "vector",
590     "entity",
591     "field",
592     "function",
593     "pointer",
594     "integer",
595
596     "variant",
597
598     "struct",
599     "union",
600     "array",
601
602     "nil",
603     "noexpr"
604 };
605
606 struct qcvm_parameter {
607     int         vtype;
608     const char *value;
609 };
610
611 static std::vector<qcvm_parameter> main_params;
612
613 #define CheckArgs(num) do {                                                    \
614     if (prog->argc != (num)) {                                                 \
615         prog->vmerror++;                                                       \
616         fprintf(stderr, "ERROR: invalid number of arguments for %s: %i, expected %i\n", \
617         __func__, prog->argc, (num));                                      \
618         return -1;                                                             \
619     }                                                                          \
620 } while (0)
621
622 #define GetGlobal(idx) ((qcany_t*)(&prog->globals[0] + (idx)))
623 #define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
624 #define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
625
626 static int qc_print(qc_program_t *prog) {
627     size_t i;
628     const char *laststr = nullptr;
629     for (i = 0; i < (size_t)prog->argc; ++i) {
630         qcany_t *str = (qcany_t*)(&prog->globals[0] + OFS_PARM0 + 3*i);
631         laststr = prog_getstring(prog, str->string);
632         printf("%s", laststr);
633     }
634     if (laststr && (prog->xflags & VMXF_TRACE)) {
635         size_t len = strlen(laststr);
636         if (!len || laststr[len-1] != '\n')
637             printf("\n");
638     }
639     return 0;
640 }
641
642 static int qc_error(qc_program_t *prog) {
643     fprintf(stderr, "*** VM raised an error:\n");
644     qc_print(prog);
645     prog->vmerror++;
646     return -1;
647 }
648
649 static int qc_ftos(qc_program_t *prog) {
650     char buffer[512];
651     qcany_t *num;
652     qcany_t str;
653     CheckArgs(1);
654     num = GetArg(0);
655     util_snprintf(buffer, sizeof(buffer), "%g", num->_float);
656     str.string = prog_tempstring(prog, buffer);
657     Return(str);
658     return 0;
659 }
660
661 static int qc_stof(qc_program_t *prog) {
662     qcany_t *str;
663     qcany_t num;
664     CheckArgs(1);
665     str = GetArg(0);
666     num._float = (float)strtod(prog_getstring(prog, str->string), nullptr);
667     Return(num);
668     return 0;
669 }
670
671 static int qc_vtos(qc_program_t *prog) {
672     char buffer[512];
673     qcany_t *num;
674     qcany_t str;
675     CheckArgs(1);
676     num = GetArg(0);
677     util_snprintf(buffer, sizeof(buffer), "'%g %g %g'", num->vector[0], num->vector[1], num->vector[2]);
678     str.string = prog_tempstring(prog, buffer);
679     Return(str);
680     return 0;
681 }
682
683 static int qc_etos(qc_program_t *prog) {
684     char buffer[512];
685     qcany_t *num;
686     qcany_t str;
687     CheckArgs(1);
688     num = GetArg(0);
689     util_snprintf(buffer, sizeof(buffer), "%i", num->_int);
690     str.string = prog_tempstring(prog, buffer);
691     Return(str);
692     return 0;
693 }
694
695 static int qc_spawn(qc_program_t *prog) {
696     qcany_t ent;
697     CheckArgs(0);
698     ent.edict = prog_spawn_entity(prog);
699     Return(ent);
700     return (ent.edict ? 0 : -1);
701 }
702
703 static int qc_kill(qc_program_t *prog) {
704     qcany_t *ent;
705     CheckArgs(1);
706     ent = GetArg(0);
707     prog_free_entity(prog, ent->edict);
708     return 0;
709 }
710
711 static int qc_sqrt(qc_program_t *prog) {
712     qcany_t *num, out;
713     CheckArgs(1);
714     num = GetArg(0);
715     out._float = sqrt(num->_float);
716     Return(out);
717     return 0;
718 }
719
720 static int qc_vlen(qc_program_t *prog) {
721     qcany_t *vec, len;
722     CheckArgs(1);
723     vec = GetArg(0);
724     len._float = sqrt(vec->vector[0] * vec->vector[0] +
725                       vec->vector[1] * vec->vector[1] +
726                       vec->vector[2] * vec->vector[2]);
727     Return(len);
728     return 0;
729 }
730
731 static int qc_normalize(qc_program_t *prog) {
732     double len;
733     qcany_t *vec;
734     qcany_t out;
735     CheckArgs(1);
736     vec = GetArg(0);
737     len = sqrt(vec->vector[0] * vec->vector[0] +
738                vec->vector[1] * vec->vector[1] +
739                vec->vector[2] * vec->vector[2]);
740     if (len)
741         len = 1.0 / len;
742     else
743         len = 0;
744     out.vector[0] = len * vec->vector[0];
745     out.vector[1] = len * vec->vector[1];
746     out.vector[2] = len * vec->vector[2];
747     Return(out);
748     return 0;
749 }
750
751 static int qc_strcat(qc_program_t *prog) {
752     char  *buffer;
753     size_t len1,   len2;
754     qcany_t *str1,  *str2;
755     qcany_t  out;
756
757     const char *cstr1;
758     const char *cstr2;
759
760     CheckArgs(2);
761     str1 = GetArg(0);
762     str2 = GetArg(1);
763     cstr1 = prog_getstring(prog, str1->string);
764     cstr2 = prog_getstring(prog, str2->string);
765     len1 = strlen(cstr1);
766     len2 = strlen(cstr2);
767     buffer = (char*)mem_a(len1 + len2 + 1);
768     memcpy(buffer, cstr1, len1);
769     memcpy(buffer+len1, cstr2, len2+1);
770     out.string = prog_tempstring(prog, buffer);
771     mem_d(buffer);
772     Return(out);
773     return 0;
774 }
775
776 static int qc_strcmp(qc_program_t *prog) {
777     qcany_t *str1,  *str2;
778     qcany_t out;
779
780     const char *cstr1;
781     const char *cstr2;
782
783     if (prog->argc != 2 && prog->argc != 3) {
784         fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
785                prog->argc);
786         return -1;
787     }
788
789     str1 = GetArg(0);
790     str2 = GetArg(1);
791     cstr1 = prog_getstring(prog, str1->string);
792     cstr2 = prog_getstring(prog, str2->string);
793     if (prog->argc == 3)
794         out._float = strncmp(cstr1, cstr2, GetArg(2)->_float);
795     else
796         out._float = strcmp(cstr1, cstr2);
797     Return(out);
798     return 0;
799 }
800
801 static int qc_floor(qc_program_t *prog) {
802     qcany_t *num, out;
803     CheckArgs(1);
804     num = GetArg(0);
805     out._float = floor(num->_float);
806     Return(out);
807     return 0;
808 }
809
810 static int qc_pow(qc_program_t *prog) {
811     qcany_t *base, *exp, out;
812     CheckArgs(2);
813     base = GetArg(0);
814     exp = GetArg(1);
815     out._float = powf(base->_float, exp->_float);
816     Return(out);
817     return 0;
818 }
819
820 static prog_builtin_t qc_builtins[] = {
821     nullptr,
822     &qc_print,       /*   1   */
823     &qc_ftos,        /*   2   */
824     &qc_spawn,       /*   3   */
825     &qc_kill,        /*   4   */
826     &qc_vtos,        /*   5   */
827     &qc_error,       /*   6   */
828     &qc_vlen,        /*   7   */
829     &qc_etos,        /*   8   */
830     &qc_stof,        /*   9   */
831     &qc_strcat,      /*   10  */
832     &qc_strcmp,      /*   11  */
833     &qc_normalize,   /*   12  */
834     &qc_sqrt,        /*   13  */
835     &qc_floor,       /*   14  */
836     &qc_pow          /*   15  */
837 };
838
839 static const char *arg0 = nullptr;
840
841 static void version(void) {
842     printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
843            GMQCC_VERSION_MAJOR,
844            GMQCC_VERSION_MINOR,
845            GMQCC_VERSION_PATCH,
846            __DATE__,
847            __TIME__
848     );
849 }
850
851 static void usage(void) {
852     printf("usage: %s [options] [parameters] file\n", arg0);
853     printf("options:\n");
854     printf("  -h, --help         print this message\n"
855            "  -trace             trace the execution\n"
856            "  -profile           perform profiling during execution\n"
857            "  -info              print information from the prog's header\n"
858            "  -disasm            disassemble and exit\n"
859            "  -disasm-func func  disassemble and exit\n"
860            "  -printdefs         list the defs section\n"
861            "  -printfields       list the field section\n"
862            "  -printfuns         list functions information\n"
863            "  -v                 be verbose\n"
864            "  -vv                be even more verbose\n");
865     printf("parameters:\n");
866     printf("  -vector <V>   pass a vector parameter to main()\n"
867            "  -float  <f>   pass a float parameter to main()\n"
868            "  -string <s>   pass a string parameter to main() \n");
869 }
870
871 static void prog_main_setparams(qc_program_t *prog) {
872     size_t i;
873     qcany_t *arg;
874
875     for (i = 0; i < main_params.size(); ++i) {
876         arg = GetGlobal(OFS_PARM0 + 3*i);
877         arg->vector[0] = 0;
878         arg->vector[1] = 0;
879         arg->vector[2] = 0;
880         switch (main_params[i].vtype) {
881             case TYPE_VECTOR:
882                 (void)util_sscanf(main_params[i].value, " %f %f %f ",
883                                        &arg->vector[0],
884                                        &arg->vector[1],
885                                        &arg->vector[2]);
886                 break;
887             case TYPE_FLOAT:
888                 arg->_float = atof(main_params[i].value);
889                 break;
890             case TYPE_STRING:
891                 arg->string = prog_tempstring(prog, main_params[i].value);
892                 break;
893             default:
894                 fprintf(stderr, "error: unhandled parameter type: %i\n", main_params[i].vtype);
895                 break;
896         }
897     }
898 }
899
900 static void prog_disasm_function(qc_program_t *prog, size_t id);
901
902 int main(int argc, char **argv) {
903     size_t      i;
904     qcint_t       fnmain = -1;
905     qc_program_t *prog;
906     size_t      xflags = VMXF_DEFAULT;
907     bool        opts_printfields = false;
908     bool        opts_printdefs   = false;
909     bool        opts_printfuns   = false;
910     bool        opts_disasm      = false;
911     bool        opts_info        = false;
912     bool        noexec           = false;
913     const char *progsfile        = nullptr;
914     int         opts_v           = 0;
915     std::vector<const char*> dis_list;
916
917     arg0 = argv[0];
918
919     if (argc < 2) {
920         usage();
921         exit(EXIT_FAILURE);
922     }
923
924     while (argc > 1) {
925         if (!strcmp(argv[1], "-h") ||
926             !strcmp(argv[1], "-help") ||
927             !strcmp(argv[1], "--help"))
928         {
929             usage();
930             exit(EXIT_SUCCESS);
931         }
932         else if (!strcmp(argv[1], "-v")) {
933             ++opts_v;
934             --argc;
935             ++argv;
936         }
937         else if (!strncmp(argv[1], "-vv", 3)) {
938             const char *av = argv[1]+1;
939             for (; *av; ++av) {
940                 if (*av == 'v')
941                     ++opts_v;
942                 else {
943                     usage();
944                     exit(EXIT_FAILURE);
945                 }
946             }
947             --argc;
948             ++argv;
949         }
950         else if (!strcmp(argv[1], "-version") ||
951                  !strcmp(argv[1], "--version"))
952         {
953             version();
954             exit(EXIT_SUCCESS);
955         }
956         else if (!strcmp(argv[1], "-trace")) {
957             --argc;
958             ++argv;
959             xflags |= VMXF_TRACE;
960         }
961         else if (!strcmp(argv[1], "-profile")) {
962             --argc;
963             ++argv;
964             xflags |= VMXF_PROFILE;
965         }
966         else if (!strcmp(argv[1], "-info")) {
967             --argc;
968             ++argv;
969             opts_info = true;
970             noexec = true;
971         }
972         else if (!strcmp(argv[1], "-disasm")) {
973             --argc;
974             ++argv;
975             opts_disasm = true;
976             noexec = true;
977         }
978         else if (!strcmp(argv[1], "-disasm-func")) {
979             --argc;
980             ++argv;
981             if (argc <= 1) {
982                 usage();
983                 exit(EXIT_FAILURE);
984             }
985             dis_list.emplace_back(argv[1]);
986             --argc;
987             ++argv;
988             noexec = true;
989         }
990         else if (!strcmp(argv[1], "-printdefs")) {
991             --argc;
992             ++argv;
993             opts_printdefs = true;
994             noexec = true;
995         }
996         else if (!strcmp(argv[1], "-printfuns")) {
997             --argc;
998             ++argv;
999             opts_printfuns = true;
1000             noexec = true;
1001         }
1002         else if (!strcmp(argv[1], "-printfields")) {
1003             --argc;
1004             ++argv;
1005             opts_printfields = true;
1006             noexec = true;
1007         }
1008         else if (!strcmp(argv[1], "-vector") ||
1009                  !strcmp(argv[1], "-string") ||
1010                  !strcmp(argv[1], "-float") )
1011         {
1012             qcvm_parameter p;
1013             if (argv[1][1] == 'f')
1014                 p.vtype = TYPE_FLOAT;
1015             else if (argv[1][1] == 's')
1016                 p.vtype = TYPE_STRING;
1017             else if (argv[1][1] == 'v')
1018                 p.vtype = TYPE_VECTOR;
1019             else
1020                 p.vtype = TYPE_VOID;
1021
1022             --argc;
1023             ++argv;
1024             if (argc < 2) {
1025                 usage();
1026                 exit(EXIT_FAILURE);
1027             }
1028             p.value = argv[1];
1029
1030             main_params.emplace_back(p);
1031             --argc;
1032             ++argv;
1033         }
1034         else if (!strcmp(argv[1], "--")) {
1035             --argc;
1036             ++argv;
1037             break;
1038         }
1039         else if (argv[1][0] != '-') {
1040             if (progsfile) {
1041                 fprintf(stderr, "only 1 program file may be specified\n");
1042                 usage();
1043                 exit(EXIT_FAILURE);
1044             }
1045             progsfile = argv[1];
1046             --argc;
1047             ++argv;
1048         }
1049         else
1050         {
1051             fprintf(stderr, "unknown parameter: %s\n", argv[1]);
1052             usage();
1053             exit(EXIT_FAILURE);
1054         }
1055     }
1056
1057     if (argc == 2 && !progsfile) {
1058         progsfile = argv[1];
1059         --argc;
1060         ++argv;
1061     }
1062
1063     if (!progsfile) {
1064         fprintf(stderr, "must specify a program to execute\n");
1065         usage();
1066         exit(EXIT_FAILURE);
1067     }
1068
1069     prog = prog_load(progsfile, noexec);
1070     if (!prog) {
1071         fprintf(stderr, "failed to load program '%s'\n", progsfile);
1072         exit(EXIT_FAILURE);
1073     }
1074
1075     prog->builtins       = qc_builtins;
1076     prog->builtins_count = GMQCC_ARRAY_COUNT(qc_builtins);
1077
1078     if (opts_info) {
1079         printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16);
1080         printf("Entity field space: %u\n", (unsigned int)prog->entityfields);
1081         printf("Globals: %zu\n", prog->globals.size());
1082         printf("Counts:\n"
1083                "      code: %zu\n"
1084                "      defs: %zu\n"
1085                "    fields: %zu\n"
1086                " functions: %zu\n"
1087                "   strings: %zu\n",
1088                prog->code.size(),
1089                prog->defs.size(),
1090                prog->fields.size(),
1091                prog->functions.size(),
1092                prog->strings.size());
1093     }
1094
1095     if (opts_info) {
1096         prog_delete(prog);
1097         return 0;
1098     }
1099     for (i = 0; i < dis_list.size(); ++i) {
1100         size_t k;
1101         printf("Looking for `%s`\n", dis_list[i]);
1102         for (k = 1; k < prog->functions.size(); ++k) {
1103             const char *name = prog_getstring(prog, prog->functions[k].name);
1104             if (!strcmp(name, dis_list[i])) {
1105                 prog_disasm_function(prog, k);
1106                 break;
1107             }
1108         }
1109     }
1110     if (opts_disasm) {
1111         for (i = 1; i < prog->functions.size(); ++i)
1112             prog_disasm_function(prog, i);
1113         return 0;
1114     }
1115     if (opts_printdefs) {
1116         const char *getstring = nullptr;
1117         for (auto &it : prog->defs) {
1118             printf("Global: %8s %-16s at %u%s",
1119                    type_name[it.type & DEF_TYPEMASK],
1120                    prog_getstring(prog, it.name),
1121                    (unsigned int)it.offset,
1122                    ((it.type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1123             if (opts_v) {
1124                 switch (it.type & DEF_TYPEMASK) {
1125                     case TYPE_FLOAT:
1126                         printf(" [init: %g]", ((qcany_t*)(&prog->globals[0] + it.offset))->_float);
1127                         break;
1128                     case TYPE_INTEGER:
1129                         printf(" [init: %i]", (int)( ((qcany_t*)(&prog->globals[0] + it.offset))->_int ));
1130                         break;
1131                     case TYPE_ENTITY:
1132                     case TYPE_FUNCTION:
1133                     case TYPE_FIELD:
1134                     case TYPE_POINTER:
1135                         printf(" [init: %u]", (unsigned)( ((qcany_t*)(&prog->globals[0] + it.offset))->_int ));
1136                         break;
1137                     case TYPE_STRING:
1138                         getstring = prog_getstring(prog, ((qcany_t*)(&prog->globals[0] + it.offset))->string);
1139                         printf(" [init: `");
1140                         print_escaped_string(getstring, strlen(getstring));
1141                         printf("`]\n");
1142                         break;
1143                     default:
1144                         break;
1145                 }
1146             }
1147             printf("\n");
1148         }
1149     }
1150     if (opts_printfields) {
1151         for (auto &it : prog->fields) {
1152             printf("Field: %8s %-16s at %d%s\n",
1153                    type_name[it.type],
1154                    prog_getstring(prog, it.name),
1155                    it.offset,
1156                    ((it.type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1157         }
1158     }
1159     if (opts_printfuns) {
1160         for (auto &it : prog->functions) {
1161             int32_t a;
1162             printf("Function: %-16s taking %u parameters:(",
1163                    prog_getstring(prog, it.name),
1164                    (unsigned int)it.nargs);
1165             for (a = 0; a < it.nargs; ++a) {
1166                 printf(" %i", it.argsize[a]);
1167             }
1168             if (opts_v > 1) {
1169                 int32_t start = it.entry;
1170                 if (start < 0)
1171                     printf(") builtin %i\n", (int)-start);
1172                 else {
1173                     size_t funsize = 0;
1174                     prog_section_statement_t *st = &prog->code[0] + start;
1175                     for (;st->opcode != INSTR_DONE; ++st)
1176                         ++funsize;
1177                     printf(") - %zu instructions", funsize);
1178                     if (opts_v > 2) {
1179                         printf(" - locals: %i + %i\n",
1180                                it.firstlocal,
1181                                it.locals);
1182                     }
1183                     else
1184                         printf("\n");
1185                 }
1186             }
1187             else if (opts_v) {
1188                 printf(") locals: %i + %i\n",
1189                        it.firstlocal,
1190                        it.locals);
1191             }
1192             else
1193                 printf(")\n");
1194         }
1195     }
1196     if (!noexec) {
1197         for (i = 1; i < prog->functions.size(); ++i) {
1198             const char *name = prog_getstring(prog, prog->functions[i].name);
1199             if (!strcmp(name, "main"))
1200                 fnmain = (qcint_t)i;
1201         }
1202         if (fnmain > 0)
1203         {
1204             prog_main_setparams(prog);
1205             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
1206         }
1207         else
1208             fprintf(stderr, "No main function found\n");
1209     }
1210
1211     prog_delete(prog);
1212     return 0;
1213 }
1214
1215 static void prog_disasm_function(qc_program_t *prog, size_t id) {
1216     prog_section_function_t *fdef = &prog->functions[0] + id;
1217     prog_section_statement_t *st;
1218
1219     if (fdef->entry < 0) {
1220         printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry);
1221         return;
1222     }
1223     else
1224         printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name));
1225
1226     st = &prog->code[0] + fdef->entry;
1227     while (st->opcode != INSTR_DONE) {
1228         prog_print_statement(prog, st);
1229         ++st;
1230     }
1231 }
1232 #else /* !QCVM_LOOP */
1233 /*
1234  * Everything from here on is not including into the compilation of the
1235  * executor.  This is simply code that is #included via #include __FILE__
1236  * see when QCVM_LOOP is defined, the rest of the code above do not get
1237  * re-included.  So this really just acts like one large macro, but it
1238  * sort of isn't, which makes it nicer looking.
1239  */
1240
1241 #define OPA ( (qcany_t*) (&prog->globals[0] + st->o1.u1) )
1242 #define OPB ( (qcany_t*) (&prog->globals[0] + st->o2.u1) )
1243 #define OPC ( (qcany_t*) (&prog->globals[0] + st->o3.u1) )
1244
1245 #define GLOBAL(x) ( (qcany_t*) (&prog->globals[0] + (x)) )
1246
1247 /* to be consistent with current darkplaces behaviour */
1248 #if !defined(FLOAT_IS_TRUE_FOR_INT)
1249 #   define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
1250 #endif
1251
1252 while (prog->vmerror == 0) {
1253     prog_section_function_t  *newf;
1254     qcany_t          *ed;
1255     qcany_t          *ptr;
1256
1257     ++st;
1258
1259 #if QCVM_PROFILE
1260     prog->profile[st - &prog->code[0]]++;
1261 #endif
1262
1263 #if QCVM_TRACE
1264     prog_print_statement(prog, st);
1265 #endif
1266
1267     switch (st->opcode)
1268     {
1269         default:
1270             qcvmerror(prog, "Illegal instruction in %s\n", prog->filename.c_str());
1271             goto cleanup;
1272
1273         case INSTR_DONE:
1274         case INSTR_RETURN:
1275             /* TODO: add instruction count to function profile count */
1276             GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
1277             GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
1278             GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
1279
1280             st = &prog->code[0] + prog_leavefunction(prog);
1281             if (prog->stack.empty())
1282                 goto cleanup;
1283
1284             break;
1285
1286         case INSTR_MUL_F:
1287             OPC->_float = OPA->_float * OPB->_float;
1288             break;
1289         case INSTR_MUL_V:
1290             OPC->_float = OPA->vector[0]*OPB->vector[0] +
1291                           OPA->vector[1]*OPB->vector[1] +
1292                           OPA->vector[2]*OPB->vector[2];
1293             break;
1294         case INSTR_MUL_FV:
1295         {
1296             qcfloat_t f = OPA->_float;
1297             OPC->vector[0] = f * OPB->vector[0];
1298             OPC->vector[1] = f * OPB->vector[1];
1299             OPC->vector[2] = f * OPB->vector[2];
1300             break;
1301         }
1302         case INSTR_MUL_VF:
1303         {
1304             qcfloat_t f = OPB->_float;
1305             OPC->vector[0] = f * OPA->vector[0];
1306             OPC->vector[1] = f * OPA->vector[1];
1307             OPC->vector[2] = f * OPA->vector[2];
1308             break;
1309         }
1310         case INSTR_DIV_F:
1311             if (OPB->_float != 0.0f)
1312                 OPC->_float = OPA->_float / OPB->_float;
1313             else
1314                 OPC->_float = 0;
1315             break;
1316
1317         case INSTR_ADD_F:
1318             OPC->_float = OPA->_float + OPB->_float;
1319             break;
1320         case INSTR_ADD_V:
1321             OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
1322             OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
1323             OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
1324             break;
1325         case INSTR_SUB_F:
1326             OPC->_float = OPA->_float - OPB->_float;
1327             break;
1328         case INSTR_SUB_V:
1329             OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
1330             OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
1331             OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
1332             break;
1333
1334         case INSTR_EQ_F:
1335             OPC->_float = (OPA->_float == OPB->_float);
1336             break;
1337         case INSTR_EQ_V:
1338             OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
1339                            (OPA->vector[1] == OPB->vector[1]) &&
1340                            (OPA->vector[2] == OPB->vector[2]) );
1341             break;
1342         case INSTR_EQ_S:
1343             OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
1344                                   prog_getstring(prog, OPB->string));
1345             break;
1346         case INSTR_EQ_E:
1347             OPC->_float = (OPA->_int == OPB->_int);
1348             break;
1349         case INSTR_EQ_FNC:
1350             OPC->_float = (OPA->function == OPB->function);
1351             break;
1352         case INSTR_NE_F:
1353             OPC->_float = (OPA->_float != OPB->_float);
1354             break;
1355         case INSTR_NE_V:
1356             OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
1357                            (OPA->vector[1] != OPB->vector[1]) ||
1358                            (OPA->vector[2] != OPB->vector[2]) );
1359             break;
1360         case INSTR_NE_S:
1361             OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
1362                                    prog_getstring(prog, OPB->string));
1363             break;
1364         case INSTR_NE_E:
1365             OPC->_float = (OPA->_int != OPB->_int);
1366             break;
1367         case INSTR_NE_FNC:
1368             OPC->_float = (OPA->function != OPB->function);
1369             break;
1370
1371         case INSTR_LE:
1372             OPC->_float = (OPA->_float <= OPB->_float);
1373             break;
1374         case INSTR_GE:
1375             OPC->_float = (OPA->_float >= OPB->_float);
1376             break;
1377         case INSTR_LT:
1378             OPC->_float = (OPA->_float < OPB->_float);
1379             break;
1380         case INSTR_GT:
1381             OPC->_float = (OPA->_float > OPB->_float);
1382             break;
1383
1384         case INSTR_LOAD_F:
1385         case INSTR_LOAD_S:
1386         case INSTR_LOAD_FLD:
1387         case INSTR_LOAD_ENT:
1388         case INSTR_LOAD_FNC:
1389             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1390                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str());
1391                 goto cleanup;
1392             }
1393             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
1394                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1395                           prog->filename.c_str(),
1396                           OPB->_int);
1397                 goto cleanup;
1398             }
1399             ed = prog_getedict(prog, OPA->edict);
1400             OPC->_int = ((qcany_t*)( ((qcint_t*)ed) + OPB->_int ))->_int;
1401             break;
1402         case INSTR_LOAD_V:
1403             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1404                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str());
1405                 goto cleanup;
1406             }
1407             if (OPB->_int < 0 || OPB->_int + 3 > (qcint_t)prog->entityfields)
1408             {
1409                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1410                           prog->filename.c_str(),
1411                           OPB->_int + 2);
1412                 goto cleanup;
1413             }
1414             ed = prog_getedict(prog, OPA->edict);
1415             ptr = (qcany_t*)( ((qcint_t*)ed) + OPB->_int );
1416             OPC->ivector[0] = ptr->ivector[0];
1417             OPC->ivector[1] = ptr->ivector[1];
1418             OPC->ivector[2] = ptr->ivector[2];
1419             break;
1420
1421         case INSTR_ADDRESS:
1422             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1423                 qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename.c_str(), OPA->edict);
1424                 goto cleanup;
1425             }
1426             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
1427             {
1428                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1429                           prog->filename.c_str(),
1430                           OPB->_int);
1431                 goto cleanup;
1432             }
1433
1434             ed = prog_getedict(prog, OPA->edict);
1435             OPC->_int = ((qcint_t*)ed) - prog->entitydata.data() + OPB->_int;
1436             break;
1437
1438         case INSTR_STORE_F:
1439         case INSTR_STORE_S:
1440         case INSTR_STORE_ENT:
1441         case INSTR_STORE_FLD:
1442         case INSTR_STORE_FNC:
1443             OPB->_int = OPA->_int;
1444             break;
1445         case INSTR_STORE_V:
1446             OPB->ivector[0] = OPA->ivector[0];
1447             OPB->ivector[1] = OPA->ivector[1];
1448             OPB->ivector[2] = OPA->ivector[2];
1449             break;
1450
1451         case INSTR_STOREP_F:
1452         case INSTR_STOREP_S:
1453         case INSTR_STOREP_ENT:
1454         case INSTR_STOREP_FLD:
1455         case INSTR_STOREP_FNC:
1456             if (OPB->_int < 0 || OPB->_int >= (qcint_t)prog->entitydata.size()) {
1457                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int);
1458                 goto cleanup;
1459             }
1460             if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites)
1461                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1462                           prog->filename.c_str(),
1463                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1464                           OPB->_int);
1465             ptr = (qcany_t*)&prog->entitydata[OPB->_int];
1466             ptr->_int = OPA->_int;
1467             break;
1468         case INSTR_STOREP_V:
1469             if (OPB->_int < 0 || OPB->_int + 2 >= (qcint_t)prog->entitydata.size()) {
1470                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int);
1471                 goto cleanup;
1472             }
1473             if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites)
1474                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1475                           prog->filename.c_str(),
1476                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1477                           OPB->_int);
1478             ptr = (qcany_t*)&prog->entitydata[OPB->_int];
1479             ptr->ivector[0] = OPA->ivector[0];
1480             ptr->ivector[1] = OPA->ivector[1];
1481             ptr->ivector[2] = OPA->ivector[2];
1482             break;
1483
1484         case INSTR_NOT_F:
1485             OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
1486             break;
1487         case INSTR_NOT_V:
1488             OPC->_float = !OPA->vector[0] &&
1489                           !OPA->vector[1] &&
1490                           !OPA->vector[2];
1491             break;
1492         case INSTR_NOT_S:
1493             OPC->_float = !OPA->string ||
1494                           !*prog_getstring(prog, OPA->string);
1495             break;
1496         case INSTR_NOT_ENT:
1497             OPC->_float = (OPA->edict == 0);
1498             break;
1499         case INSTR_NOT_FNC:
1500             OPC->_float = !OPA->function;
1501             break;
1502
1503         case INSTR_IF:
1504             /* this is consistent with darkplaces' behaviour */
1505             if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1506             {
1507                 st += st->o2.s1 - 1;    /* offset the s++ */
1508                 if (++jumpcount >= maxjumps)
1509                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
1510             }
1511             break;
1512         case INSTR_IFNOT:
1513             if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1514             {
1515                 st += st->o2.s1 - 1;    /* offset the s++ */
1516                 if (++jumpcount >= maxjumps)
1517                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
1518             }
1519             break;
1520
1521         case INSTR_CALL0:
1522         case INSTR_CALL1:
1523         case INSTR_CALL2:
1524         case INSTR_CALL3:
1525         case INSTR_CALL4:
1526         case INSTR_CALL5:
1527         case INSTR_CALL6:
1528         case INSTR_CALL7:
1529         case INSTR_CALL8:
1530             prog->argc = st->opcode - INSTR_CALL0;
1531             if (!OPA->function)
1532                 qcvmerror(prog, "nullptr function in `%s`", prog->filename.c_str());
1533
1534             if(!OPA->function || OPA->function >= (qcint_t)prog->functions.size())
1535             {
1536                 qcvmerror(prog, "CALL outside the program in `%s`", prog->filename.c_str());
1537                 goto cleanup;
1538             }
1539
1540             newf = &prog->functions[OPA->function];
1541             newf->profile++;
1542
1543             prog->statement = (st - &prog->code[0]) + 1;
1544
1545             if (newf->entry < 0)
1546             {
1547                 /* negative statements are built in functions */
1548                 qcint_t builtinnumber = -newf->entry;
1549                 if (builtinnumber < (qcint_t)prog->builtins_count && prog->builtins[builtinnumber])
1550                     prog->builtins[builtinnumber](prog);
1551                 else
1552                     qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
1553                               builtinnumber, prog->filename.c_str());
1554             }
1555             else
1556                 st = &prog->code[0] + prog_enterfunction(prog, newf) - 1; /* offset st++ */
1557             if (prog->vmerror)
1558                 goto cleanup;
1559             break;
1560
1561         case INSTR_STATE:
1562         {
1563             qcfloat_t *nextthink;
1564             qcfloat_t *time;
1565             qcfloat_t *frame;
1566             if (!prog->supports_state) {
1567                 qcvmerror(prog, "`%s` tried to execute a STATE operation but misses its defs!", prog->filename.c_str());
1568                 goto cleanup;
1569             }
1570             ed = prog_getedict(prog, prog->globals[prog->cached_globals.self]);
1571             ((qcint_t*)ed)[prog->cached_fields.think] = OPB->function;
1572
1573             frame     = (qcfloat_t*)&((qcint_t*)ed)[prog->cached_fields.frame];
1574             *frame    = OPA->_float;
1575             nextthink = (qcfloat_t*)&((qcint_t*)ed)[prog->cached_fields.nextthink];
1576             time      = (qcfloat_t*)(&prog->globals[0] + prog->cached_globals.time);
1577             *nextthink = *time + 0.1;
1578             break;
1579         }
1580
1581         case INSTR_GOTO:
1582             st += st->o1.s1 - 1;    /* offset the s++ */
1583             if (++jumpcount == 10000000)
1584                 qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
1585             break;
1586
1587         case INSTR_AND:
1588             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
1589                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1590             break;
1591         case INSTR_OR:
1592             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
1593                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1594             break;
1595
1596         case INSTR_BITAND:
1597             OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
1598             break;
1599         case INSTR_BITOR:
1600             OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
1601             break;
1602     }
1603 }
1604
1605 #undef QCVM_PROFILE
1606 #undef QCVM_TRACE
1607 #endif /* !QCVM_LOOP */