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 "std-includes.h"
5 : : #include "libsmw.h"
6 : : #include "libstr.h"
7 : : #include "assocarr.h"
8 : : #include "autoarray.h"
9 : : #include "asar.h"
10 : : #include "virtualfile.h"
11 : : #include "warnings.h"
12 : : #include "platform/file-helpers.h"
13 : : #include "assembleblock.h"
14 : : #include "asar_math.h"
15 : : #include "macro.h"
16 : : #include <cstdint>
17 : :
18 : : // randomdude999: remember to also update the .rc files (in res/windows/) when changing this.
19 : : // Couldn't find a way to automate this without shoving the version somewhere in the CMake files
20 : : const int asarver_maj=1;
21 : : const int asarver_min=9;
22 : : const int asarver_bug=0;
23 : : const bool asarver_beta=true;
24 : : bool default_math_pri=false;
25 : : bool default_math_round_off=false;
26 : : extern bool suppress_all_warnings;
27 : :
28 : : #ifdef _I_RELEASE
29 : : extern char blockbetareleases[(!asarver_beta)?1:-1];
30 : : #endif
31 : : #ifdef _I_DEBUG
32 : : extern char blockreleasedebug[(asarver_beta)?1:-1];
33 : : #endif
34 : :
35 : : unsigned const char * romdata_r;
36 : : int romlen_r;
37 : :
38 : : int pass;
39 : :
40 : : int optimizeforbank=-1;
41 : : int optimize_dp = optimize_dp_flag::NONE;
42 : : int dp_base = 0;
43 : : int optimize_address = optimize_address_flag::DEFAULT;
44 : :
45 : : string thisfilename;
46 : : int thisline;
47 : : const char * thisblock;
48 : :
49 : : string callerfilename;
50 : : int callerline=-1;
51 : :
52 : : bool errored=false;
53 : : bool ignoretitleerrors=false;
54 : :
55 : : volatile int recursioncount=0;
56 : :
57 : : virtual_filesystem* filesystem = nullptr;
58 : :
59 : : AddressToLineMapping addressToLineMapping;
60 : :
61 : 0 : int get_version_int()
62 : : {
63 : 0 : return asarver_maj * 10000 + asarver_min * 100 + asarver_bug;
64 : : }
65 : :
66 : 23 : bool setmapper()
67 : : {
68 : : int maxscore=-99999;
69 : : mapper_t bestmap=lorom;
70 : 23 : mapper_t maps[]={lorom, hirom, exlorom, exhirom};
71 [ + + ]: 115 : for (size_t mapid=0;mapid<sizeof(maps)/sizeof(maps[0]);mapid++)
72 : : {
73 : 92 : mapper=maps[mapid];
74 : : int score=0;
75 : : int highbits=0;
76 : : bool foundnull=false;
77 [ + + ]: 2024 : for (int i=0;i<21;i++)
78 : : {
79 : 1932 : unsigned char c=romdata[snestopc(0x00FFC0+i)];
80 [ - + ]: 1932 : 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
81 [ - + ]: 1932 : if (c>=128) highbits++;
82 [ + + ]: 1932 : else if (is_upper(c)) score+=3;
83 [ + + ]: 1602 : else if (c==' ') score+=2;
84 [ - + ]: 1470 : else if (is_digit(c)) score+=1;
85 [ - + ]: 1470 : else if (is_lower(c)) score+=1;
86 [ - + ]: 1470 : else if (c=='-') score+=1;
87 [ - + ]: 1470 : else if (!c) foundnull=true;
88 : 0 : else score-=3;
89 : : }
90 [ - + ]: 92 : if (highbits>0 && highbits<=14) score-=21;//high bits set on some, but not all, bytes = unlikely to be a ROM
91 [ + + ]: 92 : if ((romdata[snestopc(0x00FFDE)]^romdata[snestopc(0x00FFDC)])!=0xFF ||
92 [ - + ]: 23 : (romdata[snestopc(0x00FFDF)]^romdata[snestopc(0x00FFDD)])!=0xFF) score=-99999;//checksum doesn't match up to 0xFFFF? Not a ROM.
93 : : //too lazy to check the real checksum
94 [ + + ]: 92 : if (score>maxscore)
95 : : {
96 : : maxscore=score;
97 : : bestmap=mapper;
98 : : }
99 : : }
100 : 23 : mapper=bestmap;
101 : :
102 : : //detect oddball mappers
103 : 23 : int mapperbyte=romdata[snestopc(0x00FFD5)];
104 : 23 : int romtypebyte=romdata[snestopc(0x00FFD6)];
105 [ + - ]: 23 : if (mapper==lorom)
106 : : {
107 [ - + - - : 23 : if (mapperbyte==0x23 && (romtypebyte==0x32 || romtypebyte==0x34 || romtypebyte==0x35)) mapper=sa1rom;
- - ]
108 : : }
109 : 23 : return (maxscore>=0);
110 : : }
111 : :
112 : 93 : string getdecor()
113 : : {
114 : 93 : string e;
115 [ + - ]: 93 : if (thisfilename)
116 : : {
117 : 93 : e+=STR thisfilename;
118 [ + - ]: 186 : if (thisline!=-1) e+=STR ":"+dec(thisline+1);
119 [ + + ]: 107 : if (callerfilename) e+=STR" (called from "+callerfilename+":"+dec(callerline+1)+")";
120 : 93 : e+=": ";
121 : : }
122 : 93 : return e;
123 : : }
124 : :
125 : 3 : asar_error_id vfile_error_to_error_id(virtual_file_error vfile_error)
126 : : {
127 : : switch (vfile_error)
128 : : {
129 : : case vfe_doesnt_exist:
130 : : return error_id_file_not_found;
131 : : case vfe_access_denied:
132 : : return error_id_failed_to_open_file_access_denied;
133 : : case vfe_not_regular_file:
134 : : return error_id_failed_to_open_not_regular_file;
135 : : case vfe_unknown:
136 : : case vfe_none:
137 : : case vfe_num_errors:
138 : : return error_id_failed_to_open_file;
139 : : }
140 : :
141 : : return error_id_failed_to_open_file;
142 : : }
143 : :
144 : 3 : virtual_file_error asar_get_last_io_error()
145 : : {
146 [ + - ]: 3 : if (filesystem != nullptr)
147 : : {
148 : 3 : return filesystem->get_last_error();
149 : : }
150 : :
151 : : return vfe_unknown;
152 : : }
153 : :
154 : : static bool freespaced;
155 : 259 : static int getlenforlabel(int insnespos, int thislabel, bool exists)
156 : : {
157 [ - + - - ]: 259 : if (warnxkas && (((unsigned int)(thislabel^insnespos)&0xFFFF0000)==0))
158 : 0 : asar_throw_warning(1, warning_id_xkas_label_access);
159 : 259 : unsigned int bank = thislabel>>16;
160 : 259 : unsigned int word = thislabel&0xFFFF;
161 : 259 : unsigned int relaxed_bank = optimizeforbank < 0 ? 0 : optimizeforbank;
162 [ + + ]: 259 : if (!exists)
163 : : {
164 [ + + ]: 39 : if (!freespaced) freespaceextra++;
165 : 39 : freespaced=true;
166 : 39 : return 2;
167 : : }
168 [ + + + + : 220 : else if((optimize_dp == optimize_dp_flag::RAM) && bank == 0x7E && (word-dp_base < 0x100))
- + ]
169 : : {
170 : : return 1;
171 : : }
172 [ + + + - : 217 : else if(optimize_dp == optimize_dp_flag::ALWAYS && (bank == 0x7E || !(bank & 0x40)) && (word-dp_base < 0x100))
+ - + + ]
173 : : {
174 : : return 1;
175 : : }
176 [ + + + + ]: 211 : else if (optimize_address == optimize_address_flag::RAM && bank == 0x7E && word < 0x2000)
177 : : {
178 : : return 2;
179 : : }
180 [ + + + - : 208 : else if (optimize_address == optimize_address_flag::MIRRORS && (bank == relaxed_bank || (!(bank & 0x40) && !(relaxed_bank & 0x40))) && word < 0x2000)
+ - + - +
- ]
181 : : {
182 : : return 2;
183 : : }
184 [ - + - - : 205 : else if (optimize_address == optimize_address_flag::MIRRORS && !(bank & 0x40) && !(relaxed_bank & 0x40) && word < 0x8000)
- - - - ]
185 : : {
186 : : return 2;
187 : : }
188 [ + + ]: 205 : else if (optimizeforbank>=0)
189 : : {
190 [ + - ]: 2 : if ((unsigned int)thislabel&0xFF000000) return 3;
191 [ - + ]: 2 : else if (bank==(unsigned int)optimizeforbank) return 2;
192 : : else return 3;
193 : : }
194 [ + + ]: 203 : else if ((unsigned int)(thislabel|insnespos)&0xFF000000)
195 : : {
196 [ + + ]: 11 : if ((unsigned int)(thislabel^insnespos)&0xFF000000) return 3;
197 : : else return 2;
198 : : }
199 [ + + ]: 192 : else if ((thislabel^insnespos)&0xFF0000){ return 3; }
200 : : else { return 2;}
201 : : }
202 : :
203 : :
204 : 1572 : bool is_hex_constant(const char* str){
205 [ + + ]: 1572 : if (*str=='$')
206 : : {
207 : 1336 : str++;
208 [ + + ]: 5962 : while(is_xdigit(*str)) {
209 : 4626 : str++;
210 : : }
211 [ - + ]: 1336 : if(*str=='\0'){
212 : : return true;
213 : : }
214 : : }
215 : : return false;
216 : : }
217 : :
218 : 1443 : int getlen(const char * orgstr, bool optimizebankextraction)
219 : : {
220 : 1443 : const char * str=orgstr;
221 : 1443 : freespaced=false;
222 : :
223 : 1443 : const char* posneglabel = str;
224 : 1443 : string posnegname = posneglabelname(&posneglabel, false);
225 : :
226 [ + + ]: 1443 : if (posnegname.length() > 0)
227 : : {
228 [ + + ]: 33 : if (*posneglabel != '\0') goto notposneglabel;
229 : :
230 [ + + ]: 50 : if (!pass) return 2;
231 : : snes_label label_data;
232 : : // RPG Hacker: Umm... what kind of magic constant is this?
233 : 20 : label_data.pos = 31415926;
234 : 20 : bool found = labelval(posnegname, &label_data);
235 : 20 : return getlenforlabel(snespos, (int)label_data.pos, found);
236 : : }
237 : 1410 : notposneglabel:
238 : : int len=0;
239 [ + + ]: 2899 : while (*str)
240 : : {
241 : : int thislen=0;
242 : 1486 : bool maybebankextraction=(str==orgstr);
243 [ + + ]: 1486 : if (*str=='$')
244 : : {
245 : 1132 : str++;
246 : : int i;
247 [ + + ]: 5518 : for (i=0;is_xdigit(str[i]);i++);
248 : : //if (i&1) warn(S dec(i)+"-digit hex value");//blocked in getnum instead
249 : 1132 : thislen=(i+1)/2;
250 : 1132 : str+=i;
251 : : }
252 [ - + ]: 354 : else if (*str=='%')
253 : : {
254 : 0 : str++;
255 : : int i;
256 [ # # ]: 0 : for (i=0;str[i]=='0' || str[i]=='1';i++);
257 : : //if (i&7) warn(S dec(i)+"-digit binary value");
258 : 0 : thislen=(i+7)/8;
259 : 0 : str+=i;
260 : : }
261 [ - + - - ]: 354 : else if (str[0]=='\'' && str[2]=='\'')
262 : : {
263 : : thislen=1;
264 : 0 : str+=3;
265 : : }
266 [ + + ]: 354 : else if (is_digit(*str))
267 : : {
268 : 62 : int val=strtol(str, const_cast<char**>(&str), 10);
269 [ + - ]: 62 : if (val>=0) thislen=1;
270 [ + + ]: 62 : if (val>=256) thislen=2;
271 [ - + ]: 9 : if (val>=65536) thislen=3;
272 [ # # ]: 0 : if (val>=16777216) thislen=4;
273 : : }
274 [ + + ]: 292 : else if (is_alpha(*str) || *str=='_' || *str=='.' || *str=='?')
275 : : {
276 : : snes_label thislabel;
277 : 239 : bool exists=labelval(&str, &thislabel);
278 : 239 : thislen=getlenforlabel(snespos, (int)thislabel.pos, exists);
279 : : }
280 : 53 : else str++;
281 [ + + ]: 1486 : if (optimizebankextraction && maybebankextraction &&
282 [ + - + - : 158 : (!strcmp(str, ">>16") || !strcmp(str, "/65536") || !strcmp(str, "/$10000")))
+ - ]
283 : : return 1;
284 : : if (thislen>len) len=thislen;
285 : : }
286 : 1413 : if (len>3) return 3;
287 : : return len;
288 : 1443 : }
289 : :
290 : : struct strcompare {
291 : : bool operator() (const char * lhs, const char * rhs) const
292 : : {
293 : : return strcmp(lhs, rhs)<0;
294 : : }
295 : : };
296 : :
297 : : struct stricompare {
298 : : bool operator() (const char * lhs, const char * rhs) const
299 : : {
300 : : return stricmp(lhs, rhs)<0;
301 : : }
302 : : };
303 : :
304 : : struct sourcefile {
305 : : char** contents;
306 : : int numlines;
307 : : };
308 : :
309 : : static assocarr<sourcefile> filecontents;
310 : : assocarr<string> defines;
311 : : // needs to be separate because defines is reset between parsing arguments and patching
312 : : assocarr<string> clidefines;
313 : : assocarr<string> builtindefines;
314 : :
315 : 856 : bool validatedefinename(const char * name)
316 : : {
317 [ - + ]: 856 : if (!name[0]) return false;
318 [ + + ]: 9455 : for (int i = 0;name[i];i++)
319 : : {
320 [ - + ]: 8599 : if (!is_ualnum(name[i])) return false;
321 : : }
322 : :
323 : : return true;
324 : : }
325 : :
326 : 753118 : void resolvedefines(string& out, const char * start)
327 : : {
328 : 753118 : recurseblock rec;
329 : : const char * here=start;
330 [ + + ]: 5059954 : while (*here)
331 : : {
332 [ + + + + ]: 4306843 : if (*here=='"' && emulatexkas)
333 : : {
334 : 6 : asar_throw_warning(0, warning_id_feature_deprecated, "xkas define quotes", "removing the quotes generally does what you want");
335 : 6 : out+=*here++;
336 [ + - + + ]: 24 : while (*here && *here!='"') out+=*here++;
337 : 6 : out+=*here++;
338 : : }
339 [ + + + + ]: 4306837 : else if (here[0] == '\\' && here[1] == '\\')
340 : : {
341 : : // allow using \\ as escape sequence
342 : 3 : out += "\\";
343 : 3 : here += 2;
344 : : }
345 [ + + + + ]: 4306834 : else if (here[0] == '\\' && here[1] == '!')
346 : : {
347 : : // allow using \! to escape !
348 : 15 : out+="!";
349 : 15 : here += 2;
350 : : }
351 [ + + ]: 4306819 : else if (*here=='!')
352 : : {
353 [ + + + + : 317598 : bool first=(here==start || (here>=start+4 && here[-1]==' ' && here[-2]==':' && here[-3]==' '));//check if it's the start of a block
+ + - + -
- ]
354 : 317598 : string defname;
355 : 317598 : here++;
356 [ + + ]: 317598 : if (*here=='{')
357 : : {
358 : 54 : here++;
359 : 54 : string unprocessedname;
360 : : int braces=1;
361 : : while (true)
362 : : {
363 [ + + ]: 576 : if (*here=='{') braces++;
364 [ + + ]: 576 : if (*here=='}') braces--;
365 [ - + - - ]: 576 : if (!*here) asar_throw_error(0, error_type_line, error_id_mismatched_braces);
366 [ + + ]: 576 : if (!braces) break;
367 : 522 : unprocessedname+=*here++;
368 : : }
369 : 54 : here++;
370 : 54 : resolvedefines(defname, unprocessedname);
371 [ - + - - ]: 54 : if (!validatedefinename(defname)) asar_throw_error(0, error_type_line, error_id_invalid_define_name);
372 : 54 : }
373 : : else
374 : : {
375 [ + + ]: 644958 : while (is_ualnum(*here)) defname+=*here++;
376 : : }
377 [ - + - - : 317598 : if (warnxkas && here[0]=='(' && here[1]==')')
- - ]
378 : 0 : asar_throw_warning(0, warning_id_xkas_eat_parentheses);
379 : : //if (emulatexkas && here[0]=='(' && here[1]==')') here+=2;
380 : :
381 [ + + ]: 317598 : if (first)
382 : : {
383 : : enum {
384 : : null,
385 : : append,
386 : : expand,
387 : : domath,
388 : : setifnotset,
389 : : } mode;
390 : : if(0);
391 [ + + ]: 211206 : else if (stribegin(here, " = ")) { here+=3; mode=null; }
392 [ - + ]: 211071 : else if (stribegin(here, " += ")) { here+=4; mode=append; }
393 [ - + ]: 211071 : else if (stribegin(here, " := ")) { here+=4; mode=expand; }
394 [ + + ]: 211071 : else if (stribegin(here, " #= ")) { here+=4; mode=domath; }
395 [ + + ]: 105588 : else if (stribegin(here, " ?= ")) { here+=4; mode=setifnotset; }
396 : 105585 : else goto notdefineset;
397 [ - + - - : 105621 : if (emulatexkas && mode != null) asar_throw_warning(0, warning_id_convert_to_asar);
- - ]
398 : : //else if (stribegin(here, " equ ")) here+=5;
399 : 105621 : string val;
400 [ + + ]: 105621 : if (*here=='"')
401 : : {
402 : 21 : here++;
403 : : while (true)
404 : : {
405 [ + + ]: 147 : if (*here=='"')
406 : : {
407 [ + + + - ]: 24 : if (!here[1] || here[1]==' ') break;
408 [ + - ]: 3 : else if (here[1]=='"') here++;
409 : 0 : else asar_throw_error(0, error_type_line, error_id_broken_define_declaration);
410 : : }
411 : 126 : val+=*here++;
412 : : }
413 : 21 : here++;
414 : : }
415 : : else
416 : : {
417 [ + + + - ]: 529968 : while (*here && *here!=' ') val+=*here++;
418 : : }
419 : : //if (strqchr(val.data(), ';')) *strqchr(val.data(), ';')=0;
420 [ - + - - : 105621 : if (*here && !stribegin(here, " : ")) asar_throw_error(0, error_type_line, error_id_broken_define_declaration);
- - ]
421 : 105621 : clean(val);
422 : :
423 : : // RPG Hacker: throw an error if we're trying to overwrite built-in defines.
424 [ + + ]: 105621 : if (builtindefines.exists(defname))
425 : : {
426 : 3 : asar_throw_error(0, error_type_line, error_id_overriding_builtin_define, defname.data());
427 : : }
428 : :
429 [ + - - + : 105618 : switch (mode)
+ ]
430 : : {
431 : : case null:
432 : : {
433 : 132 : defines.create(defname) = val;
434 : : break;
435 : : }
436 : : case append:
437 : : {
438 [ # # # # ]: 0 : if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data());
439 : 0 : string oldval = defines.find(defname);
440 : 0 : val=oldval+val;
441 : 0 : defines.create(defname) = val;
442 : : break;
443 : 0 : }
444 : 0 : case expand:
445 : : {
446 : 0 : string newval;
447 : 0 : resolvedefines(newval, val);
448 : 0 : defines.create(defname) = newval;
449 : : break;
450 : 0 : }
451 : 105483 : case domath:
452 : : {
453 : 105483 : string newval;
454 : 105483 : resolvedefines(newval, val);
455 : 105483 : double num= getnumdouble(newval);
456 [ + + + + : 105483 : if (foundlabel && !foundlabel_static) asar_throw_error(0, error_type_line, error_id_define_label_math);
- + ]
457 : 105480 : defines.create(defname) = ftostr(num);
458 : : break;
459 : 105483 : }
460 : : case setifnotset:
461 : : {
462 [ + - + - ]: 3 : if (!defines.exists(defname)) defines.create(defname) = val;
463 : : break;
464 : : }
465 : : }
466 : 105621 : }
467 : : else
468 : : {
469 : 106392 : notdefineset:
470 [ + + ]: 211977 : if (!defname) out+="!";
471 : : else
472 : : {
473 [ - + - - ]: 211965 : if (!defines.exists(defname)) asar_throw_error(0, error_type_line, error_id_define_not_found, defname.data());
474 : : else {
475 : 211965 : string thisone = defines.find(defname);
476 : 211965 : resolvedefines(out, thisone);
477 : 211965 : }
478 : : }
479 : : }
480 : 317598 : }
481 : 3989221 : else out+=*here++;
482 : : }
483 : 753117 : }
484 : :
485 : : int repeatnext=1;
486 : :
487 : : bool moreonline;
488 : : bool moreonlinecond;
489 : : int fakeendif;
490 : : bool asarverallowed;
491 : : bool istoplevel;
492 : :
493 : 436216 : void assembleline(const char * fname, int linenum, const char * line)
494 : : {
495 : 436216 : recurseblock rec;
496 : 436216 : bool moreonlinetmp=moreonline;
497 : : // randomdude999: redundant, assemblefile already converted the path to absolute
498 : : //string absolutepath = filesystem->create_absolute_path("", fname);
499 : 436216 : string absolutepath = fname;
500 : : thisfilename = absolutepath;
501 : 436216 : thisline=linenum;
502 : 436216 : thisblock= nullptr;
503 : 436216 : single_line_for_tracker = 1;
504 : : try
505 : : {
506 : 436216 : string tmp;
507 [ + + + + ]: 436216 : if(inmacro) tmp = replace_macro_args(line);
508 : : else tmp = line;
509 : 436195 : clean(tmp);
510 : 436195 : string out;
511 [ + + + + ]: 436195 : if (numif==numtrue) resolvedefines(out, tmp);
512 : : else out=tmp;
513 : : // recheck quotes - defines can mess those up sometimes
514 [ + - + + : 436188 : if (!confirmquotes(out)) asar_throw_error(0, error_type_line, error_id_mismatched_quotes);
- + ]
515 : 436185 : out.qreplace(": :", ": :", true);
516 : : //puts(out);
517 [ + - ]: 436185 : autoptr<char**> blocks=qsplit(out.temp_raw(), " : ");
518 : 436185 : moreonline=true;
519 : 436185 : moreonlinecond=true;
520 : 436185 : fakeendif=0;
521 [ + + ]: 872927 : for (int block=0;moreonline;block++)
522 : : {
523 : 436992 : moreonline=(blocks[block+1] != nullptr);
524 : 436992 : int repeatthis=repeatnext;
525 : 436992 : repeatnext=1;
526 [ + + ]: 873734 : for (int i=0;i<repeatthis;i++)
527 : : {
528 : : try
529 : : {
530 : 436992 : string stripped_block = blocks[block];
531 : 436992 : strip_both(stripped_block, ' ', true);
532 : :
533 : 436992 : thisline=linenum;//do not optimize, this one is recursive
534 : 436992 : thisblock = stripped_block.data();
535 : : bool isspecialline = false;
536 [ + + ]: 436992 : if (thisblock[0] == '@')
537 : : {
538 : 6 : asar_throw_warning(0, warning_id_feature_deprecated, "prefixing Asar commands with @ or ;@", "remove the @ or ;@ prefix");
539 : :
540 : : isspecialline = true;
541 : 6 : thisblock++;
542 [ - + ]: 6 : while (is_space(*thisblock))
543 : : {
544 : 0 : thisblock++;
545 : : }
546 : : }
547 : 436992 : assembleblock(thisblock, isspecialline);
548 : 436663 : checkbankcross();
549 : 436992 : }
550 : 330 : catch (errblock&) {}
551 [ + + + + ]: 436742 : if (blocks[block][0]!='\0' && blocks[block][0]!='@') asarverallowed=false;
552 : : }
553 [ + + ]: 436742 : if(single_line_for_tracker == 1) single_line_for_tracker = 0;
554 : : }
555 [ - + ]: 435935 : if(fakeendif)
556 : : {
557 : 0 : thisline = linenum;
558 : 0 : thisblock = blocks[0];
559 : 0 : asar_throw_warning(0, warning_id_feature_deprecated, "inline if statements", "Add an \" : endif\" at the end of the line");
560 [ # # ]: 0 : if (numif==numtrue) numtrue--;
561 : 0 : numif--;
562 : : }
563 : 436726 : }
564 : 281 : catch (errline&) {}
565 : 435965 : moreonline=moreonlinetmp;
566 : 436467 : }
567 : :
568 : : int incsrcdepth=0;
569 : :
570 : : // Returns true if a file is protected via
571 : : // an "includeonce".
572 : 605 : bool file_included_once(const char* file)
573 : : {
574 [ + + ]: 704 : for (int i = 0; i < includeonce.count; ++i)
575 : : {
576 [ + + ]: 120 : if (includeonce[i] == file)
577 : : {
578 : : return true;
579 : : }
580 : : }
581 : :
582 : : return false;
583 : : }
584 : :
585 : 590 : void assemblefile(const char * filename, bool toplevel)
586 : : {
587 : 590 : incsrcdepth++;
588 : 590 : string absolutepath = filesystem->create_absolute_path(thisfilename, filename);
589 : :
590 [ + + ]: 590 : if (file_included_once(absolutepath))
591 : : {
592 : : return;
593 : : }
594 : :
595 : 569 : string prevthisfilename = thisfilename;
596 : : thisfilename = absolutepath;
597 : 569 : int prevline = thisline;
598 : 569 : thisline=-1;
599 : 569 : const char* prevthisblock = thisblock;
600 : 569 : thisblock= nullptr;
601 : : sourcefile file;
602 : 569 : file.contents = nullptr;
603 : 569 : file.numlines = 0;
604 : 569 : int startif=numif;
605 [ + + ]: 569 : if (!filecontents.exists(absolutepath))
606 : : {
607 : 110 : char * temp= readfile(absolutepath, "");
608 [ - + ]: 110 : if (!temp)
609 : : {
610 : : // RPG Hacker: This is so that we hopefully always end up with a proper decor
611 : : // and get better error messages.
612 : : thisfilename = prevthisfilename;
613 : 0 : thisline = prevline;
614 : 0 : thisblock = prevthisblock;
615 : :
616 : 0 : asar_throw_error(0, error_type_null, vfile_error_to_error_id(asar_get_last_io_error()), filename);
617 : :
618 : : return;
619 : : }
620 : 110 : file.contents =split(temp, "\n");
621 [ + + ]: 4616 : for (int i=0;file.contents[i];i++)
622 : : {
623 : 4506 : file.numlines++;
624 : : char * line= file.contents[i];
625 : : char * comment=line;
626 : 4506 : comment = strqchr(comment, ';');
627 [ + + ]: 5740 : while (comment != nullptr)
628 : : {
629 [ + + ]: 1234 : if (comment[1]!='@')
630 : : {
631 : 1232 : comment[0]='\0';
632 : : }
633 : : else
634 : : {
635 : 2 : comment[0] = ' ';
636 : : }
637 : 1234 : comment = strqchr(comment, ';');
638 : : }
639 [ + + ]: 5205 : while (strqchr(line, '\t')) *strqchr(line, '\t')=' ';
640 [ + - + + : 4506 : if (!confirmquotes(line)) { thisline = i; thisblock = line; asar_throw_error(0, error_type_null, error_id_mismatched_quotes); line[0] = '\0'; }
+ - ]
641 : 4506 : itrim(line, " ", " ", true); //todo make use strip
642 : : }
643 [ + + ]: 4616 : for(int i=0;file.contents[i];i++)
644 : : {
645 : : char* line = file.contents[i];
646 [ + + + + : 4511 : for (int j=1;strqrchr(line, ',') && !strqrchr(line, ',')[1] && file.contents[i+j];j++)
+ - ]
647 : : {
648 : : // not using strcat because the source and dest overlap here
649 : : char* otherline = file.contents[i+j];
650 : 5 : char* line_end = line + strlen(line);
651 [ + + ]: 69 : while(*otherline) *line_end++ = *otherline++;
652 : 5 : *line_end = '\0';
653 : : static char nullstr[]="";
654 : 5 : file.contents[i+j]=nullstr;
655 : : }
656 : : }
657 : 110 : filecontents.create(absolutepath) = file;
658 : : } else { // filecontents.exists(absolutepath)
659 : 459 : file = filecontents.find(absolutepath);
660 : : }
661 : : bool in_macro_def=false;
662 : 569 : asarverallowed=true;
663 [ + + + - ]: 436366 : for (int i=0;file.contents[i] && i<file.numlines;i++)
664 : : {
665 : : try
666 : : {
667 : : thisfilename= absolutepath;
668 : 436048 : thisline=i;
669 : 436048 : thisblock= nullptr;
670 : 436048 : istoplevel=toplevel;
671 [ + + - + ]: 436048 : if (stribegin(file.contents[i], "macro ") && numif==numtrue)
672 : : {
673 [ + - - + : 78 : if (in_macro_def || inmacro) asar_throw_error(0, error_type_line, error_id_nested_macro_definition);
- - ]
674 : : in_macro_def=true;
675 [ + + + + ]: 78 : if (!pass) startmacro(file.contents[i]+6);
676 : : }
677 [ + + + - ]: 435970 : else if (!stricmp(file.contents[i], "endmacro") && numif==numtrue)
678 : : {
679 [ - + - - ]: 78 : if (!in_macro_def) asar_throw_error(0, error_type_line, error_id_misplaced_endmacro);
680 : : in_macro_def=false;
681 [ + + + - ]: 78 : if (!pass) endmacro(true);
682 : : }
683 [ + + ]: 435892 : else if (in_macro_def)
684 : : {
685 [ + + + - ]: 192 : if (!pass) tomacro(file.contents[i]);
686 : : }
687 : : else
688 : : {
689 : 435700 : int prevnumif = numif;
690 : 435700 : string connectedline;
691 : 435700 : int skiplines = getconnectedlines<char**>(file.contents, i, connectedline);
692 : 435700 : assembleline(absolutepath, i, connectedline);
693 : : thisfilename = absolutepath;
694 : 435449 : i += skiplines;
695 [ + + + + : 646901 : if ((numif != prevnumif || single_line_for_tracker == 3) && (whilestatus[numif].iswhile || whilestatus[numif].is_for) && whilestatus[numif].cond)
+ + + + +
+ ]
696 : 105480 : i = whilestatus[numif].startline - 1;
697 : 435700 : }
698 : : }
699 : 252 : catch (errline&) {}
700 : : }
701 : 318 : thisline++;
702 : 318 : thisblock= nullptr;
703 [ - + ]: 318 : if (in_macro_def)
704 : : {
705 : 0 : asar_throw_error(0, error_type_null, error_id_unclosed_macro);
706 [ # # # # ]: 0 : if (!pass) endmacro(false);
707 : : }
708 [ - + ]: 318 : if (repeatnext!=1)
709 : : {
710 : 0 : repeatnext=1;
711 : 0 : asar_throw_error(0, error_type_null, error_id_rep_at_file_end);
712 : : }
713 [ - + ]: 318 : if (numif!=startif)
714 : : {
715 : 0 : numif=startif;
716 : 0 : numtrue=startif;
717 : 0 : asar_throw_error(0, error_type_null, error_id_unclosed_if);
718 : : }
719 : 318 : incsrcdepth--;
720 : 841 : }
721 : :
722 : 95 : void parse_std_includes(const char* textfile, autoarray<string>& outarray)
723 : : {
724 : 95 : char* content = readfilenative(textfile);
725 : :
726 [ + - ]: 95 : if (content != nullptr)
727 : : {
728 : : char* pos = content;
729 : :
730 [ + + ]: 285 : while (pos[0] != '\0')
731 : : {
732 : 190 : string stdinclude;
733 : :
734 : : do
735 : : {
736 [ + - + + ]: 3610 : if (pos[0] != '\r' && pos[0] != '\n')
737 : : {
738 : 3515 : stdinclude += pos[0];
739 : : }
740 : 3610 : pos++;
741 [ + + + + ]: 3610 : } while (pos[0] != '\0' && pos[0] != '\n');
742 : :
743 : 190 : stdinclude = strip_whitespace(stdinclude);
744 : :
745 [ + + ]: 190 : if (stdinclude != "")
746 : : {
747 [ + - - + ]: 95 : if (!path_is_absolute(stdinclude))
748 : : {
749 : 0 : stdinclude = dir(textfile) + stdinclude;
750 : : }
751 : 95 : outarray.append(normalize_path(stdinclude));
752 : : }
753 : 190 : }
754 : :
755 : 95 : free(content);
756 : : }
757 : 95 : }
758 : :
759 : 95 : void parse_std_defines(const char* textfile)
760 : : {
761 : :
762 : : // RPG Hacker: add built-in defines.
763 : : // (They're not really standard defines, but I was lazy and this was
764 : : // one convenient place for doing it).
765 : 95 : builtindefines.create("assembler") = "asar";
766 : 95 : builtindefines.create("assembler_ver") = dec(get_version_int());
767 : :
768 [ + - ]: 95 : if(textfile == nullptr) return;
769 : :
770 : 95 : char* content = readfilenative(textfile);
771 : :
772 [ + - ]: 95 : if (content != nullptr)
773 : : {
774 : : char* pos = content;
775 [ + + ]: 665 : while (*pos != 0) {
776 : 570 : string define_name;
777 : 570 : string define_val;
778 : :
779 [ + + + + ]: 6080 : while (*pos != '=' && *pos != '\n') {
780 : 5510 : define_name += *pos;
781 : 5510 : pos++;
782 : : }
783 [ + + ]: 570 : if (*pos != 0 && *pos != '\n') pos++; // skip =
784 [ + - + + ]: 2565 : while (*pos != 0 && *pos != '\n') {
785 : 1995 : define_val += *pos;
786 : 1995 : pos++;
787 : : }
788 [ + - ]: 570 : if (*pos != 0)
789 : 570 : pos++; // skip \n
790 : : // clean define_name
791 : 570 : define_name = strip_whitespace(define_name);
792 [ + - ]: 570 : define_name = strip_prefix(define_name, '!', false); // remove leading ! if present
793 : :
794 [ + + ]: 570 : if (define_name == "")
795 : : {
796 [ + - ]: 95 : if (define_val == "")
797 : : {
798 : 95 : continue;
799 : : }
800 : :
801 : 0 : asar_throw_error(pass, error_type_null, error_id_stddefines_no_identifier);
802 : : }
803 : :
804 [ - + - - ]: 475 : if (!validatedefinename(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_invalid, "stddefines.txt", define_name.data());
805 : :
806 : : // clean define_val
807 : : const char* defval = define_val.data();
808 : 475 : string cleaned_defval;
809 : :
810 [ + + ]: 475 : if (*defval == 0) {
811 : : // no value
812 [ - + - - ]: 95 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
813 : 95 : clidefines.create(define_name) = "";
814 : 95 : continue;
815 : : }
816 : :
817 [ + + - + ]: 570 : while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace in beginning
818 [ + + ]: 380 : if (*defval == '"') {
819 : 95 : defval++; // skip opening quote
820 [ + + + - ]: 1330 : while (*defval != '"' && *defval != 0)
821 : 1235 : cleaned_defval += *defval++;
822 : :
823 [ - + ]: 95 : if (*defval == 0) {
824 : 0 : asar_throw_error(pass, error_type_null, error_id_mismatched_quotes);
825 : : }
826 : 95 : defval++; // skip closing quote
827 [ - + - + ]: 95 : while (*defval == ' ' || *defval == '\t') defval++; // skip whitespace
828 [ - + - - ]: 95 : if (*defval != 0 && *defval != '\n')
829 : 0 : asar_throw_error(pass, error_type_null, error_id_stddefine_after_closing_quote);
830 : :
831 [ - + - - ]: 95 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
832 : 95 : clidefines.create(define_name) = cleaned_defval;
833 : 95 : continue;
834 : : }
835 : : else
836 : : {
837 : : // slightly hacky way to remove trailing whitespace
838 : : const char* defval_end = strchr(defval, '\n'); // slightly hacky way to get end of string or newline
839 [ + - ]: 285 : if (!defval_end) defval_end = strchr(defval, 0);
840 : 285 : defval_end--;
841 [ + + - + ]: 380 : while (*defval_end == ' ' || *defval_end == '\t') defval_end--;
842 : 285 : cleaned_defval = string(defval, (int)(defval_end - defval + 1));
843 : :
844 [ - + - - ]: 285 : if (clidefines.exists(define_name)) asar_throw_error(pass, error_type_null, error_id_cmdl_define_override, "Std define", define_name.data());
845 : 285 : clidefines.create(define_name) = cleaned_defval;
846 : 285 : continue;
847 : 285 : }
848 : :
849 : 570 : }
850 : 95 : free(content);
851 : : }
852 : : }
853 : :
854 : : bool checksum_fix_enabled = true;
855 : : bool force_checksum_fix = false;
856 : :
857 : : #define cfree(x) free((void*)x)
858 : 26 : static void clearmacro(const string & key, macrodata* & macro)
859 : : {
860 : : (void)key;
861 : 26 : macro->lines.~autoarray();
862 : 26 : cfree(macro->fname);
863 : 26 : cfree(macro->arguments[0]);
864 : 26 : cfree(macro->arguments);
865 : 26 : cfree(macro);
866 : 26 : }
867 : :
868 : 110 : static void clearfile(const string & key, sourcefile& filecontent)
869 : : {
870 : : (void)key;
871 : 110 : cfree(*filecontent.contents);
872 : 110 : cfree(filecontent.contents);
873 : 110 : }
874 : :
875 : 950 : static void adddefine(const string & key, string & value)
876 : : {
877 [ + - ]: 950 : if (!defines.exists(key)) defines.create(key) = value;
878 : 950 : }
879 : :
880 : : static string symbolfile;
881 : :
882 : 0 : static void printsymbol_wla(const string& key, snes_label& label)
883 : : {
884 : 0 : string line = hex2((label.pos & 0xFF0000)>>16)+":"+hex4(label.pos & 0xFFFF)+" "+key+"\n";
885 : 0 : symbolfile += line;
886 : 0 : }
887 : :
888 : 0 : static void printsymbol_nocash(const string& key, snes_label& label)
889 : : {
890 : 0 : string line = hex8(label.pos & 0xFFFFFF)+" "+key+"\n";
891 : 0 : symbolfile += line;
892 : 0 : }
893 : :
894 : 0 : string create_symbols_file(string format, uint32_t romCrc){
895 : 0 : format = lower(format);
896 : : symbolfile = "";
897 [ # # ]: 0 : if(format == "wla")
898 : : {
899 : : symbolfile = "; wla symbolic information file\n";
900 : 0 : symbolfile += "; generated by asar\n";
901 : :
902 : 0 : symbolfile += "\n[labels]\n";
903 : 0 : labels.each(printsymbol_wla);
904 : :
905 : 0 : symbolfile += "\n[source files]\n";
906 : : const autoarray<AddressToLineMapping::FileInfo>& addrToLineFileList = addressToLineMapping.getFileList();
907 [ # # ]: 0 : for (int i = 0; i < addrToLineFileList.count; ++i)
908 : : {
909 : : char addrToFileListStr[256];
910 : 0 : snprintf(addrToFileListStr, 256, "%.4x %.8x %s\n",
911 : : i,
912 : 0 : addrToLineFileList[i].fileCrc,
913 : : addrToLineFileList[i].filename.data()
914 : : );
915 : 0 : symbolfile += addrToFileListStr;
916 : : }
917 : :
918 : 0 : symbolfile += "\n[rom checksum]\n";
919 : : {
920 : : char romCrcStr[32];
921 : 0 : snprintf(romCrcStr, 32, "%.8x\n",
922 : : romCrc
923 : : );
924 : 0 : symbolfile += romCrcStr;
925 : : }
926 : :
927 : 0 : symbolfile += "\n[addr-to-line mapping]\n";
928 : : const autoarray<AddressToLineMapping::AddrToLineInfo>& addrToLineInfo = addressToLineMapping.getAddrToLineInfo();
929 [ # # ]: 0 : for (int i = 0; i < addrToLineInfo.count; ++i)
930 : : {
931 : : char addrToLineStr[32];
932 : 0 : snprintf(addrToLineStr, 32, "%.2x:%.4x %.4x:%.8x\n",
933 : 0 : (addrToLineInfo[i].addr & 0xFF0000) >> 16,
934 : 0 : addrToLineInfo[i].addr & 0xFFFF,
935 : 0 : addrToLineInfo[i].fileIdx & 0xFFFF,
936 : 0 : addrToLineInfo[i].line & 0xFFFFFFFF
937 : : );
938 : 0 : symbolfile += addrToLineStr;
939 : : }
940 : :
941 : : }
942 [ # # ]: 0 : else if (format == "nocash")
943 : : {
944 : : symbolfile = ";no$sns symbolic information file\n";
945 : 0 : symbolfile += ";generated by asar\n";
946 : 0 : symbolfile += "\n";
947 : 0 : labels.each(printsymbol_nocash);
948 : : }
949 : 0 : return symbolfile;
950 : : }
951 : :
952 : 95 : void reseteverything()
953 : : {
954 : 95 : string str;
955 : 95 : labels.reset();
956 : 95 : defines.reset();
957 : 95 : builtindefines.each(adddefine);
958 : 95 : clidefines.each(adddefine);
959 : 95 : structs.reset();
960 : :
961 : 95 : macros.each(clearmacro);
962 : 95 : macros.reset();
963 : :
964 : 95 : filecontents.each(clearfile);
965 : 95 : filecontents.reset();
966 : :
967 : 95 : writtenblocks.reset();
968 : :
969 : 95 : optimizeforbank=-1;
970 : 95 : optimize_dp = optimize_dp_flag::NONE;
971 : 95 : dp_base = 0;
972 : 95 : optimize_address = optimize_address_flag::DEFAULT;
973 : :
974 : 95 : closecachedfiles();
975 : :
976 : 95 : incsrcdepth=0;
977 : 95 : errored = false;
978 : 95 : checksum_fix_enabled = true;
979 : 95 : force_checksum_fix = false;
980 : :
981 : 95 : default_math_pri = false;
982 : 95 : default_math_round_off = false;
983 : 95 : suppress_all_warnings = false;
984 : :
985 : : #undef free
986 : 95 : }
|