]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
cppcheck had the right idea, but the wrong scope on this one.
[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 *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* prog_load(const char *filename, bool skipversion)
56 {
57     qc_program   *prog;
58     prog_header   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*)mem_a(sizeof(qc_program));
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 *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 *prog, qcint str) {
174     /* cast for return required for C++ */
175     if (str < 0 || str >= (qcint)vec_size(prog->strings))
176         return  "<<<invalid string>>>";
177
178     return prog->strings + str;
179 }
180
181 prog_section_def* prog_entfield(qc_program *prog, qcint 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* prog_getdef(qc_program *prog, qcint 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* prog_getedict(qc_program *prog, qcint e) {
201     if (e >= (qcint)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*)(prog->entitydata + (prog->entityfields * e));
207 }
208
209 qcint prog_spawn_entity(qc_program *prog) {
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     if (!e) {
228         prog->vmerror++;
229         fprintf(stderr, "Trying to free world entity\n");
230         return;
231     }
232     if (e >= (qcint)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 prog_tempstring(qc_program *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 *prog, unsigned int glob, int vtype) {
301     static char spaces[28+1] = "                            ";
302     prog_section_def *def;
303     qcany    *value;
304     int       len;
305
306     if (!glob) {
307         len = printf("<null>,");
308         goto done;
309     }
310
311     def = prog_getdef(prog, glob);
312     value = (qcany*)(&prog->globals[glob]);
313
314     len = printf("[@%u] ", glob);
315     if (def) {
316         const char *name = prog_getstring(prog, def->name);
317         if (name[0] == '#')
318             len += printf("$");
319         else
320             len += printf("%s ", name);
321         vtype = def->type & DEF_TYPEMASK;
322     }
323
324     switch (vtype) {
325         case TYPE_VOID:
326         case TYPE_ENTITY:
327         case TYPE_FIELD:
328         case TYPE_FUNCTION:
329         case TYPE_POINTER:
330             len += printf("(%i),", value->_int);
331             break;
332         case TYPE_VECTOR:
333             len += printf("'%g %g %g',", value->vector[0],
334                                          value->vector[1],
335                                          value->vector[2]);
336             break;
337         case TYPE_STRING:
338             if (value->string)
339                 len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5);
340             else
341                 len += printf("(null)");
342             len += printf(",");
343             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
344             break;
345         case TYPE_FLOAT:
346         default:
347             len += printf("%g,", value->_float);
348             break;
349     }
350 done:
351     if (len < (int)sizeof(spaces)-1) {
352         spaces[sizeof(spaces)-1-len] = 0;
353         fs_file_puts(stdout, spaces);
354         spaces[sizeof(spaces)-1-len] = ' ';
355     }
356 }
357
358 static void prog_print_statement(qc_program *prog, prog_section_statement *st) {
359     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
360         printf("<illegal instruction %d>\n", st->opcode);
361         return;
362     }
363     if ((prog->xflags & VMXF_TRACE) && vec_size(prog->function_stack)) {
364         size_t i;
365         for (i = 0; i < vec_size(prog->function_stack); ++i)
366             printf("->");
367         printf("%s:", vec_last(prog->function_stack));
368     }
369     printf(" <> %-12s", asm_instr[st->opcode].m);
370     if (st->opcode >= INSTR_IF &&
371         st->opcode <= INSTR_IFNOT)
372     {
373         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
374         printf("%d\n", st->o2.s1);
375     }
376     else if (st->opcode >= INSTR_CALL0 &&
377              st->opcode <= INSTR_CALL8)
378     {
379         trace_print_global(prog, st->o1.u1, TYPE_FUNCTION);
380         printf("\n");
381     }
382     else if (st->opcode == INSTR_GOTO)
383     {
384         printf("%i\n", st->o1.s1);
385     }
386     else
387     {
388         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
389         switch (st->opcode)
390         {
391             case INSTR_MUL_FV:
392                 t[1] = t[2] = TYPE_VECTOR;
393                 break;
394             case INSTR_MUL_VF:
395                 t[0] = t[2] = TYPE_VECTOR;
396                 break;
397             case INSTR_MUL_V:
398                 t[0] = t[1] = TYPE_VECTOR;
399                 break;
400             case INSTR_ADD_V:
401             case INSTR_SUB_V:
402             case INSTR_EQ_V:
403             case INSTR_NE_V:
404                 t[0] = t[1] = t[2] = TYPE_VECTOR;
405                 break;
406             case INSTR_EQ_S:
407             case INSTR_NE_S:
408                 t[0] = t[1] = TYPE_STRING;
409                 break;
410             case INSTR_STORE_F:
411             case INSTR_STOREP_F:
412                 t[2] = -1;
413                 break;
414             case INSTR_STORE_V:
415                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
416                 break;
417             case INSTR_STORE_S:
418                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
419                 break;
420             case INSTR_STORE_ENT:
421                 t[0] = t[1] = TYPE_ENTITY; t[2] = -1;
422                 break;
423             case INSTR_STORE_FLD:
424                 t[0] = t[1] = TYPE_FIELD; t[2] = -1;
425                 break;
426             case INSTR_STORE_FNC:
427                 t[0] = t[1] = TYPE_FUNCTION; t[2] = -1;
428                 break;
429             case INSTR_STOREP_V:
430                 t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1;
431                 break;
432             case INSTR_STOREP_S:
433                 t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1;
434                 break;
435             case INSTR_STOREP_ENT:
436                 t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1;
437                 break;
438             case INSTR_STOREP_FLD:
439                 t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1;
440                 break;
441             case INSTR_STOREP_FNC:
442                 t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1;
443                 break;
444         }
445         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
446         else           printf("(none),          ");
447         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
448         else           printf("(none),          ");
449         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
450         else           printf("(none)");
451         printf("\n");
452     }
453 }
454
455 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func) {
456     qc_exec_stack st;
457     size_t  parampos;
458     int32_t p;
459
460     /* back up locals */
461     st.localsp  = vec_size(prog->localstack);
462     st.stmt     = prog->statement;
463     st.function = func;
464
465     if (prog->xflags & VMXF_TRACE) {
466         const char *str = prog_getstring(prog, func->name);
467         vec_push(prog->function_stack, str);
468     }
469
470 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
471     if (vec_size(prog->stack))
472     {
473         prog_section_function *cur;
474         cur = prog->stack[vec_size(prog->stack)-1].function;
475         if (cur)
476         {
477             qcint *globals = prog->globals + cur->firstlocal;
478             vec_append(prog->localstack, cur->locals, globals);
479         }
480     }
481 #else
482     {
483         qcint *globals = prog->globals + func->firstlocal;
484         vec_append(prog->localstack, func->locals, globals);
485     }
486 #endif
487
488     /* copy parameters */
489     parampos = func->firstlocal;
490     for (p = 0; p < func->nargs; ++p)
491     {
492         size_t s;
493         for (s = 0; s < func->argsize[p]; ++s) {
494             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
495             ++parampos;
496         }
497     }
498
499     vec_push(prog->stack, st);
500
501     return func->entry;
502 }
503
504 static qcint prog_leavefunction(qc_program *prog) {
505     prog_section_function *prev = NULL;
506     size_t oldsp;
507
508     qc_exec_stack st = vec_last(prog->stack);
509
510     if (prog->xflags & VMXF_TRACE) {
511         if (vec_size(prog->function_stack))
512             vec_pop(prog->function_stack);
513     }
514
515 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
516     if (vec_size(prog->stack) > 1) {
517         prev  = prog->stack[vec_size(prog->stack)-2].function;
518         oldsp = prog->stack[vec_size(prog->stack)-2].localsp;
519     }
520 #else
521     prev  = prog->stack[vec_size(prog->stack)-1].function;
522     oldsp = prog->stack[vec_size(prog->stack)-1].localsp;
523 #endif
524     if (prev) {
525         qcint *globals = prog->globals + prev->firstlocal;
526         memcpy(globals, prog->localstack + oldsp, prev->locals * sizeof(prog->localstack[0]));
527         /* vec_remove(prog->localstack, oldsp, vec_size(prog->localstack)-oldsp); */
528         vec_shrinkto(prog->localstack, oldsp);
529     }
530
531     vec_pop(prog->stack);
532
533     return st.stmt - 1; /* offset the ++st */
534 }
535
536 bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps) {
537     long jumpcount = 0;
538     size_t oldxflags = prog->xflags;
539     prog_section_statement *st;
540
541     prog->vmerror = 0;
542     prog->xflags = flags;
543
544     st = prog->code + prog_enterfunction(prog, func);
545     --st;
546     switch (flags)
547     {
548         default:
549         case 0:
550         {
551 #define QCVM_LOOP    1
552 #define QCVM_PROFILE 0
553 #define QCVM_TRACE   0
554 #           include __FILE__
555         }
556         case (VMXF_TRACE):
557         {
558 #define QCVM_PROFILE 0
559 #define QCVM_TRACE   1
560 #           include __FILE__
561         }
562         case (VMXF_PROFILE):
563         {
564 #define QCVM_PROFILE 1
565 #define QCVM_TRACE   0
566 #           include __FILE__
567         }
568         case (VMXF_TRACE|VMXF_PROFILE):
569         {
570 #define QCVM_PROFILE 1
571 #define QCVM_TRACE   1
572 #           include __FILE__
573         }
574     };
575
576 cleanup:
577     prog->xflags = oldxflags;
578     vec_free(prog->localstack);
579     vec_free(prog->stack);
580     if (prog->vmerror)
581         return false;
582     return true;
583 }
584
585 /***********************************************************************
586  * main for when building the standalone executor
587  */
588
589 #if defined(QCVM_EXECUTOR)
590 #include <math.h>
591
592 opts_cmd_t opts;
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*)(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 *prog) {
636     size_t i;
637     const char *laststr = NULL;
638     for (i = 0; i < (size_t)prog->argc; ++i) {
639         qcany *str = (qcany*)(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 *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 *prog) {
659     char buffer[512];
660     qcany *num;
661     qcany 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 *prog) {
671     qcany *str;
672     qcany 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 *prog) {
681     char buffer[512];
682     qcany *num;
683     qcany 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 *prog) {
693     char buffer[512];
694     qcany *num;
695     qcany 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 *prog) {
705     qcany 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 *prog) {
713     qcany *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 *prog) {
721     qcany *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 *prog) {
730     qcany *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 *prog) {
741     double len;
742     qcany *vec;
743     qcany 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 *prog) {
761     char  *buffer;
762     size_t len1,   len2;
763     qcany *str1,  *str2;
764     qcany  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 *prog) {
786     qcany *str1,  *str2;
787     qcany 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 *prog) {
811     qcany *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 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 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
837
838 static const char *arg0 = NULL;
839
840 static void version(void) {
841     printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
842            GMQCC_VERSION_MAJOR,
843            GMQCC_VERSION_MINOR,
844            GMQCC_VERSION_PATCH,
845            __DATE__,
846            __TIME__
847     );
848 }
849
850 static void usage(void) {
851     printf("usage: %s [options] [parameters] file\n", arg0);
852     printf("options:\n");
853     printf("  -h, --help         print this message\n"
854            "  -trace             trace the execution\n"
855            "  -profile           perform profiling during execution\n"
856            "  -info              print information from the prog's header\n"
857            "  -disasm            disassemble and exit\n"
858            "  -disasm-func func  disassemble and exit\n"
859            "  -printdefs         list the defs section\n"
860            "  -printfields       list the field section\n"
861            "  -printfuns         list functions information\n"
862            "  -v                 be verbose\n"
863            "  -vv                be even more verbose\n");
864     printf("parameters:\n");
865     printf("  -vector <V>   pass a vector parameter to main()\n"
866            "  -float  <f>   pass a float parameter to main()\n"
867            "  -string <s>   pass a string parameter to main() \n");
868 }
869
870 static void prog_main_setparams(qc_program *prog) {
871     size_t i;
872     qcany *arg;
873
874     for (i = 0; i < vec_size(main_params); ++i) {
875         arg = GetGlobal(OFS_PARM0 + 3*i);
876         arg->vector[0] = 0;
877         arg->vector[1] = 0;
878         arg->vector[2] = 0;
879         switch (main_params[i].vtype) {
880             case TYPE_VECTOR:
881 #ifdef _MSC_VER
882                 (void)sscanf_s(main_params[i].value, " %f %f %f ",
883                                &arg->vector[0],
884                                &arg->vector[1],
885                                &arg->vector[2]);
886 #else
887                 (void)sscanf(main_params[i].value, " %f %f %f ",
888                              &arg->vector[0],
889                              &arg->vector[1],
890                              &arg->vector[2]);
891 #endif
892                 break;
893             case TYPE_FLOAT:
894                 arg->_float = atof(main_params[i].value);
895                 break;
896             case TYPE_STRING:
897                 arg->string = prog_tempstring(prog, main_params[i].value);
898                 break;
899             default:
900                 fprintf(stderr, "error: unhandled parameter type: %i\n", main_params[i].vtype);
901                 break;
902         }
903     }
904 }
905
906 void escapestring(char* dest, const char* src)  {
907   char c;
908   while ((c = *(src++))) {
909     switch(c) {
910       case '\t':
911         *(dest++) = '\\', *(dest++) = 't';
912         break;
913       case '\n':
914         *(dest++) = '\\', *(dest++) = 'n';
915         break;
916       case '\r':
917         *(dest++) = '\\', *(dest++) = 'r';
918         break;
919       case '\\':
920         *(dest++) = '\\', *(dest++) = '\\';
921         break;
922       case '\"':
923         *(dest++) = '\\', *(dest++) = '\"';
924         break;
925       default:
926         *(dest++) = c;
927      }
928   }
929   *dest = '\0';
930 }
931
932 void prog_disasm_function(qc_program *prog, size_t id);
933
934 int main(int argc, char **argv) {
935     size_t      i;
936     qcint       fnmain = -1;
937     qc_program *prog;
938     size_t      xflags = VMXF_DEFAULT;
939     bool        opts_printfields = false;
940     bool        opts_printdefs   = false;
941     bool        opts_printfuns   = false;
942     bool        opts_disasm      = false;
943     bool        opts_info        = false;
944     bool        noexec           = false;
945     const char *progsfile        = NULL;
946     const char **dis_list        = NULL;
947     int         opts_v           = 0;
948
949     arg0 = argv[0];
950
951     if (argc < 2) {
952         usage();
953         exit(1);
954     }
955
956     while (argc > 1) {
957         if (!strcmp(argv[1], "-h") ||
958             !strcmp(argv[1], "-help") ||
959             !strcmp(argv[1], "--help"))
960         {
961             usage();
962             exit(0);
963         }
964         else if (!strcmp(argv[1], "-v")) {
965             ++opts_v;
966             --argc;
967             ++argv;
968         }
969         else if (!strncmp(argv[1], "-vv", 3)) {
970             const char *av = argv[1]+1;
971             for (; *av; ++av) {
972                 if (*av == 'v')
973                     ++opts_v;
974                 else {
975                     usage();
976                     exit(1);
977                 }
978             }
979             --argc;
980             ++argv;
981         }
982         else if (!strcmp(argv[1], "-version") ||
983                  !strcmp(argv[1], "--version"))
984         {
985             version();
986             exit(0);
987         }
988         else if (!strcmp(argv[1], "-trace")) {
989             --argc;
990             ++argv;
991             xflags |= VMXF_TRACE;
992         }
993         else if (!strcmp(argv[1], "-profile")) {
994             --argc;
995             ++argv;
996             xflags |= VMXF_PROFILE;
997         }
998         else if (!strcmp(argv[1], "-info")) {
999             --argc;
1000             ++argv;
1001             opts_info = true;
1002             noexec = true;
1003         }
1004         else if (!strcmp(argv[1], "-disasm")) {
1005             --argc;
1006             ++argv;
1007             opts_disasm = true;
1008             noexec = true;
1009         }
1010         else if (!strcmp(argv[1], "-disasm-func")) {
1011             --argc;
1012             ++argv;
1013             if (argc <= 1) {
1014                 usage();
1015                 exit(1);
1016             }
1017             vec_push(dis_list, argv[1]);
1018             --argc;
1019             ++argv;
1020             noexec = true;
1021         }
1022         else if (!strcmp(argv[1], "-printdefs")) {
1023             --argc;
1024             ++argv;
1025             opts_printdefs = true;
1026             noexec = true;
1027         }
1028         else if (!strcmp(argv[1], "-printfuns")) {
1029             --argc;
1030             ++argv;
1031             opts_printfuns = true;
1032             noexec = true;
1033         }
1034         else if (!strcmp(argv[1], "-printfields")) {
1035             --argc;
1036             ++argv;
1037             opts_printfields = true;
1038             noexec = true;
1039         }
1040         else if (!strcmp(argv[1], "-vector") ||
1041                  !strcmp(argv[1], "-string") ||
1042                  !strcmp(argv[1], "-float") )
1043         {
1044             qcvm_parameter p;
1045             if (argv[1][1] == 'f')
1046                 p.vtype = TYPE_FLOAT;
1047             else if (argv[1][1] == 's')
1048                 p.vtype = TYPE_STRING;
1049             else if (argv[1][1] == 'v')
1050                 p.vtype = TYPE_VECTOR;
1051
1052             --argc;
1053             ++argv;
1054             if (argc < 2) {
1055                 usage();
1056                 exit(1);
1057             }
1058             p.value = argv[1];
1059
1060             vec_push(main_params, p);
1061             --argc;
1062             ++argv;
1063         }
1064         else if (!strcmp(argv[1], "--")) {
1065             --argc;
1066             ++argv;
1067             break;
1068         }
1069         else if (argv[1][0] != '-') {
1070             if (progsfile) {
1071                 fprintf(stderr, "only 1 program file may be specified\n");
1072                 usage();
1073                 exit(1);
1074             }
1075             progsfile = argv[1];
1076             --argc;
1077             ++argv;
1078         }
1079         else
1080         {
1081             fprintf(stderr, "unknown parameter: %s\n", argv[1]);
1082             usage();
1083             exit(1);
1084         }
1085     }
1086
1087     if (argc == 2 && !progsfile) {
1088         progsfile = argv[1];
1089         --argc;
1090         ++argv;
1091     }
1092
1093     if (!progsfile) {
1094         fprintf(stderr, "must specify a program to execute\n");
1095         usage();
1096         exit(1);
1097     }
1098
1099     prog = prog_load(progsfile, noexec);
1100     if (!prog) {
1101         fprintf(stderr, "failed to load program '%s'\n", progsfile);
1102         exit(1);
1103     }
1104
1105     prog->builtins       = qc_builtins;
1106     prog->builtins_count = qc_builtins_count;
1107
1108     if (opts_info) {
1109         printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16);
1110         printf("Entity field space: %u\n", (unsigned int)prog->entityfields);
1111         printf("Globals: %u\n", (unsigned int)vec_size(prog->globals));
1112         printf("Counts:\n"
1113                "      code: %lu\n"
1114                "      defs: %lu\n"
1115                "    fields: %lu\n"
1116                " functions: %lu\n"
1117                "   strings: %lu\n",
1118                (unsigned long)vec_size(prog->code),
1119                (unsigned long)vec_size(prog->defs),
1120                (unsigned long)vec_size(prog->fields),
1121                (unsigned long)vec_size(prog->functions),
1122                (unsigned long)vec_size(prog->strings));
1123     }
1124
1125     if (opts_info) {
1126         prog_delete(prog);
1127         return 0;
1128     }
1129     for (i = 0; i < vec_size(dis_list); ++i) {
1130         size_t k;
1131         printf("Looking for `%s`\n", dis_list[i]);
1132         for (k = 1; k < vec_size(prog->functions); ++k) {
1133             const char *name = prog_getstring(prog, prog->functions[k].name);
1134             if (!strcmp(name, dis_list[i])) {
1135                 prog_disasm_function(prog, k);
1136                 break;
1137             }
1138         }
1139     }
1140     if (opts_disasm) {
1141         for (i = 1; i < vec_size(prog->functions); ++i)
1142             prog_disasm_function(prog, i);
1143         return 0;
1144     }
1145     if (opts_printdefs) {
1146         char       *escape    = NULL;
1147         const char *getstring = NULL;
1148
1149         for (i = 0; i < vec_size(prog->defs); ++i) {
1150             printf("Global: %8s %-16s at %u%s",
1151                    type_name[prog->defs[i].type & DEF_TYPEMASK],
1152                    prog_getstring(prog, prog->defs[i].name),
1153                    (unsigned int)prog->defs[i].offset,
1154                    ((prog->defs[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1155             if (opts_v) {
1156                 switch (prog->defs[i].type & DEF_TYPEMASK) {
1157                     case TYPE_FLOAT:
1158                         printf(" [init: %g]", ((qcany*)(prog->globals + prog->defs[i].offset))->_float);
1159                         break;
1160                     case TYPE_INTEGER:
1161                         printf(" [init: %i]", (int)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
1162                         break;
1163                     case TYPE_ENTITY:
1164                     case TYPE_FUNCTION:
1165                     case TYPE_FIELD:
1166                     case TYPE_POINTER:
1167                         printf(" [init: %u]", (unsigned)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
1168                         break;
1169                     case TYPE_STRING:
1170                         getstring = prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string);
1171                         escape    = (char*)mem_a(strlen(getstring) * 2 + 1); /* will be enough */
1172                         escapestring(escape, getstring);
1173                         printf(" [init: `%s`]", escape);
1174
1175                         mem_d(escape); /* free */
1176                         break;
1177                     default:
1178                         break;
1179                 }
1180             }
1181             printf("\n");
1182         }
1183     }
1184     if (opts_printfields) {
1185         for (i = 0; i < vec_size(prog->fields); ++i) {
1186             printf("Field: %8s %-16s at %u%s\n",
1187                    type_name[prog->fields[i].type],
1188                    prog_getstring(prog, prog->fields[i].name),
1189                    (unsigned int)prog->fields[i].offset,
1190                    ((prog->fields[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
1191         }
1192     }
1193     if (opts_printfuns) {
1194         for (i = 0; i < vec_size(prog->functions); ++i) {
1195             int32_t a;
1196             printf("Function: %-16s taking %i parameters:(",
1197                    prog_getstring(prog, prog->functions[i].name),
1198                    (unsigned int)prog->functions[i].nargs);
1199             for (a = 0; a < prog->functions[i].nargs; ++a) {
1200                 printf(" %i", prog->functions[i].argsize[a]);
1201             }
1202             if (opts_v > 1) {
1203                 int32_t start = prog->functions[i].entry;
1204                 if (start < 0)
1205                     printf(") builtin %i\n", (int)-start);
1206                 else {
1207                     size_t funsize = 0;
1208                     prog_section_statement *st = prog->code + start;
1209                     for (;st->opcode != INSTR_DONE; ++st)
1210                         ++funsize;
1211                     printf(") - %lu instructions", (unsigned long)funsize);
1212                     if (opts_v > 2) {
1213                         printf(" - locals: %i + %i\n",
1214                                prog->functions[i].firstlocal,
1215                                prog->functions[i].locals);
1216                     }
1217                     else
1218                         printf("\n");
1219                 }
1220             }
1221             else if (opts_v) {
1222                 printf(") locals: %i + %i\n",
1223                        prog->functions[i].firstlocal,
1224                        prog->functions[i].locals);
1225             }
1226             else
1227                 printf(")\n");
1228         }
1229     }
1230     if (!noexec) {
1231         for (i = 1; i < vec_size(prog->functions); ++i) {
1232             const char *name = prog_getstring(prog, prog->functions[i].name);
1233             if (!strcmp(name, "main"))
1234                 fnmain = (qcint)i;
1235         }
1236         if (fnmain > 0)
1237         {
1238             prog_main_setparams(prog);
1239             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
1240         }
1241         else
1242             fprintf(stderr, "No main function found\n");
1243     }
1244
1245     prog_delete(prog);
1246     return 0;
1247 }
1248
1249 void prog_disasm_function(qc_program *prog, size_t id) {
1250     prog_section_function *fdef = prog->functions + id;
1251     prog_section_statement *st;
1252
1253     if (fdef->entry < 0) {
1254         printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry);
1255         return;
1256     }
1257     else
1258         printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name));
1259
1260     st = prog->code + fdef->entry;
1261     while (st->opcode != INSTR_DONE) {
1262         prog_print_statement(prog, st);
1263         ++st;
1264     }
1265 }
1266 #endif
1267 #else /* !QCVM_LOOP */
1268 /*
1269  * Everything from here on is not including into the compilation of the
1270  * executor.  This is simply code that is #included via #include __FILE__
1271  * see when QCVM_LOOP is defined, the rest of the code above do not get
1272  * re-included.  So this really just acts like one large macro, but it
1273  * sort of isn't, which makes it nicer looking.
1274  */
1275
1276 #define OPA ( (qcany*) (prog->globals + st->o1.u1) )
1277 #define OPB ( (qcany*) (prog->globals + st->o2.u1) )
1278 #define OPC ( (qcany*) (prog->globals + st->o3.u1) )
1279
1280 #define GLOBAL(x) ( (qcany*) (prog->globals + (x)) )
1281
1282 /* to be consistent with current darkplaces behaviour */
1283 #if !defined(FLOAT_IS_TRUE_FOR_INT)
1284 #   define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
1285 #endif
1286
1287 while (1) {
1288     prog_section_function  *newf;
1289     qcany          *ed;
1290     qcany          *ptr;
1291
1292     ++st;
1293
1294 #if QCVM_PROFILE
1295     prog->profile[st - prog->code]++;
1296 #endif
1297
1298 #if QCVM_TRACE
1299     prog_print_statement(prog, st);
1300 #endif
1301
1302     switch (st->opcode)
1303     {
1304         default:
1305             qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
1306             goto cleanup;
1307
1308         case INSTR_DONE:
1309         case INSTR_RETURN:
1310             /* TODO: add instruction count to function profile count */
1311             GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
1312             GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
1313             GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
1314
1315             st = prog->code + prog_leavefunction(prog);
1316             if (!vec_size(prog->stack))
1317                 goto cleanup;
1318
1319             break;
1320
1321         case INSTR_MUL_F:
1322             OPC->_float = OPA->_float * OPB->_float;
1323             break;
1324         case INSTR_MUL_V:
1325             OPC->_float = OPA->vector[0]*OPB->vector[0] +
1326                           OPA->vector[1]*OPB->vector[1] +
1327                           OPA->vector[2]*OPB->vector[2];
1328             break;
1329         case INSTR_MUL_FV:
1330         {
1331             qcfloat f = OPA->_float;
1332             OPC->vector[0] = f * OPB->vector[0];
1333             OPC->vector[1] = f * OPB->vector[1];
1334             OPC->vector[2] = f * OPB->vector[2];
1335             break;
1336         }
1337         case INSTR_MUL_VF:
1338         {
1339             qcfloat f = OPB->_float;
1340             OPC->vector[0] = f * OPA->vector[0];
1341             OPC->vector[1] = f * OPA->vector[1];
1342             OPC->vector[2] = f * OPA->vector[2];
1343             break;
1344         }
1345         case INSTR_DIV_F:
1346             if (OPB->_float != 0.0f)
1347                 OPC->_float = OPA->_float / OPB->_float;
1348             else
1349                 OPC->_float = 0;
1350             break;
1351
1352         case INSTR_ADD_F:
1353             OPC->_float = OPA->_float + OPB->_float;
1354             break;
1355         case INSTR_ADD_V:
1356             OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
1357             OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
1358             OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
1359             break;
1360         case INSTR_SUB_F:
1361             OPC->_float = OPA->_float - OPB->_float;
1362             break;
1363         case INSTR_SUB_V:
1364             OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
1365             OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
1366             OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
1367             break;
1368
1369         case INSTR_EQ_F:
1370             OPC->_float = (OPA->_float == OPB->_float);
1371             break;
1372         case INSTR_EQ_V:
1373             OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
1374                            (OPA->vector[1] == OPB->vector[1]) &&
1375                            (OPA->vector[2] == OPB->vector[2]) );
1376             break;
1377         case INSTR_EQ_S:
1378             OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
1379                                   prog_getstring(prog, OPB->string));
1380             break;
1381         case INSTR_EQ_E:
1382             OPC->_float = (OPA->_int == OPB->_int);
1383             break;
1384         case INSTR_EQ_FNC:
1385             OPC->_float = (OPA->function == OPB->function);
1386             break;
1387         case INSTR_NE_F:
1388             OPC->_float = (OPA->_float != OPB->_float);
1389             break;
1390         case INSTR_NE_V:
1391             OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
1392                            (OPA->vector[1] != OPB->vector[1]) ||
1393                            (OPA->vector[2] != OPB->vector[2]) );
1394             break;
1395         case INSTR_NE_S:
1396             OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
1397                                    prog_getstring(prog, OPB->string));
1398             break;
1399         case INSTR_NE_E:
1400             OPC->_float = (OPA->_int != OPB->_int);
1401             break;
1402         case INSTR_NE_FNC:
1403             OPC->_float = (OPA->function != OPB->function);
1404             break;
1405
1406         case INSTR_LE:
1407             OPC->_float = (OPA->_float <= OPB->_float);
1408             break;
1409         case INSTR_GE:
1410             OPC->_float = (OPA->_float >= OPB->_float);
1411             break;
1412         case INSTR_LT:
1413             OPC->_float = (OPA->_float < OPB->_float);
1414             break;
1415         case INSTR_GT:
1416             OPC->_float = (OPA->_float > OPB->_float);
1417             break;
1418
1419         case INSTR_LOAD_F:
1420         case INSTR_LOAD_S:
1421         case INSTR_LOAD_FLD:
1422         case INSTR_LOAD_ENT:
1423         case INSTR_LOAD_FNC:
1424             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1425                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
1426                 goto cleanup;
1427             }
1428             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
1429                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1430                           prog->filename,
1431                           OPB->_int);
1432                 goto cleanup;
1433             }
1434             ed = prog_getedict(prog, OPA->edict);
1435             OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
1436             break;
1437         case INSTR_LOAD_V:
1438             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1439                 qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
1440                 goto cleanup;
1441             }
1442             if (OPB->_int < 0 || OPB->_int + 3 > (qcint)prog->entityfields)
1443             {
1444                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1445                           prog->filename,
1446                           OPB->_int + 2);
1447                 goto cleanup;
1448             }
1449             ed = prog_getedict(prog, OPA->edict);
1450             ptr = (qcany*)( ((qcint*)ed) + OPB->_int );
1451             OPC->ivector[0] = ptr->ivector[0];
1452             OPC->ivector[1] = ptr->ivector[1];
1453             OPC->ivector[2] = ptr->ivector[2];
1454             break;
1455
1456         case INSTR_ADDRESS:
1457             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
1458                 qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
1459                 goto cleanup;
1460             }
1461             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
1462             {
1463                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
1464                           prog->filename,
1465                           OPB->_int);
1466                 goto cleanup;
1467             }
1468
1469             ed = prog_getedict(prog, OPA->edict);
1470             OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
1471             break;
1472
1473         case INSTR_STORE_F:
1474         case INSTR_STORE_S:
1475         case INSTR_STORE_ENT:
1476         case INSTR_STORE_FLD:
1477         case INSTR_STORE_FNC:
1478             OPB->_int = OPA->_int;
1479             break;
1480         case INSTR_STORE_V:
1481             OPB->ivector[0] = OPA->ivector[0];
1482             OPB->ivector[1] = OPA->ivector[1];
1483             OPB->ivector[2] = OPA->ivector[2];
1484             break;
1485
1486         case INSTR_STOREP_F:
1487         case INSTR_STOREP_S:
1488         case INSTR_STOREP_ENT:
1489         case INSTR_STOREP_FLD:
1490         case INSTR_STOREP_FNC:
1491             if (OPB->_int < 0 || OPB->_int >= (qcint)vec_size(prog->entitydata)) {
1492                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
1493                 goto cleanup;
1494             }
1495             if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites)
1496                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1497                           prog->filename,
1498                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1499                           OPB->_int);
1500             ptr = (qcany*)(prog->entitydata + OPB->_int);
1501             ptr->_int = OPA->_int;
1502             break;
1503         case INSTR_STOREP_V:
1504             if (OPB->_int < 0 || OPB->_int + 2 >= (qcint)vec_size(prog->entitydata)) {
1505                 qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
1506                 goto cleanup;
1507             }
1508             if (OPB->_int < (qcint)prog->entityfields && !prog->allowworldwrites)
1509                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
1510                           prog->filename,
1511                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
1512                           OPB->_int);
1513             ptr = (qcany*)(prog->entitydata + OPB->_int);
1514             ptr->ivector[0] = OPA->ivector[0];
1515             ptr->ivector[1] = OPA->ivector[1];
1516             ptr->ivector[2] = OPA->ivector[2];
1517             break;
1518
1519         case INSTR_NOT_F:
1520             OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
1521             break;
1522         case INSTR_NOT_V:
1523             OPC->_float = !OPA->vector[0] &&
1524                           !OPA->vector[1] &&
1525                           !OPA->vector[2];
1526             break;
1527         case INSTR_NOT_S:
1528             OPC->_float = !OPA->string ||
1529                           !*prog_getstring(prog, OPA->string);
1530             break;
1531         case INSTR_NOT_ENT:
1532             OPC->_float = (OPA->edict == 0);
1533             break;
1534         case INSTR_NOT_FNC:
1535             OPC->_float = !OPA->function;
1536             break;
1537
1538         case INSTR_IF:
1539             /* this is consistent with darkplaces' behaviour */
1540             if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1541             {
1542                 st += st->o2.s1 - 1;    /* offset the s++ */
1543                 if (++jumpcount >= maxjumps)
1544                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1545             }
1546             break;
1547         case INSTR_IFNOT:
1548             if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
1549             {
1550                 st += st->o2.s1 - 1;    /* offset the s++ */
1551                 if (++jumpcount >= maxjumps)
1552                     qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1553             }
1554             break;
1555
1556         case INSTR_CALL0:
1557         case INSTR_CALL1:
1558         case INSTR_CALL2:
1559         case INSTR_CALL3:
1560         case INSTR_CALL4:
1561         case INSTR_CALL5:
1562         case INSTR_CALL6:
1563         case INSTR_CALL7:
1564         case INSTR_CALL8:
1565             prog->argc = st->opcode - INSTR_CALL0;
1566             if (!OPA->function)
1567                 qcvmerror(prog, "NULL function in `%s`", prog->filename);
1568
1569             if(!OPA->function || OPA->function >= (qcint)vec_size(prog->functions))
1570             {
1571                 qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
1572                 goto cleanup;
1573             }
1574
1575             newf = &prog->functions[OPA->function];
1576             newf->profile++;
1577
1578             prog->statement = (st - prog->code) + 1;
1579
1580             if (newf->entry < 0)
1581             {
1582                 /* negative statements are built in functions */
1583                 qcint builtinnumber = -newf->entry;
1584                 if (builtinnumber < (qcint)prog->builtins_count && prog->builtins[builtinnumber])
1585                     prog->builtins[builtinnumber](prog);
1586                 else
1587                     qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
1588                               builtinnumber, prog->filename);
1589             }
1590             else
1591                 st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
1592             if (prog->vmerror)
1593                 goto cleanup;
1594             break;
1595
1596         case INSTR_STATE:
1597             qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
1598             break;
1599
1600         case INSTR_GOTO:
1601             st += st->o1.s1 - 1;    /* offset the s++ */
1602             if (++jumpcount == 10000000)
1603                 qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
1604             break;
1605
1606         case INSTR_AND:
1607             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
1608                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1609             break;
1610         case INSTR_OR:
1611             OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
1612                           FLOAT_IS_TRUE_FOR_INT(OPB->_int);
1613             break;
1614
1615         case INSTR_BITAND:
1616             OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
1617             break;
1618         case INSTR_BITOR:
1619             OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
1620             break;
1621     }
1622 }
1623
1624 #undef QCVM_PROFILE
1625 #undef QCVM_TRACE
1626 #endif /* !QCVM_LOOP */