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