Branch data Line data Source code
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 : 0 : int get_version_int()
47 : : {
48 : 0 : return asarver_maj * 10000 + asarver_min * 100 + asarver_bug;
49 : : }
50 : :
51 : 26 : bool setmapper()
52 : : {
53 : : int maxscore=-99999;
54 : : mapper_t bestmap=lorom;
55 : 26 : mapper_t maps[]={lorom, hirom, exlorom, exhirom};
56 [ + + ]: 130 : for (size_t mapid=0;mapid<sizeof(maps)/sizeof(maps[0]);mapid++)
57 : : {
58 : 104 : mapper=maps[mapid];
59 : : int score=0;
60 : : int highbits=0;
61 : : bool foundnull=false;
62 [ + + ]: 2288 : for (int i=0;i<21;i++)
63 : : {
64 : 2184 : unsigned char c=romdata[snestopc(0x00FFC0+i)];
65 [ - + ]: 2184 : 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 [ - + ]: 2184 : if (c>=128) highbits++;
67 [ + + ]: 2184 : else if (is_upper(c)) score+=3;
68 [ + + ]: 1809 : else if (c==' ') score+=2;
69 [ - + ]: 1659 : else if (is_digit(c)) score+=1;
70 [ - + ]: 1659 : else if (is_lower(c)) score+=1;
71 [ - + ]: 1659 : else if (c=='-') score+=1;
72 [ - + ]: 1659 : else if (!c) foundnull=true;
73 : 0 : else score-=3;
74 : : }
75 [ - + ]: 104 : if (highbits>0 && highbits<=14) score-=21;//high bits set on some, but not all, bytes = unlikely to be a ROM
76 [ + + ]: 104 : if ((romdata[snestopc(0x00FFDE)]^romdata[snestopc(0x00FFDC)])!=0xFF ||
77 [ - + ]: 26 : (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 [ + + ]: 104 : if (score>maxscore)
80 : : {
81 : : maxscore=score;
82 : : bestmap=mapper;
83 : : }
84 : : }
85 : 26 : mapper=bestmap;
86 : :
87 : : //detect oddball mappers
88 : 26 : int mapperbyte=romdata[snestopc(0x00FFD5)];
89 : 26 : int romtypebyte=romdata[snestopc(0x00FFD6)];
90 [ + - ]: 26 : if (mapper==lorom)
91 : : {
92 [ - + - - : 26 : if (mapperbyte==0x23 && (romtypebyte==0x32 || romtypebyte==0x34 || romtypebyte==0x35)) mapper=sa1rom;
- - ]
93 : : }
94 : 26 : 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 : 136 : string shorten_to_relative_path(const char* base_path, const char* target_path)
105 : : {
106 [ + + ]: 136 : if (stribegin(target_path, base_path)) target_path += strlen(base_path);
107 : 136 : return target_path;
108 : : }
109 : :
110 : 136 : string get_top_level_directory()
111 : : {
112 : 136 : string top_level_file_dir;
113 [ + - ]: 136 : for (int i = 0; i < callstack.count; ++i)
114 : : {
115 [ + - ]: 136 : if (callstack[i].type == callstack_entry_type::FILE)
116 : : {
117 : 136 : top_level_file_dir = dir(callstack[i].content);
118 : 136 : break;
119 : : }
120 : : }
121 : 136 : return top_level_file_dir;
122 : 0 : }
123 : :
124 : 115 : string generate_call_details_string(const char* current_block, const char* current_call, int indentation, bool add_lines)
125 : : {
126 : 115 : string e;
127 [ + + ]: 115 : if (current_block != nullptr || current_call != nullptr)
128 : : {
129 : 103 : string indent;
130 [ - + ]: 103 : if (add_lines) indent += "|";
131 [ + + ]: 515 : for (; indentation > 0; --indentation) indent += " ";
132 : :
133 [ + - ]: 206 : if (current_block != nullptr) e += STR "\n"+indent+"in block: ["+current_block+"]";
134 [ - + ]: 103 : if (current_call != nullptr) e += STR "\n"+indent+"in macro call: [%"+current_call+"]";
135 : 103 : }
136 : 115 : return e;
137 : : }
138 : :
139 : 136 : 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 : 136 : return shorten_to_relative_path(get_top_level_directory(), current_file);
147 : : }
148 : :
149 : 136 : string generate_filename_and_line(const char* current_file, int current_line_no)
150 : : {
151 : 272 : return STR current_file
152 [ + + + + : 272 : + (current_line_no>=0?STR ":"+dec(current_line_no+1):"");
+ + ]
153 : : }
154 : :
155 : 0 : string format_stack_line(const printable_callstack_entry& entry, int stack_frame_index)
156 : : {
157 : 0 : string indent = "\n| ";
158 : 0 : indent += dec(stack_frame_index);
159 : 0 : 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 [ # # ]: 0 : if (stack_frame_index < 100) indent += " ";
163 [ # # ]: 0 : if (stack_frame_index < 10) indent += " ";
164 : : return indent
165 : 0 : + generate_filename_and_line(entry.prettypath, entry.lineno)
166 : 0 : + entry.details;
167 : 0 : }
168 : :
169 : 0 : 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 : 0 : printable_callstack_entry new_entry;
172 : : new_entry.fullpath = current_file;
173 : 0 : new_entry.prettypath = get_pretty_filename(current_file);
174 : 0 : new_entry.lineno = current_line_no;
175 : 0 : new_entry.details = generate_call_details_string(current_block, current_call, indentation, add_lines).raw();
176 : 0 : out->append(new_entry);
177 : 0 : }
178 : :
179 : 115 : 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 [ + - ]: 386 : for (int i = callstack.count-1; i >= 0 ; --i)
186 : : {
187 [ + - + + : 386 : switch (callstack[i].type)
- ]
188 : : {
189 : : case callstack_entry_type::FILE:
190 : : current_file = callstack[i].content;
191 [ + + ]: 115 : if (exclude_block) current_block = nullptr;
192 : 115 : *location = generate_filename_and_line(get_pretty_filename(current_file), current_line_no);
193 : 115 : *details = generate_call_details_string(current_block, current_call, 4, false);
194 : 115 : return;
195 : 0 : case callstack_entry_type::MACRO_CALL:
196 [ # # ]: 0 : if (current_call == nullptr) current_call = callstack[i].content;
197 : : break;
198 : 109 : case callstack_entry_type::LINE:
199 [ + + ]: 109 : if (current_block == nullptr && current_call == nullptr) current_block = callstack[i].content;
200 [ + - ]: 218 : if (current_line_no == -1) current_line_no = callstack[i].lineno;
201 : : break;
202 : 162 : case callstack_entry_type::BLOCK:
203 [ + + ]: 162 : if (current_block == nullptr) current_block = callstack[i].content;
204 : : break;
205 : : }
206 : : }
207 : : *location = "";
208 : : *details = "";
209 : : }
210 : :
211 : 0 : void get_full_printable_callstack(autoarray<printable_callstack_entry>* out, int indentation, bool add_lines)
212 : : {
213 : 0 : 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 [ # # ]: 0 : for (int i = 0; i < callstack.count; ++i)
219 : : {
220 [ # # # # : 0 : switch (callstack[i].type)
# ]
221 : : {
222 : 0 : case callstack_entry_type::FILE:
223 [ # # ]: 0 : if (current_file != nullptr)
224 : : {
225 : 0 : 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 : 0 : break;
232 : 0 : case callstack_entry_type::MACRO_CALL:
233 : : current_block = nullptr;
234 : : current_call = callstack[i].content;
235 : 0 : break;
236 : : case callstack_entry_type::LINE:
237 : 0 : current_line_no = callstack[i].lineno;
238 : : current_block = callstack[i].content;
239 : 0 : break;
240 : : case callstack_entry_type::BLOCK:
241 : : current_block = callstack[i].content;
242 : 0 : break;
243 : : }
244 : : }
245 : 0 : }
246 : :
247 : 0 : string get_full_callstack()
248 : : {
249 : 0 : autoarray<printable_callstack_entry> printable_stack;
250 : 0 : get_full_printable_callstack(&printable_stack, 12, true);
251 : :
252 : 0 : string e;
253 [ # # ]: 0 : if (printable_stack.count > 0)
254 : : {
255 : 0 : e += "\nFull call stack:";
256 [ # # ]: 0 : for (int i = printable_stack.count-1; i >= 0; --i)
257 : : {
258 : 0 : e += format_stack_line(printable_stack[i], i);
259 : : }
260 : : }
261 : 0 : return e;
262 : 0 : }
263 : :
264 : : // RPG Hacker: This function essetially replicates classic Asar behavior
265 : : // of only printing a single macro call below the current level.
266 : 115 : string get_simple_callstack()
267 : : {
268 : : int i;
269 : : const char* current_call = nullptr;
270 [ + + ]: 2513 : for (i = callstack.count-1; i >= 0 ; --i)
271 : : {
272 [ + + ]: 2419 : if (callstack[i].type == callstack_entry_type::MACRO_CALL)
273 : : {
274 : : current_call = callstack[i].content;
275 : 21 : break;
276 : : }
277 : : }
278 : :
279 : : const char* current_file = nullptr;
280 : : int current_line_no = -1;
281 [ + + ]: 115 : if (current_call != nullptr)
282 : : {
283 : : bool stop = false;
284 [ + - ]: 84 : for (int j = i-1; j >= 0 ; --j)
285 : : {
286 [ + - + + ]: 84 : switch (callstack[j].type)
287 : : {
288 : 21 : case callstack_entry_type::FILE:
289 [ - + ]: 21 : if (current_file != nullptr)
290 : : {
291 : : stop = true;
292 : : break;
293 : : }
294 : : current_file = callstack[j].content;
295 : 21 : break;
296 : : case callstack_entry_type::MACRO_CALL:
297 : : stop = true;
298 : : break;
299 : 21 : case callstack_entry_type::LINE:
300 [ + - ]: 42 : if (current_line_no == -1) current_line_no = callstack[j].lineno;
301 : : break;
302 : : case callstack_entry_type::BLOCK:
303 : : break;
304 : : }
305 : :
306 [ + + ]: 84 : if (current_file != nullptr && current_line_no != -1) stop = true;
307 : :
308 [ + - ]: 63 : if (stop) break;
309 : : }
310 : : }
311 : :
312 : 115 : string e;
313 [ + + ]: 115 : if (current_call != nullptr && current_file != nullptr)
314 : : {
315 : 42 : e += STR "\n called from: " + generate_filename_and_line(get_pretty_filename(current_file), current_line_no)
316 : 42 : + ": [%" + current_call + "]";
317 : : }
318 : 115 : return e;
319 : : }
320 : :
321 : 115 : string get_callstack()
322 : : {
323 [ + - ]: 115 : if (simple_callstacks)
324 : 115 : return get_simple_callstack();
325 : : else
326 : 0 : return get_full_callstack();
327 : : }
328 : :
329 : 9 : 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 : 9 : virtual_file_error asar_get_last_io_error()
349 : : {
350 [ + - ]: 9 : if (filesystem != nullptr)
351 : : {
352 : 9 : return filesystem->get_last_error();
353 : : }
354 : :
355 : : return vfe_unknown;
356 : : }
357 : :
358 : : static bool freespaced;
359 : 279 : static int getlenforlabel(snes_label thislabel, bool exists)
360 : : {
361 : 279 : unsigned int bank = thislabel.pos>>16;
362 : 279 : unsigned int word = thislabel.pos&0xFFFF;
363 : 279 : unsigned int relaxed_bank = optimizeforbank < 0 ? 0 : optimizeforbank;
364 [ + + ]: 279 : if (!exists)
365 : : {
366 : : return 2;
367 : : }
368 [ + + + + : 233 : else if((optimize_dp == optimize_dp_flag::RAM) && bank == 0x7E && (word-dp_base < 0x100))
- + ]
369 : : {
370 : : return 1;
371 : : }
372 [ + + + - : 230 : else if(optimize_dp == optimize_dp_flag::ALWAYS && (bank == 0x7E || !(bank & 0x40)) && (word-dp_base < 0x100))
+ - + + ]
373 : : {
374 : : return 1;
375 : : }
376 [ + + + + ]: 221 : else if (optimize_address == optimize_address_flag::RAM && bank == 0x7E && word < 0x2000)
377 : : {
378 : : return 2;
379 : : }
380 [ + + + + : 218 : else if (optimize_address == optimize_address_flag::MIRRORS && (bank == relaxed_bank || (!(bank & 0x40) && !(relaxed_bank & 0x40))) && word < 0x2000)
+ - + - +
+ ]
381 : : {
382 : : return 2;
383 : : }
384 [ + + + - : 215 : else if (optimize_address == optimize_address_flag::MIRRORS && !(bank & 0x40) && !(relaxed_bank & 0x40) && word < 0x8000)
+ - + - ]
385 : : {
386 : : return 2;
387 : : }
388 [ + + ]: 215 : else if (optimizeforbank>=0)
389 : : {
390 [ + - ]: 6 : if (thislabel.freespace_id > 0) return 3;
391 [ + - ]: 6 : else if (bank==(unsigned int)optimizeforbank) return 2;
392 : 6 : else return 3;
393 : : }
394 [ + + - + ]: 209 : else if (thislabel.freespace_id > 0 || freespaceid > 0)
395 : : {
396 : : // TODO: check whether they're pinned to the same bank
397 [ + + ]: 19 : if (thislabel.freespace_id != freespaceid) return 3;
398 : 10 : else return 2;
399 : : }
400 [ + + ]: 190 : else if (bank != snespos >> 16){ return 3; }
401 : 180 : else { return 2;}
402 : : }
403 : :
404 : :
405 : 1630 : bool is_hex_constant(const char* str){
406 [ + + ]: 1630 : if (*str=='$')
407 : : {
408 : 1386 : str++;
409 [ + + ]: 6300 : while(is_xdigit(*str)) {
410 : 4914 : str++;
411 : : }
412 [ - + ]: 1386 : if(*str=='\0'){
413 : : return true;
414 : : }
415 : : }
416 : : return false;
417 : : }
418 : :
419 : 1498 : int getlen(const char * orgstr, bool optimizebankextraction)
420 : : {
421 : 1498 : const char * str=orgstr;
422 : 1498 : freespaced=false;
423 : :
424 : 1498 : const char* posneglabel = str;
425 : 1498 : string posnegname = posneglabelname(&posneglabel, false);
426 : :
427 [ + + ]: 1498 : if (posnegname.length() > 0)
428 : : {
429 [ - + ]: 30 : if (*posneglabel != '\0') goto notposneglabel;
430 : :
431 [ + + ]: 50 : if (!pass) return 2;
432 : 20 : snes_label label_data;
433 : : // RPG Hacker: Umm... what kind of magic constant is this?
434 : 20 : label_data.pos = 31415926;
435 : 20 : bool found = labelval(posnegname, &label_data);
436 : 20 : return getlenforlabel(label_data, found);
437 : : }
438 : 1468 : notposneglabel:
439 : : int len=0;
440 [ + + ]: 3006 : while (*str)
441 : : {
442 : : int thislen=0;
443 : 1538 : bool maybebankextraction=(str==orgstr);
444 [ + + ]: 1538 : if (*str=='$')
445 : : {
446 : 1179 : str++;
447 : : int i;
448 [ + + ]: 5847 : for (i=0;is_xdigit(str[i]);i++);
449 : : //if (i&1) warn(S dec(i)+"-digit hex value");//blocked in getnum instead
450 : 1179 : thislen=(i+1)/2;
451 : 1179 : str+=i;
452 : : }
453 [ - + ]: 359 : else if (*str=='%')
454 : : {
455 : 0 : str++;
456 : : int i;
457 [ # # ]: 0 : for (i=0;str[i]=='0' || str[i]=='1';i++);
458 : : //if (i&7) warn(S dec(i)+"-digit binary value");
459 : 0 : thislen=(i+7)/8;
460 : 0 : str+=i;
461 : : }
462 [ - + - - ]: 359 : else if (str[0]=='\'' && str[2]=='\'')
463 : : {
464 : : thislen=1;
465 : 0 : str+=3;
466 : : }
467 [ + + ]: 359 : else if (is_digit(*str))
468 : : {
469 : 50 : int val=strtol(str, const_cast<char**>(&str), 10);
470 [ + - ]: 50 : if (val>=0) thislen=1;
471 [ + + ]: 50 : if (val>=256) thislen=2;
472 [ - + ]: 9 : if (val>=65536) thislen=3;
473 : : }
474 [ + + + - : 309 : else if (is_ualpha(*str) || *str=='.' || *str=='?')
+ - ]
475 : : {
476 : 259 : snes_label thislabel;
477 : 259 : bool exists=labelval(&str, &thislabel);
478 : 259 : thislen=getlenforlabel(thislabel, exists);
479 : : }
480 : 50 : else str++;
481 [ + + ]: 1538 : if (optimizebankextraction && maybebankextraction &&
482 [ + - + - : 155 : (!strcmp(str, ">>16") || !strcmp(str, "/65536") || !strcmp(str, "/$10000")))
+ - ]
483 : : return 1;
484 : : if (thislen>len) len=thislen;
485 : : }
486 : : return len;
487 : 1498 : }
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 : 1246 : bool validatedefinename(const char * name)
516 : : {
517 [ - + ]: 1246 : if (!name[0]) return false;
518 [ + + ]: 14325 : for (int i = 0;name[i];i++)
519 : : {
520 [ - + ]: 13079 : if (!is_ualnum(name[i])) return false;
521 : : }
522 : :
523 : : return true;
524 : : }
525 : :
526 : 27060 : void resolvedefines(string& out, const char * start)
527 : : {
528 : 27060 : recurseblock rec;
529 : : const char * here=start;
530 [ + + ]: 27059 : if (!strchr(here, '!'))
531 : : {
532 : 21869 : out += here;
533 : : return;
534 : : }
535 [ + + ]: 40581 : while (*here)
536 : : {
537 [ + + + + ]: 35406 : if (here[0] == '\\' && here[1] == '\\')
538 : : {
539 : : // allow using \\ as escape sequence
540 [ - + ]: 3 : if (in_macro_def > 0) out += "\\";
541 : 3 : out += "\\";
542 : 3 : here += 2;
543 : : }
544 [ + + + - ]: 35403 : else if (here[0] == '\\' && here[1] == '!')
545 : : {
546 : : // allow using \! to escape !
547 [ + + ]: 21 : if (in_macro_def > 0) out += "\\";
548 : 21 : out+="!";
549 : 21 : here += 2;
550 : : }
551 [ + + ]: 35382 : else if (*here=='!')
552 : : {
553 [ + + + + : 5565 : 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 : 5565 : here++;
556 : :
557 : : int depth = 0;
558 [ + + ]: 5622 : for (const char* depth_str = here; *depth_str=='^'; depth_str++)
559 : : {
560 : 57 : depth++;
561 : : }
562 : 5565 : here += depth;
563 : :
564 [ + + ]: 5565 : if (depth != in_macro_def)
565 : : {
566 : 660 : out += '!';
567 [ + + ]: 693 : for (int i=0; i < depth; ++i) out += '^';
568 [ + + - + ]: 660 : 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 : 651 : }
571 : :
572 [ + + ]: 4905 : if (*here=='{')
573 : : {
574 : 54 : here++;
575 : 54 : string unprocessedname;
576 : : int braces=1;
577 : : while (true)
578 : : {
579 [ + + ]: 576 : if (*here=='{') braces++;
580 [ + + ]: 576 : if (*here=='}') braces--;
581 [ - + - - ]: 576 : if (!*here) asar_throw_error(0, error_type_line, error_id_mismatched_braces);
582 [ + + ]: 576 : if (!braces) break;
583 : 522 : unprocessedname+=*here++;
584 : : }
585 : 54 : here++;
586 : 54 : resolvedefines(defname, unprocessedname);
587 [ - + - - ]: 54 : if (!validatedefinename(defname)) asar_throw_error(0, error_type_line, error_id_invalid_define_name);
588 : 54 : }
589 : : else
590 : : {
591 [ + + ]: 29748 : while (is_ualnum(*here)) defname+=*here++;
592 : : }
593 : :
594 [ + + ]: 4905 : if (first)
595 : : {
596 : : enum {
597 : : null,
598 : : append,
599 : : expand,
600 : : domath,
601 : : setifnotset,
602 : : } mode;
603 : : if(0);
604 [ + + ]: 2607 : else if (stribegin(here, " = ")) { here+=3; mode=null; }
605 [ + + ]: 2361 : else if (stribegin(here, " += ")) { here+=4; mode=append; }
606 [ + + ]: 2310 : else if (stribegin(here, " := ")) { here+=4; mode=expand; }
607 [ + + ]: 2283 : else if (stribegin(here, " #= ")) { here+=4; mode=domath; }
608 [ + + ]: 1320 : else if (stribegin(here, " ?= ")) { here+=4; mode=setifnotset; }
609 : 1317 : else goto notdefineset;
610 : 1290 : string val;
611 [ + + ]: 1290 : if (*here=='"')
612 : : {
613 : 84 : here++;
614 : : while (true)
615 : : {
616 [ + + ]: 429 : if (*here=='"')
617 : : {
618 [ + + + - ]: 87 : if (!here[1] || here[1]==' ') break;
619 [ + - ]: 3 : else if (here[1]=='"') here++;
620 : 0 : else asar_throw_error(0, error_type_line, error_id_broken_define_declaration);
621 : : }
622 : 345 : val+=*here++;
623 : : }
624 : 84 : here++;
625 : : }
626 : : else
627 : : {
628 [ + + + - ]: 9495 : while (*here && *here!=' ') val+=*here++;
629 : : }
630 : : //if (strqchr(val.data(), ';')) *strqchr(val.data(), ';')=0;
631 [ - + - - : 1290 : 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 : 1290 : val.qnormalize();
636 : :
637 : : // RPG Hacker: throw an error if we're trying to overwrite built-in defines.
638 [ + + ]: 1290 : if (builtindefines.exists(defname))
639 : : {
640 : 3 : asar_throw_error(0, error_type_line, error_id_overriding_builtin_define, defname.data());
641 : : }
642 : :
643 [ + + + + : 1287 : switch (mode)
+ ]
644 : : {
645 : : case null:
646 : : {
647 : 243 : defines.create(defname) = val;
648 : : break;
649 : : }
650 : : case append:
651 : : {
652 [ - + - - ]: 51 : if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data());
653 : 51 : string oldval = defines.find(defname);
654 : 51 : val=oldval+val;
655 : 51 : defines.create(defname) = val;
656 : : break;
657 : 51 : }
658 : 27 : case expand:
659 : : {
660 : 27 : string newval;
661 : 27 : resolvedefines(newval, val);
662 : 27 : defines.create(defname) = newval;
663 : : break;
664 : 27 : }
665 : 963 : case domath:
666 : : {
667 : 963 : string newval;
668 : 963 : resolvedefines(newval, val);
669 : 963 : double num= getnumdouble(newval);
670 [ + + + + : 963 : if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_line, error_id_define_label_math);
- + ]
671 : 960 : defines.create(defname) = ftostr(num);
672 : : break;
673 : 963 : }
674 : : case setifnotset:
675 : : {
676 [ + - + - ]: 3 : if (!defines.exists(defname)) defines.create(defname) = val;
677 : : break;
678 : : }
679 : : }
680 : 1290 : }
681 : : else
682 : : {
683 : 2298 : notdefineset:
684 [ + + ]: 3615 : if (!defname) out+="!";
685 : : else
686 : : {
687 [ - + - - ]: 3600 : if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data());
688 : : else {
689 : 3600 : string thisone = defines.find(defname);
690 : 3600 : resolvedefines(out, thisone);
691 : 3600 : }
692 : : }
693 : : }
694 : 5565 : }
695 : 29817 : else out+=*here++;
696 : : }
697 [ + - + + : 5175 : if (!confirmquotes(out)) { asar_throw_error(0, error_type_null, error_id_mismatched_quotes); out = ""; }
+ - ]
698 : 27059 : }
699 : :
700 : : bool moreonline;
701 : : bool asarverallowed = false;
702 : :
703 : 21560 : void assembleline(const char * fname, int linenum, const char * line, int& single_line_for_tracker)
704 : : {
705 : 21560 : recurseblock rec;
706 : 21560 : bool moreonlinetmp=moreonline;
707 : : // randomdude999: redundant, assemblefile already converted the path to absolute
708 : : //string absolutepath = filesystem->create_absolute_path("", fname);
709 : 21560 : string absolutepath = fname;
710 : 21560 : single_line_for_tracker = 1;
711 : : try
712 : : {
713 : 21560 : string out=line;
714 : 21560 : out.qreplace(": :", ": :");
715 [ + - ]: 21560 : autoptr<char**> blocks=qsplitstr(out.temp_raw(), " : ");
716 : 21560 : moreonline=true;
717 [ + + ]: 43475 : for (int block=0;moreonline;block++)
718 : : {
719 : 22418 : moreonline=(blocks[block+1] != nullptr);
720 : : try
721 : : {
722 : 22418 : string stripped_block = strip_whitespace(blocks[block]);
723 : :
724 : 22418 : callstack_push cs_push(callstack_entry_type::BLOCK, stripped_block);
725 : :
726 : 22418 : assembleblock(stripped_block, single_line_for_tracker);
727 : 21768 : checkbankcross();
728 : 23070 : }
729 : 652 : catch (errblock&) {}
730 [ + + ]: 21915 : if (blocks[block][0]!='\0') asarverallowed=false;
731 [ + + ]: 21915 : if(single_line_for_tracker == 1) single_line_for_tracker = 0;
732 : : }
733 : 22063 : }
734 : 503 : catch (errline&) {}
735 : 21057 : moreonline=moreonlinetmp;
736 : 22063 : }
737 : :
738 : : int incsrcdepth=0;
739 : :
740 : : // Returns true if a file is protected via
741 : : // an "includeonce".
742 : 925 : bool file_included_once(const char* file)
743 : : {
744 [ + + ]: 1024 : for (int i = 0; i < includeonce.count; ++i)
745 : : {
746 [ + + ]: 120 : 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 : 910 : void assemblefile(const char * filename)
759 : : {
760 : 910 : incsrcdepth++;
761 : 910 : string absolutepath = filesystem->create_absolute_path(get_current_file_name(), filename);
762 : :
763 [ + + ]: 910 : if (file_included_once(absolutepath))
764 : : {
765 : : return;
766 : : }
767 : :
768 : 889 : callstack_push cs_push(callstack_entry_type::FILE, absolutepath);
769 : :
770 : : sourcefile file;
771 : 889 : file.contents = nullptr;
772 : 889 : file.numlines = 0;
773 : 889 : int startif=numif;
774 [ + + ]: 889 : if (!filecontents.exists(absolutepath))
775 : : {
776 : 137 : char * temp = readfile(absolutepath, "");
777 [ + + ]: 136 : if (!temp)
778 : : {
779 : 3 : asar_throw_error(0, error_type_null, vfile_error_to_error_id(asar_get_last_io_error()), filename);
780 : :
781 : : return;
782 : : }
783 : 133 : sourcefile& newfile = filecontents.create(absolutepath);
784 : 133 : newfile.contents =split(temp, '\n');
785 : 133 : newfile.data = temp;
786 [ + + ]: 5495 : for (int i=0;newfile.contents[i];i++)
787 : : {
788 : 5362 : newfile.numlines++;
789 : : char * line= newfile.contents[i];
790 : 5362 : char * comment = strqchr(line, ';');
791 [ + + ]: 5362 : if(comment) *comment = 0;
792 [ + - + + : 5362 : 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 : 5362 : newfile.contents[i] = strip_whitespace(line);
794 : : }
795 [ + + ]: 5495 : for(int i=0;newfile.contents[i];i++)
796 : : {
797 : : char* line = newfile.contents[i];
798 [ + + ]: 5362 : if(!*line) continue;
799 [ + + + - ]: 3405 : 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 : 5 : char* line_end = line + strlen(line);
804 [ + + ]: 69 : while(*otherline) *line_end++ = *otherline++;
805 : 5 : *line_end = '\0';
806 : : static char nullstr[]="";
807 : 5 : newfile.contents[i+j]=nullstr;
808 : : }
809 : : }
810 : 133 : file = newfile;
811 : : } else { // filecontents.exists(absolutepath)
812 : 752 : file = filecontents.find(absolutepath);
813 : : }
814 : 885 : asarverallowed=true;
815 [ + + + - ]: 20148 : for (int i=0;file.contents[i] && i<file.numlines;i++)
816 : : {
817 : 19767 : string connectedline;
818 : 19767 : int skiplines = getconnectedlines<char**>(file.contents, i, connectedline);
819 : :
820 : 19767 : bool was_loop_end = do_line_logic(connectedline, absolutepath, i);
821 : 19263 : i += skiplines;
822 : :
823 : : // if a loop ended on this line, should it run again?
824 [ + + + + ]: 19263 : if (was_loop_end && whilestatus[numif].cond)
825 : 666 : i = whilestatus[numif].startline - 1;
826 : 19767 : }
827 [ + + ]: 387 : while (in_macro_def > 0)
828 : : {
829 : 6 : asar_throw_error(0, error_type_null, error_id_unclosed_macro, macro_defs[in_macro_def-1].data());
830 [ + + + + : 6 : if (!pass && in_macro_def == 1) endmacro(false);
+ - ]
831 : 6 : in_macro_def--;
832 : 6 : macro_defs.remove(in_macro_def);
833 : : }
834 [ - + ]: 381 : if (numif!=startif)
835 : : {
836 : 0 : numif=startif;
837 : 0 : numtrue=startif;
838 : 0 : asar_throw_error(0, error_type_null, error_id_unclosed_if);
839 : : }
840 : 381 : incsrcdepth--;
841 : 1415 : }
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 : 23277 : bool do_line_logic(const char* line, const char* filename, int lineno)
848 : : {
849 : 23277 : int prevnumif = numif;
850 : 23277 : int single_line_for_tracker = 1;
851 : : try
852 : : {
853 : 23277 : string current_line;
854 [ + + + + : 23277 : if (numif==numtrue || (numtrue+1==numif && stribegin(line, "elseif ")))
+ + ]
855 : : {
856 : 21798 : callstack_push cs_push(callstack_entry_type::LINE, line, lineno);
857 : 21798 : string tmp=replace_macro_args(line);
858 : 21747 : tmp.qnormalize();
859 : 21747 : resolvedefines(current_line, tmp);
860 : 21814 : }
861 : : else current_line=line;
862 : :
863 : 23210 : callstack_push cs_push(callstack_entry_type::LINE, current_line, lineno);
864 : :
865 [ + + + + ]: 23210 : 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 : 258 : string macro_name = current_line.data()+6;
870 : 258 : char * startpar=strqchr(macro_name.data(), '(');
871 [ + - ]: 258 : if (startpar) *startpar=0;
872 : 258 : 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 : 258 : in_macro_def++;
879 [ + + ]: 258 : if (!pass)
880 : : {
881 [ + + + + ]: 86 : if (in_macro_def == 1) startmacro(current_line.data()+6);
882 : 16 : else tomacro(current_line);
883 : : }
884 : 258 : }
885 [ + + + + ]: 22952 : else if (!stricmp(current_line, "endmacro") && numif==numtrue)
886 : : {
887 [ - + - - ]: 252 : if (in_macro_def == 0) asar_throw_error(0, error_type_line, error_id_misplaced_endmacro);
888 : : else
889 : : {
890 : 252 : in_macro_def--;
891 : 252 : macro_defs.remove(in_macro_def);
892 [ + + ]: 252 : if (!pass)
893 : : {
894 [ + + + - ]: 84 : if (in_macro_def == 0) endmacro(true);
895 : 15 : else tomacro(current_line);
896 : : }
897 : : }
898 : : }
899 [ + + ]: 22700 : else if (in_macro_def > 0)
900 : : {
901 [ + + + - ]: 1140 : if (!pass) tomacro(current_line);
902 : : }
903 : : else
904 : : {
905 : 21560 : assembleline(filename, lineno, current_line, single_line_for_tracker);
906 : : }
907 : 23781 : }
908 : 571 : catch (errline&) {}
909 [ + + ]: 20013 : return (numif != prevnumif || single_line_for_tracker == 3)
910 [ + + + + : 48336 : && (whilestatus[numif].iswhile || whilestatus[numif].is_for);
+ + ]
911 : : }
912 : :
913 : :
914 : 115 : void parse_std_includes(const char* textfile, autoarray<string>& outarray)
915 : : {
916 : 115 : char* content = readfilenative(textfile);
917 : :
918 [ + - ]: 115 : if (content != nullptr)
919 : : {
920 : : char* pos = content;
921 : :
922 [ + + ]: 345 : while (pos[0] != '\0')
923 : : {
924 : 230 : string stdinclude;
925 : :
926 : : do
927 : : {
928 [ + - + + ]: 4370 : if (pos[0] != '\r' && pos[0] != '\n')
929 : : {
930 : 4255 : stdinclude += pos[0];
931 : : }
932 : 4370 : pos++;
933 [ + + + + ]: 4370 : } while (pos[0] != '\0' && pos[0] != '\n');
934 : :
935 : 230 : strip_whitespace(stdinclude);
936 : :
937 [ + + ]: 230 : if (stdinclude != "")
938 : : {
939 [ + - - + ]: 115 : if (!path_is_absolute(stdinclude))
940 : : {
941 : 0 : stdinclude = dir(textfile) + stdinclude;
942 : : }
943 : 115 : outarray.append(normalize_path(stdinclude));
944 : : }
945 : 230 : }
946 : :
947 : 115 : free(content);
948 : : }
949 : 115 : }
950 : :
951 : 115 : 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 : 115 : builtindefines.create("assembler") = "asar";
958 : 115 : builtindefines.create("assembler_ver") = dec(get_version_int());
959 : 115 : builtindefines.create("assembler_time") = dec(time(nullptr));
960 : :
961 [ + - ]: 115 : if(textfile == nullptr) return;
962 : :
963 : 115 : char* content = readfilenative(textfile);
964 : :
965 [ + - ]: 115 : if (content != nullptr)
966 : : {
967 : : char* pos = content;
968 [ + + ]: 805 : while (*pos != 0) {
969 : 690 : string define_name;
970 : 690 : string define_val;
971 : :
972 [ + + + + ]: 7360 : while (*pos != '=' && *pos != '\n') {
973 : 6670 : define_name += *pos;
974 : 6670 : pos++;
975 : : }
976 [ + + ]: 690 : if (*pos != 0 && *pos != '\n') pos++; // skip =
977 [ + - + + ]: 3105 : while (*pos != 0 && *pos != '\n') {
978 : 2415 : define_val += *pos;
979 : 2415 : pos++;
980 : : }
981 [ + - ]: 690 : if (*pos != 0)
982 : 690 : pos++; // skip \n
983 : : // clean define_name
984 : 690 : strip_whitespace(define_name);
985 : 690 : define_name.strip_prefix('!'); // remove leading ! if present
986 : :
987 [ + + ]: 690 : if (define_name == "")
988 : : {
989 [ + - ]: 115 : if (define_val == "")
990 : : {
991 : 115 : continue;
992 : : }
993 : :
994 : 0 : asar_throw_error(pass, error_type_null, error_id_stddefines_no_identifier);
995 : : }
996 : :
997 [ - + - - ]: 575 : 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 : 575 : string cleaned_defval;
1002 : :
1003 [ + + ]: 575 : if (*defval == 0) {
1004 : : // no value
1005 [ - + - - ]: 115 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
1006 : 115 : clidefines.create(define_name) = "";
1007 : 115 : continue;
1008 : : }
1009 : :
1010 [ + + - + ]: 690 : while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace in beginning
1011 [ + + ]: 460 : if (*defval == '"') {
1012 : 115 : defval++; // skip opening quote
1013 [ + + + - ]: 1610 : while (*defval != '"' && *defval != 0)
1014 : 1495 : cleaned_defval += *defval++;
1015 : :
1016 [ - + ]: 115 : if (*defval == 0) {
1017 : 0 : asar_throw_error(pass, error_type_null, error_id_mismatched_quotes);
1018 : : }
1019 : 115 : defval++; // skip closing quote
1020 [ - + - + ]: 115 : while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace
1021 [ - + - - ]: 115 : if (*defval != 0 && *defval != '\n')
1022 : 0 : asar_throw_error(pass, error_type_null, error_id_stddefine_after_closing_quote);
1023 : :
1024 [ - + - - ]: 115 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
1025 : 115 : clidefines.create(define_name) = cleaned_defval;
1026 : 115 : continue;
1027 : : }
1028 : : else
1029 : : {
1030 : : // slightly hacky way to remove trailing whitespace
1031 : : const char* defval_end = strchr(defval, '\n'); // slightly hacky way to get end of string or newline
1032 [ + - ]: 345 : if (!defval_end) defval_end = strchr(defval, 0);
1033 : 345 : defval_end--;
1034 [ + + - + ]: 460 : while (*defval_end == ' ' || *defval_end == '\t') defval_end--;
1035 : 345 : cleaned_defval = string(defval, (int)(defval_end - defval + 1));
1036 : :
1037 [ - + - - ]: 345 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
1038 : 345 : clidefines.create(define_name) = cleaned_defval;
1039 : 345 : continue;
1040 : 345 : }
1041 : :
1042 : 690 : }
1043 : 115 : 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 : 69 : static void clearmacro(const string & key, macrodata* & macro)
1052 : : {
1053 : : (void)key;
1054 : 69 : freemacro(macro);
1055 : 69 : }
1056 : :
1057 : 133 : static void clearfile(const string & key, sourcefile& filecontent)
1058 : : {
1059 : : (void)key;
1060 : 133 : cfree(filecontent.data);
1061 : 133 : cfree(filecontent.contents);
1062 : 133 : }
1063 : : #undef cfree
1064 : :
1065 : 1495 : static void adddefine(const string & key, string & value)
1066 : : {
1067 [ + - ]: 1495 : if (!defines.exists(key)) defines.create(key) = value;
1068 : 1495 : }
1069 : :
1070 : : static string symbolfile;
1071 : :
1072 : 0 : static void printsymbol_wla(const string& key, snes_label& label)
1073 : : {
1074 : 0 : string line = hex((label.pos & 0xFF0000)>>16, 2)+":"+hex(label.pos & 0xFFFF, 4)+" "+key+"\n";
1075 : 0 : symbolfile += line;
1076 : 0 : }
1077 : :
1078 : 0 : static void printsymbol_nocash(const string& key, snes_label& label)
1079 : : {
1080 : 0 : string line = hex(label.pos & 0xFFFFFF, 8)+" "+key+"\n";
1081 : 0 : symbolfile += line;
1082 : 0 : }
1083 : :
1084 : 0 : string create_symbols_file(string format, uint32_t romCrc){
1085 : 0 : format = lower(format);
1086 : : symbolfile = "";
1087 [ # # ]: 0 : if(format == "wla")
1088 : : {
1089 : : symbolfile = "; wla symbolic information file\n";
1090 : 0 : symbolfile += "; generated by asar\n";
1091 : :
1092 : 0 : symbolfile += "\n[labels]\n";
1093 : 0 : labels.each(printsymbol_wla);
1094 : :
1095 : 0 : symbolfile += "\n[source files]\n";
1096 : : const autoarray<AddressToLineMapping::FileInfo>& addrToLineFileList = addressToLineMapping.getFileList();
1097 [ # # ]: 0 : for (int i = 0; i < addrToLineFileList.count; ++i)
1098 : : {
1099 : : char addrToFileListStr[256];
1100 : 0 : snprintf(addrToFileListStr, 256, "%.4x %.8x %s\n",
1101 : : i,
1102 : 0 : addrToLineFileList[i].fileCrc,
1103 : : addrToLineFileList[i].filename.data()
1104 : : );
1105 : 0 : symbolfile += addrToFileListStr;
1106 : : }
1107 : :
1108 : 0 : symbolfile += "\n[rom checksum]\n";
1109 : : {
1110 : : char romCrcStr[32];
1111 : 0 : snprintf(romCrcStr, 32, "%.8x\n",
1112 : : romCrc
1113 : : );
1114 : 0 : symbolfile += romCrcStr;
1115 : : }
1116 : :
1117 : 0 : symbolfile += "\n[addr-to-line mapping]\n";
1118 : : const autoarray<AddressToLineMapping::AddrToLineInfo>& addrToLineInfo = addressToLineMapping.getAddrToLineInfo();
1119 [ # # ]: 0 : for (int i = 0; i < addrToLineInfo.count; ++i)
1120 : : {
1121 : : char addrToLineStr[32];
1122 : 0 : snprintf(addrToLineStr, 32, "%.2x:%.4x %.4x:%.8x\n",
1123 : 0 : (addrToLineInfo[i].addr & 0xFF0000) >> 16,
1124 : 0 : addrToLineInfo[i].addr & 0xFFFF,
1125 : 0 : addrToLineInfo[i].fileIdx & 0xFFFF,
1126 : 0 : addrToLineInfo[i].line & 0xFFFFFFFF
1127 : : );
1128 : 0 : symbolfile += addrToLineStr;
1129 : : }
1130 : :
1131 : : }
1132 [ # # ]: 0 : else if (format == "nocash")
1133 : : {
1134 : : symbolfile = ";no$sns symbolic information file\n";
1135 : 0 : symbolfile += ";generated by asar\n";
1136 : 0 : symbolfile += "\n";
1137 : 0 : labels.each(printsymbol_nocash);
1138 : : }
1139 : 0 : return symbolfile;
1140 : : }
1141 : :
1142 : :
1143 : 1 : bool in_top_level_file()
1144 : : {
1145 : : int num_files = 0;
1146 [ + + ]: 5 : for (int i = callstack.count-1; i >= 0; --i)
1147 : : {
1148 [ + + ]: 4 : if (callstack[i].type == callstack_entry_type::FILE)
1149 : : {
1150 : 1 : num_files++;
1151 [ + - ]: 1 : if (num_files > 1) break;
1152 : : }
1153 : : }
1154 : 1 : return (num_files <= 1);
1155 : : }
1156 : :
1157 : 4243 : const char* get_current_file_name()
1158 : : {
1159 [ + + ]: 15827 : for (int i = callstack.count-1; i >= 0; --i)
1160 : : {
1161 [ + + ]: 15492 : if (callstack[i].type == callstack_entry_type::FILE)
1162 : 3908 : return callstack[i].content.raw();
1163 : : }
1164 : : return nullptr;
1165 : : }
1166 : :
1167 : 4278 : int get_current_line()
1168 : : {
1169 [ + - ]: 12694 : for (int i = callstack.count-1; i >= 0; --i)
1170 : : {
1171 [ + + ]: 16972 : if (callstack[i].type == callstack_entry_type::LINE) return callstack[i].lineno;
1172 : : }
1173 : : return -1;
1174 : : }
1175 : :
1176 : 115 : const char* get_current_block()
1177 : : {
1178 [ + + ]: 121 : for (int i = callstack.count-1; i >= 0; --i)
1179 : : {
1180 [ + + + + ]: 315 : 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 : 115 : void reseteverything()
1187 : : {
1188 : 115 : string str;
1189 : 115 : labels.reset();
1190 : 115 : defines.reset();
1191 : 115 : builtindefines.each(adddefine);
1192 : 115 : clidefines.each(adddefine);
1193 : 115 : structs.reset();
1194 : :
1195 : 115 : macros.each(clearmacro);
1196 : 115 : macros.reset();
1197 : :
1198 : 115 : filecontents.each(clearfile);
1199 : 115 : filecontents.reset();
1200 : :
1201 : 115 : writtenblocks.reset();
1202 : :
1203 : 115 : optimizeforbank=-1;
1204 : 115 : optimize_dp = optimize_dp_flag::NONE;
1205 : 115 : dp_base = 0;
1206 : 115 : optimize_address = optimize_address_flag::DEFAULT;
1207 : :
1208 : 115 : closecachedfiles();
1209 : :
1210 : 115 : incsrcdepth=0;
1211 : 115 : label_counter = 0;
1212 : 115 : errored = false;
1213 : 115 : checksum_fix_enabled = true;
1214 : 115 : force_checksum_fix = false;
1215 : :
1216 : 115 : in_macro_def = 0;
1217 : :
1218 : : #ifndef ASAR_SHARED
1219 : 115 : free(const_cast<unsigned char*>(romdata_r));
1220 : : #endif
1221 : :
1222 : 115 : callstack.reset();
1223 : 115 : simple_callstacks = true;
1224 : : #undef free
1225 : 115 : }
|