]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
-Wassign-function-types - since in qcc you may assign functionpointers which take...
[xonotic/gmqcc.git] / exec.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdarg.h>
5
6 #include "gmqcc.h"
7
8 MEM_VEC_FUNCTIONS(qc_program,   prog_section_statement, code)
9 MEM_VEC_FUNCTIONS(qc_program,   prog_section_def,       defs)
10 MEM_VEC_FUNCTIONS(qc_program,   prog_section_def,       fields)
11 MEM_VEC_FUNCTIONS(qc_program,   prog_section_function,  functions)
12 MEM_VEC_FUNCTIONS(qc_program,   char,                   strings)
13 MEM_VEC_FUN_APPEND(qc_program,  char,                   strings)
14 MEM_VEC_FUN_RESIZE(qc_program,  char,                   strings)
15 MEM_VEC_FUNCTIONS(qc_program,   qcint,                  globals)
16 MEM_VEC_FUNCTIONS(qc_program,   qcint,                  entitydata)
17 MEM_VEC_FUNCTIONS(qc_program,   bool,                   entitypool)
18
19 MEM_VEC_FUNCTIONS(qc_program,   qcint,         localstack)
20 MEM_VEC_FUN_APPEND(qc_program,  qcint,         localstack)
21 MEM_VEC_FUN_RESIZE(qc_program,  qcint,         localstack)
22 MEM_VEC_FUNCTIONS(qc_program,   qc_exec_stack, stack)
23
24 MEM_VEC_FUNCTIONS(qc_program,   size_t, profile)
25 MEM_VEC_FUN_RESIZE(qc_program,  size_t, profile)
26
27 MEM_VEC_FUNCTIONS(qc_program,   prog_builtin, builtins)
28
29 static void loaderror(const char *fmt, ...)
30 {
31     int     err = errno;
32     va_list ap;
33     va_start(ap, fmt);
34     vprintf(fmt, ap);
35     va_end(ap);
36     printf(": %s\n", strerror(err));
37 }
38
39 static void qcvmerror(qc_program *prog, const char *fmt, ...)
40 {
41     va_list ap;
42
43     prog->vmerror++;
44
45     va_start(ap, fmt);
46     vprintf(fmt, ap);
47     va_end(ap);
48     putchar('\n');
49 }
50
51 qc_program* prog_load(const char *filename)
52 {
53     qc_program *prog;
54     prog_header header;
55     size_t      i;
56     FILE *file;
57
58     file = util_fopen(filename, "rb");
59     if (!file)
60         return NULL;
61
62     if (fread(&header, sizeof(header), 1, file) != 1) {
63         loaderror("failed to read header from '%s'", filename);
64         fclose(file);
65         return NULL;
66     }
67
68     if (header.version != 6) {
69         loaderror("header says this is a version %i progs, we need version 6\n", header.version);
70         fclose(file);
71         return NULL;
72     }
73
74     prog = (qc_program*)mem_a(sizeof(qc_program));
75     if (!prog) {
76         fclose(file);
77         printf("failed to allocate program data\n");
78         return NULL;
79     }
80     memset(prog, 0, sizeof(*prog));
81
82     prog->entityfields = header.entfield;
83     prog->crc16 = header.crc16;
84
85     prog->filename = util_strdup(filename);
86     if (!prog->filename) {
87         loaderror("failed to store program name");
88         goto error;
89     }
90
91 #define read_data(hdrvar, progvar, type)                                         \
92     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {                      \
93         loaderror("seek failed");                                                \
94         goto error;                                                              \
95     }                                                                            \
96     prog->progvar##_alloc = header.hdrvar.length;                                \
97     prog->progvar##_count = header.hdrvar.length;                                \
98     prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
99     if (!prog->progvar)                                                          \
100         goto error;                                                              \
101     if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
102         != header.hdrvar.length) {                                               \
103         loaderror("read failed");                                                \
104         goto error;                                                              \
105     }
106 #define read_data1(x, y) read_data(x, x, y)
107
108     read_data (statements, code, prog_section_statement);
109     read_data1(defs,             prog_section_def);
110     read_data1(fields,           prog_section_def);
111     read_data1(functions,        prog_section_function);
112     read_data1(strings,          char);
113     read_data1(globals,          qcint);
114
115     fclose(file);
116
117     /* profile counters */
118     if (!qc_program_profile_resize(prog, prog->code_count))
119         goto error;
120
121     /* Add tempstring area */
122     prog->tempstring_start = prog->strings_count;
123     prog->tempstring_at    = prog->strings_count;
124     if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
125         goto error;
126
127     /* spawn the world entity */
128     if (!qc_program_entitypool_add(prog, true)) {
129         loaderror("failed to allocate world entity\n");
130         goto error;
131     }
132     for (i = 0; i < prog->entityfields; ++i) {
133         if (!qc_program_entitydata_add(prog, 0)) {
134             loaderror("failed to allocate world data\n");
135             goto error;
136         }
137     }
138     prog->entities = 1;
139
140     return prog;
141
142 error:
143     if (prog->filename)   mem_d(prog->filename);
144     if (prog->code)       mem_d(prog->code);
145     if (prog->defs)       mem_d(prog->defs);
146     if (prog->fields)     mem_d(prog->fields);
147     if (prog->functions)  mem_d(prog->functions);
148     if (prog->strings)    mem_d(prog->strings);
149     if (prog->globals)    mem_d(prog->globals);
150     if (prog->entitydata) mem_d(prog->entitydata);
151     if (prog->entitypool) mem_d(prog->entitypool);
152     mem_d(prog);
153     return NULL;
154 }
155
156 void prog_delete(qc_program *prog)
157 {
158     if (prog->filename) mem_d(prog->filename);
159     MEM_VECTOR_CLEAR(prog, code);
160     MEM_VECTOR_CLEAR(prog, defs);
161     MEM_VECTOR_CLEAR(prog, fields);
162     MEM_VECTOR_CLEAR(prog, functions);
163     MEM_VECTOR_CLEAR(prog, strings);
164     MEM_VECTOR_CLEAR(prog, globals);
165     MEM_VECTOR_CLEAR(prog, entitydata);
166     MEM_VECTOR_CLEAR(prog, entitypool);
167     MEM_VECTOR_CLEAR(prog, localstack);
168     MEM_VECTOR_CLEAR(prog, stack);
169     MEM_VECTOR_CLEAR(prog, profile);
170
171     if (prog->builtins_alloc) {
172         MEM_VECTOR_CLEAR(prog, builtins);
173     }
174     /* otherwise the builtins were statically allocated */
175     mem_d(prog);
176 }
177
178 /***********************************************************************
179  * VM code
180  */
181
182 char* prog_getstring(qc_program *prog, qcint str)
183 {
184     if (str < 0 || str >= prog->strings_count)
185         return "<<<invalid string>>>";
186     return prog->strings + str;
187 }
188
189 prog_section_def* prog_entfield(qc_program *prog, qcint off)
190 {
191     size_t i;
192     for (i = 0; i < prog->fields_count; ++i) {
193         if (prog->fields[i].offset == off)
194             return (prog->fields + i);
195     }
196     return NULL;
197 }
198
199 prog_section_def* prog_getdef(qc_program *prog, qcint off)
200 {
201     size_t i;
202     for (i = 0; i < prog->defs_count; ++i) {
203         if (prog->defs[i].offset == off)
204             return (prog->defs + i);
205     }
206     return NULL;
207 }
208
209 qcany* prog_getedict(qc_program *prog, qcint e)
210 {
211     if (e >= prog->entitypool_count) {
212         prog->vmerror++;
213         printf("Accessing out of bounds edict %i\n", (int)e);
214         e = 0;
215     }
216     return (qcany*)(prog->entitydata + (prog->entityfields * e));
217 }
218
219 qcint prog_spawn_entity(qc_program *prog)
220 {
221     char  *data;
222     size_t i;
223     qcint  e;
224     for (e = 0; e < (qcint)prog->entitypool_count; ++e) {
225         if (!prog->entitypool[e]) {
226             data = (char*)(prog->entitydata + (prog->entityfields * e));
227             memset(data, 0, prog->entityfields * sizeof(qcint));
228             return e;
229         }
230     }
231     if (!qc_program_entitypool_add(prog, true)) {
232         prog->vmerror++;
233         printf("Failed to allocate entity\n");
234         return 0;
235     }
236     prog->entities++;
237     for (i = 0; i < prog->entityfields; ++i) {
238         if (!qc_program_entitydata_add(prog, 0)) {
239             printf("Failed to allocate entity\n");
240             return 0;
241         }
242     }
243     data = (char*)(prog->entitydata + (prog->entityfields * e));
244     memset(data, 0, prog->entityfields * sizeof(qcint));
245     return e;
246 }
247
248 void prog_free_entity(qc_program *prog, qcint e)
249 {
250     if (!e) {
251         prog->vmerror++;
252         printf("Trying to free world entity\n");
253         return;
254     }
255     if (e >= prog->entitypool_count) {
256         prog->vmerror++;
257         printf("Trying to free out of bounds entity\n");
258         return;
259     }
260     if (!prog->entitypool[e]) {
261         prog->vmerror++;
262         printf("Double free on entity\n");
263         return;
264     }
265     prog->entitypool[e] = false;
266 }
267
268 qcint prog_tempstring(qc_program *prog, const char *_str)
269 {
270     /* we don't access it, but the macro-generated functions don't use
271      * const
272      */
273     char *str = (char*)_str;
274
275     size_t len = strlen(str);
276     size_t at = prog->tempstring_at;
277
278     /* when we reach the end we start over */
279     if (at + len >= prog->strings_count)
280         at = prog->tempstring_start;
281
282     /* when it doesn't fit, reallocate */
283     if (at + len >= prog->strings_count)
284     {
285         prog->strings_count = at;
286         if (!qc_program_strings_append(prog, str, len+1)) {
287             prog->vmerror = VMERR_TEMPSTRING_ALLOC;
288             return 0;
289         }
290         return at;
291     }
292
293     /* when it fits, just copy */
294     memcpy(prog->strings + at, str, len+1);
295     prog->tempstring_at += len+1;
296     return at;
297 }
298
299 static int print_escaped_string(const char *str, size_t maxlen)
300 {
301     int len = 2;
302     putchar('"');
303     --maxlen; /* because we're lazy and have escape sequences */
304     while (*str) {
305         if (len >= maxlen) {
306             putchar('.');
307             putchar('.');
308             putchar('.');
309             len += 3;
310             break;
311         }
312         switch (*str) {
313             case '\a': len += 2; putchar('\\'); putchar('a'); break;
314             case '\b': len += 2; putchar('\\'); putchar('b'); break;
315             case '\r': len += 2; putchar('\\'); putchar('r'); break;
316             case '\n': len += 2; putchar('\\'); putchar('n'); break;
317             case '\t': len += 2; putchar('\\'); putchar('t'); break;
318             case '\f': len += 2; putchar('\\'); putchar('f'); break;
319             case '\v': len += 2; putchar('\\'); putchar('v'); break;
320             case '\\': len += 2; putchar('\\'); putchar('\\'); break;
321             case '"':  len += 2; putchar('\\'); putchar('"'); break;
322             default:
323                 ++len;
324                 putchar(*str);
325                 break;
326         }
327         ++str;
328     }
329     putchar('"');
330     return len;
331 }
332
333 static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
334 {
335     static char spaces[28+1] = "                            ";
336     prog_section_def *def;
337     qcany    *value;
338     int       len;
339
340     if (!glob) {
341         len = printf("<null>,");
342         goto done;
343     }
344
345     def = prog_getdef(prog, glob);
346     value = (qcany*)(&prog->globals[glob]);
347
348     if (def) {
349         const char *name = prog_getstring(prog, def->name);
350         if (name[0] == '#')
351             len = printf("$");
352         else
353             len = printf("%s ", name);
354         vtype = def->type;
355     }
356     else
357         len = printf("[@%u] ", glob);
358
359     switch (vtype) {
360         case TYPE_VOID:
361         case TYPE_ENTITY:
362         case TYPE_FIELD:
363         case TYPE_FUNCTION:
364         case TYPE_POINTER:
365             len += printf("(%i),", value->_int);
366             break;
367         case TYPE_VECTOR:
368             len += printf("'%g %g %g',", value->vector[0],
369                                          value->vector[1],
370                                          value->vector[2]);
371             break;
372         case TYPE_STRING:
373             len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5);
374             len += printf(",");
375             /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
376             break;
377         case TYPE_FLOAT:
378         default:
379             len += printf("%g,", value->_float);
380             break;
381     }
382 done:
383     if (len < sizeof(spaces)-1) {
384         spaces[sizeof(spaces)-1-len] = 0;
385         printf(spaces);
386         spaces[sizeof(spaces)-1-len] = ' ';
387     }
388 }
389
390 static void prog_print_statement(qc_program *prog, prog_section_statement *st)
391 {
392     if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
393         printf("<illegal instruction %d>\n", st->opcode);
394         return;
395     }
396     printf(" <> %-12s", asm_instr[st->opcode].m);
397     if (st->opcode >= INSTR_IF &&
398         st->opcode <= INSTR_IFNOT)
399     {
400         trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
401         printf("%d\n", st->o2.s1);
402     }
403     else if (st->opcode >= INSTR_CALL0 &&
404              st->opcode <= INSTR_CALL8)
405     {
406         printf("\n");
407     }
408     else if (st->opcode == INSTR_GOTO)
409     {
410         printf("%i\n", st->o1.s1);
411     }
412     else
413     {
414         int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
415         switch (st->opcode)
416         {
417             case INSTR_MUL_FV:
418                 t[1] = t[2] = TYPE_VECTOR;
419                 break;
420             case INSTR_MUL_VF:
421                 t[0] = t[2] = TYPE_VECTOR;
422                 break;
423             case INSTR_MUL_V:
424                 t[0] = t[1] = TYPE_VECTOR;
425                 break;
426             case INSTR_ADD_V:
427             case INSTR_SUB_V:
428             case INSTR_EQ_V:
429             case INSTR_NE_V:
430                 t[0] = t[1] = t[2] = TYPE_VECTOR;
431                 break;
432             case INSTR_EQ_S:
433             case INSTR_NE_S:
434                 t[0] = t[1] = TYPE_STRING;
435                 break;
436             case INSTR_STORE_F:
437             case INSTR_STOREP_F:
438                 t[2] = -1;
439                 break;
440             case INSTR_STORE_V:
441                 t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
442                 break;
443             case INSTR_STORE_S:
444                 t[0] = t[1] = TYPE_STRING; t[2] = -1;
445                 break;
446             case INSTR_STORE_ENT:
447                 t[0] = t[1] = TYPE_ENTITY; t[2] = -1;
448                 break;
449             case INSTR_STORE_FLD:
450                 t[0] = t[1] = TYPE_FIELD; t[2] = -1;
451                 break;
452             case INSTR_STORE_FNC:
453                 t[0] = t[1] = TYPE_FUNCTION; t[2] = -1;
454                 break;
455             case INSTR_STOREP_V:
456                 t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1;
457                 break;
458             case INSTR_STOREP_S:
459                 t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1;
460                 break;
461             case INSTR_STOREP_ENT:
462                 t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1;
463                 break;
464             case INSTR_STOREP_FLD:
465                 t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1;
466                 break;
467             case INSTR_STOREP_FNC:
468                 t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1;
469                 break;
470         }
471         if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
472         else           printf("(none),          ");
473         if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
474         else           printf("(none),          ");
475         if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
476         else           printf("(none)");
477         printf("\n");
478     }
479     fflush(stdout);
480 }
481
482 static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
483 {
484     qc_exec_stack st;
485     size_t p, parampos;
486
487     /* back up locals */
488     st.localsp  = prog->localstack_count;
489     st.stmt     = prog->statement;
490     st.function = func;
491
492 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
493     if (prog->stack_count)
494     {
495         prog_section_function *cur;
496         cur = prog->stack[prog->stack_count-1].function;
497         if (cur)
498         {
499             qcint *globals = prog->globals + cur->firstlocal;
500             if (!qc_program_localstack_append(prog, globals, cur->locals))
501             {
502                 printf("out of memory\n");
503                 exit(1);
504             }
505         }
506     }
507 #else
508     {
509         qcint *globals = prog->globals + func->firstlocal;
510         if (!qc_program_localstack_append(prog, globals, func->locals))
511         {
512             printf("out of memory\n");
513             exit(1);
514         }
515     }
516 #endif
517
518     /* copy parameters */
519     parampos = func->firstlocal;
520     for (p = 0; p < func->nargs; ++p)
521     {
522         size_t s;
523         for (s = 0; s < func->argsize[p]; ++s) {
524             prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
525             ++parampos;
526         }
527     }
528
529     if (!qc_program_stack_add(prog, st)) {
530         printf("out of memory\n");
531         exit(1);
532     }
533
534     return func->entry;
535 }
536
537 static qcint prog_leavefunction(qc_program *prog)
538 {
539     prog_section_function *prev = NULL;
540     size_t oldsp;
541
542     qc_exec_stack st = prog->stack[prog->stack_count-1];
543
544 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
545     if (prog->stack_count > 1) {
546         prev  = prog->stack[prog->stack_count-2].function;
547         oldsp = prog->stack[prog->stack_count-2].localsp;
548     }
549 #else
550     prev  = prog->stack[prog->stack_count-1].function;
551     oldsp = prog->stack[prog->stack_count-1].localsp;
552 #endif
553     if (prev) {
554         qcint *globals = prog->globals + prev->firstlocal;
555         memcpy(globals, prog->localstack + oldsp, prev->locals);
556         if (!qc_program_localstack_resize(prog, oldsp)) {
557             printf("out of memory\n");
558             exit(1);
559         }
560     }
561
562     if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
563         printf("out of memory\n");
564         exit(1);
565     }
566
567     return st.stmt - 1; /* offset the ++st */
568 }
569
570 bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
571 {
572     long jumpcount = 0;
573     size_t oldxflags = prog->xflags;
574     prog_section_statement *st;
575
576     prog->vmerror = 0;
577     prog->xflags = flags;
578
579     st = prog->code + prog_enterfunction(prog, func);
580     --st;
581     switch (flags)
582     {
583         default:
584         case 0:
585         {
586 #define QCVM_PROFILE 0
587 #define QCVM_TRACE   0
588 #           include "execloop.h"
589             break;
590         }
591         case (VMXF_TRACE):
592         {
593 #define QCVM_PROFILE 0
594 #define QCVM_TRACE   1
595 #           include "execloop.h"
596             break;
597         }
598         case (VMXF_PROFILE):
599         {
600 #define QCVM_PROFILE 1
601 #define QCVM_TRACE   0
602 #           include "execloop.h"
603             break;
604         }
605         case (VMXF_TRACE|VMXF_PROFILE):
606         {
607 #define QCVM_PROFILE 1
608 #define QCVM_TRACE   1
609 #           include "execloop.h"
610             break;
611         }
612     };
613
614 cleanup:
615     prog->xflags = oldxflags;
616     prog->localstack_count = 0;
617     prog->stack_count = 0;
618     if (prog->vmerror)
619         return false;
620     return true;
621 }
622
623 /***********************************************************************
624  * main for when building the standalone executor
625  */
626
627 #if defined(QCVM_EXECUTOR)
628 #include <math.h>
629
630 const char *type_name[TYPE_COUNT] = {
631     "void",
632     "string",
633     "float",
634     "vector",
635     "entity",
636     "field",
637     "function",
638     "pointer",
639 #if 0
640     "integer",
641 #endif
642     "variant"
643 };
644
645 bool        opts_debug    = false;
646 bool        opts_memchk   = false;
647
648 typedef struct {
649     int         vtype;
650     const char *value;
651 } qcvm_parameter;
652
653 VECTOR_MAKE(qcvm_parameter, main_params);
654
655 #define CheckArgs(num) do {                                                    \
656     if (prog->argc != (num)) {                                                 \
657         prog->vmerror++;                                                       \
658         printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
659         __FUNCTION__, prog->argc, (num));                                      \
660         return -1;                                                             \
661     }                                                                          \
662 } while (0)
663
664 #define GetGlobal(idx) ((qcany*)(prog->globals + (idx)))
665 #define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
666 #define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
667
668 static int qc_print(qc_program *prog)
669 {
670     size_t i;
671     const char *laststr = NULL;
672     for (i = 0; i < prog->argc; ++i) {
673         qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i);
674         printf("%s", (laststr = prog_getstring(prog, str->string)));
675     }
676     if (laststr && (prog->xflags & VMXF_TRACE)) {
677         size_t len = strlen(laststr);
678         if (!len || laststr[len-1] != '\n')
679             printf("\n");
680     }
681     return 0;
682 }
683
684 static int qc_error(qc_program *prog)
685 {
686     printf("*** VM raised an error:\n");
687     qc_print(prog);
688     prog->vmerror++;
689     return -1;
690 }
691
692 static int qc_ftos(qc_program *prog)
693 {
694     char buffer[512];
695     qcany *num;
696     qcany str;
697     CheckArgs(1);
698     num = GetArg(0);
699     snprintf(buffer, sizeof(buffer), "%g", num->_float);
700     str.string = prog_tempstring(prog, buffer);
701     Return(str);
702     return 0;
703 }
704
705 static int qc_vtos(qc_program *prog)
706 {
707     char buffer[512];
708     qcany *num;
709     qcany str;
710     CheckArgs(1);
711     num = GetArg(0);
712     snprintf(buffer, sizeof(buffer), "'%g %g %g'", num->vector[0], num->vector[1], num->vector[2]);
713     str.string = prog_tempstring(prog, buffer);
714     Return(str);
715     return 0;
716 }
717
718 static int qc_etos(qc_program *prog)
719 {
720     char buffer[512];
721     qcany *num;
722     qcany str;
723     CheckArgs(1);
724     num = GetArg(0);
725     snprintf(buffer, sizeof(buffer), "%i", num->_int);
726     str.string = prog_tempstring(prog, buffer);
727     Return(str);
728     return 0;
729 }
730
731 static int qc_spawn(qc_program *prog)
732 {
733     qcany ent;
734     CheckArgs(0);
735     ent.edict = prog_spawn_entity(prog);
736     Return(ent);
737     return (ent.edict ? 0 : -1);
738 }
739
740 static int qc_kill(qc_program *prog)
741 {
742     qcany *ent;
743     CheckArgs(1);
744     ent = GetArg(0);
745     prog_free_entity(prog, ent->edict);
746     return 0;
747 }
748
749 static int qc_vlen(qc_program *prog)
750 {
751     qcany *vec, len;
752     CheckArgs(1);
753     vec = GetArg(0);
754     len._float = sqrt(vec->vector[0] * vec->vector[0] + 
755                       vec->vector[1] * vec->vector[1] +
756                       vec->vector[2] * vec->vector[2]);
757     Return(len);
758     return 0;
759 }
760
761 static prog_builtin qc_builtins[] = {
762     NULL,
763     &qc_print, /*   1   */
764     &qc_ftos,  /*   2   */
765     &qc_spawn, /*   3   */
766     &qc_kill,  /*   4   */
767     &qc_vtos,  /*   5   */
768     &qc_error, /*   6   */
769     &qc_vlen,  /*   7   */
770     &qc_etos   /*   8   */
771 };
772 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
773
774 static const char *arg0 = NULL;
775
776 void usage()
777 {
778     printf("usage: [-debug] %s file\n", arg0);
779     exit(1);
780 }
781
782 static void prog_main_setparams(qc_program *prog)
783 {
784     size_t i;
785     qcany *arg;
786
787     for (i = 0; i < main_params_elements; ++i) {
788         arg = GetGlobal(OFS_PARM0 + 3*i);
789         arg->vector[0] = 0;
790         arg->vector[1] = 0;
791         arg->vector[2] = 0;
792         switch (main_params_data[i].vtype) {
793             case TYPE_VECTOR:
794 #ifdef WIN32
795                 (void)sscanf_s(main_params_data[i].value, " %f %f %f ",
796                                &arg->vector[0],
797                                &arg->vector[1],
798                                &arg->vector[2]);
799 #else
800                 (void)sscanf(main_params_data[i].value, " %f %f %f ",
801                              &arg->vector[0],
802                              &arg->vector[1],
803                              &arg->vector[2]);
804 #endif
805                 break;
806             case TYPE_FLOAT:
807                 arg->_float = atof(main_params_data[i].value);
808                 break;
809             case TYPE_STRING:
810                 arg->string = prog_tempstring(prog, main_params_data[i].value);
811                 break;
812             default:
813                 printf("error: unhandled parameter type: %i\n", main_params_data[i].vtype);
814                 break;
815         }
816     }
817 }
818
819 int main(int argc, char **argv)
820 {
821     size_t      i;
822     qcint       fnmain = -1;
823     qc_program *prog;
824     size_t      xflags = VMXF_DEFAULT;
825     bool        opts_printfields = false;
826     bool        opts_printdefs   = false;
827     bool        opts_info  = false;
828
829     arg0 = argv[0];
830
831     if (argc < 2)
832         usage();
833
834     while (argc > 2) {
835         if (!strcmp(argv[1], "-trace")) {
836             --argc;
837             ++argv;
838             xflags |= VMXF_TRACE;
839         }
840         else if (!strcmp(argv[1], "-profile")) {
841             --argc;
842             ++argv;
843             xflags |= VMXF_PROFILE;
844         }
845         else if (!strcmp(argv[1], "-info")) {
846             --argc;
847             ++argv;
848             opts_info = true;
849         }
850         else if (!strcmp(argv[1], "-printdefs")) {
851             --argc;
852             ++argv;
853             opts_printdefs = true;
854         }
855         else if (!strcmp(argv[1], "-printfields")) {
856             --argc;
857             ++argv;
858             opts_printfields = true;
859         }
860         else if (!strcmp(argv[1], "-vector") ||
861                  !strcmp(argv[1], "-string") ||
862                  !strcmp(argv[1], "-float") )
863         {
864             qcvm_parameter p;
865             if (argv[1][1] == 'f')
866                 p.vtype = TYPE_FLOAT;
867             else if (argv[1][1] == 's')
868                 p.vtype = TYPE_STRING;
869             else if (argv[1][1] == 'v')
870                 p.vtype = TYPE_VECTOR;
871
872             --argc;
873             ++argv;
874             if (argc < 3)
875                 usage();
876             p.value = argv[1];
877
878             if (main_params_add(p) < 0) {
879                 if (main_params_data)
880                     mem_d(main_params_data);
881                 printf("cannot add parameter\n");
882                 exit(1);
883             }
884             --argc;
885             ++argv;
886         }
887         else
888             usage();
889     }
890
891
892     prog = prog_load(argv[1]);
893     if (!prog) {
894         printf("failed to load program '%s'\n", argv[1]);
895         exit(1);
896     }
897
898     prog->builtins       = qc_builtins;
899     prog->builtins_count = qc_builtins_count;
900     prog->builtins_alloc = 0;
901
902     if (opts_info) {
903         printf("Program's system-checksum = 0x%04x\n", (int)prog->crc16);
904         printf("Entity field space: %i\n", (int)prog->entityfields);
905     }
906
907     for (i = 1; i < prog->functions_count; ++i) {
908         const char *name = prog_getstring(prog, prog->functions[i].name);
909         /* printf("Found function: %s\n", name); */
910         if (!strcmp(name, "main"))
911             fnmain = (qcint)i;
912     }
913     if (opts_info) {
914         prog_delete(prog);
915         return 0;
916     }
917     if (opts_printdefs) {
918         for (i = 0; i < prog->defs_count; ++i) {
919             printf("Global: %8s %-16s at %u\n",
920                    type_name[prog->defs[i].type & DEF_TYPEMASK],
921                    prog_getstring(prog, prog->defs[i].name),
922                    (unsigned int)prog->defs[i].offset);
923         }
924     }
925     else if (opts_printfields) {
926         for (i = 0; i < prog->fields_count; ++i) {
927             printf("Field: %8s %-16s at %u\n",
928                    type_name[prog->fields[i].type],
929                    prog_getstring(prog, prog->fields[i].name),
930                    (unsigned int)prog->fields[i].offset);
931         }
932     }
933     else
934     {
935         if (fnmain > 0)
936         {
937             prog_main_setparams(prog);
938             prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
939         }
940         else
941             printf("No main function found\n");
942     }
943
944     prog_delete(prog);
945     return 0;
946 }
947 #endif