diff options
Diffstat (limited to 'src/NDSCart.cpp')
| -rw-r--r-- | src/NDSCart.cpp | 582 | 
1 files changed, 282 insertions, 300 deletions
| diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 3d231ac..cdc26ef 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1,5 +1,5 @@  /* -    Copyright 2016-2021 Arisotura +    Copyright 2016-2022 melonDS team      This file is part of melonDS. @@ -22,11 +22,11 @@  #include "DSi.h"  #include "NDSCart.h"  #include "ARM.h" +#include "CRC32.h"  #include "DSi_AES.h"  #include "Platform.h"  #include "ROMList.h"  #include "melonDLDI.h" -#include "NDSCart_SRAMManager.h"  namespace NDSCart @@ -51,12 +51,9 @@ u32 TransferDir;  u8 TransferCmd[8];  bool CartInserted; -char CartName[256];  u8* CartROM;  u32 CartROMSize;  u32 CartID; -bool CartIsHomebrew; -bool CartIsDSi;  NDSHeader Header;  NDSBanner Banner; @@ -135,13 +132,32 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod)      }  } -void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) +void Key1_LoadKeyBuf(bool dsi)  { -    // TODO: source the key data from different possible places -    if (dsi && NDS::ConsoleType==1) -        memcpy(Key1_KeyBuf, &DSi::ARM7iBIOS[0xC6D0], 0x1048); // hax +    // it is possible that this gets called before the BIOSes are loaded +    // so we will read from the BIOS files directly + +    if (Platform::GetConfigBool(Platform::ExternalBIOSEnable)) +    { +        std::string path = Platform::GetConfigString(dsi ? Platform::DSi_BIOS7Path : Platform::BIOS7Path); +        FILE* f = Platform::OpenLocalFile(path, "rb"); +        if (f) +        { +            fseek(f, dsi ? 0xC6D0 : 0x0030, SEEK_SET); +            fread(Key1_KeyBuf, 0x1048, 1, f); +            fclose(f); +        } +    }      else -        memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax +    { +        // well +        memset(Key1_KeyBuf, 0, 0x1048); +    } +} + +void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) +{ +    Key1_LoadKeyBuf(dsi);      u32 keycode[3] = {idcode, idcode>>1, idcode<<1};      if (level >= 1) Key1_ApplyKeycode(keycode, mod); @@ -191,6 +207,22 @@ CartCommon::~CartCommon()  {  } +u32 CartCommon::Checksum() +{ +    u32 crc = CRC32(ROM, 0x40); + +    crc = CRC32(&ROM[Header.ARM9ROMOffset], Header.ARM9Size, crc); +    crc = CRC32(&ROM[Header.ARM7ROMOffset], Header.ARM7Size, crc); + +    if (IsDSi) +    { +        crc = CRC32(&ROM[Header.DSiARM9iROMOffset], Header.DSiARM9iSize, crc); +        crc = CRC32(&ROM[Header.DSiARM7iROMOffset], Header.DSiARM7iSize, crc); +    } + +    return crc; +} +  void CartCommon::Reset()  {      CmdEncMode = 0; @@ -198,11 +230,11 @@ void CartCommon::Reset()      DSiMode = false;  } -void CartCommon::SetupDirectBoot() +void CartCommon::SetupDirectBoot(std::string romname)  {      CmdEncMode = 2;      DataEncMode = 2; -    DSiMode = IsDSi && NDS::ConsoleType==1; +    DSiMode = IsDSi && (NDS::ConsoleType==1);  }  void CartCommon::DoSavestate(Savestate* file) @@ -214,20 +246,11 @@ void CartCommon::DoSavestate(Savestate* file)      file->Bool32(&DSiMode);  } -void CartCommon::LoadSave(const char* path, u32 type) -{ -} - -void CartCommon::RelocateSave(const char* path, bool write) +void CartCommon::SetupSave(u32 type)  {  } -int CartCommon::ImportSRAM(const u8* data, u32 length) -{ -    return 0; -} - -void CartCommon::FlushSRAMFile() +void CartCommon::LoadSave(const u8* savedata, u32 savelen)  {  } @@ -392,11 +415,7 @@ void CartRetail::DoSavestate(Savestate* file)      CartCommon::DoSavestate(file);      // we reload the SRAM contents. -    // it should be the same file (as it should be the same ROM, duh) -    // but the contents may change - -    //if (!file->Saving && SRAMLength) -    //    delete[] SRAM; +    // it should be the same file, but the contents may change      u32 oldlen = SRAMLength; @@ -407,13 +426,11 @@ void CartRetail::DoSavestate(Savestate* file)          printf("oh well. loading it anyway. adsfgdsf\n");          if (oldlen) delete[] SRAM; +        SRAM = nullptr;          if (SRAMLength) SRAM = new u8[SRAMLength];      }      if (SRAMLength)      { -        //if (!file->Saving) -        //    SRAM = new u8[SRAMLength]; -          file->VarArray(SRAM, SRAMLength);      } @@ -423,20 +440,14 @@ void CartRetail::DoSavestate(Savestate* file)      file->Var32(&SRAMAddr);      file->Var8(&SRAMStatus); -    // SRAMManager might now have an old buffer (or one from the future or alternate timeline!) -    if (!file->Saving) -    { -        SRAMFileDirty = false; -        NDSCart_SRAMManager::RequestFlush(); -    } +    if ((!file->Saving) && SRAM) +        Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength);  } -void CartRetail::LoadSave(const char* path, u32 type) +void CartRetail::SetupSave(u32 type)  {      if (SRAM) delete[] SRAM; - -    strncpy(SRAMPath, path, 1023); -    SRAMPath[1023] = '\0'; +    SRAM = nullptr;      if (type > 10) type = 0;      int sramlen[] = @@ -455,18 +466,6 @@ void CartRetail::LoadSave(const char* path, u32 type)          memset(SRAM, 0xFF, SRAMLength);      } -    FILE* f = Platform::OpenFile(path, "rb"); -    if (f) -    { -        fseek(f, 0, SEEK_SET); -        fread(SRAM, 1, SRAMLength, f); - -        fclose(f); -    } - -    SRAMFileDirty = false; -    NDSCart_SRAMManager::Setup(path, SRAM, SRAMLength); -      switch (type)      {      case 1: SRAMType = 1; break; // EEPROM, small @@ -483,47 +482,13 @@ void CartRetail::LoadSave(const char* path, u32 type)      }  } -void CartRetail::RelocateSave(const char* path, bool write) -{ -    if (!write) -    { -        LoadSave(path, 0); // lazy -        return; -    } - -    strncpy(SRAMPath, path, 1023); -    SRAMPath[1023] = '\0'; - -    FILE* f = Platform::OpenFile(path, "wb"); -    if (!f) -    { -        printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n"); -        return; -    } - -    fwrite(SRAM, SRAMLength, 1, f); -    fclose(f); -} - -int CartRetail::ImportSRAM(const u8* data, u32 length) -{ -    memcpy(SRAM, data, std::min(length, SRAMLength)); -    FILE* f = Platform::OpenFile(SRAMPath, "wb"); -    if (f) -    { -        fwrite(SRAM, SRAMLength, 1, f); -        fclose(f); -    } - -    return length - SRAMLength; -} - -void CartRetail::FlushSRAMFile() +void CartRetail::LoadSave(const u8* savedata, u32 savelen)  { -    if (!SRAMFileDirty) return; +    if (!SRAM) return; -    SRAMFileDirty = false; -    NDSCart_SRAMManager::RequestFlush(); +    u32 len = std::min(savelen, SRAMLength); +    memcpy(SRAM, savedata, len); +    Platform::WriteNDSSave(savedata, len, 0, len);  }  int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) @@ -564,17 +529,17 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)          {          case 0x04: // write disable              SRAMStatus &= ~(1<<1); -            break; +            return 0;          case 0x06: // write enable              SRAMStatus |= (1<<1); -            break; +            return 0;          default:              SRAMCmd = val;              SRAMAddr = 0;          } -        return 0; +        return 0xFF;      }      switch (SRAMType) @@ -582,7 +547,7 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)      case 1: return SRAMWrite_EEPROMTiny(val, pos, last);      case 2: return SRAMWrite_EEPROM(val, pos, last);      case 3: return SRAMWrite_FLASH(val, pos, last); -    default: return 0; +    default: return 0xFF;      }  } @@ -624,6 +589,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)          if (pos < 2)          {              SRAMAddr = val; +            SRAMFirstAddr = SRAMAddr;          }          else          { @@ -631,11 +597,15 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)              if (SRAMStatus & (1<<1))              {                  SRAM[(SRAMAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF] = val; -                SRAMFileDirty |= last;              }              SRAMAddr++;          } -        if (last) SRAMStatus &= ~(1<<1); +        if (last) +        { +            SRAMStatus &= ~(1<<1); +            Platform::WriteNDSSave(SRAM, SRAMLength, +                                   (SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr); +        }          return 0;      case 0x03: // read low @@ -658,7 +628,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)      default:          if (pos == 1)              printf("unknown tiny EEPROM save command %02X\n", SRAMCmd); -        return 0; +        return 0xFF;      }  } @@ -683,6 +653,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)          {              SRAMAddr <<= 8;              SRAMAddr |= val; +            SRAMFirstAddr = SRAMAddr;          }          else          { @@ -690,11 +661,15 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)              if (SRAMStatus & (1<<1))              {                  SRAM[SRAMAddr & (SRAMLength-1)] = val; -                SRAMFileDirty |= last;              }              SRAMAddr++;          } -        if (last) SRAMStatus &= ~(1<<1); +        if (last) +        { +            SRAMStatus &= ~(1<<1); +            Platform::WriteNDSSave(SRAM, SRAMLength, +                                   SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); +        }          return 0;      case 0x03: // read @@ -719,7 +694,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)      default:          if (pos == 1)              printf("unknown EEPROM save command %02X\n", SRAMCmd); -        return 0; +        return 0xFF;      }  } @@ -735,6 +710,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)          {              SRAMAddr <<= 8;              SRAMAddr |= val; +            SRAMFirstAddr = SRAMAddr;          }          else          { @@ -742,11 +718,15 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)              {                  // CHECKME: should it be &=~val ??                  SRAM[SRAMAddr & (SRAMLength-1)] = 0; -                SRAMFileDirty |= last;              }              SRAMAddr++;          } -        if (last) SRAMStatus &= ~(1<<1); +        if (last) +        { +            SRAMStatus &= ~(1<<1); +            Platform::WriteNDSSave(SRAM, SRAMLength, +                                   SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); +        }          return 0;      case 0x03: // read @@ -768,17 +748,22 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)          {              SRAMAddr <<= 8;              SRAMAddr |= val; +            SRAMFirstAddr = SRAMAddr;          }          else          {              if (SRAMStatus & (1<<1))              {                  SRAM[SRAMAddr & (SRAMLength-1)] = val; -                SRAMFileDirty |= last;              }              SRAMAddr++;          } -        if (last) SRAMStatus &= ~(1<<1); +        if (last) +        { +            SRAMStatus &= ~(1<<1); +            Platform::WriteNDSSave(SRAM, SRAMLength, +                                   SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); +        }          return 0;      case 0x0B: // fast read @@ -809,6 +794,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)          {              SRAMAddr <<= 8;              SRAMAddr |= val; +            SRAMFirstAddr = SRAMAddr;          }          if ((pos == 3) && (SRAMStatus & (1<<1)))          { @@ -817,9 +803,13 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)                  SRAM[SRAMAddr & (SRAMLength-1)] = 0;                  SRAMAddr++;              } -            SRAMFileDirty = true;          } -        if (last) SRAMStatus &= ~(1<<1); +        if (last) +        { +            SRAMStatus &= ~(1<<1); +            Platform::WriteNDSSave(SRAM, SRAMLength, +                                   SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); +        }          return 0;      case 0xDB: // page erase @@ -827,6 +817,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)          {              SRAMAddr <<= 8;              SRAMAddr |= val; +            SRAMFirstAddr = SRAMAddr;          }          if ((pos == 3) && (SRAMStatus & (1<<1)))          { @@ -835,15 +826,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)                  SRAM[SRAMAddr & (SRAMLength-1)] = 0;                  SRAMAddr++;              } -            SRAMFileDirty = true;          } -        if (last) SRAMStatus &= ~(1<<1); +        if (last) +        { +            SRAMStatus &= ~(1<<1); +            Platform::WriteNDSSave(SRAM, SRAMLength, +                                   SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); +        }          return 0;      default:          if (pos == 1)              printf("unknown FLASH save command %02X\n", SRAMCmd); -        return 0; +        return 0xFF;      }  } @@ -884,19 +879,12 @@ void CartRetailNAND::DoSavestate(Savestate* file)          BuildSRAMID();  } -void CartRetailNAND::LoadSave(const char* path, u32 type) +void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen)  { -    CartRetail::LoadSave(path, type); +    CartRetail::LoadSave(savedata, savelen);      BuildSRAMID();  } -int CartRetailNAND::ImportSRAM(const u8* data, u32 length) -{ -    int ret = CartRetail::ImportSRAM(data, length); -    BuildSRAMID(); -    return ret; -} -  int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len)  {      if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); @@ -926,7 +914,7 @@ int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len)              if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000))              {                  memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); -                SRAMFileDirty = true; +                Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800);              }              SRAMAddr = 0; @@ -1164,8 +1152,30 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)  CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)  { +    SD = nullptr; +} + +CartHomebrew::~CartHomebrew() +{ +    if (SD) +    { +        SD->Close(); +        delete SD; +    } +} + +void CartHomebrew::Reset() +{ +    CartCommon::Reset(); +      ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly); +    if (SD) +    { +        SD->Close(); +        delete SD; +    } +      if (Platform::GetConfigBool(Platform::DLDI_Enable))      {          std::string folderpath; @@ -1185,29 +1195,15 @@ CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len,          SD = nullptr;  } -CartHomebrew::~CartHomebrew() -{ -    if (SD) -    { -        SD->Close(); -        delete SD; -    } -} - -void CartHomebrew::Reset() -{ -    CartCommon::Reset(); -} - -void CartHomebrew::SetupDirectBoot() +void CartHomebrew::SetupDirectBoot(std::string romname)  { -    CartCommon::SetupDirectBoot(); +    CartCommon::SetupDirectBoot(romname);      if (SD)      {          // add the ROM to the SD volume -        if (!SD->InjectFile(CartName, CartROM, CartROMSize)) +        if (!SD->InjectFile(romname, CartROM, CartROMSize))              return;          // setup argv command line @@ -1216,7 +1212,7 @@ void CartHomebrew::SetupDirectBoot()          int argvlen;          strncpy(argv, "fat:/", 511); -        strncat(argv, CartName, 511); +        strncat(argv, romname.c_str(), 511);          argvlen = strlen(argv);          void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32; @@ -1295,40 +1291,8 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)      }  } -void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) +void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly)  { -    u32 offset = *(u32*)&ROM[0x20]; -    u32 size = *(u32*)&ROM[0x2C]; - -    u8* binary = &ROM[offset]; -    u32 dldioffset = 0; - -    for (u32 i = 0; i < size; i++) -    { -        if (*(u32*)&binary[i  ] == 0xBF8DA5ED && -            *(u32*)&binary[i+4] == 0x69684320 && -            *(u32*)&binary[i+8] == 0x006D6873) -        { -            dldioffset = i; -            break; -        } -    } - -    if (!dldioffset) -    { -        return; -    } - -    printf("DLDI structure found at %08X (%08X)\n", dldioffset, offset+dldioffset); - -    if (*(u32*)&patch[0] != 0xBF8DA5ED || -        *(u32*)&patch[4] != 0x69684320 || -        *(u32*)&patch[8] != 0x006D6873) -    { -        printf("bad DLDI patch\n"); -        return; -    } -      if (patch[0x0D] > binary[dldioffset+0x0F])      {          printf("DLDI driver ain't gonna fit, sorry\n"); @@ -1425,7 +1389,37 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly)          *(u32*)&binary[writesec_addr+0x04] = 0xE12FFF1E; // bx lr      } -    printf("applied DLDI patch\n"); +    printf("applied DLDI patch at %08X\n", dldioffset); +} + +void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) +{ +    if (*(u32*)&patch[0] != 0xBF8DA5ED || +        *(u32*)&patch[4] != 0x69684320 || +        *(u32*)&patch[8] != 0x006D6873) +    { +        printf("bad DLDI patch\n"); +        return; +    } + +    u32 offset = *(u32*)&ROM[0x20]; +    u32 size = *(u32*)&ROM[0x2C]; + +    u8* binary = &ROM[offset]; + +    for (u32 i = 0; i < size; ) +    { +        if (*(u32*)&binary[i  ] == 0xBF8DA5ED && +            *(u32*)&binary[i+4] == 0x69684320 && +            *(u32*)&binary[i+8] == 0x006D6873) +        { +            printf("DLDI structure found at %08X (%08X)\n", i, offset+i); +            ApplyDLDIPatchAt(binary, i, patch, patchlen, readonly); +            i += patchlen; +        } +        else +            i++; +    }  }  void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) @@ -1441,6 +1435,7 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)  bool Init()  { +    CartInserted = false;      CartROM = nullptr;      Cart = nullptr; @@ -1455,17 +1450,6 @@ void DeInit()  void Reset()  { -    CartInserted = false; -    if (CartROM) delete[] CartROM; -    CartROM = nullptr; -    CartROMSize = 0; -    CartID = 0; -    CartIsHomebrew = false; -    CartIsDSi = false; - -    if (Cart) delete Cart; -    Cart = nullptr; -      ResetCart();  } @@ -1494,6 +1478,30 @@ void DoSavestate(Savestate* file)      // (TODO: system to verify that indeed the right ROM is loaded)      // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing) +    u32 carttype = 0; +    u32 cartchk = 0; +    if (Cart) +    { +        carttype = Cart->Type(); +        cartchk = Cart->Checksum(); +    } + +    if (file->Saving) +    { +        file->Var32(&carttype); +        file->Var32(&cartchk); +    } +    else +    { +        u32 savetype; +        file->Var32(&savetype); +        if (savetype != carttype) return; + +        u32 savechk; +        file->Var32(&savechk); +        if (savechk != cartchk) return; +    } +      if (Cart) Cart->DoSavestate(file);  } @@ -1542,11 +1550,6 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params)  void DecryptSecureArea(u8* out)  { -    // TODO: source decryption data from different possible sources -    // * original DS-mode ARM7 BIOS has the key data at 0x30 -    // * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed -    //   decryption data at 0x1000 (and at the beginning of the DSi region if any) -      u32 gamecode = (u32)Header.GameCode[3] << 24 |                     (u32)Header.GameCode[2] << 16 |                     (u32)Header.GameCode[1] << 8  | @@ -1576,10 +1579,41 @@ void DecryptSecureArea(u8* out)      }  } -bool LoadROMCommon(u32 filelength, const char *sram, bool direct) +bool LoadROM(const u8* romdata, u32 romlen)  { +    if (CartInserted) +        EjectCart(); + +    memset(&Header, 0, sizeof(Header)); +    memset(&Banner, 0, sizeof(Banner)); + +    CartROMSize = 0x200; +    while (CartROMSize < romlen) +        CartROMSize <<= 1; + +    try +    { +        CartROM = new u8[CartROMSize]; +    } +    catch (const std::bad_alloc& e) +    { +        printf("NDSCart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize); +        return false; +    } + +    memset(CartROM, 0, CartROMSize); +    memcpy(CartROM, romdata, romlen); +      memcpy(&Header, CartROM, sizeof(Header)); -    memcpy(&Banner, CartROM + Header.BannerOffset, sizeof(Banner)); + +    u8 unitcode = Header.UnitCode; +    bool dsi = (unitcode & 0x02) != 0; + +    size_t bannersize = dsi ? 0x23C0 : 0xA40; +    if (Header.BannerOffset >= 0x200 && Header.BannerOffset < (CartROMSize - bannersize)) +    { +        memcpy(&Banner, CartROM + Header.BannerOffset, bannersize); +    }      printf("Game code: %.4s\n", Header.GameCode); @@ -1588,8 +1622,8 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)                     (u32)Header.GameCode[1] << 8  |                     (u32)Header.GameCode[0]; -    u8 unitcode = Header.UnitCode; -    CartIsDSi = (unitcode & 0x02) != 0; +    u32 arm9base = Header.ARM9ROMOffset; +    bool homebrew = (arm9base < 0x4000) || (gamecode == 0x23232323);      ROMListEntry romparams;      if (!ReadROMParams(gamecode, &romparams)) @@ -1599,7 +1633,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)          romparams.GameCode = gamecode;          romparams.ROMSize = CartROMSize; -        if (*(u32*)&CartROM[0x20] < 0x4000) +        if (homebrew)              romparams.SaveMemType = 0; // no saveRAM for homebrew          else              romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME) @@ -1607,7 +1641,8 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)      else          printf("ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); -    if (romparams.ROMSize != filelength) printf("!! bad ROM size %d (expected %d) rounded to %d\n", filelength, romparams.ROMSize, CartROMSize); +    if (romparams.ROMSize != romlen) +        printf("!! bad ROM size %d (expected %d) rounded to %d\n", romlen, romparams.ROMSize, CartROMSize);      // generate a ROM ID      // note: most games don't check the actual value @@ -1622,7 +1657,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)      if (romparams.SaveMemType >= 8 && romparams.SaveMemType <= 10)          CartID |= 0x08000000; // NAND flag -    if (CartIsDSi) +    if (dsi)          CartID |= 0x40000000;      // cart ID for Jam with the Band @@ -1633,35 +1668,26 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)      printf("Cart ID: %08X\n", CartID); -    u32 arm9base = *(u32*)&CartROM[0x20]; - -    if (arm9base < 0x8000) +    if (arm9base >= 0x4000 && arm9base < 0x8000)      { -        if (arm9base >= 0x4000) +        // reencrypt secure area if needed +        if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF)          { -            // reencrypt secure area if needed -            if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF) -            { -                printf("Re-encrypting cart secure area\n"); +            printf("Re-encrypting cart secure area\n"); -                strncpy((char*)&CartROM[arm9base], "encryObj", 8); +            strncpy((char*)&CartROM[arm9base], "encryObj", 8); -                Key1_InitKeycode(false, gamecode, 3, 2); -                for (u32 i = 0; i < 0x800; i += 8) -                    Key1_Encrypt((u32*)&CartROM[arm9base + i]); +            Key1_InitKeycode(false, gamecode, 3, 2); +            for (u32 i = 0; i < 0x800; i += 8) +                Key1_Encrypt((u32*)&CartROM[arm9base + i]); -                Key1_InitKeycode(false, gamecode, 2, 2); -                Key1_Encrypt((u32*)&CartROM[arm9base]); -            } +            Key1_InitKeycode(false, gamecode, 2, 2); +            Key1_Encrypt((u32*)&CartROM[arm9base]);          }      } -    if ((arm9base < 0x4000) || (gamecode == 0x23232323)) -    { -        CartIsHomebrew = true; -    } -      CartInserted = true; +    DSi::SetCartInserted(true);      u32 irversion = 0;      if ((gamecode & 0xFF) == 'I') @@ -1672,7 +1698,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)              irversion = 2; // Pokémon HG/SS, B/W, B2/W2      } -    if (CartIsHomebrew) +    if (homebrew)          Cart = new CartHomebrew(CartROM, CartROMSize, CartID);      else if (CartID & 0x08000000)          Cart = new CartRetailNAND(CartROM, CartROMSize, CartID); @@ -1684,99 +1710,46 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)          Cart = new CartRetail(CartROM, CartROMSize, CartID);      if (Cart) -    {          Cart->Reset(); -        if (direct) -        { -            NDS::SetupDirectBoot(); -            Cart->SetupDirectBoot(); -        } -    } - -    // encryption -    Key1_InitKeycode(false, gamecode, 2, 2); -    // save -    printf("Save file: %s\n", sram); -    if (Cart) Cart->LoadSave(sram, romparams.SaveMemType); +    if (Cart && romparams.SaveMemType > 0) +        Cart->SetupSave(romparams.SaveMemType);      return true;  } -bool LoadROM(const char* path, const char* sram, bool direct) +void LoadSave(const u8* savedata, u32 savelen)  { -    // TODO: streaming mode? for really big ROMs or systems with limited RAM -    // for now we're lazy -    // also TODO: validate what we're loading!! - -    FILE* f = Platform::OpenFile(path, "rb"); -    if (!f) -    { -        return false; -    } - -    NDS::Reset(); - -    char* romname = strrchr((char*)path, '/'); -    if (!romname) -    { -        romname = strrchr((char*)path, '\\'); -        if (!romname) -            romname = (char*)&path[-1]; -    } -    romname++; -    strncpy(CartName, romname, 255); CartName[255] = '\0'; - -    fseek(f, 0, SEEK_END); -    u32 len = (u32)ftell(f); - -    CartROMSize = 0x200; -    while (CartROMSize < len) -        CartROMSize <<= 1; - -    CartROM = new u8[CartROMSize]; -    memset(CartROM, 0, CartROMSize); -    fseek(f, 0, SEEK_SET); -    fread(CartROM, 1, len, f); - -    fclose(f); - -    return LoadROMCommon(len, sram, direct); +    if (Cart) +        Cart->LoadSave(savedata, savelen);  } -bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct) +void SetupDirectBoot(std::string romname)  { -    NDS::Reset(); - -    // TODO: make it more meaningful? -    strncpy(CartName, "rom.nds", 256); +    if (Cart) +        Cart->SetupDirectBoot(romname); +} -    u32 len = filelength; -    CartROMSize = 0x200; -    while (CartROMSize < len) -        CartROMSize <<= 1; +void EjectCart() +{ +    if (!CartInserted) return; -    CartROM = new u8[CartROMSize]; -    memset(CartROM, 0, CartROMSize); -    memcpy(CartROM, romdata, filelength); +    // ejecting the cart triggers the gamecard IRQ +    NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); +    NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); -    return LoadROMCommon(filelength, sram, direct); -} +    if (Cart) delete Cart; +    Cart = nullptr; -void RelocateSave(const char* path, bool write) -{ -    if (Cart) Cart->RelocateSave(path, write); -} +    CartInserted = false; +    if (CartROM) delete[] CartROM; +    CartROM = nullptr; +    CartROMSize = 0; +    CartID = 0; -void FlushSRAMFile() -{ -    if (Cart) Cart->FlushSRAMFile(); -} +    DSi::SetCartInserted(false); -int ImportSRAM(const u8* data, u32 length) -{ -    if (Cart) return Cart->ImportSRAM(data, length); -    return 0; +    // CHECKME: does an eject imply anything for the ROM/SPI transfer registers?  }  void ResetCart() @@ -1840,9 +1813,8 @@ void ROMPrepareData(u32 param)  void WriteROMCnt(u32 val)  { -    ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x00800000); - -    if (!(SPICnt & (1<<15))) return; +    u32 xferstart = (val & ~ROMCnt) & (1<<31); +    ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x20800000);      // all this junk would only really be useful if melonDS was interfaced to      // a DS cart reader @@ -1866,7 +1838,11 @@ void WriteROMCnt(u32 val)          printf("key2 Y: %02X%08X\n", (u32)(Key2_Y>>32), (u32)Key2_Y);      } -    if (!(ROMCnt & (1<<31))) return; +    // transfers will only start when bit31 changes from 0 to 1 +    // and if AUXSPICNT is configured correctly +    if (!(SPICnt & (1<<15))) return; +    if (SPICnt & (1<<13)) return; +    if (!xferstart) return;      u32 datasize = (ROMCnt >> 24) & 0x7;      if (datasize == 7) @@ -1880,6 +1856,8 @@ void WriteROMCnt(u32 val)      *(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0];      *(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4]; +    memset(TransferData, 0xFF, TransferLen); +      /*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n",             SPICnt, ROMCnt,             TransferCmd[0], TransferCmd[1], TransferCmd[2], TransferCmd[3], @@ -1903,6 +1881,7 @@ void WriteROMCnt(u32 val)      // thus a command would take 8 cycles to be transferred      // and it would take 4 cycles to receive a word of data      // TODO: advance read position if bit28 is set +    // TODO: during a write transfer, bit23 is set immediately when beginning the transfer(?)      u32 xfercycle = (ROMCnt & (1<<27)) ? 8 : 5;      u32 cmddelay = 8; @@ -1983,6 +1962,10 @@ void WriteSPICnt(u16 val)      }      SPICnt = (SPICnt & 0x0080) | (val & 0xE043); + +    // AUXSPICNT can be changed during a transfer +    // in this case, the transfer continues until the end, even if bit13 or bit15 are cleared +    // if the transfer speed is changed, the transfer continues at the new speed (TODO)      if (SPICnt & (1<<7))          printf("!! CHANGING AUXSPICNT DURING TRANSFER: %04X\n", val);  } @@ -2005,8 +1988,7 @@ void WriteSPIData(u8 val)  {      if (!(SPICnt & (1<<15))) return;      if (!(SPICnt & (1<<13))) return; - -    if (SPICnt & (1<<7)) printf("!! WRITING AUXSPIDATA DURING PENDING TRANSFER\n"); +    if (SPICnt & (1<<7)) return;      SPICnt |= (1<<7); |