asar coverage - build #160


src/asar/
File: src/asar/asar_math.cpp
Date: 2024-01-27 01:25:07
Lines:
458/498
92.0%
Functions:
83/102
81.4%
Branches:
469/835
56.2%

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