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