diff options
Diffstat (limited to 'src/NDSCart.cpp')
| -rw-r--r-- | src/NDSCart.cpp | 596 | 
1 files changed, 235 insertions, 361 deletions
| diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 03dfe4c..c959663 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -21,6 +21,7 @@  #include "NDS.h"  #include "NDSCart.h"  #include "ARM.h" +#include "CRC32.h"  #include "melon_fopen.h" @@ -28,11 +29,6 @@  namespace NDSCart_SRAM  { -// BIG SRAM TODO!!!! -// USE A DATABASE TO IDENTIFY SAVE MEMORY TYPE -// (and maybe keep autodetect as a last resort??) -// AUTODETECT IS BLARGY AND IS A MESS -  u8* SRAM;  u32 SRAMLength; @@ -40,15 +36,6 @@ char SRAMPath[1024];  void (*WriteFunc)(u8 val, bool islast); -u32 Discover_MemoryType; -u32 Discover_Likeliness; -u8* Discover_Buffer; -u32 Discover_DataPos; -u32 Discover_LastPC; -u32 Discover_AddrLength; -u32 Discover_Addr; -u32 Discover_LikelySize; -  u32 Hold;  u8 CurCmd;  u32 DataPos; @@ -68,37 +55,25 @@ void Write_Discover(u8 val, bool islast);  bool Init()  {      SRAM = NULL; -    Discover_Buffer = NULL;      return true;  }  void DeInit()  {      if (SRAM) delete[] SRAM; -    if (Discover_Buffer) delete[] Discover_Buffer;  }  void Reset()  {      if (SRAM) delete[] SRAM; -    if (Discover_Buffer) delete[] Discover_Buffer;      SRAM = NULL; -    Discover_Buffer = NULL;  }  void DoSavestate(Savestate* file)  {      file->Section("NDCS"); -    // CHECKME/TODO/whatever -    // should the autodetect status shit go in the savestate??? -    // I don't think so. -    // worst case is that the savestate was taken before autodetect took place completely -    // and that causes it to yield a different result, fucking things up -    // but that is unlikely -    // and we should just use a goddamn database anyway, this is a trainwreck - -    // we reload the SRAM contents, tho. +    // we reload the SRAM contents.      // it should be the same file (as it should be the same ROM, duh)      // but the contents may change      // TODO maybe: possibility to save to a separate file when using savestates???? @@ -138,15 +113,9 @@ void DoSavestate(Savestate* file)      file->Var32(&Addr);  } -void LoadSave(const char* path) +void LoadSave(const char* path, u32 type)  {      if (SRAM) delete[] SRAM; -    if (Discover_Buffer) delete[] Discover_Buffer; - -    Discover_Buffer = NULL; -    Discover_LastPC = 0; -    Discover_AddrLength = 0x7FFFFFFF; -    Discover_LikelySize = 0;      strncpy(SRAMPath, path, 1023);      SRAMPath[1023] = '\0'; @@ -162,32 +131,35 @@ void LoadSave(const char* path)          fread(SRAM, SRAMLength, 1, f);          fclose(f); +    } +    else +    { +        if (type > 8) type = 0; +        int sramlen[] = {0, 512, 8192, 65536, 256*1024, 512*1024, 1024*1024, 8192*1024, 32768*1024}; +        SRAMLength = sramlen[type]; -        switch (SRAMLength) +        if (SRAMLength)          { -        case 512: WriteFunc = Write_EEPROMTiny; break; -        case 8192: -        case 65536: WriteFunc = Write_EEPROM; break; -        case 256*1024: -        case 512*1024: -        case 1024*1024: -        case 8192*1024: WriteFunc = Write_Flash; break; -        default: -            printf("!! BAD SAVE LENGTH %d\n", SRAMLength); -            WriteFunc = Write_Null; -            break; +            SRAM = new u8[SRAMLength]; +            memset(SRAM, 0, SRAMLength);          }      } -    else + +    switch (SRAMLength)      { -        SRAMLength = 0; -        WriteFunc = Write_Discover; -        Discover_MemoryType = 2; -        Discover_Likeliness = 0; - -        Discover_DataPos = 0; -        Discover_Buffer = new u8[256*1024]; -        memset(Discover_Buffer, 0, 256*1024); +    case 512: WriteFunc = Write_EEPROMTiny; break; +    case 8192: +    case 65536: WriteFunc = Write_EEPROM; break; +    case 256*1024: +    case 512*1024: +    case 1024*1024: +    case 8192*1024: WriteFunc = Write_Flash; break; +    case 32768*1024: WriteFunc = Write_Null; break; // NAND FLASH, handled differently +    default: +        printf("!! BAD SAVE LENGTH %d\n", SRAMLength); +    case 0: +        WriteFunc = Write_Null; +        break;      }      Hold = 0; @@ -200,7 +172,7 @@ void RelocateSave(const char* path, bool write)  {      if (!write)      { -        LoadSave(path); // lazy +        LoadSave(path, 0); // lazy          return;      } @@ -223,240 +195,6 @@ u8 Read()      return Data;  } -void SetMemoryType() -{ -    SRAMLength = 0; - -    if (Discover_LikelySize) -    { -        if (Discover_LikelySize >= 0x40000) -        { -            Discover_MemoryType = 4; // FLASH -            SRAMLength = Discover_LikelySize; -        } -        else if (Discover_LikelySize == 0x10000 || Discover_MemoryType == 3) -        { -            if (Discover_MemoryType > 3) -                printf("incoherent detection result: type=%d size=%X\n", Discover_MemoryType, Discover_LikelySize); - -            Discover_MemoryType = 3; -        } -        else if (Discover_LikelySize == 0x2000) -        { -            if (Discover_MemoryType > 3) -                printf("incoherent detection result: type=%d size=%X\n", Discover_MemoryType, Discover_LikelySize); - -            Discover_MemoryType = 2; -        } -        else if (Discover_LikelySize == 0x200 || Discover_MemoryType == 1) -        { -            if (Discover_MemoryType > 1) -                printf("incoherent detection result: type=%d size=%X\n", Discover_MemoryType, Discover_LikelySize); - -            Discover_MemoryType = 1; -        } -        else -        { -            printf("bad save size %X. assuming EEPROM 64K\n", Discover_LikelySize); -            Discover_MemoryType = 2; -        } -    } - -    switch (Discover_MemoryType) -    { -    case 1: -        printf("Save memory type: EEPROM 4k\n"); -        WriteFunc = Write_EEPROMTiny; -        SRAMLength = 512; -        break; - -    case 2: -        printf("Save memory type: EEPROM 64k\n"); -        WriteFunc = Write_EEPROM; -        SRAMLength = 8192; -        break; - -    case 3: -        printf("Save memory type: EEPROM 512k\n"); -        WriteFunc = Write_EEPROM; -        SRAMLength = 65536; -        break; - -    case 4: -        if (!SRAMLength) SRAMLength = 256*1024; -        printf("Save memory type: Flash %dk\n", SRAMLength/1024); -        WriteFunc = Write_Flash; -        break; - -    case 5: -        printf("Save memory type: ...something else\n"); -        WriteFunc = Write_Null; -        SRAMLength = 0; -        break; -    } - -    if (!SRAMLength) -        return; - -    SRAM = new u8[SRAMLength]; - -    // replay writes that occured during discovery -    u8 prev_cmd = CurCmd; -    u32 pos = 0; -    while (pos < 256*1024) -    { -        u32 len = *(u32*)&Discover_Buffer[pos]; -        pos += 4; -        if (len == 0) break; - -        CurCmd = Discover_Buffer[pos++]; -        DataPos = 0; -        Addr = 0; -        Data = 0; -        for (u32 i = 1; i < len; i++) -        { -            WriteFunc(Discover_Buffer[pos++], (i==(len-1))); -            DataPos++; -        } -    } - -    CurCmd = prev_cmd; - -    delete[] Discover_Buffer; -    Discover_Buffer = NULL; -} - -void Write_Discover(u8 val, bool islast) -{ -    // attempt at autodetecting the type of save memory. -    // we basically hope the game will be nice and clear whole pages of memory. - -    if (CurCmd == 0x03 || CurCmd == 0x0B) -    { -        if (Discover_Likeliness) // writes have occured before this read -        { -            // apply. and pray. -            SetMemoryType(); - -            DataPos = 0; -            Addr = 0; -            Data = 0; -            return WriteFunc(val, islast); -        } -        else -        { -            if (DataPos == 0) -            { -                Discover_LastPC = NDS::GetPC((NDS::ExMemCnt[0] >> 11) & 0x1); -                Discover_Addr = val; -            } -            else -            { -                bool addrlenchange = false; - -                Discover_Addr <<= 8; -                Discover_Addr |= val; - -                u32 pc = NDS::GetPC((NDS::ExMemCnt[0] >> 11) & 0x1); -                if ((pc != Discover_LastPC) || islast) -                { -                    if (DataPos < Discover_AddrLength) -                    { -                        Discover_AddrLength = DataPos; -                        addrlenchange = true; -                    } -                } - -                if (DataPos == Discover_AddrLength) -                { -                    Discover_Addr >>= 8; - -                    // determine the address for this read -                    // the idea is to see how far the game reads -                    // but we need margins for games that have antipiracy -                    // as those will generally read just past the limit -                    // and expect it to wrap to zero - -                    u32 likelysize = 0; - -                    if (DataPos == 3) // FLASH -                    { -                        if (Discover_Addr >= 0x101000) -                            likelysize = 0x800000; // 8M -                        else if (Discover_Addr >= 0x81000) -                            likelysize = 0x100000; // 1M -                        else if (Discover_Addr >= 0x41000) -                            likelysize = 0x80000; // 512K -                        else -                            likelysize = 0x40000; // 256K -                    } -                    else if (DataPos == 2) // EEPROM -                    { -                        if (Discover_Addr >= 0x3000) -                            likelysize = 0x10000; // 64K -                        else -                            likelysize = 0x2000; // 8K -                    } -                    else if (DataPos == 1) // tiny EEPROM -                    { -                        likelysize = 0x200; // always 4K -                    } - -                    if ((likelysize > Discover_LikelySize) || addrlenchange) -                        Discover_LikelySize = likelysize; -                } -            } - -            Data = 0; -            return; -        } -    } - -    if (CurCmd == 0x02 || CurCmd == 0x0A) -    { -        if (DataPos == 0) -            Discover_Buffer[Discover_DataPos + 4] = CurCmd; - -        Discover_Buffer[Discover_DataPos + 5 + DataPos] = val; - -        if (islast) -        { -            u32 len = DataPos+1; - -            *(u32*)&Discover_Buffer[Discover_DataPos] = len+1; -            Discover_DataPos += 5+len; - -            if (Discover_Likeliness <= len) -            { -                Discover_Likeliness = len; - -                if (len > 3+256) // bigger Flash, FRAM, whatever -                { -                    Discover_MemoryType = 5; -                } -                else if ((len > 2+128) || (len > 1+16 && CurCmd == 0xA)) // Flash -                { -                    Discover_MemoryType = 4; -                } -                else if (len > 2+32) // EEPROM 512k -                { -                    Discover_MemoryType = 3; -                } -                else if (len > 1+16 || (len != 1+16 && CurCmd != 0x0A)) // EEPROM 64k -                { -                    Discover_MemoryType = 2; -                } -                else // EEPROM 4k -                { -                    Discover_MemoryType = 1; -                } -            } - -            printf("discover: type=%d likeliness=%d\n", Discover_MemoryType, Discover_Likeliness); -        } -    } -} -  void Write_Null(u8 val, bool islast) {}  void Write_EEPROMTiny(u8 val, bool islast) @@ -733,6 +471,7 @@ u32 DataOutLen;  bool CartInserted;  u8* CartROM;  u32 CartROMSize; +u32 CartCRC;  u32 CartID;  bool CartIsHomebrew; @@ -745,6 +484,12 @@ u64 Key2_X;  u64 Key2_Y; +void ROMCommand_Retail(u8* cmd); +void ROMCommand_RetailNAND(u8* cmd); + +void (*ROMCommandHandler)(u8* cmd); + +  u32 ByteSwap(u32 val)  {      return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); @@ -886,6 +631,8 @@ void Reset()      CartID = 0;      CartIsHomebrew = false; +    ROMCommandHandler = NULL; +      CmdEncMode = 0;      DataEncMode = 0; @@ -1062,6 +809,63 @@ void ApplyDLDIPatch()  } +bool ReadROMParams(u32* params) +{ +    // format for romlist.bin: +    // [CRC32] [ROM size] [save type] [reserved] +    // list must be sorted by CRC + +    FILE* f = melon_fopen_local("romlist.bin", "rb"); +    if (!f) return false; + +    fseek(f, 0, SEEK_END); +    u32 len = (u32)ftell(f); +    u32 maxlen = len; +    len >>= 4; // 16 bytes per entry + +    u32 offset = 0; +    u32 chk_size = len >> 1; +    for (;;) +    { +        u32 crc = 0; +        fseek(f, offset + (chk_size << 4), SEEK_SET); +        fread(&crc, 4, 1, f); + +        printf("chk_size=%d, crc=%08X, wanted=%08X, offset=%08X\n", chk_size, crc, CartCRC, offset); + +        if (crc == CartCRC) +        { +            fread(params, 4, 3, f); +            fclose(f); +            return true; +        } +        else +        { +            if (crc < CartCRC) +            { +                if (chk_size == 0) +                    offset += 0x10; +                else +                    offset += (chk_size << 4); +            } +            else if (chk_size == 0) +            { +                fclose(f); +                return false; +            } + +            chk_size >>= 1; +        } + +        if (offset >= maxlen) +        { +            fclose(f); +            return false; +        } +    } +} + +  bool LoadROM(const char* path, const char* sram, bool direct)  {      // TODO: streaming mode? for really big ROMs or systems with limited RAM @@ -1094,10 +898,40 @@ bool LoadROM(const char* path, const char* sram, bool direct)      fclose(f);      //CartROM = f; +    CartCRC = CRC32(CartROM, CartROMSize); +    printf("ROM CRC32: %08X\n", CartCRC); + +    u32 romparams[3]; +    if (!ReadROMParams(romparams)) +    { +        // set defaults +        printf("ROM entry not found\n"); + +        romparams[0] = CartROMSize; +        if (*(u32*)&CartROM[0x20] < 0x4000) +            romparams[1] = 0; // no saveRAM for homebrew +        else +            romparams[1] = 2; // assume EEPROM 64k (TODO FIXME) +    } +    else +        printf("ROM entry: %08X %08X %08X\n", romparams[0], romparams[1], romparams[2]); + +    if (romparams[0] != len) printf("!! bad ROM size %d (expected %d) rounded to %d\n", len, romparams[0], CartROMSize); +      // generate a ROM ID      // note: most games don't check the actual value      // it just has to stay the same throughout gameplay -    CartID = 0x00001FC2; +    CartID = 0x000000C2; + +    if (CartROMSize <= 128*1024*1024) +        CartID |= ((CartROMSize >> 20) - 1) << 8; +    else +        CartID |= (0x100 - (CartROMSize >> 28)) << 8; + +    if (romparams[1] == 8) +        CartID |= 0x08000000; // NAND flag + +    printf("Cart ID: %08X\n", CartID);      if (*(u32*)&CartROM[0x20] < 0x4000)      { @@ -1114,6 +948,12 @@ bool LoadROM(const char* path, const char* sram, bool direct)      CartInserted = true; +    // TODO: support more fancy cart types (homebrew?, flashcarts, etc) +    if (CartID & 0x08000000) +        ROMCommandHandler = ROMCommand_RetailNAND; +    else +        ROMCommandHandler = ROMCommand_Retail; +      u32 arm9base = *(u32*)&CartROM[0x20];      if (arm9base < 0x8000)      { @@ -1144,7 +984,7 @@ bool LoadROM(const char* path, const char* sram, bool direct)      // save      printf("Save file: %s\n", sram); -    NDSCart_SRAM::LoadSave(sram); +    NDSCart_SRAM::LoadSave(sram, romparams[1]);      return true;  } @@ -1206,7 +1046,88 @@ void ROMPrepareData(u32 param)          NDS::CheckDMAs(0, 0x05);  } -u32 sc_addr = 0; + +void ROMCommand_Retail(u8* cmd) +{ +    switch (cmd[0]) +    { +    case 0xB7: +        { +            u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; +            memset(DataOut, 0, DataOutLen); + +            if (((addr + DataOutLen - 1) >> 12) != (addr >> 12)) +            { +                u32 len1 = 0x1000 - (addr & 0xFFF); +                ReadROM_B7(addr, len1, 0); +                ReadROM_B7(addr+len1, DataOutLen-len1, len1); +            } +            else +                ReadROM_B7(addr, DataOutLen, 0); +        } +        break; + +    default: +        printf("unknown retail cart command %02X\n", cmd[0]); +        break; +    } +} + +void ROMCommand_RetailNAND(u8* cmd) +{ +    switch (cmd[0]) +    { +    case 0x94: // NAND init +        { +            // initial value: should have bit7 clear +            NDSCart_SRAM::StatusReg = 0; + +            // Jam with the Band stores words 6-9 of this at 0x02131BB0 +            // it doesn't seem to use those anywhere later +            for (u32 pos = 0; pos < DataOutLen; pos += 4) +                *(u32*)&DataOut[pos] = 0; +        } +        break; + +    case 0xB2: // set savemem addr +        { +            NDSCart_SRAM::StatusReg |= 0x20; +        } +        break; + +    case 0xB7: +        { +            u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; +            memset(DataOut, 0, DataOutLen); + +            if (((addr + DataOutLen - 1) >> 12) != (addr >> 12)) +            { +                u32 len1 = 0x1000 - (addr & 0xFFF); +                ReadROM_B7(addr, len1, 0); +                ReadROM_B7(addr+len1, DataOutLen-len1, len1); +            } +            else +                ReadROM_B7(addr, DataOutLen, 0); +        } +        break; + +    case 0xD6: // NAND status +        { +            // status reg bits: +            // * bit7: busy? error? +            // * bit5: accessing savemem + +            for (u32 pos = 0; pos < DataOutLen; pos += 4) +                *(u32*)&DataOut[pos] = NDSCart_SRAM::StatusReg * 0x01010101; +        } +        break; + +    default: +        printf("unknown NAND command %02X %04Xn", cmd[0], DataOutLen); +        break; +    } +} +  void WriteROMCnt(u32 val)  { @@ -1297,81 +1218,34 @@ void WriteROMCnt(u32 val)          if (CartInserted) CmdEncMode = 1;          break; -    case 0xB7: +    default: +        if (CmdEncMode == 1)          { -            u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; -            memset(DataOut, 0, DataOutLen); - -            if (((addr + DataOutLen - 1) >> 12) != (addr >> 12)) +            switch (cmd[0] & 0xF0)              { -                u32 len1 = 0x1000 - (addr & 0xFFF); -                ReadROM_B7(addr, len1, 0); -                ReadROM_B7(addr+len1, DataOutLen-len1, len1); -            } -            else -                ReadROM_B7(addr, DataOutLen, 0); -        } -        break; - +            case 0x40: +                DataEncMode = 2; +                break; -    // SUPERCARD EMULATION TEST -    // TODO: INTEGRATE BETTER!!!! +            case 0x10: +                for (u32 pos = 0; pos < DataOutLen; pos += 4) +                    *(u32*)&DataOut[pos] = CartID; +                break; -    case 0x70: // init??? returns whether SDHC addressing should be used -        for (u32 pos = 0; pos < DataOutLen; pos += 4) -            *(u32*)&DataOut[pos] = 0; -        break; - -    case 0x53: // set address for read -        sc_addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; -        printf("SUPERCARD: read %08X\n", sc_addr); -        break; - -    case 0x80: // read operation busy, I guess -        // TODO: make it take some time -        for (u32 pos = 0; pos < DataOutLen; pos += 4) -            *(u32*)&DataOut[pos] = 0; -        break; - -    case 0x81: // read data -        { -            if (DataOutLen != 0x200) -                printf("SUPERCARD: BOGUS READ %d\n", DataOutLen); - -            // TODO: this is really inefficient. just testing -            FILE* f = fopen("scsd.bin", "rb"); -            fseek(f, sc_addr, SEEK_SET); -            fread(DataOut, 1, 0x200, f); -            fclose(f); -        } -        break; - -    // SUPERCARD EMULATION TEST END - - -    default: -        switch (cmd[0] & 0xF0) -        { -        case 0x40: -            DataEncMode = 2; -            break; - -        case 0x10: -            for (u32 pos = 0; pos < DataOutLen; pos += 4) -                *(u32*)&DataOut[pos] = CartID; -            break; +            case 0x20: +                { +                    u32 addr = (cmd[2] & 0xF0) << 8; +                    ReadROM(addr, 0x1000, 0); +                } +                break; -        case 0x20: -            { -                u32 addr = (cmd[2] & 0xF0) << 8; -                ReadROM(addr, 0x1000, 0); +            case 0xA0: +                CmdEncMode = 2; +                break;              } -            break; - -        case 0xA0: -            CmdEncMode = 2; -            break;          } +        else if (ROMCommandHandler) +            ROMCommandHandler(cmd);          break;      } |