asar coverage - build #80


src/asar/
File: src/asar/main.cpp
Date: 2024-01-19 04:38:38
Lines:
470/565
83.2%
Functions:
30/38
78.9%
Branches:
462/679
68.0%

Line Branch Exec Source
1 // "because satanism is best defeated by summoning a bigger satan"
2 // ~Alcaro, 2019 (discussing Asar)
3 #include "addr2line.h"
4 #include "asar.h"
5 #include "virtualfile.h"
6 #include "platform/file-helpers.h"
7 #include "assembleblock.h"
8 #include "asar_math.h"
9 #include "macro.h"
10 #include <ctime>
11 // randomdude999: remember to also update the .rc files (in res/windows/) when changing this.
12 // Couldn't find a way to automate this without shoving the version somewhere in the CMake files
13 const int asarver_maj=2;
14 const int asarver_min=0;
15 const int asarver_bug=0;
16 const bool asarver_beta=true;
17
18 #ifdef _I_RELEASE
19 extern char blockbetareleases[(!asarver_beta)?1:-1];
20 #endif
21 #ifdef _I_DEBUG
22 extern char blockreleasedebug[(asarver_beta)?1:-1];
23 #endif
24
25 unsigned const char * romdata_r;
26 int romlen_r;
27
28 int pass;
29
30 int optimizeforbank=-1;
31 int optimize_dp = optimize_dp_flag::NONE;
32 int dp_base = 0;
33 int optimize_address = optimize_address_flag::DEFAULT;
34
35 autoarray<callstack_entry> callstack;
36
37 bool errored=false;
38 bool ignoretitleerrors=false;
39
40 volatile int recursioncount=0;
41
42 virtual_filesystem* filesystem = nullptr;
43
44 AddressToLineMapping addressToLineMapping;
45
46 int get_version_int()
47 {
48 return asarver_maj * 10000 + asarver_min * 100 + asarver_bug;
49 }
50
51 58 bool setmapper()
52 {
53 int maxscore=-99999;
54 mapper_t bestmap=lorom;
55 58 mapper_t maps[]={lorom, hirom, exlorom, exhirom};
56
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 58 times.
290 for (size_t mapid=0;mapid<sizeof(maps)/sizeof(maps[0]);mapid++)
57 {
58 232 mapper=maps[mapid];
59 int score=0;
60 int highbits=0;
61 bool foundnull=false;
62
2/2
✓ Branch 0 taken 4872 times.
✓ Branch 1 taken 232 times.
5104 for (int i=0;i<21;i++)
63 {
64 4872 unsigned char c=romdata[snestopc(0x00FFC0+i)];
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4872 times.
4872 if (foundnull && c) score-=4;//according to some documents, NUL terminated names are possible - but they shouldn't appear in the middle of the name
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4872 times.
4872 if (c>=128) highbits++;
67
2/2
✓ Branch 0 taken 840 times.
✓ Branch 1 taken 4032 times.
4872 else if (is_upper(c)) score+=3;
68
2/2
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 3696 times.
4032 else if (c==' ') score+=2;
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3696 times.
3696 else if (is_digit(c)) score+=1;
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3696 times.
3696 else if (is_lower(c)) score+=1;
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3696 times.
3696 else if (c=='-') score+=1;
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3696 times.
3696 else if (!c) foundnull=true;
73 else score-=3;
74 }
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
232 if (highbits>0 && highbits<=14) score-=21;//high bits set on some, but not all, bytes = unlikely to be a ROM
76
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 174 times.
232 if ((romdata[snestopc(0x00FFDE)]^romdata[snestopc(0x00FFDC)])!=0xFF ||
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 (romdata[snestopc(0x00FFDF)]^romdata[snestopc(0x00FFDD)])!=0xFF) score=-99999;//checksum doesn't match up to 0xFFFF? Not a ROM.
78 //too lazy to check the real checksum
79
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 174 times.
232 if (score>maxscore)
80 {
81 maxscore=score;
82 bestmap=mapper;
83 }
84 }
85 58 mapper=bestmap;
86
87 //detect oddball mappers
88 58 int mapperbyte=romdata[snestopc(0x00FFD5)];
89 58 int romtypebyte=romdata[snestopc(0x00FFD6)];
90
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 if (mapper==lorom)
91 {
92
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
58 if (mapperbyte==0x23 && (romtypebyte==0x32 || romtypebyte==0x34 || romtypebyte==0x35)) mapper=sa1rom;
93 }
94 58 return (maxscore>=0);
95 }
96
97
98 // TODO: Make this accessible to the DLL interface.
99 // Will require lib API addition.
100 bool simple_callstacks = true;
101
102 // Shortens target_path to a relative path, but only if it resides
103 // within base_path or a child directory of it.
104 272 string shorten_to_relative_path(const char* base_path, const char* target_path)
105 {
106
2/2
✓ Branch 0 taken 270 times.
✓ Branch 1 taken 2 times.
272 if (stribegin(target_path, base_path)) target_path += strlen(base_path);
107 272 return target_path;
108 }
109
110 272 string get_top_level_directory()
111 {
112 136 string top_level_file_dir;
113
1/2
✓ Branch 0 taken 272 times.
✗ Branch 1 not taken.
272 for (int i = 0; i < callstack.count; ++i)
114 {
115
1/2
✓ Branch 0 taken 272 times.
✗ Branch 1 not taken.
272 if (callstack[i].type == callstack_entry_type::FILE)
116 {
117
1/2
✓ Branch 0 taken 272 times.
✗ Branch 1 not taken.
272 top_level_file_dir = dir(callstack[i].content);
118 272 break;
119 }
120 }
121 272 return top_level_file_dir;
122 }
123
124 230 string generate_call_details_string(const char* current_block, const char* current_call, int indentation, bool add_lines)
125 {
126 115 string e;
127
2/2
✓ Branch 0 taken 206 times.
✓ Branch 1 taken 24 times.
230 if (current_block != nullptr || current_call != nullptr)
128 {
129 103 string indent;
130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if (add_lines) indent += "|";
131
2/2
✓ Branch 0 taken 824 times.
✓ Branch 1 taken 206 times.
1030 for (; indentation > 0; --indentation) indent += " ";
132
133
1/2
✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
412 if (current_block != nullptr) e += STR "\n"+indent+"in block: ["+current_block+"]";
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
206 if (current_call != nullptr) e += STR "\n"+indent+"in macro call: [%"+current_call+"]";
135 206 }
136 230 return e;
137 }
138
139 272 string get_pretty_filename(const char* current_file)
140 {
141 // RPG Hacker: One could make an argument that we shouldn't shorten paths
142 // here, since some IDEs support jumping to files by double-clicking their
143 // paths. However, AFAIK, no IDE supports this for Asar yet, and if it's
144 // ever desired, we could just make it a command line option. Until then,
145 // I think it's more important to optimize for pretty command line display.
146 272 return shorten_to_relative_path(get_top_level_directory(), current_file);
147 }
148
149 272 string generate_filename_and_line(const char* current_file, int current_line_no)
150 {
151 408 return STR current_file
152
6/6
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 260 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 260 times.
✓ Branch 5 taken 12 times.
544 + (current_line_no>=0?STR ":"+dec(current_line_no+1):"");
153 }
154
155 string format_stack_line(const printable_callstack_entry& entry, int stack_frame_index)
156 {
157 string indent = "\n| ";
158 indent += dec(stack_frame_index);
159 indent += ": ";
160 // RPG Hacker: We'll probably never have a call stack in the
161 // hundreds even, so this very specific, lazy solution suffices.
162 if (stack_frame_index < 100) indent += " ";
163 if (stack_frame_index < 10) indent += " ";
164 return indent
165 + generate_filename_and_line(entry.prettypath, entry.lineno)
166 + entry.details;
167 }
168
169 void push_stack_line(autoarray<printable_callstack_entry>* out, const char* current_file, const char* current_block, const char* current_call, int current_line_no, int indentation, bool add_lines)
170 {
171 printable_callstack_entry new_entry;
172 new_entry.fullpath = current_file;
173 new_entry.prettypath = get_pretty_filename(current_file);
174 new_entry.lineno = current_line_no;
175 new_entry.details = generate_call_details_string(current_block, current_call, indentation, add_lines).raw();
176 out->append(new_entry);
177 }
178
179 230 void get_current_line_details(string* location, string* details, bool exclude_block)
180 {
181 const char* current_file = nullptr;
182 const char* current_block = nullptr;
183 const char* current_call = nullptr;
184 int current_line_no = -1;
185
1/2
✓ Branch 0 taken 772 times.
✗ Branch 1 not taken.
772 for (int i = callstack.count-1; i >= 0 ; --i)
186 {
187
3/5
✓ Branch 0 taken 230 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 218 times.
✓ Branch 3 taken 324 times.
✗ Branch 4 not taken.
772 switch (callstack[i].type)
188 {
189 case callstack_entry_type::FILE:
190 current_file = callstack[i].content;
191
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 208 times.
230 if (exclude_block) current_block = nullptr;
192 230 *location = generate_filename_and_line(get_pretty_filename(current_file), current_line_no);
193 230 *details = generate_call_details_string(current_block, current_call, 4, false);
194 230 return;
195 case callstack_entry_type::MACRO_CALL:
196 if (current_call == nullptr) current_call = callstack[i].content;
197 break;
198 218 case callstack_entry_type::LINE:
199
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 164 times.
218 if (current_block == nullptr && current_call == nullptr) current_block = callstack[i].content;
200
1/2
✓ Branch 0 taken 218 times.
✗ Branch 1 not taken.
436 if (current_line_no == -1) current_line_no = callstack[i].lineno;
201 break;
202 324 case callstack_entry_type::BLOCK:
203
2/2
✓ Branch 0 taken 164 times.
✓ Branch 1 taken 160 times.
324 if (current_block == nullptr) current_block = callstack[i].content;
204 break;
205 }
206 }
207 *location = "";
208 *details = "";
209 }
210
211 void get_full_printable_callstack(autoarray<printable_callstack_entry>* out, int indentation, bool add_lines)
212 {
213 out->reset();
214 const char* current_file = nullptr;
215 const char* current_block = nullptr;
216 const char* current_call = nullptr;
217 int current_line_no = -1;
218 for (int i = 0; i < callstack.count; ++i)
219 {
220 switch (callstack[i].type)
221 {
222 case callstack_entry_type::FILE:
223 if (current_file != nullptr)
224 {
225 push_stack_line(out, current_file, current_block, current_call, current_line_no, indentation, add_lines);
226 }
227 current_file = callstack[i].content;
228 current_block = nullptr;
229 current_call = nullptr;
230 current_line_no = -1;
231 break;
232 case callstack_entry_type::MACRO_CALL:
233 current_block = nullptr;
234 current_call = callstack[i].content;
235 break;
236 case callstack_entry_type::LINE:
237 current_line_no = callstack[i].lineno;
238 current_block = callstack[i].content;
239 break;
240 case callstack_entry_type::BLOCK:
241 current_block = callstack[i].content;
242 break;
243 }
244 }
245 }
246
247 string get_full_callstack()
248 {
249 autoarray<printable_callstack_entry> printable_stack;
250 get_full_printable_callstack(&printable_stack, 12, true);
251
252 string e;
253 if (printable_stack.count > 0)
254 {
255 e += "\nFull call stack:";
256 for (int i = printable_stack.count-1; i >= 0; --i)
257 {
258 e += format_stack_line(printable_stack[i], i);
259 }
260 }
261 return e;
262 }
263
264 // RPG Hacker: This function essetially replicates classic Asar behavior
265 // of only printing a single macro call below the current level.
266 230 string get_simple_callstack()
267 {
268 int i;
269 const char* current_call = nullptr;
270
2/2
✓ Branch 0 taken 22838 times.
✓ Branch 1 taken 188 times.
23026 for (i = callstack.count-1; i >= 0 ; --i)
271 {
272
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 22796 times.
22838 if (callstack[i].type == callstack_entry_type::MACRO_CALL)
273 {
274 current_call = callstack[i].content;
275 42 break;
276 }
277 }
278
279 const char* current_file = nullptr;
280 int current_line_no = -1;
281
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 188 times.
230 if (current_call != nullptr)
282 {
283 bool stop = false;
284
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 for (int j = i-1; j >= 0 ; --j)
285 {
286
3/4
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 84 times.
168 switch (callstack[j].type)
287 {
288 42 case callstack_entry_type::FILE:
289
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (current_file != nullptr)
290 {
291 stop = true;
292 break;
293 }
294 current_file = callstack[j].content;
295 42 break;
296 case callstack_entry_type::MACRO_CALL:
297 stop = true;
298 break;
299 42 case callstack_entry_type::LINE:
300
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
84 if (current_line_no == -1) current_line_no = callstack[j].lineno;
301 break;
302 case callstack_entry_type::BLOCK:
303 break;
304 }
305
306
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 42 times.
168 if (current_file != nullptr && current_line_no != -1) stop = true;
307
308
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 if (stop) break;
309 }
310 }
311
312 115 string e;
313
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 188 times.
230 if (current_call != nullptr && current_file != nullptr)
314 {
315
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
84 e += STR "\n called from: " + generate_filename_and_line(get_pretty_filename(current_file), current_line_no)
316 84 + ": [%" + current_call + "]";
317 }
318 230 return e;
319 }
320
321 230 string get_callstack()
322 {
323
1/2
✓ Branch 0 taken 230 times.
✗ Branch 1 not taken.
230 if (simple_callstacks)
324 230 return get_simple_callstack();
325 else
326 return get_full_callstack();
327 }
328
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 asar_error_id vfile_error_to_error_id(virtual_file_error vfile_error)
330 {
331 switch (vfile_error)
332 {
333 case vfe_doesnt_exist:
334 return error_id_file_not_found;
335 case vfe_access_denied:
336 return error_id_failed_to_open_file_access_denied;
337 case vfe_not_regular_file:
338 return error_id_failed_to_open_not_regular_file;
339 case vfe_unknown:
340 case vfe_none:
341 case vfe_num_errors:
342 return error_id_failed_to_open_file;
343 }
344
345 return error_id_failed_to_open_file;
346 }
347
348 18 virtual_file_error asar_get_last_io_error()
349 {
350
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (filesystem != nullptr)
351 {
352 18 return filesystem->get_last_error();
353 }
354
355 return vfe_unknown;
356 }
357
358 static bool freespaced;
359 558 static int getlenforlabel(snes_label thislabel, bool exists)
360 {
361 558 unsigned int bank = thislabel.pos>>16;
362 558 unsigned int word = thislabel.pos&0xFFFF;
363 558 unsigned int relaxed_bank = optimizeforbank < 0 ? 0 : optimizeforbank;
364
2/2
✓ Branch 0 taken 466 times.
✓ Branch 1 taken 92 times.
558 if (!exists)
365 {
366 return 2;
367 }
368
5/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 454 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
466 else if((optimize_dp == optimize_dp_flag::RAM) && bank == 0x7E && (word-dp_base < 0x100))
369 {
370 return 1;
371 }
372
6/8
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 434 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✓ Branch 7 taken 18 times.
460 else if(optimize_dp == optimize_dp_flag::ALWAYS && (bank == 0x7E || !(bank & 0x40)) && (word-dp_base < 0x100))
373 {
374 return 1;
375 }
376
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 430 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
442 else if (optimize_address == optimize_address_flag::RAM && bank == 0x7E && word < 0x2000)
377 {
378 return 2;
379 }
380
8/10
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 412 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✓ Branch 9 taken 6 times.
436 else if (optimize_address == optimize_address_flag::MIRRORS && (bank == relaxed_bank || (!(bank & 0x40) && !(relaxed_bank & 0x40))) && word < 0x2000)
381 {
382 return 2;
383 }
384
5/8
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 412 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
430 else if (optimize_address == optimize_address_flag::MIRRORS && !(bank & 0x40) && !(relaxed_bank & 0x40) && word < 0x8000)
385 {
386 return 2;
387 }
388
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 418 times.
430 else if (optimizeforbank>=0)
389 {
390
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (thislabel.freespace_id > 0) return 3;
391
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 else if (bank==(unsigned int)optimizeforbank) return 2;
392 12 else return 3;
393 }
394
3/4
✓ Branch 0 taken 380 times.
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 380 times.
418 else if (thislabel.freespace_id > 0 || freespaceid > 0)
395 {
396 // TODO: check whether they're pinned to the same bank
397
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 18 times.
38 if (thislabel.freespace_id != freespaceid) return 3;
398 20 else return 2;
399 }
400
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 20 times.
380 else if (bank != snespos >> 16){ return 3; }
401 360 else { return 2;}
402 }
403
404
405 3260 bool is_hex_constant(const char* str){
406
2/2
✓ Branch 0 taken 2772 times.
✓ Branch 1 taken 488 times.
3260 if (*str=='$')
407 {
408 2772 str++;
409
2/2
✓ Branch 0 taken 9828 times.
✓ Branch 1 taken 2772 times.
12600 while(is_xdigit(*str)) {
410 9828 str++;
411 }
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2772 times.
2772 if(*str=='\0'){
413 return true;
414 }
415 }
416 return false;
417 }
418
419 2996 int getlen(const char * orgstr, bool optimizebankextraction)
420 {
421 2996 const char * str=orgstr;
422 2996 freespaced=false;
423
424 2996 const char* posneglabel = str;
425 2996 string posnegname = posneglabelname(&posneglabel, false);
426
427
2/2
✓ Branch 0 taken 2936 times.
✓ Branch 1 taken 60 times.
2996 if (posnegname.length() > 0)
428 {
429
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (*posneglabel != '\0') goto notposneglabel;
430
431
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 20 times.
100 if (!pass) return 2;
432 20 snes_label label_data;
433 // RPG Hacker: Umm... what kind of magic constant is this?
434 40 label_data.pos = 31415926;
435
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 bool found = labelval(posnegname, &label_data);
436 40 return getlenforlabel(label_data, found);
437 }
438 2936 notposneglabel:
439 int len=0;
440
2/2
✓ Branch 0 taken 3076 times.
✓ Branch 1 taken 2936 times.
6012 while (*str)
441 {
442 int thislen=0;
443 3076 bool maybebankextraction=(str==orgstr);
444
2/2
✓ Branch 0 taken 2358 times.
✓ Branch 1 taken 718 times.
3076 if (*str=='$')
445 {
446 2358 str++;
447 int i;
448
2/2
✓ Branch 0 taken 9336 times.
✓ Branch 1 taken 2358 times.
11694 for (i=0;is_xdigit(str[i]);i++);
449 //if (i&1) warn(S dec(i)+"-digit hex value");//blocked in getnum instead
450 2358 thislen=(i+1)/2;
451 2358 str+=i;
452 }
453
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 718 times.
718 else if (*str=='%')
454 {
455 str++;
456 int i;
457 for (i=0;str[i]=='0' || str[i]=='1';i++);
458 //if (i&7) warn(S dec(i)+"-digit binary value");
459 thislen=(i+7)/8;
460 str+=i;
461 }
462
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 718 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
718 else if (str[0]=='\'' && str[2]=='\'')
463 {
464 thislen=1;
465 str+=3;
466 }
467
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 618 times.
718 else if (is_digit(*str))
468 {
469 100 int val=strtol(str, const_cast<char**>(&str), 10);
470
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 if (val>=0) thislen=1;
471
2/2
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 18 times.
100 if (val>=256) thislen=2;
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (val>=65536) thislen=3;
473 }
474
4/6
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 518 times.
✓ Branch 2 taken 100 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 100 times.
✗ Branch 5 not taken.
618 else if (is_ualpha(*str) || *str=='.' || *str=='?')
475 {
476 259 snes_label thislabel;
477
1/2
✓ Branch 0 taken 518 times.
✗ Branch 1 not taken.
518 bool exists=labelval(&str, &thislabel);
478 518 thislen=getlenforlabel(thislabel, exists);
479 }
480 100 else str++;
481
2/2
✓ Branch 0 taken 310 times.
✓ Branch 1 taken 2766 times.
3076 if (optimizebankextraction && maybebankextraction &&
482
3/6
✓ Branch 0 taken 310 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 310 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 310 times.
✗ Branch 5 not taken.
310 (!strcmp(str, ">>16") || !strcmp(str, "/65536") || !strcmp(str, "/$10000")))
483 return 1;
484 if (thislen>len) len=thislen;
485 }
486 return len;
487 2996 }
488
489 struct strcompare {
490 bool operator() (const char * lhs, const char * rhs) const
491 {
492 return strcmp(lhs, rhs)<0;
493 }
494 };
495
496 struct stricompare {
497 bool operator() (const char * lhs, const char * rhs) const
498 {
499 return stricmp(lhs, rhs)<0;
500 }
501 };
502
503 struct sourcefile {
504 char *data;
505 char** contents;
506 int numlines;
507 };
508
509 static assocarr<sourcefile> filecontents;
510 assocarr<string> defines;
511 // needs to be separate because defines is reset between parsing arguments and patching
512 assocarr<string> clidefines;
513 assocarr<string> builtindefines;
514
515 2552 bool validatedefinename(const char * name)
516 {
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2552 times.
2552 if (!name[0]) return false;
518
2/2
✓ Branch 0 taken 26818 times.
✓ Branch 1 taken 2552 times.
29370 for (int i = 0;name[i];i++)
519 {
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26818 times.
26818 if (!is_ualnum(name[i])) return false;
521 }
522
523 return true;
524 }
525
526 59232 void resolvedefines(string& out, const char * start)
527 {
528 27366 recurseblock rec;
529 const char * here=start;
530
2/2
✓ Branch 0 taken 48850 times.
✓ Branch 1 taken 10380 times.
59230 if (!strchr(here, '!'))
531 {
532 48850 out += here;
533 return;
534 }
535
2/2
✓ Branch 0 taken 70812 times.
✓ Branch 1 taken 10350 times.
81162 while (*here)
536 {
537
4/4
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 70764 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 42 times.
70812 if (here[0] == '\\' && here[1] == '\\')
538 {
539 // allow using \\ as escape sequence
540
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (in_macro_def > 0) out += "\\";
541 6 out += "\\";
542 6 here += 2;
543 }
544
3/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 70764 times.
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
70806 else if (here[0] == '\\' && here[1] == '!')
545 {
546 // allow using \! to escape !
547
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 30 times.
42 if (in_macro_def > 0) out += "\\";
548 42 out+="!";
549 42 here += 2;
550 }
551
2/2
✓ Branch 0 taken 11130 times.
✓ Branch 1 taken 59634 times.
70764 else if (*here=='!')
552 {
553
9/10
✓ Branch 0 taken 5406 times.
✓ Branch 1 taken 5724 times.
✓ Branch 2 taken 3324 times.
✓ Branch 3 taken 2082 times.
✓ Branch 4 taken 2526 times.
✓ Branch 5 taken 798 times.
✓ Branch 6 taken 108 times.
✓ Branch 7 taken 2418 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 108 times.
11130 bool first=(here==start || (here>=start+4 && here[-1]==' ' && here[-2]==':' && here[-3]==' '));//check if it's the start of a block
554 5565 string defname;
555 11130 here++;
556
557 int depth = 0;
558
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 11130 times.
11244 for (const char* depth_str = here; *depth_str=='^'; depth_str++)
559 {
560 114 depth++;
561 }
562 11130 here += depth;
563
564
2/2
✓ Branch 0 taken 1320 times.
✓ Branch 1 taken 9810 times.
11130 if (depth != in_macro_def)
565 {
566 1320 out += '!';
567
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 1320 times.
1386 for (int i=0; i < depth; ++i) out += '^';
568
3/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1302 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
1320 if (depth > in_macro_def) asar_throw_error(0, error_type_line, error_id_invalid_depth_resolve, "define", "define", depth, in_macro_def);
569 continue;
570 1302 }
571
572
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 9702 times.
9810 if (*here=='{')
573 {
574 108 here++;
575 54 string unprocessedname;
576 int braces=1;
577 while (true)
578 {
579
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 1080 times.
1152 if (*here=='{') braces++;
580
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 972 times.
1152 if (*here=='}') braces--;
581
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1152 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1152 if (!*here) asar_throw_error(0, error_type_line, error_id_mismatched_braces);
582
2/2
✓ Branch 0 taken 1044 times.
✓ Branch 1 taken 108 times.
1152 if (!braces) break;
583 1044 unprocessedname+=*here++;
584 }
585
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 here++;
586
1/2
✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
108 resolvedefines(defname, unprocessedname);
587
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
108 if (!validatedefinename(defname)) asar_throw_error(0, error_type_line, error_id_invalid_define_name);
588 108 }
589 else
590 {
591
2/2
✓ Branch 0 taken 49794 times.
✓ Branch 1 taken 9702 times.
59496 while (is_ualnum(*here)) defname+=*here++;
592 }
593
594
2/2
✓ Branch 0 taken 5214 times.
✓ Branch 1 taken 4596 times.
9810 if (first)
595 {
596 enum {
597 null,
598 append,
599 expand,
600 domath,
601 setifnotset,
602 } mode;
603 if(0);
604
2/2
✓ Branch 0 taken 492 times.
✓ Branch 1 taken 4722 times.
5214 else if (stribegin(here, " = ")) { here+=3; mode=null; }
605
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 4620 times.
4722 else if (stribegin(here, " += ")) { here+=4; mode=append; }
606
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 4566 times.
4620 else if (stribegin(here, " := ")) { here+=4; mode=expand; }
607
2/2
✓ Branch 0 taken 1926 times.
✓ Branch 1 taken 2640 times.
4566 else if (stribegin(here, " #= ")) { here+=4; mode=domath; }
608
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2634 times.
2640 else if (stribegin(here, " ?= ")) { here+=4; mode=setifnotset; }
609 2634 else goto notdefineset;
610 1290 string val;
611
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 2412 times.
2580 if (*here=='"')
612 {
613 168 here++;
614 while (true)
615 {
616
2/2
✓ Branch 0 taken 684 times.
✓ Branch 1 taken 174 times.
858 if (*here=='"')
617 {
618
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
174 if (!here[1] || here[1]==' ') break;
619
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 else if (here[1]=='"') here++;
620 else asar_throw_error(0, error_type_line, error_id_broken_define_declaration);
621 }
622 690 val+=*here++;
623 }
624 168 here++;
625 }
626 else
627 {
628
3/4
✓ Branch 0 taken 16578 times.
✓ Branch 1 taken 2412 times.
✓ Branch 2 taken 16578 times.
✗ Branch 3 not taken.
18990 while (*here && *here!=' ') val+=*here++;
629 }
630 //if (strqchr(val.data(), ';')) *strqchr(val.data(), ';')=0;
631
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2580 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2580 if (*here && !stribegin(here, " : ")) asar_throw_error(0, error_type_line, error_id_broken_define_declaration);
632 // RPG Hacker: Is it really a good idea to normalize
633 // the content of defines? That kinda violates their
634 // functionality as a string replacement mechanism.
635
1/2
✓ Branch 0 taken 2580 times.
✗ Branch 1 not taken.
2580 val.qnormalize();
636
637 // RPG Hacker: throw an error if we're trying to overwrite built-in defines.
638
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2574 times.
2580 if (builtindefines.exists(defname))
639 {
640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 asar_throw_error(0, error_type_line, error_id_overriding_builtin_define, defname.data());
641 }
642
643
5/5
✓ Branch 0 taken 486 times.
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 1926 times.
✓ Branch 4 taken 6 times.
2574 switch (mode)
644 {
645 case null:
646 {
647
1/2
✓ Branch 0 taken 486 times.
✗ Branch 1 not taken.
486 defines.create(defname) = val;
648 break;
649 }
650 case append:
651 {
652
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
102 if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data());
653 102 string oldval = defines.find(defname);
654
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 val=oldval+val;
655
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 defines.create(defname) = val;
656 break;
657 102 }
658 27 case expand:
659 {
660 27 string newval;
661
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 resolvedefines(newval, val);
662
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 defines.create(defname) = newval;
663 break;
664 54 }
665 963 case domath:
666 {
667 963 string newval;
668
1/2
✓ Branch 0 taken 1926 times.
✗ Branch 1 not taken.
1926 resolvedefines(newval, val);
669
1/2
✓ Branch 0 taken 1926 times.
✗ Branch 1 not taken.
1926 double num= getnumdouble(newval);
670
5/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1914 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
1926 if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_line, error_id_define_label_math);
671
1/2
✓ Branch 0 taken 1920 times.
✗ Branch 1 not taken.
1920 defines.create(defname) = ftostr(num);
672 break;
673 1926 }
674 case setifnotset:
675 {
676
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 if (!defines.exists(defname)) defines.create(defname) = val;
677 break;
678 }
679 }
680 2580 }
681 else
682 {
683 4596 notdefineset:
684
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 7200 times.
7230 if (!defname) out+="!";
685 else
686 {
687
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7200 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7200 if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data());
688 else {
689 7200 string thisone = defines.find(defname);
690
1/2
✓ Branch 0 taken 7200 times.
✗ Branch 1 not taken.
7200 resolvedefines(out, thisone);
691 7200 }
692 }
693 }
694 11130 }
695 59634 else out+=*here++;
696 }
697
4/6
✓ Branch 0 taken 10350 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 10344 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
10350 if (!confirmquotes(out)) { asar_throw_error(0, error_type_null, error_id_mismatched_quotes); out = ""; }
698 59230 }
699
700 bool moreonline;
701 bool asarverallowed = false;
702
703 48352 void assembleline(const char * fname, int linenum, const char * line, int& single_line_for_tracker)
704 {
705 21926 recurseblock rec;
706 48352 bool moreonlinetmp=moreonline;
707 // randomdude999: redundant, assemblefile already converted the path to absolute
708 //string absolutepath = filesystem->create_absolute_path("", fname);
709 21926 string absolutepath = fname;
710 48352 single_line_for_tracker = 1;
711 try
712 {
713 21926 string out=line;
714
1/2
✓ Branch 0 taken 48352 times.
✗ Branch 1 not taken.
48352 out.qreplace(": :", ": :");
715
1/2
✓ Branch 0 taken 48352 times.
✗ Branch 1 not taken.
48352 autoptr<char**> blocks=qsplitstr(out.temp_raw(), " : ");
716 48352 moreonline=true;
717
2/2
✓ Branch 0 taken 50080 times.
✓ Branch 1 taken 42846 times.
92926 for (int block=0;moreonline;block++)
718 {
719 50080 moreonline=(blocks[block+1] != nullptr);
720 try
721 {
722 50080 string stripped_block = strip_whitespace(blocks[block]);
723
724 22790 callstack_push cs_push(callstack_entry_type::BLOCK, stripped_block);
725
726
2/2
✓ Branch 0 taken 44280 times.
✓ Branch 1 taken 5800 times.
50080 assembleblock(stripped_block, single_line_for_tracker);
727
2/2
✓ Branch 0 taken 44276 times.
✓ Branch 1 taken 4 times.
44280 checkbankcross();
728 55884 }
729
2/2
✓ Branch 0 taken 5506 times.
✓ Branch 1 taken 298 times.
5804 catch (errblock&) {}
730
2/2
✓ Branch 0 taken 29272 times.
✓ Branch 1 taken 15302 times.
44574 if (blocks[block][0]!='\0') asarverallowed=false;
731
2/2
✓ Branch 0 taken 42516 times.
✓ Branch 1 taken 2058 times.
44574 if(single_line_for_tracker == 1) single_line_for_tracker = 0;
732 }
733 53858 }
734
1/2
✓ Branch 0 taken 5506 times.
✗ Branch 1 not taken.
5506 catch (errline&) {}
735 42846 moreonline=moreonlinetmp;
736 53858 }
737
738 int incsrcdepth=0;
739
740 // Returns true if a file is protected via
741 // an "includeonce".
742 6368 bool file_included_once(const char* file)
743 {
744
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 6326 times.
6566 for (int i = 0; i < includeonce.count; ++i)
745 {
746
2/2
✓ Branch 0 taken 198 times.
✓ Branch 1 taken 42 times.
240 if (includeonce[i] == file)
747 {
748 return true;
749 }
750 }
751
752 return false;
753 }
754
755 autoarray<string> macro_defs;
756 int in_macro_def=0;
757
758 6338 void assemblefile(const char * filename)
759 {
760 6338 incsrcdepth++;
761 6338 string absolutepath = filesystem->create_absolute_path(get_current_file_name(), filename);
762
763
2/2
✓ Branch 0 taken 6296 times.
✓ Branch 1 taken 42 times.
6338 if (file_included_once(absolutepath))
764 {
765 return;
766 }
767
768 898 callstack_push cs_push(callstack_entry_type::FILE, absolutepath);
769
770 sourcefile file;
771 6296 file.contents = nullptr;
772 6296 file.numlines = 0;
773
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 6016 times.
6296 int startif=numif;
774
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 6016 times.
6296 if (!filecontents.exists(absolutepath))
775 {
776
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 2 times.
280 char * temp = readfile(absolutepath, "");
777
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 272 times.
278 if (!temp)
778 {
779
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 asar_throw_error(0, error_type_null, vfile_error_to_error_id(asar_get_last_io_error()), filename);
780
781 return;
782 }
783
1/2
✓ Branch 0 taken 272 times.
✗ Branch 1 not taken.
272 sourcefile& newfile = filecontents.create(absolutepath);
784
1/2
✓ Branch 0 taken 272 times.
✗ Branch 1 not taken.
272 newfile.contents =split(temp, '\n');
785 272 newfile.data = temp;
786
2/2
✓ Branch 0 taken 10968 times.
✓ Branch 1 taken 272 times.
11240 for (int i=0;newfile.contents[i];i++)
787 {
788 10968 newfile.numlines++;
789 char * line= newfile.contents[i];
790 10968 char * comment = strqchr(line, ';');
791
2/2
✓ Branch 0 taken 2884 times.
✓ Branch 1 taken 8084 times.
10968 if(comment) *comment = 0;
792
4/6
✓ Branch 0 taken 10968 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 10964 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
10970 if (!confirmquotes(line)) { callstack_push cs_push(callstack_entry_type::LINE, line, i); asar_throw_error(0, error_type_null, error_id_mismatched_quotes); line[0] = '\0'; }
793 10968 newfile.contents[i] = strip_whitespace(line);
794 }
795
2/2
✓ Branch 0 taken 10968 times.
✓ Branch 1 taken 272 times.
11240 for(int i=0;newfile.contents[i];i++)
796 {
797 char* line = newfile.contents[i];
798
2/2
✓ Branch 0 taken 4052 times.
✓ Branch 1 taken 6916 times.
10968 if(!*line) continue;
799
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6916 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
6926 for (int j=1;line[strlen(line) - 1] == ',' && newfile.contents[i+j];j++)
800 {
801 // not using strcat because the source and dest overlap here
802 char* otherline = newfile.contents[i+j];
803 10 char* line_end = line + strlen(line);
804
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 10 times.
138 while(*otherline) *line_end++ = *otherline++;
805 10 *line_end = '\0';
806 static char nullstr[]="";
807 10 newfile.contents[i+j]=nullstr;
808 }
809 }
810 272 file = newfile;
811 } else { // filecontents.exists(absolutepath)
812 6016 file = filecontents.find(absolutepath);
813 }
814 6288 asarverallowed=true;
815
3/4
✓ Branch 0 taken 44766 times.
✓ Branch 1 taken 780 times.
✓ Branch 2 taken 44766 times.
✗ Branch 3 not taken.
45546 for (int i=0;file.contents[i] && i<file.numlines;i++)
816 {
817 20133 string connectedline;
818 44766 int skiplines = getconnectedlines<char**>(file.contents, i, connectedline);
819
820
2/2
✓ Branch 0 taken 39258 times.
✓ Branch 1 taken 5508 times.
44766 bool was_loop_end = do_line_logic(connectedline, absolutepath, i);
821 39258 i += skiplines;
822
823 // if a loop ended on this line, should it run again?
824
4/4
✓ Branch 0 taken 1788 times.
✓ Branch 1 taken 37470 times.
✓ Branch 2 taken 456 times.
✓ Branch 3 taken 1332 times.
39258 if (was_loop_end && whilestatus[numif].cond)
825 1332 i = whilestatus[numif].startline - 1;
826 44766 }
827
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 780 times.
792 while (in_macro_def > 0)
828 {
829
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 asar_throw_error(0, error_type_null, error_id_unclosed_macro, macro_defs[in_macro_def-1].data());
830
5/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
12 if (!pass && in_macro_def == 1) endmacro(false);
831 12 in_macro_def--;
832 12 macro_defs.remove(in_macro_def);
833 }
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 780 times.
780 if (numif!=startif)
835 {
836 numif=startif;
837 numtrue=startif;
838 asar_throw_error(0, error_type_null, error_id_unclosed_if);
839 }
840 780 incsrcdepth--;
841 11848 }
842
843 // RPG Hacker: At some point, this should probably be merged
844 // into assembleline(), since the two names just cause
845 // confusion otherwise.
846 // return value is "did a loop end on this line"
847 51786 bool do_line_logic(const char* line, const char* filename, int lineno)
848 {
849 51786 int prevnumif = numif;
850 51786 int single_line_for_tracker = 1;
851 try
852 {
853 23643 string current_line;
854
6/6
✓ Branch 0 taken 3246 times.
✓ Branch 1 taken 48540 times.
✓ Branch 2 taken 2760 times.
✓ Branch 3 taken 486 times.
✓ Branch 4 taken 168 times.
✓ Branch 5 taken 2592 times.
51786 if (numif==numtrue || (numtrue+1==numif && stribegin(line, "elseif ")))
855 {
856 22104 callstack_push cs_push(callstack_entry_type::LINE, line, lineno);
857
2/2
✓ Branch 0 taken 48606 times.
✓ Branch 1 taken 102 times.
48708 string tmp=replace_macro_args(line);
858
1/2
✓ Branch 0 taken 48606 times.
✗ Branch 1 not taken.
48606 tmp.qnormalize();
859
2/2
✓ Branch 0 taken 48574 times.
✓ Branch 1 taken 32 times.
48606 resolvedefines(current_line, tmp);
860 48740 }
861 else current_line=line;
862
863 23576 callstack_push cs_push(callstack_entry_type::LINE, current_line, lineno);
864
865
4/4
✓ Branch 0 taken 552 times.
✓ Branch 1 taken 51100 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 516 times.
51652 if (stribegin(current_line, "macro ") && numif==numtrue)
866 {
867 // RPG Hacker: Slight redundancy here with code that is
868 // also in startmacro(). Could improve this for Asar 2.0.
869 516 string macro_name = current_line.data()+6;
870 516 char * startpar=strqchr(macro_name.data(), '(');
871
1/2
✓ Branch 0 taken 516 times.
✗ Branch 1 not taken.
516 if (startpar) *startpar=0;
872 516 macro_defs.append(macro_name);
873
874 // RPG Hacker: I think it would make more logical sense
875 // to have this ++ after the if, but hat breaks compatibility
876 // with at least one test, and it generally leads to more
877 // errors being output after a broken macro declaration.
878 516 in_macro_def++;
879
2/2
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 344 times.
516 if (!pass)
880 {
881
4/4
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 138 times.
✓ Branch 3 taken 2 times.
172 if (in_macro_def == 1) startmacro(current_line.data()+6);
882
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 else tomacro(current_line);
883 }
884 516 }
885
6/6
✓ Branch 0 taken 28088 times.
✓ Branch 1 taken 23048 times.
✓ Branch 2 taken 288 times.
✓ Branch 3 taken 27800 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 252 times.
51136 else if (!stricmp(current_line, "endmacro") && numif==numtrue)
886 {
887
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 504 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
504 if (in_macro_def == 0) asar_throw_error(0, error_type_line, error_id_misplaced_endmacro);
888 else
889 {
890 504 in_macro_def--;
891 504 macro_defs.remove(in_macro_def);
892
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 336 times.
504 if (!pass)
893 {
894
3/4
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 138 times.
✗ Branch 3 not taken.
168 if (in_macro_def == 0) endmacro(true);
895
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 else tomacro(current_line);
896 }
897 }
898 }
899
2/2
✓ Branch 0 taken 2280 times.
✓ Branch 1 taken 48352 times.
50632 else if (in_macro_def > 0)
900 {
901
3/4
✓ Branch 0 taken 760 times.
✓ Branch 1 taken 1520 times.
✓ Branch 2 taken 760 times.
✗ Branch 3 not taken.
2280 if (!pass) tomacro(current_line);
902 }
903 else
904 {
905
2/2
✓ Branch 0 taken 42846 times.
✓ Branch 1 taken 5506 times.
48352 assembleline(filename, lineno, current_line, single_line_for_tracker);
906 }
907 57294 }
908
2/2
✓ Branch 0 taken 5508 times.
✓ Branch 1 taken 134 times.
5642 catch (errline&) {}
909
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 40674 times.
40734 return (numif != prevnumif || single_line_for_tracker == 3)
910
6/6
✓ Branch 0 taken 40734 times.
✓ Branch 1 taken 5544 times.
✓ Branch 2 taken 3134 times.
✓ Branch 3 taken 2470 times.
✓ Branch 4 taken 2694 times.
✓ Branch 5 taken 440 times.
98160 && (whilestatus[numif].iswhile || whilestatus[numif].is_for);
911 }
912
913
914 236 void parse_std_includes(const char* textfile, autoarray<string>& outarray)
915 {
916 236 char* content = readfilenative(textfile);
917
918
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 if (content != nullptr)
919 {
920 char* pos = content;
921
922
2/2
✓ Branch 0 taken 472 times.
✓ Branch 1 taken 236 times.
708 while (pos[0] != '\0')
923 {
924 236 string stdinclude;
925
926 do
927 {
928
3/4
✓ Branch 0 taken 9204 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8968 times.
✓ Branch 3 taken 236 times.
9204 if (pos[0] != '\r' && pos[0] != '\n')
929 {
930 8968 stdinclude += pos[0];
931 }
932 9204 pos++;
933
4/4
✓ Branch 0 taken 8968 times.
✓ Branch 1 taken 236 times.
✓ Branch 2 taken 8732 times.
✓ Branch 3 taken 236 times.
9204 } while (pos[0] != '\0' && pos[0] != '\n');
934
935 472 strip_whitespace(stdinclude);
936
937
2/2
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 236 times.
472 if (stdinclude != "")
938 {
939
2/4
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 236 times.
236 if (!path_is_absolute(stdinclude))
940 {
941 stdinclude = dir(textfile) + stdinclude;
942 }
943
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 outarray.append(normalize_path(stdinclude));
944 }
945 472 }
946
947 236 free(content);
948 }
949 236 }
950
951 236 void parse_std_defines(const char* textfile)
952 {
953
954 // RPG Hacker: add built-in defines.
955 // (They're not really standard defines, but I was lazy and this was
956 // one convenient place for doing it).
957 236 builtindefines.create("assembler") = "asar";
958
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 builtindefines.create("assembler_ver") = dec(get_version_int());
959
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 builtindefines.create("assembler_time") = dec(time(nullptr));
960
961
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 if(textfile == nullptr) return;
962
963 236 char* content = readfilenative(textfile);
964
965
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 if (content != nullptr)
966 {
967 char* pos = content;
968
2/2
✓ Branch 0 taken 1416 times.
✓ Branch 1 taken 236 times.
1652 while (*pos != 0) {
969 708 string define_name;
970 708 string define_val;
971
972
4/4
✓ Branch 0 taken 14160 times.
✓ Branch 1 taken 944 times.
✓ Branch 2 taken 13688 times.
✓ Branch 3 taken 472 times.
15104 while (*pos != '=' && *pos != '\n') {
973 13688 define_name += *pos;
974 13688 pos++;
975 }
976
2/2
✓ Branch 0 taken 944 times.
✓ Branch 1 taken 472 times.
1416 if (*pos != 0 && *pos != '\n') pos++; // skip =
977
3/4
✓ Branch 0 taken 6372 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4956 times.
✓ Branch 3 taken 1416 times.
6372 while (*pos != 0 && *pos != '\n') {
978 4956 define_val += *pos;
979 4956 pos++;
980 }
981
1/2
✓ Branch 0 taken 1416 times.
✗ Branch 1 not taken.
1416 if (*pos != 0)
982 1416 pos++; // skip \n
983 // clean define_name
984 1416 strip_whitespace(define_name);
985 1416 define_name.strip_prefix('!'); // remove leading ! if present
986
987
2/2
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 1180 times.
1416 if (define_name == "")
988 {
989
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 if (define_val == "")
990 {
991 236 continue;
992 }
993
994 asar_throw_error(pass, error_type_null, error_id_stddefines_no_identifier);
995 }
996
997
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1180 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1180 if (!validatedefinename(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, "stddefines.txt", define_name.data());
998
999 // clean define_val
1000 const char* defval = define_val.data();
1001 590 string cleaned_defval;
1002
1003
2/2
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 944 times.
1180 if (*defval == 0) {
1004 // no value
1005
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
236 if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
1006
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 clidefines.create(define_name) = "";
1007 236 continue;
1008 }
1009
1010
3/4
✓ Branch 0 taken 472 times.
✓ Branch 1 taken 944 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 944 times.
1416 while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace in beginning
1011
2/2
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 708 times.
944 if (*defval == '"') {
1012 236 defval++; // skip opening quote
1013
3/4
✓ Branch 0 taken 3068 times.
✓ Branch 1 taken 236 times.
✓ Branch 2 taken 3068 times.
✗ Branch 3 not taken.
3304 while (*defval != '"' && *defval != 0)
1014 3068 cleaned_defval += *defval++;
1015
1016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
236 if (*defval == 0) {
1017 asar_throw_error(pass, error_type_null, error_id_mismatched_quotes);
1018 }
1019 236 defval++; // skip closing quote
1020
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 236 times.
236 while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace
1021
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
236 if (*defval != 0 && *defval != '\n')
1022 asar_throw_error(pass, error_type_null, error_id_stddefine_after_closing_quote);
1023
1024
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 236 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
236 if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
1025
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 clidefines.create(define_name) = cleaned_defval;
1026 236 continue;
1027 }
1028 else
1029 {
1030 // slightly hacky way to remove trailing whitespace
1031 354 const char* defval_end = strchr(defval, '\n'); // slightly hacky way to get end of string or newline
1032
1/2
✓ Branch 0 taken 708 times.
✗ Branch 1 not taken.
708 if (!defval_end) defval_end = strchr(defval, 0);
1033 708 defval_end--;
1034
3/4
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 708 times.
944 while (*defval_end == ' ' || *defval_end == '\t') defval_end--;
1035
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 708 times.
708 cleaned_defval = string(defval, (int)(defval_end - defval + 1));
1036
1037
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
708 if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
1038
1/2
✓ Branch 0 taken 708 times.
✗ Branch 1 not taken.
708 clidefines.create(define_name) = cleaned_defval;
1039 708 continue;
1040 708 }
1041
1042 1416 }
1043 236 free(content);
1044 }
1045 }
1046
1047 bool checksum_fix_enabled = true;
1048 bool force_checksum_fix = false;
1049
1050 #define cfree(x) free((void*)x)
1051 138 static void clearmacro(const string & key, macrodata* & macro)
1052 {
1053 (void)key;
1054 138 freemacro(macro);
1055 138 }
1056
1057 272 static void clearfile(const string & key, sourcefile& filecontent)
1058 {
1059 (void)key;
1060 272 cfree(filecontent.data);
1061 272 cfree(filecontent.contents);
1062 272 }
1063 #undef cfree
1064
1065
1/2
✓ Branch 0 taken 3068 times.
✗ Branch 1 not taken.
3068 static void adddefine(const string & key, string & value)
1066 {
1067
1/2
✓ Branch 0 taken 3068 times.
✗ Branch 1 not taken.
3068 if (!defines.exists(key)) defines.create(key) = value;
1068 3068 }
1069
1070 static string symbolfile;
1071
1072 static void printsymbol_wla(const string& key, snes_label& label)
1073 {
1074 string line = hex((label.pos & 0xFF0000)>>16, 2)+":"+hex(label.pos & 0xFFFF, 4)+" "+key+"\n";
1075 symbolfile += line;
1076 }
1077
1078 static void printsymbol_nocash(const string& key, snes_label& label)
1079 {
1080 string line = hex(label.pos & 0xFFFFFF, 8)+" "+key+"\n";
1081 symbolfile += line;
1082 }
1083
1084 string create_symbols_file(string format, uint32_t romCrc){
1085 format = lower(format);
1086 symbolfile = "";
1087 if(format == "wla")
1088 {
1089 symbolfile = "; wla symbolic information file\n";
1090 symbolfile += "; generated by asar\n";
1091
1092 symbolfile += "\n[labels]\n";
1093 labels.each(printsymbol_wla);
1094
1095 symbolfile += "\n[source files]\n";
1096 const autoarray<AddressToLineMapping::FileInfo>& addrToLineFileList = addressToLineMapping.getFileList();
1097 for (int i = 0; i < addrToLineFileList.count; ++i)
1098 {
1099 char addrToFileListStr[256];
1100 snprintf(addrToFileListStr, 256, "%.4x %.8x %s\n",
1101 i,
1102 addrToLineFileList[i].fileCrc,
1103 addrToLineFileList[i].filename.data()
1104 );
1105 symbolfile += addrToFileListStr;
1106 }
1107
1108 symbolfile += "\n[rom checksum]\n";
1109 {
1110 char romCrcStr[32];
1111 snprintf(romCrcStr, 32, "%.8x\n",
1112 romCrc
1113 );
1114 symbolfile += romCrcStr;
1115 }
1116
1117 symbolfile += "\n[addr-to-line mapping]\n";
1118 const autoarray<AddressToLineMapping::AddrToLineInfo>& addrToLineInfo = addressToLineMapping.getAddrToLineInfo();
1119 for (int i = 0; i < addrToLineInfo.count; ++i)
1120 {
1121 char addrToLineStr[32];
1122 snprintf(addrToLineStr, 32, "%.2x:%.4x %.4x:%.8x\n",
1123 (addrToLineInfo[i].addr & 0xFF0000) >> 16,
1124 addrToLineInfo[i].addr & 0xFFFF,
1125 addrToLineInfo[i].fileIdx & 0xFFFF,
1126 addrToLineInfo[i].line & 0xFFFFFFFF
1127 );
1128 symbolfile += addrToLineStr;
1129 }
1130
1131 }
1132 else if (format == "nocash")
1133 {
1134 symbolfile = ";no$sns symbolic information file\n";
1135 symbolfile += ";generated by asar\n";
1136 symbolfile += "\n";
1137 labels.each(printsymbol_nocash);
1138 }
1139 return symbolfile;
1140 }
1141
1142
1143 2 bool in_top_level_file()
1144 {
1145 int num_files = 0;
1146
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 for (int i = callstack.count-1; i >= 0; --i)
1147 {
1148
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (callstack[i].type == callstack_entry_type::FILE)
1149 {
1150 2 num_files++;
1151
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (num_files > 1) break;
1152 }
1153 }
1154 2 return (num_files <= 1);
1155 }
1156
1157 17548 const char* get_current_file_name()
1158 {
1159
2/2
✓ Branch 0 taken 67160 times.
✓ Branch 1 taken 688 times.
67848 for (int i = callstack.count-1; i >= 0; --i)
1160 {
1161
2/2
✓ Branch 0 taken 16860 times.
✓ Branch 1 taken 50300 times.
67160 if (callstack[i].type == callstack_entry_type::FILE)
1162 16860 return callstack[i].content.raw();
1163 }
1164 return nullptr;
1165 }
1166
1167 8612 int get_current_line()
1168 {
1169
1/2
✓ Branch 0 taken 25556 times.
✗ Branch 1 not taken.
25556 for (int i = callstack.count-1; i >= 0; --i)
1170 {
1171
2/2
✓ Branch 0 taken 8612 times.
✓ Branch 1 taken 16944 times.
34168 if (callstack[i].type == callstack_entry_type::LINE) return callstack[i].lineno;
1172 }
1173 return -1;
1174 }
1175
1176 230 const char* get_current_block()
1177 {
1178
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 10 times.
242 for (int i = callstack.count-1; i >= 0; --i)
1179 {
1180
4/4
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 54 times.
✓ Branch 2 taken 166 times.
✓ Branch 3 taken 12 times.
630 if (callstack[i].type == callstack_entry_type::LINE || callstack[i].type == callstack_entry_type::BLOCK) return callstack[i].content.raw();
1181 }
1182 return nullptr;
1183 }
1184
1185
1186 236 void reseteverything()
1187 {
1188 118 string str;
1189 236 labels.reset();
1190 236 defines.reset();
1191
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 builtindefines.each(adddefine);
1192
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 clidefines.each(adddefine);
1193 236 structs.reset();
1194
1195
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 macros.each(clearmacro);
1196 236 macros.reset();
1197
1198
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 filecontents.each(clearfile);
1199 236 filecontents.reset();
1200
1201 236 writtenblocks.reset();
1202
1203 236 optimizeforbank=-1;
1204 236 optimize_dp = optimize_dp_flag::NONE;
1205 236 dp_base = 0;
1206 236 optimize_address = optimize_address_flag::DEFAULT;
1207
1208
1/2
✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
236 closecachedfiles();
1209
1210 236 incsrcdepth=0;
1211 236 label_counter = 0;
1212 236 errored = false;
1213 236 checksum_fix_enabled = true;
1214 236 force_checksum_fix = false;
1215
1216 236 in_macro_def = 0;
1217
1218 #ifndef ASAR_SHARED
1219 236 free(const_cast<unsigned char*>(romdata_r));
1220 #endif
1221
1222 236 callstack.reset();
1223 236 simple_callstacks = true;
1224 #undef free
1225 236 }
1226