LCOV - code coverage report
Current view: top level - asar - main.cpp (source / functions) Coverage Total Hit
Test: asar build #66 Lines: 80.2 % 673 540
Test Date: 2024-01-16 02:45:19 Functions: 81.6 % 38 31
Branches: 70.3 % 710 499

             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 : }
        

Generated by: LCOV version 2.0-1