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