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 : 115 : int get_version_int()
47 : : {
48 : 115 : return asarver_maj * 10000 + asarver_min * 100 + asarver_bug;
49 : : }
50 : :
51 : 52 : bool setmapper()
52 : : {
53 : 26 : int maxscore=-99999;
54 : 26 : mapper_t bestmap=lorom;
55 : 52 : mapper_t maps[]={lorom, hirom, exlorom, exhirom};
56 [ + + ]: 260 : for (size_t mapid=0;mapid<sizeof(maps)/sizeof(maps[0]);mapid++)
57 : : {
58 : 208 : mapper=maps[mapid];
59 : 104 : int score=0;
60 : 104 : int highbits=0;
61 : 104 : bool foundnull=false;
62 [ + + ]: 4576 : for (int i=0;i<21;i++)
63 : : {
64 : 4368 : unsigned char c=romdata[snestopc(0x00FFC0+i)];
65 [ + + - + ]: 4368 : 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 [ - + ]: 4368 : if (c>=128) highbits++;
67 [ + + ]: 4368 : else if (is_upper(c)) score+=3;
68 [ + + ]: 3618 : else if (c==' ') score+=2;
69 [ - + ]: 3318 : else if (is_digit(c)) score+=1;
70 [ - + ]: 3318 : else if (is_lower(c)) score+=1;
71 [ - + ]: 3318 : else if (c=='-') score+=1;
72 [ + + ]: 3318 : else if (!c) foundnull=true;
73 : 0 : else score-=3;
74 : : }
75 [ - + - - ]: 208 : if (highbits>0 && highbits<=14) score-=21;//high bits set on some, but not all, bytes = unlikely to be a ROM
76 [ + + + + ]: 234 : if ((romdata[snestopc(0x00FFDE)]^romdata[snestopc(0x00FFDC)])!=0xFF ||
77 [ - + ]: 130 : (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 [ + + ]: 208 : if (score>maxscore)
80 : : {
81 : 26 : maxscore=score;
82 : 26 : bestmap=mapper;
83 : : }
84 : : }
85 : 52 : mapper=bestmap;
86 : :
87 : : //detect oddball mappers
88 : 52 : int mapperbyte=romdata[snestopc(0x00FFD5)];
89 : 52 : int romtypebyte=romdata[snestopc(0x00FFD6)];
90 [ + - ]: 52 : if (mapper==lorom)
91 : : {
92 [ - + - - : 52 : if (mapperbyte==0x23 && (romtypebyte==0x32 || romtypebyte==0x34 || romtypebyte==0x35)) mapper=sa1rom;
- - - - ]
93 : : }
94 : 52 : 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 [ + + ]: 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 : 272 : string top_level_file_dir;
113 [ + - ]: 272 : for (int i = 0; i < callstack.count; ++i)
114 : : {
115 [ + - + - ]: 272 : if (callstack[i].type == callstack_entry_type::FILE)
116 : : {
117 : 272 : top_level_file_dir = dir(callstack[i].content);
118 : 272 : break;
119 : : }
120 : : }
121 : 272 : return top_level_file_dir;
122 : 0 : }
123 : :
124 : 230 : string generate_call_details_string(const char* current_block, const char* current_call, int indentation, bool add_lines)
125 : : {
126 : 230 : string e;
127 [ + + - + ]: 230 : if (current_block != nullptr || current_call != nullptr)
128 : : {
129 : 206 : string indent;
130 [ - + - - ]: 206 : if (add_lines) indent += "|";
131 [ + + + + ]: 1030 : for (; indentation > 0; --indentation) indent += " ";
132 : :
133 [ + - + - : 309 : if (current_block != nullptr) e += STR "\n"+indent+"in block: ["+current_block+"]";
+ - + - +
- + - +
- ]
134 [ - + - - : 206 : if (current_call != nullptr) e += STR "\n"+indent+"in macro call: [%"+current_call+"]";
- - - - -
- - - -
- ]
135 : 206 : }
136 : 230 : return e;
137 : 0 : }
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 : 408 : 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 : 544 : return STR current_file
152 [ + + + + : 680 : + (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 : 0 : 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 : 230 : void get_current_line_details(string* location, string* details, bool exclude_block)
180 : : {
181 : 115 : const char* current_file = nullptr;
182 : 115 : const char* current_block = nullptr;
183 : 115 : const char* current_call = nullptr;
184 : 115 : int current_line_no = -1;
185 [ + - ]: 772 : for (int i = callstack.count-1; i >= 0 ; --i)
186 : : {
187 [ + - + + : 772 : switch (callstack[i].type)
- ]
188 : : {
189 : 115 : case callstack_entry_type::FILE:
190 : 115 : current_file = callstack[i].content;
191 [ + + ]: 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 : 0 : case callstack_entry_type::MACRO_CALL:
196 [ # # ]: 0 : if (current_call == nullptr) current_call = callstack[i].content;
197 : 0 : break;
198 : 218 : case callstack_entry_type::LINE:
199 [ + + + - ]: 218 : if (current_block == nullptr && current_call == nullptr) current_block = callstack[i].content;
200 [ + - ]: 327 : if (current_line_no == -1) current_line_no = callstack[i].lineno;
201 : 109 : break;
202 : 324 : case callstack_entry_type::BLOCK:
203 [ + + ]: 324 : if (current_block == nullptr) current_block = callstack[i].content;
204 : 162 : break;
205 : : }
206 : : }
207 : 0 : *location = "";
208 : 0 : *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 : 0 : const char* current_file = nullptr;
215 : 0 : const char* current_block = nullptr;
216 : 0 : const char* current_call = nullptr;
217 : 0 : 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 : 0 : current_file = callstack[i].content;
228 : 0 : current_block = nullptr;
229 : 0 : current_call = nullptr;
230 : 0 : current_line_no = -1;
231 : 0 : break;
232 : 0 : case callstack_entry_type::MACRO_CALL:
233 : 0 : current_block = nullptr;
234 : 0 : current_call = callstack[i].content;
235 : 0 : break;
236 : 0 : case callstack_entry_type::LINE:
237 : 0 : current_line_no = callstack[i].lineno;
238 : 0 : current_block = callstack[i].content;
239 : 0 : break;
240 : 0 : case callstack_entry_type::BLOCK:
241 : 0 : 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 : 230 : string get_simple_callstack()
267 : : {
268 : : int i;
269 : 115 : const char* current_call = nullptr;
270 [ + + ]: 13870 : for (i = callstack.count-1; i >= 0 ; --i)
271 : : {
272 [ + + ]: 13682 : if (callstack[i].type == callstack_entry_type::MACRO_CALL)
273 : : {
274 : 21 : current_call = callstack[i].content;
275 : 42 : break;
276 : : }
277 : : }
278 : :
279 : 115 : const char* current_file = nullptr;
280 : 115 : int current_line_no = -1;
281 [ + + ]: 230 : if (current_call != nullptr)
282 : : {
283 : 21 : bool stop = false;
284 [ + - ]: 168 : for (int j = i-1; j >= 0 ; --j)
285 : : {
286 [ + - + + : 168 : switch (callstack[j].type)
- ]
287 : : {
288 : 42 : case callstack_entry_type::FILE:
289 [ - + ]: 42 : if (current_file != nullptr)
290 : : {
291 : 0 : stop = true;
292 : 0 : break;
293 : : }
294 : 21 : current_file = callstack[j].content;
295 : 42 : break;
296 : 0 : case callstack_entry_type::MACRO_CALL:
297 : 0 : stop = true;
298 : 0 : break;
299 : 42 : case callstack_entry_type::LINE:
300 [ + - ]: 63 : if (current_line_no == -1) current_line_no = callstack[j].lineno;
301 : 21 : break;
302 : 42 : case callstack_entry_type::BLOCK:
303 : 42 : break;
304 : : }
305 : :
306 [ + + + - ]: 168 : if (current_file != nullptr && current_line_no != -1) stop = true;
307 : :
308 [ + + ]: 147 : if (stop) break;
309 : : }
310 : : }
311 : :
312 : 230 : string e;
313 [ + + + - ]: 230 : if (current_call != nullptr && current_file != nullptr)
314 : : {
315 : 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 [ + - ]: 230 : if (simple_callstacks)
324 : 230 : return get_simple_callstack();
325 : : else
326 : 0 : return get_full_callstack();
327 : : }
328 : :
329 : 18 : asar_error_id vfile_error_to_error_id(virtual_file_error vfile_error)
330 : : {
331 [ + - + - : 9 : switch (vfile_error)
- ]
332 : : {
333 : 3 : case vfe_doesnt_exist:
334 : 3 : return error_id_file_not_found;
335 : 0 : case vfe_access_denied:
336 : 0 : return error_id_failed_to_open_file_access_denied;
337 : 6 : case vfe_not_regular_file:
338 : 6 : return error_id_failed_to_open_not_regular_file;
339 : 0 : case vfe_unknown:
340 : : case vfe_none:
341 : : case vfe_num_errors:
342 : 0 : return error_id_failed_to_open_file;
343 : : }
344 : :
345 : 0 : return error_id_failed_to_open_file;
346 : : }
347 : :
348 : 18 : virtual_file_error asar_get_last_io_error()
349 : : {
350 [ + - ]: 18 : if (filesystem != nullptr)
351 : : {
352 : 18 : return filesystem->get_last_error();
353 : : }
354 : :
355 : 0 : 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 [ + + ]: 558 : if (!exists)
365 : : {
366 : 46 : return 2;
367 : : }
368 [ + + + + : 466 : else if((optimize_dp == optimize_dp_flag::RAM) && bank == 0x7E && (word-dp_base < 0x100))
+ + ]
369 : : {
370 : 3 : return 1;
371 : : }
372 [ + + + - : 460 : else if(optimize_dp == optimize_dp_flag::ALWAYS && (bank == 0x7E || !(bank & 0x40)) && (word-dp_base < 0x100))
+ - + + ]
373 : : {
374 : 9 : return 1;
375 : : }
376 [ + + + + : 442 : else if (optimize_address == optimize_address_flag::RAM && bank == 0x7E && word < 0x2000)
+ - ]
377 : : {
378 : 3 : return 2;
379 : : }
380 [ + + + + : 436 : else if (optimize_address == optimize_address_flag::MIRRORS && (bank == relaxed_bank || (!(bank & 0x40) && !(relaxed_bank & 0x40))) && word < 0x2000)
+ - + - +
+ ]
381 : : {
382 : 3 : return 2;
383 : : }
384 [ + + + - : 430 : else if (optimize_address == optimize_address_flag::MIRRORS && !(bank & 0x40) && !(relaxed_bank & 0x40) && word < 0x8000)
+ - + + ]
385 : : {
386 : 0 : return 2;
387 : : }
388 [ + + ]: 430 : else if (optimizeforbank>=0)
389 : : {
390 [ + + ]: 12 : if (thislabel.freespace_id > 0) return 3;
391 [ + + ]: 12 : else if (bank==(unsigned int)optimizeforbank) return 2;
392 : 12 : else return 3;
393 : : }
394 [ + + - + ]: 418 : else if (thislabel.freespace_id > 0 || freespaceid > 0)
395 : : {
396 : : // TODO: check whether they're pinned to the same bank
397 [ + + ]: 38 : if (thislabel.freespace_id != freespaceid) return 3;
398 : 20 : else return 2;
399 : : }
400 [ + + ]: 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 [ + + ]: 3260 : if (*str=='$')
407 : : {
408 : 2772 : str++;
409 [ + + ]: 12600 : while(is_xdigit(*str)) {
410 : 9828 : str++;
411 : : }
412 [ + + ]: 2772 : if(*str=='\0'){
413 : 1386 : return true;
414 : : }
415 : : }
416 : 244 : 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 [ + + + + ]: 2996 : if (posnegname.length() > 0)
428 : : {
429 [ - + ]: 60 : if (*posneglabel != '\0') goto notposneglabel;
430 : :
431 [ + + ]: 100 : if (!pass) return 2;
432 : 40 : snes_label label_data;
433 : : // RPG Hacker: Umm... what kind of magic constant is this?
434 : 40 : label_data.pos = 31415926;
435 : 40 : bool found = labelval(posnegname, &label_data);
436 : 40 : return getlenforlabel(label_data, found);
437 : : }
438 : 2936 : notposneglabel:
439 : 1468 : int len=0;
440 [ + + ]: 6012 : while (*str)
441 : : {
442 : 1538 : int thislen=0;
443 : 3076 : bool maybebankextraction=(str==orgstr);
444 [ + + ]: 3076 : if (*str=='$')
445 : : {
446 : 2358 : str++;
447 : : int i;
448 [ + + ]: 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 [ - + ]: 718 : 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 [ - + - - ]: 718 : else if (str[0]=='\'' && str[2]=='\'')
463 : : {
464 : 0 : thislen=1;
465 : 0 : str+=3;
466 : : }
467 [ + + ]: 718 : else if (is_digit(*str))
468 : : {
469 : 100 : int val=strtol(str, const_cast<char**>(&str), 10);
470 [ + - ]: 100 : if (val>=0) thislen=1;
471 [ + + ]: 100 : if (val>=256) thislen=2;
472 [ - + ]: 59 : if (val>=65536) thislen=3;
473 : : }
474 [ + + + - : 618 : else if (is_ualpha(*str) || *str=='.' || *str=='?')
+ + + + ]
475 : : {
476 : 518 : snes_label thislabel;
477 : 518 : bool exists=labelval(&str, &thislabel);
478 : 518 : thislen=getlenforlabel(thislabel, exists);
479 : : }
480 : 100 : else str++;
481 [ + + + + ]: 3076 : if (optimizebankextraction && maybebankextraction &&
482 [ + - + - : 310 : (!strcmp(str, ">>16") || !strcmp(str, "/65536") || !strcmp(str, "/$10000")))
+ + ]
483 : 0 : return 1;
484 [ + + ]: 1538 : if (thislen>len) len=thislen;
485 : : }
486 : 1468 : 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 : 2492 : bool validatedefinename(const char * name)
516 : : {
517 [ - + ]: 2492 : if (!name[0]) return false;
518 [ + + ]: 28650 : for (int i = 0;name[i];i++)
519 : : {
520 [ - + ]: 26158 : if (!is_ualnum(name[i])) return false;
521 : : }
522 : :
523 : 1246 : return true;
524 : : }
525 : :
526 : 56331 : void resolvedefines(string& out, const char * start)
527 : : {
528 : 56331 : recurseblock rec;
529 : 29270 : const char * here=start;
530 [ + + ]: 56329 : if (!strchr(here, '!'))
531 : : {
532 : 45949 : out += here;
533 : 24080 : return;
534 : : }
535 [ + + ]: 81162 : while (*here)
536 : : {
537 [ + + + + ]: 70812 : if (here[0] == '\\' && here[1] == '\\')
538 : : {
539 : : // allow using \\ as escape sequence
540 [ - + - - ]: 6 : if (in_macro_def > 0) out += "\\";
541 : 6 : out += "\\";
542 : 6 : here += 2;
543 : : }
544 [ + + + - ]: 70806 : else if (here[0] == '\\' && here[1] == '!')
545 : : {
546 : : // allow using \! to escape !
547 [ + + + - ]: 42 : if (in_macro_def > 0) out += "\\";
548 : 42 : out+="!";
549 : 42 : here += 2;
550 : : }
551 [ + + ]: 70764 : else if (*here=='!')
552 : : {
553 [ + + + + : 11130 : bool first=(here==start || (here>=start+4 && here[-1]==' ' && here[-2]==':' && here[-3]==' '));//check if it's the start of a block
+ + + + -
+ ]
554 : 11130 : string defname;
555 : 11130 : here++;
556 : :
557 : 5565 : int depth = 0;
558 [ + + ]: 11244 : for (const char* depth_str = here; *depth_str=='^'; depth_str++)
559 : : {
560 : 114 : depth++;
561 : : }
562 : 11130 : here += depth;
563 : :
564 [ + + ]: 11130 : if (depth != in_macro_def)
565 : : {
566 [ + - ]: 1320 : out += '!';
567 [ + + + + ]: 1386 : for (int i=0; i < depth; ++i) out += '^';
568 [ + + - + ]: 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 : 651 : continue;
570 : 1302 : }
571 : :
572 [ + + ]: 9810 : if (*here=='{')
573 : : {
574 : 108 : here++;
575 : 108 : string unprocessedname;
576 : 54 : int braces=1;
577 : : while (true)
578 : : {
579 [ + + ]: 1152 : if (*here=='{') braces++;
580 [ + + ]: 1152 : if (*here=='}') braces--;
581 [ - + - - ]: 1152 : if (!*here) asar_throw_error(0, error_type_line, error_id_mismatched_braces);
582 [ + + ]: 1152 : if (!braces) break;
583 : 1044 : unprocessedname+=*here++;
584 : : }
585 : 108 : here++;
586 : 108 : resolvedefines(defname, unprocessedname);
587 [ - + - - ]: 108 : if (!validatedefinename(defname)) asar_throw_error(0, error_type_line, error_id_invalid_define_name);
588 : 108 : }
589 : : else
590 : : {
591 [ + + + + ]: 59496 : while (is_ualnum(*here)) defname+=*here++;
592 : : }
593 : :
594 [ + + ]: 9810 : if (first)
595 : : {
596 : : enum {
597 : : null,
598 : : append,
599 : : expand,
600 : : domath,
601 : : setifnotset,
602 : : } mode;
603 : : if(0);
604 [ + + ]: 5214 : else if (stribegin(here, " = ")) { here+=3; mode=null; }
605 [ + + ]: 4722 : else if (stribegin(here, " += ")) { here+=4; mode=append; }
606 [ + + ]: 4620 : else if (stribegin(here, " := ")) { here+=4; mode=expand; }
607 [ + + ]: 4566 : else if (stribegin(here, " #= ")) { here+=4; mode=domath; }
608 [ + + ]: 2640 : else if (stribegin(here, " ?= ")) { here+=4; mode=setifnotset; }
609 : 2634 : else goto notdefineset;
610 : 2580 : string val;
611 [ + + ]: 2580 : if (*here=='"')
612 : : {
613 : 168 : here++;
614 : : while (true)
615 : : {
616 [ + + ]: 858 : if (*here=='"')
617 : : {
618 [ + + + - ]: 174 : if (!here[1] || here[1]==' ') break;
619 [ + - ]: 6 : else if (here[1]=='"') here++;
620 : 0 : 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 [ + + + + : 18990 : while (*here && *here!=' ') val+=*here++;
+ - ]
629 : : }
630 : : //if (strqchr(val.data(), ';')) *strqchr(val.data(), ';')=0;
631 [ - + - - : 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 : 2580 : val.qnormalize();
636 : :
637 : : // RPG Hacker: throw an error if we're trying to overwrite built-in defines.
638 [ + + + + ]: 2580 : if (builtindefines.exists(defname))
639 : : {
640 : 6 : asar_throw_error(0, error_type_line, error_id_overriding_builtin_define, defname.data());
641 : : }
642 : :
643 [ + + + + : 2574 : switch (mode)
+ - ]
644 : : {
645 : 243 : case null:
646 : : {
647 : 486 : defines.create(defname) = val;
648 : 243 : break;
649 : : }
650 : 51 : case append:
651 : : {
652 [ + + - + : 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 : 102 : val=oldval+val;
655 : 102 : defines.create(defname) = val;
656 : 51 : break;
657 : 102 : }
658 : 54 : case expand:
659 : : {
660 : 54 : string newval;
661 : 54 : resolvedefines(newval, val);
662 : 54 : defines.create(defname) = newval;
663 : 27 : break;
664 : 54 : }
665 : 1926 : case domath:
666 : : {
667 : 1926 : string newval;
668 : 1926 : resolvedefines(newval, val);
669 : 1926 : double num= getnumdouble(newval);
670 [ + + + + : 1926 : if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_line, error_id_define_label_math);
- + ]
671 : 1920 : defines.create(defname) = ftostr(num);
672 : 960 : break;
673 : 1926 : }
674 : 3 : case setifnotset:
675 : : {
676 [ + - + - : 6 : if (!defines.exists(defname)) defines.create(defname) = val;
+ - + - ]
677 : 3 : break;
678 : : }
679 : : }
680 : 2580 : }
681 : : else
682 : : {
683 : 4596 : notdefineset:
684 [ + + + + : 7230 : if (!defname) out+="!";
+ - ]
685 : : else
686 : : {
687 [ + + - + : 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 : 7200 : resolvedefines(out, thisone);
691 : 7200 : }
692 : : }
693 : : }
694 : 11130 : }
695 : 59634 : else out+=*here++;
696 : : }
697 [ + - + + : 10350 : if (!confirmquotes(out)) { asar_throw_error(0, error_type_null, error_id_mismatched_quotes); out = ""; }
+ - + - ]
698 : 56329 : }
699 : :
700 : : bool moreonline;
701 : : bool asarverallowed = false;
702 : :
703 : 45331 : void assembleline(const char * fname, int linenum, const char * line, int& single_line_for_tracker)
704 : : {
705 : 45331 : recurseblock rec;
706 : 45331 : bool moreonlinetmp=moreonline;
707 : : // randomdude999: redundant, assemblefile already converted the path to absolute
708 : : //string absolutepath = filesystem->create_absolute_path("", fname);
709 : 45331 : string absolutepath = fname;
710 : 45331 : single_line_for_tracker = 1;
711 : : try
712 : : {
713 : 45331 : string out=line;
714 : 45331 : out.qreplace(": :", ": :");
715 [ + - ]: 45331 : autoptr<char**> blocks=qsplitstr(out.temp_raw(), " : ");
716 : 45331 : moreonline=true;
717 [ + + ]: 89161 : for (int block=0;moreonline;block++)
718 : : {
719 : 47047 : moreonline=(blocks[block+1] != nullptr);
720 : : try
721 : : {
722 : 47047 : string stripped_block = strip_whitespace(blocks[block]);
723 : :
724 : 47047 : callstack_push cs_push(callstack_entry_type::BLOCK, stripped_block);
725 : :
726 : 47047 : assembleblock(stripped_block, single_line_for_tracker);
727 : 43536 : checkbankcross();
728 : 50562 : }
729 : 3515 : catch (errblock&) {}
730 [ + + ]: 43830 : if (blocks[block][0]!='\0') asarverallowed=false;
731 [ + + ]: 43830 : if(single_line_for_tracker == 1) single_line_for_tracker = 0;
732 : : }
733 : 48548 : }
734 : 3217 : catch (errline&) {}
735 : 42114 : moreonline=moreonlinetmp;
736 : 48548 : }
737 : :
738 : : int incsrcdepth=0;
739 : :
740 : : // Returns true if a file is protected via
741 : : // an "includeonce".
742 : 4061 : bool file_included_once(const char* file)
743 : : {
744 [ + + ]: 4259 : for (int i = 0; i < includeonce.count; ++i)
745 : : {
746 [ + + ]: 240 : if (includeonce[i] == file)
747 : : {
748 : 21 : return true;
749 : : }
750 : : }
751 : :
752 : 3115 : return false;
753 : : }
754 : :
755 : : autoarray<string> macro_defs;
756 : : int in_macro_def=0;
757 : :
758 : 4031 : void assemblefile(const char * filename)
759 : : {
760 : 4031 : incsrcdepth++;
761 : 4031 : string absolutepath = filesystem->create_absolute_path(get_current_file_name(), filename);
762 : :
763 [ + + + + ]: 4031 : if (file_included_once(absolutepath))
764 : : {
765 : 21 : return;
766 : : }
767 : :
768 : 3989 : callstack_push cs_push(callstack_entry_type::FILE, absolutepath);
769 : :
770 : : sourcefile file;
771 : 3989 : file.contents = nullptr;
772 : 3989 : file.numlines = 0;
773 : 3989 : int startif=numif;
774 [ + + + + ]: 3989 : if (!filecontents.exists(absolutepath))
775 : : {
776 : 274 : char * temp = readfile(absolutepath, "");
777 [ + + ]: 272 : if (!temp)
778 : : {
779 : 6 : asar_throw_error(0, error_type_null, vfile_error_to_error_id(asar_get_last_io_error()), filename);
780 : :
781 : 3 : return;
782 : : }
783 : 266 : sourcefile& newfile = filecontents.create(absolutepath);
784 : 266 : newfile.contents =split(temp, '\n');
785 : 266 : newfile.data = temp;
786 [ + + ]: 10990 : for (int i=0;newfile.contents[i];i++)
787 : : {
788 : 10724 : newfile.numlines++;
789 : 5362 : char * line= newfile.contents[i];
790 : 10724 : char * comment = strqchr(line, ';');
791 [ + + ]: 10724 : if(comment) *comment = 0;
792 [ + - + + : 10724 : 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 : 10724 : newfile.contents[i] = strip_whitespace(line);
794 : : }
795 [ + + ]: 10990 : for(int i=0;newfile.contents[i];i++)
796 : : {
797 : 5362 : char* line = newfile.contents[i];
798 [ + + ]: 10724 : if(!*line) continue;
799 [ + + + - ]: 6810 : 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 : 5 : char* otherline = newfile.contents[i+j];
803 : 10 : char* line_end = line + strlen(line);
804 [ + + ]: 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 : 266 : file = newfile;
811 : : } else { // filecontents.exists(absolutepath)
812 : 3715 : file = filecontents.find(absolutepath);
813 : : }
814 : 3981 : asarverallowed=true;
815 [ + + + - ]: 42507 : for (int i=0;file.contents[i] && i<file.numlines;i++)
816 : : {
817 : 41745 : string connectedline;
818 [ + - ]: 41745 : int skiplines = getconnectedlines<char**>(file.contents, i, connectedline);
819 : :
820 : 41745 : bool was_loop_end = do_line_logic(connectedline, absolutepath, i);
821 : 38526 : i += skiplines;
822 : :
823 : : // if a loop ended on this line, should it run again?
824 [ + + + + : 38526 : if (was_loop_end && whilestatus[numif].cond)
+ + + + ]
825 : 1332 : i = whilestatus[numif].startline - 1;
826 : 41745 : }
827 [ + + ]: 774 : while (in_macro_def > 0)
828 : : {
829 : 12 : asar_throw_error(0, error_type_null, error_id_unclosed_macro, macro_defs[in_macro_def-1].data());
830 [ + + + + : 12 : if (!pass && in_macro_def == 1) endmacro(false);
+ - ]
831 : 12 : in_macro_def--;
832 : 12 : macro_defs.remove(in_macro_def);
833 : : }
834 [ - + ]: 762 : 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 : 762 : incsrcdepth--;
841 : 7255 : }
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 : 48765 : bool do_line_logic(const char* line, const char* filename, int lineno)
848 : : {
849 : 48765 : int prevnumif = numif;
850 : 48765 : int single_line_for_tracker = 1;
851 : : try
852 : : {
853 : 48765 : string current_line;
854 [ + + + + : 48765 : if (numif==numtrue || (numtrue+1==numif && stribegin(line, "elseif ")))
+ + + + ]
855 : : {
856 : 45807 : callstack_push cs_push(callstack_entry_type::LINE, line, lineno);
857 : 45807 : string tmp=replace_macro_args(line);
858 : 45705 : tmp.qnormalize();
859 : 45705 : resolvedefines(current_line, tmp);
860 : 45839 : }
861 : 1479 : else current_line=line;
862 : :
863 : 48631 : callstack_push cs_push(callstack_entry_type::LINE, current_line, lineno);
864 : :
865 [ + + + + : 48631 : 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 [ + - ]: 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 [ + + ]: 516 : if (!pass)
880 : : {
881 [ + + + + ]: 172 : if (in_macro_def == 1) startmacro(current_line.data()+6);
882 : 32 : else tomacro(current_line);
883 : : }
884 : 516 : }
885 [ + + + + : 48115 : else if (!stricmp(current_line, "endmacro") && numif==numtrue)
+ + + + ]
886 : : {
887 [ - + - - ]: 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 [ + + ]: 504 : if (!pass)
893 : : {
894 [ + + + - ]: 168 : if (in_macro_def == 0) endmacro(true);
895 : 30 : else tomacro(current_line);
896 : : }
897 : : }
898 : : }
899 [ + + ]: 47611 : else if (in_macro_def > 0)
900 : : {
901 [ + + + - ]: 2280 : if (!pass) tomacro(current_line);
902 : : }
903 : : else
904 : : {
905 : 45331 : assembleline(filename, lineno, current_line, single_line_for_tracker);
906 : : }
907 : 51984 : }
908 : 3353 : catch (errline&) {}
909 [ + + ]: 40026 : return (numif != prevnumif || single_line_for_tracker == 3)
910 [ + + + + : 91122 : && (whilestatus[numif].iswhile || whilestatus[numif].is_for);
+ + + - +
+ ]
911 : : }
912 : :
913 : :
914 : 230 : void parse_std_includes(const char* textfile, autoarray<string>& outarray)
915 : : {
916 : 230 : char* content = readfilenative(textfile);
917 : :
918 [ + - ]: 230 : if (content != nullptr)
919 : : {
920 : 115 : char* pos = content;
921 : :
922 [ + + ]: 690 : while (pos[0] != '\0')
923 : : {
924 : 460 : string stdinclude;
925 : :
926 : : do
927 : : {
928 [ + - + + ]: 8970 : if (pos[0] != '\r' && pos[0] != '\n')
929 : : {
930 : 8740 : stdinclude += pos[0];
931 : : }
932 : 8970 : pos++;
933 [ + + + + ]: 8970 : } while (pos[0] != '\0' && pos[0] != '\n');
934 : :
935 : 460 : strip_whitespace(stdinclude);
936 : :
937 [ + + ]: 460 : if (stdinclude != "")
938 : : {
939 [ + - - + ]: 230 : if (!path_is_absolute(stdinclude))
940 : : {
941 : 0 : stdinclude = dir(textfile) + stdinclude;
942 : : }
943 : 230 : outarray.append(normalize_path(stdinclude));
944 : : }
945 : 460 : }
946 : :
947 : 230 : free(content);
948 : : }
949 : 230 : }
950 : :
951 : 230 : 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 : 230 : builtindefines.create("assembler") = "asar";
958 : 230 : builtindefines.create("assembler_ver") = dec(get_version_int());
959 : 230 : builtindefines.create("assembler_time") = dec(time(nullptr));
960 : :
961 [ + + ]: 230 : if(textfile == nullptr) return;
962 : :
963 : 230 : char* content = readfilenative(textfile);
964 : :
965 [ + - ]: 230 : if (content != nullptr)
966 : : {
967 : 115 : char* pos = content;
968 [ + + ]: 1610 : while (*pos != 0) {
969 : 1380 : string define_name;
970 : 1380 : string define_val;
971 : :
972 [ + + + + ]: 14720 : while (*pos != '=' && *pos != '\n') {
973 : 13340 : define_name += *pos;
974 : 13340 : pos++;
975 : : }
976 [ + + + + ]: 1380 : if (*pos != 0 && *pos != '\n') pos++; // skip =
977 [ + - + + ]: 6210 : while (*pos != 0 && *pos != '\n') {
978 : 4830 : define_val += *pos;
979 : 4830 : pos++;
980 : : }
981 [ + - ]: 1380 : if (*pos != 0)
982 : 1380 : pos++; // skip \n
983 : : // clean define_name
984 : 1380 : strip_whitespace(define_name);
985 [ + - ]: 1380 : define_name.strip_prefix('!'); // remove leading ! if present
986 : :
987 [ + + ]: 1380 : if (define_name == "")
988 : : {
989 [ + - ]: 230 : if (define_val == "")
990 : : {
991 : 230 : continue;
992 : : }
993 : :
994 : 0 : asar_throw_error(pass, error_type_null, error_id_stddefines_no_identifier);
995 : : }
996 : :
997 [ - + - - ]: 1150 : 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 : 575 : const char* defval = define_val.data();
1001 : 1150 : string cleaned_defval;
1002 : :
1003 [ + + ]: 1150 : if (*defval == 0) {
1004 : : // no value
1005 [ + + - + : 230 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
- - ]
1006 : 230 : clidefines.create(define_name) = "";
1007 : 230 : continue;
1008 : : }
1009 : :
1010 [ + + - + ]: 1380 : while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace in beginning
1011 [ + + ]: 920 : if (*defval == '"') {
1012 : 230 : defval++; // skip opening quote
1013 [ + + + - ]: 3220 : while (*defval != '"' && *defval != 0)
1014 : 2990 : cleaned_defval += *defval++;
1015 : :
1016 [ - + ]: 230 : if (*defval == 0) {
1017 : 0 : asar_throw_error(pass, error_type_null, error_id_mismatched_quotes);
1018 : : }
1019 : 230 : defval++; // skip closing quote
1020 [ - + - + ]: 230 : while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace
1021 [ - + - - ]: 230 : if (*defval != 0 && *defval != '\n')
1022 : 0 : asar_throw_error(pass, error_type_null, error_id_stddefine_after_closing_quote);
1023 : :
1024 [ + + - + : 230 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
- - ]
1025 : 230 : clidefines.create(define_name) = cleaned_defval;
1026 : 230 : continue;
1027 : : }
1028 : : else
1029 : : {
1030 : : // slightly hacky way to remove trailing whitespace
1031 : 345 : const char* defval_end = strchr(defval, '\n'); // slightly hacky way to get end of string or newline
1032 [ + - ]: 690 : if (!defval_end) defval_end = strchr(defval, 0);
1033 : 690 : defval_end--;
1034 [ + + - + ]: 920 : while (*defval_end == ' ' || *defval_end == '\t') defval_end--;
1035 : 690 : cleaned_defval = string(defval, (int)(defval_end - defval + 1));
1036 : :
1037 [ + + - + : 690 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
- - ]
1038 : 690 : clidefines.create(define_name) = cleaned_defval;
1039 : 690 : continue;
1040 : 690 : }
1041 : :
1042 : 1380 : }
1043 : 230 : 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 : 266 : static void clearfile(const string & key, sourcefile& filecontent)
1058 : : {
1059 : : (void)key;
1060 : 266 : cfree(filecontent.data);
1061 : 266 : cfree(filecontent.contents);
1062 : 266 : }
1063 : : #undef cfree
1064 : :
1065 : 2990 : static void adddefine(const string & key, string & value)
1066 : : {
1067 [ + - ]: 2990 : if (!defines.exists(key)) defines.create(key) = value;
1068 : 2990 : }
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 : 0 : symbolfile = "";
1087 [ # # ]: 0 : if(format == "wla")
1088 : : {
1089 : 0 : 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 : 0 : 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 : 0 : 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 : 0 : 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 : 0 : 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 : 2 : bool in_top_level_file()
1144 : : {
1145 : 1 : int num_files = 0;
1146 [ + + ]: 10 : for (int i = callstack.count-1; i >= 0; --i)
1147 : : {
1148 [ + + ]: 8 : if (callstack[i].type == callstack_entry_type::FILE)
1149 : : {
1150 : 2 : num_files++;
1151 [ + + ]: 2 : if (num_files > 1) break;
1152 : : }
1153 : : }
1154 : 2 : return (num_files <= 1);
1155 : : }
1156 : :
1157 : 12908 : const char* get_current_file_name()
1158 : : {
1159 [ + + ]: 49342 : for (int i = callstack.count-1; i >= 0; --i)
1160 : : {
1161 [ + + ]: 48672 : if (callstack[i].type == callstack_entry_type::FILE)
1162 : 12238 : return callstack[i].content.raw();
1163 : : }
1164 : 335 : return nullptr;
1165 : : }
1166 : :
1167 : 8556 : int get_current_line()
1168 : : {
1169 [ + - ]: 25388 : for (int i = callstack.count-1; i >= 0; --i)
1170 : : {
1171 [ + + ]: 29666 : if (callstack[i].type == callstack_entry_type::LINE) return callstack[i].lineno;
1172 : : }
1173 : 0 : return -1;
1174 : : }
1175 : :
1176 : 230 : const char* get_current_block()
1177 : : {
1178 [ + + ]: 242 : for (int i = callstack.count-1; i >= 0; --i)
1179 : : {
1180 [ + + + + : 431 : if (callstack[i].type == callstack_entry_type::LINE || callstack[i].type == callstack_entry_type::BLOCK) return callstack[i].content.raw();
+ + ]
1181 : : }
1182 : 5 : return nullptr;
1183 : : }
1184 : :
1185 : :
1186 : 230 : void reseteverything()
1187 : : {
1188 : 230 : string str;
1189 : 230 : labels.reset();
1190 : 230 : defines.reset();
1191 : 230 : builtindefines.each(adddefine);
1192 : 230 : clidefines.each(adddefine);
1193 : 230 : structs.reset();
1194 : :
1195 : 230 : macros.each(clearmacro);
1196 : 230 : macros.reset();
1197 : :
1198 : 230 : filecontents.each(clearfile);
1199 : 230 : filecontents.reset();
1200 : :
1201 : 230 : writtenblocks.reset();
1202 : :
1203 : 230 : optimizeforbank=-1;
1204 : 230 : optimize_dp = optimize_dp_flag::NONE;
1205 : 230 : dp_base = 0;
1206 : 230 : optimize_address = optimize_address_flag::DEFAULT;
1207 : :
1208 : 230 : closecachedfiles();
1209 : :
1210 : 230 : incsrcdepth=0;
1211 : 230 : label_counter = 0;
1212 : 230 : errored = false;
1213 : 230 : checksum_fix_enabled = true;
1214 : 230 : force_checksum_fix = false;
1215 : :
1216 : 230 : in_macro_def = 0;
1217 : :
1218 : : #ifndef ASAR_SHARED
1219 : 230 : free(const_cast<unsigned char*>(romdata_r));
1220 : : #endif
1221 : :
1222 : 230 : callstack.reset();
1223 : 230 : simple_callstacks = true;
1224 : : #undef free
1225 : 230 : }
|