Branch data Line data Source code
1 : : #include "platform/file-helpers.h"
2 : : #include "asar.h"
3 : : #include "virtualfile.h"
4 : : #include "assembleblock.h"
5 : : #include "macro.h"
6 : : #include "asar_math.h"
7 : : #include "table.h"
8 : : #include "unicode.h"
9 : : #include <cmath>
10 : : #include <functional>
11 : : #include <algorithm>
12 : :
13 : : static const char * str;
14 : : //save before calling eval if needed after
15 : : static const char * current_user_function_name;
16 : :
17 : : static double getnumcore();
18 : : static double getnum();
19 : : static double eval(int depth);
20 : :
21 : : //label (bool foundLabel) (bool forwardLabel)
22 : : //userfunction
23 : :
24 : : bool foundlabel;
25 : : bool foundlabel_static;
26 : : bool forwardlabel;
27 : :
28 : : struct cachedfile {
29 : : string filename;
30 : : virtual_file_handle filehandle;
31 : : size_t filesize;
32 : : bool used;
33 : : };
34 : :
35 : : #define numcachedfiles 16
36 : :
37 : : static cachedfile cachedfiles[numcachedfiles];
38 : : static int cachedfileindex = 0;
39 : :
40 : : // Opens a file, trying to open it from cache first
41 : :
42 : 56 : static cachedfile * opencachedfile(string fname, bool should_error)
43 : : {
44 : 28 : cachedfile * cachedfilehandle = nullptr;
45 : :
46 : 56 : const char* current_file = get_current_file_name();
47 : :
48 : : // RPG Hacker: Only using a combined path here because that should
49 : : // hopefully result in a unique string for every file, whereas
50 : : // fname could be a relative path, which isn't guaranteed to be unique.
51 : : // Note that this does not affect how we open the file - this is
52 : : // handled by the filesystem and uses our include paths etc.
53 : 56 : string combinedname = filesystem->create_absolute_path(dir(current_file), fname);
54 : :
55 [ + + ]: 368 : for (int i = 0; i < numcachedfiles; i++)
56 : : {
57 [ + + + + : 350 : if (cachedfiles[i].used && cachedfiles[i].filename == combinedname)
+ + ]
58 : : {
59 : 38 : cachedfilehandle = &cachedfiles[i];
60 : 19 : break;
61 : : }
62 : : }
63 : :
64 [ + + ]: 28 : if (cachedfilehandle == nullptr)
65 : : {
66 [ - + ]: 18 : if (cachedfiles[cachedfileindex].used)
67 : : {
68 : 0 : filesystem->close_file(cachedfiles[cachedfileindex].filehandle);
69 : 0 : cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
70 : 0 : cachedfiles[cachedfileindex].used = false;
71 : : }
72 : :
73 : 18 : cachedfilehandle = &cachedfiles[cachedfileindex];
74 : : }
75 : :
76 [ + - ]: 28 : if (cachedfilehandle != nullptr)
77 : : {
78 [ + + ]: 56 : if (!cachedfilehandle->used)
79 : : {
80 : 18 : cachedfilehandle->filehandle = filesystem->open_file(fname, current_file);
81 [ + + ]: 18 : if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE)
82 : : {
83 : 8 : cachedfilehandle->used = true;
84 : 8 : cachedfilehandle->filename = combinedname;
85 : 8 : cachedfilehandle->filesize = filesystem->get_file_size(cachedfilehandle->filehandle);
86 : 8 : cachedfileindex++;
87 : : // randomdude999: when we run out of cached files, just start overwriting ones from the start
88 [ - + ]: 8 : if (cachedfileindex >= numcachedfiles) cachedfileindex = 0;
89 : : }
90 : : }
91 : : }
92 : :
93 [ + + + + : 56 : if ((cachedfilehandle == nullptr || cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && should_error)
- + ]
94 : : {
95 : 0 : asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), fname.data());
96 : : }
97 : :
98 : 56 : return cachedfilehandle;
99 : 56 : }
100 : :
101 : :
102 : 460 : void closecachedfiles()
103 : : {
104 [ + + ]: 7820 : for (int i = 0; i < numcachedfiles; i++)
105 : : {
106 [ + + ]: 7360 : if (cachedfiles[i].used)
107 : : {
108 [ + - + - ]: 8 : if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE && filesystem)
109 : : {
110 : 8 : filesystem->close_file(cachedfiles[i].filehandle);
111 : 8 : cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
112 : : }
113 : :
114 : 8 : cachedfiles[i].used = false;
115 : : }
116 : : }
117 : :
118 : 460 : cachedfileindex = 0;
119 : 460 : }
120 : :
121 : :
122 : 6 : static int struct_size(const char *name)
123 : : {
124 [ + - - + : 6 : if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
- + ]
125 [ + + ]: 6 : else if(!structs.exists(name)) return 0;
126 : 6 : return structs.find(name).struct_size;
127 : : }
128 : :
129 : 38 : static int object_size(const char *name)
130 : : {
131 [ + + - + : 38 : if(pass && !structs.exists(name)) asar_throw_error(2, error_type_block, error_id_struct_not_found, name);
- + ]
132 [ + + ]: 38 : else if(!structs.exists(name)) return 0;
133 : 38 : return structs.find(name).object_size;
134 : : }
135 : :
136 : 10 : static int data_size(const char *name)
137 : : {
138 : : int label;
139 [ + + - + : 10 : if(!labels.exists(name)) asar_throw_error(2, error_type_block, error_id_label_not_found, name);
- - ]
140 : 10 : foundlabel = true;
141 : 10 : snes_label label_data = labels.find(name);
142 : 10 : foundlabel_static &= label_data.is_static;
143 : 5 : label = label_data.id;
144 : 10 : snes_label selected_label;
145 : 10 : selected_label.id = 0xFFFFFF;
146 : 10 : selected_label.pos = 0xFFFFFF;
147 : 10 : labels.each([&selected_label, label](const char *key, snes_label current_label){
148 [ + + + + ]: 30 : if(label < current_label.id && current_label.id < selected_label.id){
149 : 6 : selected_label = current_label;
150 : : }
151 : 30 : });
152 [ + + + - ]: 10 : if(selected_label.id == 0xFFFFFF) asar_throw_warning(2, warning_id_datasize_last_label, name);
153 [ + + + - ]: 10 : if(selected_label.pos-label_data.pos > 0xFFFF) asar_throw_warning(2, warning_id_datasize_exceeds_size, name);
154 : 10 : return selected_label.pos-label_data.pos;
155 : : }
156 : :
157 : :
158 : 170 : string get_string_argument()
159 : : {
160 [ - + ]: 170 : while (*str==' ') str++;
161 [ + - ]: 170 : if (*str=='"')
162 : : {
163 : 170 : const char * strpos = str + 1;
164 [ - + - - ]: 85 : while (*str!='"' && *str!='\0') str++;
165 : 170 : str = strchr(str + 1, '"');
166 : 170 : string tempname(strpos , (int)(str - strpos));
167 : 170 : str++;
168 [ - + ]: 170 : while (*str==' ') str++; //eat space
169 : 170 : return tempname;
170 : 170 : }//todo make this error a better one later
171 : 0 : asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
172 : 0 : return ""; //never actually called, but I don't feel like figuring out __attribute__ ((noreturn)) on MSVC
173 : : }
174 : :
175 : : //only returns alphanumeric (and _) starting with alpha or _
176 : 372 : string get_symbol_argument()
177 : : {
178 [ - + ]: 372 : while (*str==' ') str++; //is this proper? Dunno yet.
179 : 186 : const char * strpos = str;
180 [ + + ]: 372 : if(is_ualpha(*str)) str++;
181 [ + + + + : 1580 : while (is_ualnum(*str) || *str == '.') str++;
+ + ]
182 [ - + ]: 372 : if(strpos == str){
183 : : //error nothing was read, this is a placeholder error
184 : 0 : asar_throw_error(2, error_type_block, error_id_string_literal_not_terminated);
185 : : }
186 : :
187 : 372 : string symbol = string(strpos, (int)(str - strpos));
188 [ - + ]: 372 : while (*str==' ') str++; //eat spaces
189 : 372 : return symbol;
190 : : }
191 : :
192 : 424 : double get_double_argument()
193 : : {
194 [ - + ]: 424 : while (*str==' ') str++;
195 : 424 : double result = eval(0);
196 [ - + ]: 424 : while (*str==' ') str++; //eat spaces
197 : 424 : return result;
198 : : }
199 : :
200 : : //will eat the comma
201 : 72 : bool has_next_parameter()
202 : : {
203 [ + + ]: 72 : if (*str==',')
204 : : {
205 : 24 : str++;
206 : 24 : return true;
207 : : }
208 : 24 : return false;
209 : : }
210 : :
211 : 190 : void require_next_parameter()
212 : : {
213 [ + - ]: 190 : if (*str==',')
214 : : {
215 : 190 : str++;
216 : 190 : return;
217 : : }
218 : 0 : asar_throw_error(2, error_type_block, error_id_require_parameter);
219 : : }
220 : :
221 : 4 : template <typename F> double asar_unary_wrapper()
222 : : {
223 : 4 : return F()(get_double_argument());
224 : : }
225 : :
226 : 2 : template <double (*F)(double)> double asar_unary_wrapper()
227 : : {
228 : 2 : return F(get_double_argument());
229 : : }
230 : :
231 : : //possibly restrict type T in the future....
232 : : //first a case for functors
233 : 44 : template <typename F> double asar_binary_wrapper()
234 : : {
235 : 44 : double first = get_double_argument();
236 : 44 : require_next_parameter();
237 : 44 : return F()(first, get_double_argument());
238 : : }
239 : : //this could be DRY with if constexpr....oh well
240 : 26 : template <double (*F)(double, double)> double asar_binary_wrapper()
241 : : {
242 : 26 : double first = get_double_argument();
243 : 26 : require_next_parameter();
244 : 26 : return F(first, get_double_argument());
245 : : }
246 : :
247 : 1 : double asar_bank(double a)
248 : : {
249 : 2 : return (int)a >> 16;
250 : : }
251 : :
252 : :
253 : 3 : double asar_logical_nand(double a, double b)
254 : : {
255 [ + + - + ]: 6 : return !(a && b);
256 : : }
257 : :
258 : :
259 : 3 : double asar_logical_nor(double a, double b)
260 : : {
261 [ + + + + ]: 6 : return !(a || b);
262 : : }
263 : :
264 : :
265 : 3 : double asar_logical_xor(double a, double b)
266 : : {
267 : 6 : return !!a ^ !!b;
268 : : }
269 : :
270 : 5 : double asar_max(double a, double b)
271 : : {
272 [ + + + + : 10 : return a > b ? a : b;
- - ]
273 : : }
274 : :
275 : 5 : double asar_min(double a, double b)
276 : : {
277 [ + + + + : 10 : return a < b ? a : b;
- - ]
278 : : }
279 : :
280 : 6 : double asar_clamp()
281 : : {
282 : 6 : double value = get_double_argument();
283 : 6 : require_next_parameter();
284 : 6 : double low = get_double_argument();
285 : 6 : require_next_parameter();
286 : 6 : double high = get_double_argument();
287 : :
288 : 6 : return asar_max(low, asar_min(high, value));
289 : : }
290 : :
291 : 4 : double asar_safediv()
292 : : {
293 : 4 : double dividend = get_double_argument();
294 : 4 : require_next_parameter();
295 : 4 : double divisor = get_double_argument();
296 : 4 : require_next_parameter();
297 : 4 : double default_value = get_double_argument();
298 : :
299 [ + + ]: 4 : return divisor == 0.0 ? default_value : dividend / divisor;
300 : : }
301 : :
302 : 16 : double asar_select()
303 : : {
304 : 16 : double selector = get_double_argument();
305 : 16 : require_next_parameter();
306 : 16 : double a = get_double_argument();
307 : 16 : require_next_parameter();
308 : 16 : double b = get_double_argument();
309 : :
310 [ + + ]: 16 : return selector == 0.0 ? b : a;
311 : : }
312 : :
313 : 58 : double asar_snestopc_wrapper()
314 : : {
315 : 58 : return snestopc(get_double_argument());
316 : : }
317 : :
318 : 42 : double asar_pctosnes_wrapper()
319 : : {
320 : 42 : return pctosnes(get_double_argument());
321 : : }
322 : :
323 : 6 : double asar_realbase_wrapper()
324 : : {
325 : : //need a better way to do this to make sure it is useful...
326 : : //foundlabel=true; //Going to consider this an implicit label because we don't really have a better system
327 : 6 : return realsnespos;
328 : : }
329 : :
330 : 10 : template <int count> double asar_read()
331 : : {
332 : 10 : int target = get_double_argument();
333 : 10 : int addr=snestopc_pick(target);
334 [ - + ]: 10 : if(has_next_parameter())
335 : : {
336 : 0 : double default_value = get_double_argument();
337 [ # # ]: 0 : if (addr<0) return default_value;
338 [ # # ]: 0 : else if (addr+count>romlen_r) return default_value;
339 : : }
340 : : else
341 : : {
342 [ - + - - : 10 : if (addr<0) asar_throw_error(2, error_type_block, error_id_snes_address_doesnt_map_to_rom, (hex((unsigned int)target, 6) + " in read function").data());
- - - - ]
343 [ + + + + : 14 : else if (addr+count>romlen_r) asar_throw_error(2, error_type_block, error_id_snes_address_out_of_bounds, (hex(target, 6) + " in read function").data());
+ - - + ]
344 : : }
345 : :
346 : 4 : unsigned int value = 0;
347 [ + + ]: 28 : for(int i = 0; i < count; i++)
348 : : {
349 : 20 : value |= romdata_r[addr+i] << (8 * i);
350 : : }
351 : 8 : return value;
352 : : }
353 : :
354 : 24 : template <int count> double asar_canread()
355 : : {
356 : 12 : int length = count;
357 [ - + ]: 12 : if(!length)
358 : : {
359 : 0 : length = get_double_argument();
360 : : }
361 : 24 : int addr=snestopc_pick(get_double_argument());
362 [ + - + + ]: 24 : if (addr<0 || addr+length-1>=romlen_r) return 0;
363 : 12 : else return 1;
364 : : }
365 : :
366 : 28 : template <size_t count> double asar_readfile()
367 : : {
368 : : static_assert(count && count <= 4, "invalid count"); //1-4 inclusive
369 : :
370 : 28 : string name = get_string_argument();
371 : 28 : require_next_parameter();
372 : 28 : size_t offset = get_double_argument();
373 : 28 : bool should_error = !has_next_parameter();
374 : 28 : cachedfile * fhandle = opencachedfile(name, should_error);
375 [ + + ]: 28 : if(!should_error)
376 : : {
377 : 12 : double default_value = get_double_argument();
378 [ + - + + ]: 12 : if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return default_value;
379 [ + + ]: 10 : if (offset < 0 || offset + count > fhandle->filesize) return default_value;
380 : : }
381 : : else
382 : : {
383 [ + - - + : 16 : if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
- - - - -
- ]
384 [ - + - - : 16 : if (offset < 0 || offset + count > fhandle->filesize) asar_throw_error(2, error_type_block, error_id_file_offset_out_of_bounds, dec(offset).data(), name.data());
- - ]
385 : : }
386 : :
387 : 22 : unsigned char data[4] = { 0, 0, 0, 0 };
388 : 22 : filesystem->read_file(fhandle->filehandle, data, offset, count);
389 : :
390 : 11 : unsigned int value = 0;
391 [ + + ]: 58 : for(size_t i = 0; i < count; i++)
392 : : {
393 : 36 : value |= data[i] << (8 * i);
394 : : }
395 : :
396 : 22 : return value;
397 : 28 : }
398 : :
399 : 14 : template <size_t count> double asar_canreadfile()
400 : : {
401 : 14 : string name = get_string_argument();
402 : 14 : require_next_parameter();
403 : 14 : size_t offset = get_double_argument();
404 : 7 : size_t length = count;
405 : : if(!count)
406 : : {
407 : 4 : require_next_parameter();
408 : 4 : length = get_double_argument();
409 : : }
410 : 14 : cachedfile * fhandle = opencachedfile(name, false);
411 [ + - + + ]: 14 : if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return 0;
412 [ + + ]: 12 : if (offset < 0 || offset + length > fhandle->filesize) return 0;
413 : 4 : return 1;
414 : 14 : }
415 : :
416 : : // returns 0 if the file is OK, 1 if the file doesn't exist, 2 if it couldn't be opened for some other reason
417 : 12 : static double asar_filestatus()
418 : : {
419 : 12 : cachedfile * fhandle = opencachedfile(get_string_argument(), false);
420 [ + - + + ]: 12 : if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
421 : : {
422 [ + + ]: 6 : if (filesystem->get_last_error() == vfe_doesnt_exist)
423 : : {
424 : 3 : return 1;
425 : : }
426 : : else
427 : : {
428 : 0 : return 2;
429 : : }
430 : : }
431 : 3 : return 0;
432 : : }
433 : :
434 : : // Returns the size of the specified file.
435 : 2 : static double asar_filesize()
436 : : {
437 : 2 : string name = get_string_argument();
438 : 2 : cachedfile * fhandle = opencachedfile(name, false);
439 [ + - - + : 2 : if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) asar_throw_error(2, error_type_block, vfile_error_to_error_id(asar_get_last_io_error()), name.data());
- - - - -
- ]
440 : 2 : return (double)fhandle->filesize;
441 : 2 : }
442 : :
443 : : // Checks whether the specified define is defined.
444 : 74 : static double asar_isdefined()
445 : : {
446 : 74 : return defines.exists(get_string_argument());
447 : : }
448 : :
449 : : // RPG Hacker: What exactly makes this function overly complicated, you ask?
450 : : // Well, it converts a double to a string and then back to a double.
451 : : // It was the quickest reliable solution I could find, though, so there's that.
452 : 4 : static double asar_round()
453 : : {
454 : 4 : double number = get_double_argument();
455 : 4 : require_next_parameter();
456 : :
457 : : // Hue hue hue... ass!
458 : : // OK, sorry, I apologize.
459 : 4 : string asstring = ftostrvar(number, get_double_argument());
460 : :
461 : : // Some hacky shenanigans with variables going on here
462 : 4 : const char * strbackup = str;
463 : 4 : str = asstring;
464 : 4 : double asdouble = (double)getnum();
465 : 4 : str = strbackup;
466 : :
467 : 4 : return asdouble;
468 : 4 : }
469 : :
470 : 356 : static double asar_structsize_wrapper()
471 : : {
472 : 356 : string symbol = get_symbol_argument();
473 [ + + ]: 356 : if(symbol == "..."){
474 [ + + - + ]: 350 : if(!inmacro) asar_throw_error(2, error_type_block, error_id_vararg_sizeof_nomacro);
475 [ + + - + ]: 348 : if(numvarargs == -1) asar_throw_error(2, error_type_block, error_id_macro_not_varadic, "sizeof(...)");
476 : 346 : return numvarargs;
477 : : }
478 : 6 : return (double)struct_size(symbol);
479 : 356 : }
480 : :
481 : 6 : static double asar_objectsize_wrapper()
482 : : {
483 : 6 : return (double)object_size(get_symbol_argument());
484 : : }
485 : :
486 : 10 : static double asar_datasize_wrapper()
487 : : {
488 : 10 : return (double)data_size(get_symbol_argument());
489 : : }
490 : :
491 : 12 : static double asar_stringsequal()
492 : : {
493 : 12 : string string1 = get_string_argument();
494 : 12 : require_next_parameter();
495 [ + - + + ]: 27 : return (strcmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0);
496 : 12 : }
497 : :
498 : 6 : static double asar_stringsequalnocase()
499 : : {
500 : 6 : string string1 = get_string_argument();
501 : 6 : require_next_parameter();
502 [ + - + + : 12 : return (stricmp(string1, get_string_argument()) == 0 ? 1.0 : 0.0);
+ - ]
503 : 6 : }
504 : :
505 : 0 : static double asar_char()
506 : : {
507 : 0 : string string1 = get_string_argument();
508 : 0 : require_next_parameter();
509 : 0 : int offset = get_double_argument();
510 [ # # # # : 0 : if(offset >= string1.length()) asar_throw_error(2, error_type_block, error_id_oob, offset, string1.length());
# # # # ]
511 : 0 : return string1[offset];
512 : 0 : }
513 : :
514 : 0 : static double asar_strlen()
515 : : {
516 : 0 : return get_string_argument().length();
517 : : }
518 : :
519 : 34 : string copy_arg()
520 : : {
521 [ + + ]: 34 : if(*str == '"')
522 : : {
523 : 4 : string t = "\"";
524 : 8 : return (t += get_string_argument() + "\"");
525 : 4 : }
526 : :
527 : 30 : string result;
528 : 15 : bool is_symbolic = true;
529 : 15 : int parlevel=0;
530 : 15 : int i = 0;
531 [ - + + + : 60 : while(parlevel > 0 || (str[i] != ',' && str[i] != ')'))
+ + ]
532 : : {
533 : 30 : is_symbolic &= is_ualnum(str[i]);
534 [ - + ]: 30 : if(str[i] == '(') parlevel++;
535 [ - + ]: 30 : else if(str[i] == ')') parlevel--;
536 : 30 : i++;
537 : : }
538 : 30 : result += string(str, i);
539 : 30 : str += i;
540 : :
541 [ - + ]: 30 : if(!is_symbolic)
542 : : {
543 : 0 : const char *oldstr=str;
544 : 0 : str = (const char *)result;
545 : 0 : result = ftostr(eval(0));
546 : 0 : str = oldstr;
547 : : }
548 : 30 : return result;
549 : 30 : }
550 : :
551 : : assocarr<double (*)()> builtin_functions =
552 : : {
553 : : {"sqrt", asar_unary_wrapper<sqrt>},
554 : : {"sin", asar_unary_wrapper<sin>},
555 : : {"cos", asar_unary_wrapper<cos>},
556 : : {"tan", asar_unary_wrapper<tan>},
557 : : {"asin", asar_unary_wrapper<asin>},
558 : : {"acos", asar_unary_wrapper<acos>},
559 : : {"atan", asar_unary_wrapper<atan>},
560 : : {"arcsin", asar_unary_wrapper<asin>},
561 : : {"arccos", asar_unary_wrapper<acos>},
562 : : {"arctan", asar_unary_wrapper<atan>},
563 : : {"log", asar_unary_wrapper<log>},
564 : : {"log10", asar_unary_wrapper<log10>},
565 : : {"log2", asar_unary_wrapper<log2>},
566 : :
567 : : {"ceil", asar_unary_wrapper<ceil>},
568 : : {"floor", asar_unary_wrapper<floor>},
569 : :
570 : : {"read1", asar_read<1>}, //This handles the safe and unsafe variant
571 : : {"read2", asar_read<2>},
572 : : {"read3", asar_read<3>},
573 : : {"read4", asar_read<4>},
574 : : {"canread", asar_canread<0>},
575 : : {"canread1", asar_canread<1>},
576 : : {"canread2", asar_canread<2>},
577 : : {"canread3", asar_canread<3>},
578 : : {"canread4", asar_canread<4>},
579 : :
580 : : {"readfile1", asar_readfile<1>},
581 : : {"readfile2", asar_readfile<2>},
582 : : {"readfile3", asar_readfile<3>},
583 : : {"readfile4", asar_readfile<4>},
584 : : {"canreadfile", asar_canreadfile<0>},
585 : : {"canreadfile1", asar_canreadfile<1>},
586 : : {"canreadfile2", asar_canreadfile<2>},
587 : : {"canreadfile3", asar_canreadfile<3>},
588 : : {"canreadfile4", asar_canreadfile<4>},
589 : :
590 : : {"filesize", asar_filesize},
591 : : {"getfilestatus", asar_filestatus},
592 : :
593 : : {"defined", asar_isdefined},
594 : :
595 : : {"snestopc", asar_snestopc_wrapper},
596 : : {"pctosnes", asar_pctosnes_wrapper},
597 : : {"realbase", asar_realbase_wrapper},
598 : :
599 : : {"max", asar_binary_wrapper<asar_max>},
600 : : {"min", asar_binary_wrapper<asar_min>},
601 : : {"clamp", asar_clamp},
602 : :
603 : : {"safediv", asar_safediv},
604 : :
605 : : {"select", asar_select},
606 : : {"bank", asar_unary_wrapper<asar_bank>},
607 : : {"not", asar_unary_wrapper<std::logical_not<unsigned int>>},
608 : : {"equal", asar_binary_wrapper<std::equal_to<double>>},
609 : : {"notequal", asar_binary_wrapper<std::not_equal_to<double>>},
610 : : {"less", asar_binary_wrapper<std::less<double>>},
611 : : {"lessequal", asar_binary_wrapper<std::less_equal<double>>},
612 : : {"greater", asar_binary_wrapper<std::greater<double>>},
613 : : {"greaterequal", asar_binary_wrapper<std::greater_equal<double>>},
614 : :
615 : : {"and", asar_binary_wrapper<std::logical_and<unsigned int>>},
616 : : {"or", asar_binary_wrapper<std::logical_or<unsigned int>>},
617 : : {"nand", asar_binary_wrapper<asar_logical_nand>},
618 : : {"nor", asar_binary_wrapper<asar_logical_nor>},
619 : : {"xor", asar_binary_wrapper<asar_logical_xor>},
620 : :
621 : : {"round", asar_round},
622 : :
623 : : {"sizeof", asar_structsize_wrapper},
624 : : {"objectsize", asar_objectsize_wrapper},
625 : : {"datasize", asar_datasize_wrapper},
626 : :
627 : : {"stringsequal", asar_stringsequal},
628 : : {"stringsequalnocase", asar_stringsequalnocase},
629 : : {"char", asar_char},
630 : : {"stringlength", asar_strlen}
631 : : };
632 : :
633 : : assocarr<double (*)()> functions;
634 : :
635 : : struct funcdat {
636 : : autoptr<char*> name;
637 : : int numargs;
638 : : autoptr<char*> argbuf;//this one isn't used, it's just to free it up
639 : : autoptr<char**> arguments;
640 : : autoptr<char*> content;
641 : : };
642 : : static assocarr<funcdat> user_functions;
643 : :
644 : 22 : static double asar_call_user_function()
645 : : {
646 : 22 : autoarray<string> args;
647 : 22 : funcdat &user_function = user_functions[current_user_function_name];
648 : 22 : string real_content;
649 : :
650 [ - + ]: 22 : while (*str==' ') str++;
651 : 22 : bool has_next = *str != ')';
652 : :
653 [ + + ]: 56 : for (int i=0;i<user_function.numargs;i++)
654 : : {
655 [ - + ]: 34 : if(!has_next)
656 : : {
657 : 0 : asar_throw_error(2, error_type_block, error_id_expected_parameter, current_user_function_name);
658 : : }
659 : 51 : args[i] = copy_arg();
660 : 34 : has_next = has_next_parameter();
661 : : }
662 : :
663 [ - + ]: 22 : if(has_next)
664 : : {
665 : 0 : asar_throw_error(2, error_type_block, error_id_unexpected_parameter, current_user_function_name);
666 : : }
667 : :
668 [ + + ]: 106 : for(int i=0; user_function.content[i]; i++)
669 : : {
670 [ + + ]: 84 : if(!is_ualpha(user_function.content[i]))
671 : : {
672 : 46 : real_content += user_function.content[i];
673 : 46 : continue;
674 : : }
675 : 19 : bool found = false;
676 [ + + ]: 104 : for (int j=0;user_function.arguments[j];j++)
677 : : {
678 : : //this should *always* have a null term or another character after
679 : 66 : bool potential_arg = stribegin(user_function.content+i, user_function.arguments[j]);
680 : 66 : int next_char = i+strlen(user_function.arguments[j]);
681 [ + + + - : 66 : if(potential_arg && !is_ualnum(user_function.content[next_char]))
+ + ]
682 : : {
683 : 34 : real_content += args[j];
684 : 34 : i = next_char - 1;
685 : 17 : found = true;
686 : : }
687 : : }
688 : :
689 [ + + ]: 38 : if(!found){
690 [ + + ]: 40 : for(; is_ualnum(user_function.content[i]); i++){
691 : 36 : real_content += user_function.content[i];
692 : : }
693 : 4 : real_content += user_function.content[i];
694 : : }
695 : : }
696 : 22 : const char * oldstr=str;
697 : 22 : str = (const char *)real_content;
698 : 22 : double result = eval(0);
699 : 22 : str = oldstr;
700 : 22 : return result;
701 : 22 : }
702 : :
703 : 36 : void createuserfunc(const char * name, const char * arguments, const char * content)
704 : : {
705 [ - + ]: 36 : if (!confirmqpar(content)) asar_throw_error(0, error_type_block, error_id_mismatched_parentheses);
706 [ - + ]: 36 : if(functions.exists(name)) //functions holds both types.
707 : : {
708 : 0 : asar_throw_error(0, error_type_block, error_id_function_redefined, name);
709 : : }
710 : 36 : funcdat& user_function=user_functions[name];
711 : 36 : user_function.name= duplicate_string(name);
712 : 36 : user_function.argbuf= duplicate_string(arguments);
713 : 36 : user_function.arguments=split(user_function.argbuf, ',', &(user_function.numargs));
714 : 36 : user_function.content= duplicate_string(content);
715 [ + + ]: 90 : for (int i=0;user_function.arguments[i];i++)
716 : : {
717 [ + + ]: 144 : for(int j=0;user_function.arguments[j];j++)
718 : : {
719 [ + + - + : 90 : if(i!=j && !stricmp(user_function.arguments[i], user_function.arguments[j]))
- + ]
720 : : {
721 : 0 : asar_throw_error(0, error_type_block, error_id_duplicate_param_name, user_function.arguments[i], name);
722 : : }
723 : : }
724 [ - + ]: 54 : if (!confirmname(user_function.arguments[i]))
725 : : {
726 : 0 : user_functions.remove(name);
727 : 0 : asar_throw_error(0, error_type_block, error_id_invalid_param_name);
728 : : }
729 : : }
730 : :
731 : 36 : functions[name] = asar_call_user_function;
732 : 36 : }
733 : :
734 : : inline const long hextable[] = {
735 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
736 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
737 : : -1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,
738 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
739 : : -1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
740 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
741 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
742 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
743 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
744 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
745 : : -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
746 : : };
747 : :
748 : 17024 : static double getnumcore()
749 : : {
750 [ + + ]: 17024 : if (*str=='$')
751 : : {
752 [ - + ]: 4636 : if (!is_xdigit(*++str)) asar_throw_error(2, error_type_block, error_id_invalid_hex_value);
753 : 2318 : uint64_t ret = 0;
754 [ + + ]: 21092 : while (hextable[0 + *str] >= 0) {
755 : 16456 : ret = (ret << 4) | hextable[0 + *str++];
756 : : }
757 : 4636 : return ret;
758 : : }
759 [ + + + + : 12388 : if (is_ualpha(*str) || *str=='.' || *str=='?')
+ + + + ]
760 : : {
761 : 1252 : const char * start=str;
762 [ + + + + : 11104 : while (is_ualnum(*str) || *str == '.') str++;
+ + ]
763 : 1252 : int len=(int)(str-start);
764 [ + + ]: 1288 : while (*str==' ') str++;
765 [ + + ]: 1252 : if (*str=='(')
766 : : {
767 : 788 : str++;
768 : : // RPG Hacker: This is only here to assure that all strings are still
769 : : // alive in memory when we call our functions further down
770 : : double result;
771 : : while (true)
772 : : {
773 [ - + ]: 788 : while (*str==' ') str++;
774 : 788 : string function_name = string(start, len);
775 [ + - + - ]: 788 : if(functions.exists(function_name))
776 : : {
777 : 788 : current_user_function_name = function_name;
778 : 788 : result = functions[function_name]();
779 : : }
780 : : else
781 : : {
782 : 0 : str++;
783 : 0 : break;
784 : : }
785 : :
786 [ + - ]: 782 : if (*str==')')
787 : : {
788 : 782 : str++;
789 : 391 : return result;
790 : : }
791 : 0 : asar_throw_error(2, error_type_block, error_id_malformed_function_call);
792 : 788 : }
793 : :
794 : 0 : asar_throw_error(2, error_type_block, error_id_function_not_found, start);
795 : : }
796 : : else
797 : : {
798 : 464 : foundlabel=true;
799 : :
800 : 232 : const char *old_start = start;
801 : 464 : snes_label label_data = labelval(&start);
802 : 462 : int i=(int)label_data.pos;
803 : 462 : foundlabel_static &= label_data.is_static;
804 [ + + ]: 462 : if(str < start)
805 : : {
806 [ + + ]: 38 : if ((str = strchr(str, '[')))
807 : : {
808 : 32 : string struct_name = substr(old_start, (int)(str - old_start));
809 : 32 : str++;
810 : 32 : i += (int)(eval(0) * object_size(struct_name));
811 : 32 : }
812 : : }
813 : :
814 : 462 : str=start;
815 [ + + ]: 462 : if (i==-1) forwardlabel=true;
816 : 462 : return (int)i&0xFFFFFF;
817 : : }
818 : : }
819 [ + + ]: 11136 : if (*str=='(')
820 : : {
821 : 146 : str++;
822 : 146 : double rval=eval(0);
823 [ - + ]: 146 : if (*str != ')') asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
824 : 146 : str++;
825 : 146 : return rval;
826 : : }
827 [ + + ]: 10990 : if (*str=='%')
828 : : {
829 [ - + - - ]: 24 : if (str[1] != '0' && str[1] != '1') asar_throw_error(2, error_type_block, error_id_invalid_binary_value);
830 : 24 : return strtoull(str+1, const_cast<char**>(&str), 2);
831 : : }
832 [ + + ]: 10966 : if (*str=='\'')
833 : : {
834 [ - + - - ]: 220 : if (!str[1]) asar_throw_error(2, error_type_block, error_id_invalid_character);
835 : : int orig_val;
836 : 220 : str++;
837 : 220 : str += utf8_val(&orig_val, str);
838 [ - + - - ]: 220 : if (orig_val == -1) asar_throw_error(0, error_type_block, error_id_invalid_utf8);
839 [ - + - - ]: 220 : if (*str != '\'') asar_throw_error(2, error_type_block, error_id_invalid_character);
840 : 220 : int64_t rval=thetable.get_val(orig_val);
841 [ - + ]: 220 : if (rval == -1)
842 : : {
843 : : // RPG Hacker: Should be fine to not check return value of codepoint_to_utf8() here, because
844 : : // our error cases above already made sure that orig_val contains valid data at this point.
845 : 0 : string u8_str;
846 : 0 : codepoint_to_utf8(&u8_str, orig_val);
847 : 0 : asar_throw_error(2, error_type_block, error_id_undefined_char, u8_str.data());
848 : 0 : }
849 : 220 : str++;
850 : 220 : return rval;
851 : : }
852 [ + + ]: 10746 : if (is_digit(*str))
853 : : {
854 : 5368 : const char* end = str;
855 [ + + + + : 25034 : while (is_digit(*end) || *end == '.') end++;
+ + ]
856 : 10736 : string number;
857 : 10736 : number.assign(str, (int)(end - str));
858 : 10736 : str = end;
859 : 10736 : return atof(number);
860 : 10736 : }
861 : 10 : asar_throw_error(2, error_type_block, error_id_invalid_number);
862 : 0 : return 0.0;
863 : : }
864 : :
865 : 17050 : inline double sanitize(double val)
866 : : {
867 [ - + ]: 17050 : if (val != val) asar_throw_error(2, error_type_block, error_id_nan);
868 : 17050 : return val;
869 : : }
870 : :
871 : 17068 : static double getnum()
872 : : {
873 [ + + ]: 19390 : while (*str==' ') str++;
874 [ + + ]: 17068 : if(*str == '$') return sanitize(getnumcore()); //optimize for the common case
875 : : #define prefix(sym, func) if (*str == sym) { str+=1; double val=getnum(); return sanitize(func); }
876 : : #define prefix2(sym, sym2, func) if (*str == sym && *(str+1) == sym2) { str+=2; double val=getnum(); return sanitize(func); }
877 : 12432 : prefix('-', -val);
878 : 12396 : prefix('~', ~(int)val);
879 [ + + + + ]: 12392 : prefix2('<', ':', (int)val>>16);
880 : 12388 : prefix('+', val);
881 : : #undef prefix
882 : : #undef prefix2
883 : 12388 : return sanitize(getnumcore());
884 : : }
885 : :
886 : 9636 : int64_t getnum(const char* instr)
887 : : {
888 : 9636 : double num = math(instr);
889 [ + + ]: 9604 : if(num < (double)INT64_MIN) {
890 : 0 : return INT64_MIN;
891 [ + + ]: 9604 : } else if(num > (double)INT64_MAX) {
892 : 0 : return INT64_MAX;
893 : : }
894 : 9604 : return (int64_t)num;
895 : : }
896 : :
897 : : // RPG Hacker: Same function as above, but doesn't truncate our number via int conversion
898 : 1968 : double getnumdouble(const char * instr)
899 : : {
900 : 1968 : return math(instr);
901 : : }
902 : :
903 : :
904 : 2 : static double oper_wrapped_throw(asar_error_id errid)
905 : : {
906 : 2 : asar_throw_error(2, error_type_block, errid);
907 : 0 : return 0.0;
908 : : }
909 : :
910 : 17048 : static double eval(int depth)
911 : : {
912 : 17048 : const char* posneglabel = str;
913 : 17048 : string posnegname = posneglabelname(&posneglabel, false);
914 : :
915 [ + + + + ]: 17048 : if (posnegname.length() > 0)
916 : : {
917 [ + + + - ]: 64 : if (*posneglabel != '\0' && *posneglabel != ')') goto notposneglabel;
918 : :
919 : 28 : str = posneglabel;
920 : :
921 : 28 : foundlabel=true;
922 [ + + ]: 28 : if (*(posneglabel-1) == '+') forwardlabel=true;
923 : 28 : snes_label label_data = labelval(posnegname);
924 : 28 : foundlabel_static &= label_data.is_static;
925 : 28 : return label_data.pos & 0xFFFFFF;
926 : : }
927 : 16984 : notposneglabel:
928 : 34040 : recurseblock rec;
929 : 17020 : double left=getnum();
930 : : double right;
931 [ + + ]: 19324 : while (*str==' ') str++;
932 [ + + + + : 21820 : while (*str && *str != ')' && *str != ','&& *str != ']')
+ + + + ]
933 : : {
934 [ - + ]: 4870 : while (*str==' ') str++;
935 : : #define oper(name, thisdepth, contents) \
936 : : if (!strncmp(str, name, strlen(name))) \
937 : : { \
938 : : if (depth<=thisdepth) \
939 : : { \
940 : : str+=strlen(name); \
941 : : right=eval(thisdepth+1); \
942 : : } \
943 : : else return left; \
944 : : left=contents; \
945 : : continue; \
946 : : }
947 : 4870 : oper("**", 6, pow((double)left, (double)right));
948 : 4858 : oper("*", 5, left*right);
949 [ + + + - : 4828 : oper("/", 5, right != 0.0 ? left / right : oper_wrapped_throw(error_id_division_by_zero));
+ - + + -
+ ]
950 [ - + - - : 4820 : oper("%", 5, right != 0.0 ? fmod((double)left, (double)right) : oper_wrapped_throw(error_id_modulo_by_zero));
- - - - -
- ]
951 : 4820 : oper("+", 4, left+right);
952 : 2878 : oper("-", 4, left-right);
953 [ - + - - : 2352 : oper("<<", 3, right >= 0.0 ? (int64_t)left<<(uint64_t)right : oper_wrapped_throw(error_id_negative_shift));
- - - - -
- ]
954 [ - + - - : 2352 : oper(">>", 3, right >= 0.0 ? (int64_t)left>>(uint64_t)right : oper_wrapped_throw(error_id_negative_shift));
- - - - -
- ]
955 : :
956 : : //these two needed checked early to avoid bitwise from eating a operator
957 [ + + + + : 2355 : oper("&&", 0, (int64_t)left&&(int64_t)right);
+ - + - +
+ ]
958 [ + + + + : 2328 : oper("||", 0, (int64_t)left||(int64_t)right);
+ - + - -
+ ]
959 : 2316 : oper("&", 2, (int64_t)left&(int64_t)right);
960 : 2316 : oper("|", 2, (int64_t)left|(int64_t)right);
961 : 2316 : oper("^", 2, (int64_t)left^(int64_t)right);
962 : :
963 [ + + + - : 2316 : oper(">=", 1, left>=right);
+ - + + ]
964 [ - + - - : 2310 : oper("<=", 1, left<=right);
- - - - ]
965 [ + + + - : 2325 : oper(">", 1, left>right);
+ - + + ]
966 [ + + + - : 2271 : oper("<", 1, left<right);
+ - + + ]
967 [ + + + - : 501 : oper("==", 1, left==right);
+ - + + ]
968 [ + + + - : 18 : oper("!=", 1, left!=right);
+ - + + ]
969 : 12 : asar_throw_error(2, error_type_block, error_id_unknown_operator);
970 : : #undef oper
971 : : }
972 : 8475 : return left;
973 : 17048 : }
974 : :
975 : : //static autoptr<char*> freeme;
976 : 11604 : double math(const char * s)
977 : : {
978 : : //free(freeme);
979 : : //freeme=NULL;
980 : 11604 : foundlabel=false;
981 : 11604 : foundlabel_static=true;
982 : 11604 : forwardlabel=false;
983 : 11604 : str = s;
984 : 11604 : double rval = eval(0);
985 [ - + ]: 11572 : if (*str)
986 : : {
987 [ # # ]: 0 : if (*str == ',') asar_throw_error(2, error_type_block, error_id_invalid_input);
988 : 0 : else asar_throw_error(2, error_type_block, error_id_mismatched_parentheses);
989 : : }
990 : 11572 : return rval;
991 : : }
992 : :
993 : 670 : void initmathcore()
994 : : {
995 : 670 : functions.reset();
996 : 670 : builtin_functions.each([](const char* key, double (*val)()) {
997 : 43550 : functions[key] = val;
998 : 43550 : });
999 : 670 : user_functions.reset();
1000 : 670 : }
1001 : :
1002 : 660 : void deinitmathcore()
1003 : : {
1004 : : //not needed
1005 : 660 : }
|