]> git.xonotic.org Git - xonotic/gmqcc.git/blob - exec.c
Listing functions
[xonotic/gmqcc.git] / exec.c
1 #include "gmqcc.h"
2
3 #define QCVM_EXECUTOR
4
5 #define _MEM_VEC_FUN_APPEND(Tself, Twhat, mem)                       \
6 bool GMQCC_WARN Tself##_##mem##_append(Tself *s, Twhat *p, size_t c) \
7 {                                                                    \
8     Twhat *reall;                                                    \
9     if (s->mem##_count+c >= s->mem##_alloc) {                        \
10         if (!s->mem##_alloc) {                                       \
11             s->mem##_alloc = c < 16 ? 16 : c;                        \
12         } else {                                                     \
13             s->mem##_alloc *= 2;                                     \
14             if (s->mem##_count+c >= s->mem##_alloc) {                \
15                 s->mem##_alloc = s->mem##_count+c;                   \
16             }                                                        \
17         }                                                            \
18         reall = (Twhat*)mem_a(sizeof(Twhat) * s->mem##_alloc);       \
19         if (!reall) {                                                \
20             return false;                                            \
21         }                                                            \
22         memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count);       \
23         mem_d(s->mem);                                               \
24         s->mem = reall;                                              \
25     }                                                                \
26     memcpy(&s->mem[s->mem##_count], p, c*sizeof(*p));                \
27     s->mem##_count += c;                                             \
28     return true;                                                     \
29 }
30
31 #define _MEM_VEC_FUN_RESIZE(Tself, Twhat, mem)                   \
32 bool GMQCC_WARN Tself##_##mem##_resize(Tself *s, size_t c)       \
33 {                                                                \
34     Twhat *reall;                                                \
35     reall = (Twhat*)mem_a(sizeof(Twhat) * c);                    \
36     if (c > s->mem##_count) {                                    \
37         memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count);   \
38     } else {                                                     \
39         memcpy(reall, s->mem, sizeof(Twhat) * c);                \
40     }                                                            \
41     s->mem##_count = c;                                          \
42     s->mem##_alloc = c;                                          \
43     return true;                                                 \
44 }
45
46 /* darkplaces has (or will have) a 64 bit prog loader
47  * where the 32 bit qc program is autoconverted on load.
48  * Since we may want to support that as well, let's redefine
49  * float and int here.
50  */
51 typedef float   qcfloat;
52 typedef int32_t qcint;
53
54 typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1];
55 typedef char qcint_size_is_correct   [sizeof(int)     == 4 ?1:-1];
56
57 typedef struct {
58     uint32_t offset;
59     uint32_t length;
60 } prog_section;
61
62 typedef struct {
63     uint32_t     version;
64     uint16_t     crc16;
65     uint16_t     skip;
66
67     prog_section statements;
68     prog_section defs;
69     prog_section fields;
70     prog_section functions;
71     prog_section strings;
72     prog_section globals;
73     uint32_t     entfield;
74 } prog_header;
75
76 typedef prog_section_both      prog_def;
77 typedef prog_section_function  prog_function;
78 typedef prog_section_statement prog_statement;
79
80 enum {
81     VMERR_OK,
82     VMERR_TEMPSTRING_ALLOC,
83
84     VMERR_END
85 };
86
87 typedef struct {
88     char           *filename;
89
90     MEM_VECTOR_MAKE(prog_statement, code);
91     MEM_VECTOR_MAKE(prog_def,       defs);
92     MEM_VECTOR_MAKE(prog_def,       fields);
93     MEM_VECTOR_MAKE(prog_function,  functions);
94     MEM_VECTOR_MAKE(char,           strings);
95     MEM_VECTOR_MAKE(qcint,          globals);
96     MEM_VECTOR_MAKE(qcint,          entitydata);
97
98     size_t tempstring_start;
99     size_t tempstring_at;
100
101     qcint  vmerror;
102
103     MEM_VECTOR_MAKE(qcint,  localstack);
104     MEM_VECTOR_MAKE(size_t, localsp);
105 } qc_program;
106 MEM_VEC_FUNCTIONS(qc_program, prog_statement, code)
107 MEM_VEC_FUNCTIONS(qc_program, prog_def,       defs)
108 MEM_VEC_FUNCTIONS(qc_program, prog_def,       fields)
109 MEM_VEC_FUNCTIONS(qc_program, prog_function,  functions)
110 MEM_VEC_FUNCTIONS(qc_program, char,           strings)
111 _MEM_VEC_FUN_APPEND(qc_program, char, strings)
112 _MEM_VEC_FUN_RESIZE(qc_program, char, strings)
113 MEM_VEC_FUNCTIONS(qc_program, qcint,          globals)
114 MEM_VEC_FUNCTIONS(qc_program, qcint,          entitydata)
115
116 MEM_VEC_FUNCTIONS(qc_program,   qcint, localstack)
117 _MEM_VEC_FUN_APPEND(qc_program, qcint, localstack)
118 _MEM_VEC_FUN_RESIZE(qc_program, qcint, localstack)
119 MEM_VEC_FUNCTIONS(qc_program,   size_t, localsp)
120
121 qc_program* prog_load(const char *filename)
122 {
123     qc_program *prog;
124     prog_header header;
125     FILE *file;
126
127     file = fopen(filename, "rb");
128     if (!file)
129         return NULL;
130
131     if (fread(&header, sizeof(header), 1, file) != 1) {
132         perror("read");
133         fclose(file);
134         return NULL;
135     }
136
137     if (header.version != 6) {
138         printf("header says this is a version %i progs, we need version 6\n",
139                header.version);
140         fclose(file);
141         return NULL;
142     }
143
144     prog = (qc_program*)mem_a(sizeof(qc_program));
145     if (!prog) {
146         fclose(file);
147         printf("failed to allocate program data\n");
148         return NULL;
149     }
150     memset(prog, 0, sizeof(*prog));
151
152     prog->filename = util_strdup(filename);
153     if (!prog->filename)
154         goto error;
155 #define read_data(hdrvar, progvar, type)                                         \
156     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {                      \
157         perror("fseek");                                                         \
158         goto error;                                                              \
159     }                                                                            \
160     prog->progvar##_alloc = header.hdrvar.length;                                \
161     prog->progvar##_count = header.hdrvar.length;                                \
162     prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
163     if (!prog->progvar)                                                          \
164         goto error;                                                              \
165     if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
166         != header.hdrvar.length) {                                               \
167         perror("read");                                                          \
168         goto error;                                                              \
169     }
170 #define read_data1(x, y) read_data(x, x, y)
171
172     read_data (statements, code, prog_statement);
173     read_data1(defs,             prog_def);
174     read_data1(fields,           prog_def);
175     read_data1(functions,        prog_function);
176     read_data1(strings,          char);
177     read_data1(globals,          qcint);
178
179     fclose(file);
180
181     /* Add tempstring area */
182     prog->tempstring_start = prog->strings_count;
183     prog->tempstring_at    = prog->strings_count;
184     if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
185         goto error;
186
187     return prog;
188
189 error:
190     if (prog->filename)   mem_d(prog->filename);
191     if (prog->code)       mem_d(prog->code);
192     if (prog->defs)       mem_d(prog->defs);
193     if (prog->fields)     mem_d(prog->fields);
194     if (prog->functions)  mem_d(prog->functions);
195     if (prog->strings)    mem_d(prog->strings);
196     if (prog->globals)    mem_d(prog->globals);
197     if (prog->entitydata) mem_d(prog->entitydata);
198     mem_d(prog);
199     return NULL;
200 }
201
202 void prog_delete(qc_program *prog)
203 {
204     if (prog->filename) mem_d(prog->filename);
205     MEM_VECTOR_CLEAR(prog, code);
206     MEM_VECTOR_CLEAR(prog, defs);
207     MEM_VECTOR_CLEAR(prog, fields);
208     MEM_VECTOR_CLEAR(prog, functions);
209     MEM_VECTOR_CLEAR(prog, strings);
210     MEM_VECTOR_CLEAR(prog, globals);
211     MEM_VECTOR_CLEAR(prog, entitydata);
212     MEM_VECTOR_CLEAR(prog, localstack);
213     MEM_VECTOR_CLEAR(prog, localsp);
214     mem_d(prog);
215 }
216
217 /***********************************************************************
218  * VM code
219  */
220
221 char* prog_getstring(qc_program *prog, qcint str)
222 {
223     if (str < 0 || str >= prog->strings_count)
224         return prog->strings;
225     return prog->strings + str;
226 }
227
228 qcint prog_tempstring(qc_program *prog, const char *_str)
229 {
230     /* we don't access it, but the macro-generated functions don't use
231      * const
232      */
233     char *str = (char*)_str;
234
235     size_t len = strlen(str);
236     size_t at = prog->tempstring_at;
237
238     /* when we reach the end we start over */
239     if (at + len >= prog->strings_count)
240         at = prog->tempstring_start;
241
242     /* when it doesn't fit, reallocate */
243     if (at + len >= prog->strings_count)
244     {
245         prog->strings_count = at;
246         if (!qc_program_strings_append(prog, str, len+1)) {
247             prog->vmerror = VMERR_TEMPSTRING_ALLOC;
248             return 0;
249         }
250         return at;
251     }
252
253     /* when it fits, just copy */
254     memcpy(prog->strings + at, str, len+1);
255     prog->tempstring_at += len+1;
256     return at;
257 }
258
259 /***********************************************************************
260  * main for when building the standalone executor
261  */
262
263 #if defined(QCVM_EXECUTOR)
264 int main(int argc, char **argv)
265 {
266     size_t      i;
267     qc_program *prog;
268
269     if (argc != 2) {
270         printf("usage: %s file\n", argv[0]);
271         exit(1);
272     }
273
274     prog = prog_load(argv[1]);
275     if (!prog) {
276         printf("failed to load program '%s'\n", argv[1]);
277         exit(1);
278     }
279
280     for (i = 1; i < prog->functions_count; ++i) {
281         printf("Found function: %s\n", prog_getstring(prog, prog->functions[i].name));
282     }
283
284     prog_delete(prog);
285     return 0;
286 }
287 #endif