asar coverage - build #301


src/asar/
File: src/asar/math_functions.cpp
Date: 2025-03-18 20:16:22
Lines:
266/277
96.0%
Functions:
100/108
92.6%
Branches:
415/653
63.6%

Line Branch Exec Source
1 #include "asar.h"
2 #include "assembleblock.h"
3 #include "math_ast.h"
4 #include "platform/file-helpers.h"
5 #include "macro.h"
6 #include <cinttypes>
7
8 namespace {
9 struct cachedfile {
10 string filename;
11 virtual_file_handle filehandle;
12 size_t filesize;
13 bool used;
14 };
15
16 #define numcachedfiles 16
17
18 cachedfile cachedfiles[numcachedfiles];
19 int cachedfileindex = 0;
20
21 // Opens a file, trying to open it from cache first
22
23 68 cachedfile * opencachedfile(string fname, bool should_error)
24 {
25 68 cachedfile * cachedfilehandle = nullptr;
26
27
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 const char* current_file = get_current_file_name();
28
29 // RPG Hacker: Only using a combined path here because that should
30 // hopefully result in a unique string for every file, whereas
31 // fname could be a relative path, which isn't guaranteed to be unique.
32 // Note that this does not affect how we open the file - this is
33 // handled by the filesystem and uses our include paths etc.
34
3/8
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 34 times.
✗ Branch 7 not taken.
68 string combinedname = filesystem->create_absolute_path(dir(current_file), fname);
35
36
2/2
✓ Branch 0 taken 392 times.
✓ Branch 1 taken 20 times.
412 for (int i = 0; i < numcachedfiles; i++)
37 {
38
10/11
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 154 times.
✓ Branch 2 taken 220 times.
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 178 times.
✓ Branch 5 taken 172 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
✓ Branch 8 taken 18 times.
✓ Branch 9 taken 24 times.
✓ Branch 10 taken 172 times.
392 if (cachedfiles[i].used && cachedfiles[i].filename == combinedname)
39 {
40 48 cachedfilehandle = &cachedfiles[i];
41 48 break;
42 }
43 }
44
45
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 48 times.
68 if (cachedfilehandle == nullptr)
46 {
47
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
20 if (cachedfiles[cachedfileindex].used)
48 {
49 filesystem->close_file(cachedfiles[cachedfileindex].filehandle);
50 cachedfiles[cachedfileindex].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
51 cachedfiles[cachedfileindex].used = false;
52 }
53
54 20 cachedfilehandle = &cachedfiles[cachedfileindex];
55 }
56
57
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 if (cachedfilehandle != nullptr)
58 {
59
5/5
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 24 times.
68 if (!cachedfilehandle->used)
60 {
61
2/3
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 cachedfilehandle->filehandle = filesystem->open_file(fname, current_file);
62
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
20 if (cachedfilehandle->filehandle != INVALID_VIRTUAL_FILE_HANDLE)
63 {
64 10 cachedfilehandle->used = true;
65
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
10 cachedfilehandle->filename = combinedname;
66
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
10 cachedfilehandle->filesize = filesystem->get_file_size(cachedfilehandle->filehandle);
67 10 cachedfileindex++;
68 // randomdude999: when we run out of cached files, just start overwriting ones from the start
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (cachedfileindex >= numcachedfiles) cachedfileindex = 0;
70 }
71 }
72 }
73
74
6/8
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 29 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 34 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
68 if ((cachedfilehandle == nullptr || cachedfilehandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) && should_error)
75 {
76 throw_vfile_error(2, asar_get_last_io_error(), fname.data());
77 }
78
79 68 return cachedfilehandle;
80 68 }
81 // closecachedfiles is declared at the end of the file outside namespace{}
82
83
84
85 886 void assert_argc(const std::vector<math_val>& args, int expected_args) {
86
3/3
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 440 times.
✓ Branch 2 taken 440 times.
886 if((int)args.size() != expected_args) {
87 12 throw_err_block(2, err_argument_count, expected_args, (int)args.size());
88 }
89 874 }
90
91 // these templates could be implemented in a fully generic way too (without
92 // specializing per argument count), but that looks a little too cryptic to me.
93 template<math_val (*F)()>
94 68 math_val fixed_arity(const std::vector<math_val>& args) {
95 68 assert_argc(args, 0);
96 68 return F();
97 }
98 template<math_val (*F)(math_val)>
99 1124 math_val fixed_arity(const std::vector<math_val>& args) {
100 1124 assert_argc(args, 1);
101
5/6
✓ Branch 0 taken 278 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 556 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 278 times.
✓ Branch 5 taken 6 times.
1136 return F(args[0]);
102 }
103 template<math_val (*F)(math_val,math_val)>
104 120 math_val fixed_arity(const std::vector<math_val>& args) {
105 120 assert_argc(args, 2);
106
7/11
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 24 times.
✓ Branch 10 taken 6 times.
168 return F(args[0], args[1]);
107 }
108 template<math_val (*F)(math_val,math_val,math_val)>
109 52 math_val fixed_arity(const std::vector<math_val>& args) {
110 52 assert_argc(args, 3);
111
7/14
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 13 times.
✓ Branch 7 taken 13 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 13 times.
✗ Branch 13 not taken.
52 return F(args[0], args[1], args[2]);
112 }
113
114 template<double (*F)(double)>
115 76 math_val fn_unary_real(const std::vector<math_val>& args) {
116
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
76 assert_argc(args, 1);
117
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
52 double val = F(args[0].get_double());
118
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
52 if (val != val) throw_err_block(2, err_nan);
119 88 return val;
120 };
121
122 template<double (*F)(double)>
123 60 math_val fn_rounding(const std::vector<math_val>& args) {
124 60 assert_argc(args, 1);
125 60 double val = F(args[0].get_double());
126 // TODO where should we check this? math_val(double) constructor maybe?
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
60 if (val != val) throw_err_block(2, err_nan);
128
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
60 return math_val(val).get_integer();
129 };
130
131 template<math_binop_type OP>
132 64 math_val math_binop_function(const std::vector<math_val>& args) {
133 64 assert_argc(args, 2);
134
5/11
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 16 times.
✗ Branch 10 not taken.
64 return evaluate_binop(args[0], args[1], OP);
135 }
136
137 template<bool (*F)(bool, bool)>
138 60 math_val math_bool_binop_function(const std::vector<math_val>& args) {
139 60 assert_argc(args, 2);
140 60 return (int64_t)F(args[0].get_bool(), args[1].get_bool());
141 }
142
143
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
6 bool fn_and(bool a, bool b) { return a && b; }
144
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
6 bool fn_or(bool a, bool b) { return a || b; }
145
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
6 bool fn_nand(bool a, bool b) { return !(a && b); }
146
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
6 bool fn_nor(bool a, bool b) { return !(a || b); }
147 6 bool fn_xor(bool a, bool b) { return a ^ b; }
148
149 16 math_val fn_select(math_val cond, math_val a, math_val b) {
150
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 return cond.get_bool() ? a : b;
151 }
152
153 template<int (*F)(int)>
154 200 math_val fn_snes_pc(math_val arg) {
155 200 return (int64_t)F(arg.get_integer());
156 }
157
158 template<int& variable>
159 68 math_val fn_pc_realbase() {
160 68 return (int64_t)variable;
161 }
162 48 int haslabel_always() {
163 48 return 3;
164 }
165 template<int& variable>
166 24 int getlen_pc_realbase(const std::vector<owned_node>& args, bool could_be_bank_ex) {
167 // todo : should this use freespaceid even with base active???
168 24 return getlenforlabel(variable, freespaceid, true);
169 }
170
171 4 math_val fn_bank(math_val arg) {
172 4 return (arg.get_integer() >> 16);
173 }
174 12 int getlen_bank(const std::vector<owned_node>& args, bool could_be_bank_ex) {
175 12 return 1;
176 }
177
178 4 math_val fn_safediv(math_val a, math_val b, math_val default_) {
179
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(b.get_double() == 0.0) return default_;
180
6/10
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
2 return evaluate_binop(a, b, math_binop_type::div);
181 }
182
183 16 math_val fn_not(math_val arg) {
184 16 return (int64_t)!arg.get_bool();
185 }
186
187 template<int (*F)(const char*, const char*)>
188 36 math_val fn_str_eq(math_val va, math_val vb) {
189 36 const string& a = va.get_str();
190 36 const string& b = vb.get_str();
191 36 bool result = F(a.data(), b.data()) == 0;
192 36 return (int64_t)result;
193 }
194
195 24 math_val fn_char(math_val str_, math_val ind_) {
196 24 const string& s = str_.get_str();
197 24 int64_t ind = ind_.get_integer();
198
7/7
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 6 times.
24 if(ind < 0 || ind >= s.length())
199 12 throw_err_block(2, err_oob, (int)ind, s.length());
200 12 return (int64_t)(unsigned char)s[ind];
201 }
202 6 math_val fn_strlen(math_val val) {
203 6 return (int64_t)val.get_str().length();
204 }
205
206 template<int count>
207 80 math_val fn_read(const std::vector<math_val>& args) {
208
9/9
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 17 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 17 times.
80 if(args.size() < 1 || args.size() > 2) {
209 // TODO expected amount should be string to show the range
210
2/3
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
12 throw_err_block(2, err_argument_count, 2, (int)args.size());
211 }
212
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
68 int64_t target = args[0].get_integer();
213 68 int addr = snestopc(target);
214
215
3/3
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 11 times.
68 if(args.size() == 2) {
216
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
24 math_val default_val = args[1];
217
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
24 if(addr < 0) return default_val;
218
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 if(addr + count > romlen_r) return default_val;
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
24 } else {
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
44 if (addr < 0)
221 throw_err_block(2, err_snes_address_doesnt_map_to_rom, (hex((unsigned int)target, 6) + " in read function").data());
222
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
44 else if (addr + count > romlen_r)
223
5/11
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
14 throw_err_block(2, err_snes_address_out_of_bounds, (hex(target, 6) + " in read function").data());
224 }
225
226 40 int64_t value = 0;
227
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 20 times.
104 for(int i = 0; i < count; i++)
228 {
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
64 value |= romdata_r[addr+i] << (8 * i);
230 }
231 40 return value;
232 }
233 template<int count>
234 120 math_val fn_canread(const std::vector<math_val>& args) {
235 120 int64_t length = count;
236 int64_t addr;
237 if(count == 0) {
238 48 assert_argc(args, 2);
239 48 length = args[0].get_integer();
240 48 addr = args[1].get_integer();
241 } else {
242 72 assert_argc(args, 1);
243 72 addr = args[0].get_integer();
244 }
245 120 int addr_pc = snestopc(addr);
246
4/4
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 24 times.
120 if (addr_pc < 0 || addr_pc + length > romlen_r) return (int64_t)0;
247 48 else return (int64_t)1;
248 }
249
250 template<unsigned int count>
251 80 math_val fn_readfile(const std::vector<math_val>& args) {
252
6/9
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 20 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 20 times.
80 if(args.size() < 2 || args.size() > 3) {
253 // TODO expected amount should be string to show the range
254 throw_err_block(2, err_argument_count, 3, (int)args.size());
255 }
256
3/6
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
80 string fname = args[0].get_str();
257
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
80 int64_t offset = args[1].get_integer();
258 80 bool should_error = args.size() == 2;
259
4/6
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
80 cachedfile * fhandle = opencachedfile(fname, should_error);
260
261
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 28 times.
80 if(!should_error) {
262
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
24 math_val default_val = args[2];
263
5/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
24 if(fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return default_val;
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
20 if(offset < 0) return default_val;
265
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
20 if((size_t)offset + count > fhandle->filesize) return default_val;
266
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
24 } else {
267
3/6
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
56 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
268 throw_vfile_error(2, asar_get_last_io_error(), fname.data());
269
6/6
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 8 times.
56 if (offset < 0 || (size_t)offset + count > fhandle->filesize)
270
4/7
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
48 throw_err_block(2, err_file_offset_out_of_bounds, dec(offset).data(), fname.data());
271 }
272
273 44 unsigned char data[4] = { 0, 0, 0, 0 };
274
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
44 filesystem->read_file(fhandle->filehandle, data, offset, count);
275
276 44 int64_t value = 0;
277
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 22 times.
116 for(size_t i = 0; i < count; i++)
278 {
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
72 value |= data[i] << (8 * i);
280 }
281
282 44 return value;
283 80 }
284
285 template<unsigned int count>
286 28 math_val fn_canreadfile(const std::vector<math_val>& args) {
287 28 string fname;
288 28 int64_t length = count;
289 int64_t offset;
290 if(count == 0) {
291
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 assert_argc(args, 3);
292
3/7
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
8 fname = args[0].get_str();
293
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
8 offset = args[1].get_integer();
294
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
8 length = args[2].get_integer();
295 } else {
296
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
20 assert_argc(args, 2);
297
3/7
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
20 fname = args[0].get_str();
298
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
20 offset = args[1].get_integer();
299 }
300
301
4/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
28 cachedfile * fhandle = opencachedfile(fname, false);
302
5/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
28 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) return (int64_t)0;
303
5/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
24 if (offset < 0 || offset + length > (int64_t)fhandle->filesize) return (int64_t)0;
304 16 return (int64_t)1;
305 28 }
306
307 10 math_val fn_min(math_val a, math_val b) {
308
5/9
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
10 math_val cond = evaluate_binop(a, b, math_binop_type::comp_lt);
309
5/7
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
20 return cond.get_bool() ? a : b;
310 10 }
311 10 math_val fn_max(math_val a, math_val b) {
312
5/9
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
10 math_val cond = evaluate_binop(a, b, math_binop_type::comp_gt);
313
5/7
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
20 return cond.get_bool() ? a : b;
314 10 }
315 6 math_val fn_clamp(math_val val, math_val lo, math_val hi) {
316 // compute temp = min(hi, val)
317
5/9
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
6 math_val temp = fn_min(hi, val);
318
6/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
12 return fn_max(lo, temp);
319 6 }
320
321 10 math_val fn_round(math_val val, math_val precision) {
322 // i used to hate the float->str->float approach, but the
323 // alternatives do end up quite messy if implemented properly
324
5/9
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
10 string as_str = ftostrvar(val.get_double(), precision.get_integer());
325
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
10 double res = std::atof(as_str);
326 20 return res;
327 10 }
328 14 math_val fn_isdefined(math_val defname) {
329 14 return (int64_t)defines.exists(defname.get_str());
330 }
331
332 2 math_val fn_filesize(math_val fname) {
333
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 string name = fname.get_str();
334
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 cachedfile * fhandle = opencachedfile(name, false);
335
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
2 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE)
336 throw_vfile_error(2, asar_get_last_io_error(), name.data());
337 4 return (int64_t)fhandle->filesize;
338 2 }
339
340 12 math_val fn_filestatus(math_val fname) {
341
5/9
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
12 cachedfile * fhandle = opencachedfile(fname.get_str(), false);
342
5/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
12 if (fhandle == nullptr || fhandle->filehandle == INVALID_VIRTUAL_FILE_HANDLE) {
343
2/3
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 if (filesystem->get_last_error() == vfe_doesnt_exist)
344 6 return (int64_t)1;
345 else return (int64_t)2;
346 }
347 6 return (int64_t)0;
348 }
349
350 364 math_val fn_sizeof(math_val val) {
351
4/6
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 182 times.
✓ Branch 2 taken 182 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 182 times.
✗ Branch 5 not taken.
364 string symbol = val.get_identifier();
352 // TODO: do we have a better spot where to parse this...?
353
2/2
✓ Branch 0 taken 352 times.
✓ Branch 1 taken 12 times.
364 if(symbol == "..."){
354
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 351 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 175 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
352 if(!inmacro) throw_err_block(2, err_vararg_sizeof_nomacro);
355
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 346 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
350 if(numvarargs == -1) throw_err_block(2, err_macro_not_varadic, "sizeof(...)");
356 346 return (int64_t)numvarargs;
357 }
358
8/12
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
12 if(pass && !structs.exists(symbol)) throw_err_block(2, err_struct_not_found, symbol.data());
359
4/5
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
12 else if(!structs.exists(symbol)) return (int64_t)0;
360
2/3
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 return (int64_t)structs.find(symbol).struct_size;
361 364 }
362
363 28 math_val fn_objectsize(math_val val) {
364
4/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
28 string symbol = val.get_identifier();
365
8/12
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 14 times.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
28 if(pass && !structs.exists(symbol)) throw_err_block(2, err_struct_not_found, symbol.data());
366
4/5
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 14 times.
28 else if(!structs.exists(symbol)) return (int64_t)0;
367
2/3
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
28 return (int64_t)structs.find(symbol).object_size;
368 28 }
369
370 16 math_val fn_datasize(math_val val) {
371
4/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
16 string name = val.get_identifier();
372 int label;
373
7/12
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
10 if(pass && !labels.exists(name)) throw_err_block(2, err_label_not_found, name.data());
374
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 5 times.
10 else if(!labels.exists(name)) return (int64_t)0;
375
2/3
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 snes_label label_data = labels.find(name);
376
377 10 label = label_data.id;
378 10 snes_label selected_label;
379 10 selected_label.id = 0xFFFFFF;
380 10 selected_label.pos = 0xFFFFFF;
381 29 labels.each([&selected_label, label](const char *key, snes_label current_label){
382
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
45 if(label < current_label.id && current_label.id < selected_label.id){
383 8 selected_label = current_label;
384 }
385 30 });
386
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
10 if(selected_label.id == 0xFFFFFF) throw_warning(2, warn_datasize_last_label, name.data());
387
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
10 if(selected_label.pos-label_data.pos > 0xFFFF) throw_warning(2, warn_datasize_exceeds_size, name.data());
388 10 return (int64_t)(selected_label.pos-label_data.pos);
389 10 }
390
391 template<string (*inner)(math_val, int), int defaultprec = 0>
392 90 math_val fn_fmt_num(const std::vector<math_val>& args) {
393
6/9
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 24 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 24 times.
90 if(args.size() < 1 || args.size() > 2) {
394 throw_err_block(2, err_argument_count, 3, (int)args.size());
395 }
396
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
90 math_val num = args[0];
397
4/7
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
✗ Branch 6 not taken.
90 int64_t prec = args.size() == 2 ? args[1].get_integer() : defaultprec;
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
90 if (prec < 0) prec = 0;
399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
90 if (prec > 64) prec = 64;
400
5/10
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 24 times.
✗ Branch 9 not taken.
180 return inner(num, prec);
401 90 }
402
403 10 string fmt_bin(math_val value, int precision) {
404
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 int64_t num = value.get_integer();
405 5 char buffer[65];
406 10 string out;
407
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
10 if (num < 0) {
408
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 out += '-';
409 2 num = -num;
410 // decrement precision because we've output one char already
411 2 precision -= 1;
412
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (precision<0) precision = 0;
413 }
414
2/2
✓ Branch 0 taken 640 times.
✓ Branch 1 taken 10 times.
650 for (int j = 0; j < 64; j++) {
415
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 320 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 320 times.
640 buffer[63 - j] = '0' + ((num & (1ull << j)) >> j);
416 }
417 10 buffer[64] = 0;
418 10 int startidx = 0;
419
6/6
✓ Branch 0 taken 576 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 287 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 287 times.
✓ Branch 5 taken 1 times.
584 while (startidx < 64 - precision && buffer[startidx] == '0') startidx++;
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (startidx == 64) startidx--; // always want to print at least one digit
421
2/3
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 out += (buffer + startidx);
422 20 return out;
423 }
424
425 9 string fmt_dec(math_val value, int precision) {
426
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 int64_t num = value.get_integer();
427 5 char buffer[66];
428
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 snprintf(buffer, 66, "%0*" PRId64, precision, num);
429
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 return string(buffer);
430 }
431
432 12 string fmt_hex(math_val value, int precision) {
433
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 int64_t num = value.get_integer();
434 7 char buffer[66];
435
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 7 times.
12 snprintf(buffer, 66, "%0*" PRIX64, precision, num);
436
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 return string(buffer);
437 }
438
439 14 string fmt_double(math_val value, int precision) {
440 14 return ftostrvar(value.get_double(), precision);
441 }
442
443 } // namespace
444
445 576 void closecachedfiles()
446 {
447
2/2
✓ Branch 0 taken 9216 times.
✓ Branch 1 taken 576 times.
9792 for (int i = 0; i < numcachedfiles; i++)
448 {
449
5/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4075 times.
✓ Branch 2 taken 5136 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 5131 times.
9216 if (cachedfiles[i].used)
450 {
451
4/7
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
10 if (cachedfiles[i].filehandle != INVALID_VIRTUAL_FILE_HANDLE && filesystem)
452 {
453 10 filesystem->close_file(cachedfiles[i].filehandle);
454 10 cachedfiles[i].filehandle = INVALID_VIRTUAL_FILE_HANDLE;
455 }
456
457 10 cachedfiles[i].used = false;
458 }
459 }
460
461 576 cachedfileindex = 0;
462 576 }
463
464 const std::unordered_map<string, math_builtin_function> builtin_functions = {
465 { "sqrt", fn_unary_real<sqrt> },
466 { "sin", fn_unary_real<sin> },
467 { "cos", fn_unary_real<cos> },
468 { "tan", fn_unary_real<tan> },
469 { "asin", fn_unary_real<asin> },
470 { "acos", fn_unary_real<acos> },
471 { "atan", fn_unary_real<atan> },
472 { "arcsin", fn_unary_real<asin> },
473 { "arccos", fn_unary_real<acos> },
474 { "arctan", fn_unary_real<atan> },
475 { "log", fn_unary_real<log> },
476 { "log10", fn_unary_real<log10> },
477 { "log2", fn_unary_real<log2> },
478
479 { "ceil", fn_rounding<ceil> },
480 { "floor", fn_rounding<floor> },
481
482 { "read1", fn_read<1> }, //This handles the safe and unsafe variant
483 { "read2", fn_read<2> },
484 { "read3", fn_read<3> },
485 { "read4", fn_read<4> },
486 { "canread", fn_canread<0> },
487 { "canread1", fn_canread<1> },
488 { "canread2", fn_canread<2> },
489 { "canread3", fn_canread<3> },
490 { "canread4", fn_canread<4> },
491
492 { "readfile1", fn_readfile<1> },
493 { "readfile2", fn_readfile<2> },
494 { "readfile3", fn_readfile<3> },
495 { "readfile4", fn_readfile<4> },
496 { "canreadfile", fn_canreadfile<0> },
497 { "canreadfile1", fn_canreadfile<1> },
498 { "canreadfile2", fn_canreadfile<2> },
499 { "canreadfile3", fn_canreadfile<3> },
500 { "canreadfile4", fn_canreadfile<4> },
501
502 { "filesize", fixed_arity<fn_filesize> },
503 { "getfilestatus", fixed_arity<fn_filestatus> },
504
505 { "defined", fixed_arity<fn_isdefined> },
506
507 { "snestopc", fixed_arity<fn_snes_pc<snestopc>> },
508 { "pctosnes", fixed_arity<fn_snes_pc<pctosnes>> },
509 { "realbase", { fixed_arity<fn_pc_realbase<realsnespos>>, haslabel_always, getlen_pc_realbase<realsnespos> } },
510 { "pc", { fixed_arity<fn_pc_realbase<snespos>>, haslabel_always, getlen_pc_realbase<snespos> } },
511
512 { "max", fixed_arity<fn_max> },
513 { "min", fixed_arity<fn_min> },
514 { "clamp", fixed_arity<fn_clamp> },
515
516 { "safediv", fixed_arity<fn_safediv> },
517
518 { "select", fixed_arity<fn_select> },
519 { "bank", { fixed_arity<fn_bank>, getlen_bank } },
520 { "not", fixed_arity<fn_not> },
521 { "equal", math_binop_function<math_binop_type::comp_eq> },
522 { "notequal", math_binop_function<math_binop_type::comp_ne> },
523 { "less", math_binop_function<math_binop_type::comp_lt> },
524 { "lessequal", math_binop_function<math_binop_type::comp_le> },
525 { "greater", math_binop_function<math_binop_type::comp_gt> },
526 { "greaterequal", math_binop_function<math_binop_type::comp_ge> },
527
528 { "and", math_bool_binop_function<fn_and> },
529 { "or", math_bool_binop_function<fn_or> },
530 { "nand", math_bool_binop_function<fn_nand> },
531 { "nor", math_bool_binop_function<fn_nor> },
532 { "xor", math_bool_binop_function<fn_xor> },
533
534 { "round", fixed_arity<fn_round> },
535
536 { "sizeof", fixed_arity<fn_sizeof> },
537 { "objectsize", fixed_arity<fn_objectsize> },
538 { "datasize", { fixed_arity<fn_datasize>, haslabel_always } },
539
540 { "stringsequal", fixed_arity<fn_str_eq<strcmp>> },
541 { "stringsequalnocase", fixed_arity<fn_str_eq<stricmp>> },
542 { "char", fixed_arity<fn_char> },
543 { "stringlength", fixed_arity<fn_strlen> },
544
545 { "bin", fn_fmt_num<fmt_bin> },
546 { "dec", fn_fmt_num<fmt_dec> },
547 { "hex", fn_fmt_num<fmt_hex> },
548 { "double", fn_fmt_num<fmt_double, 5> },
549 };
550