From dfb2dff352a8e6eaf4e3d0e5109dc88d586ef18b Mon Sep 17 00:00:00 2001 From: Balint Kovacs Date: Wed, 3 Feb 2021 21:48:32 +0000 Subject: [PATCH] Some groundwork for CARD2 save support --- arm9/source/gamecart/card_spi.c | 70 +++++++++++++++------------------ arm9/source/gamecart/card_spi.h | 8 ++-- arm9/source/gamecart/gamecart.c | 43 +++++++++----------- arm9/source/gamecart/gamecart.h | 11 +++++- arm9/source/virtual/vcart.c | 14 ++----- 5 files changed, 67 insertions(+), 79 deletions(-) diff --git a/arm9/source/gamecart/card_spi.c b/arm9/source/gamecart/card_spi.c index 914771e..c32d24f 100644 --- a/arm9/source/gamecart/card_spi.c +++ b/arm9/source/gamecart/card_spi.c @@ -101,12 +101,12 @@ const CardSPITypeData * const FLASH_1MB_CTR = flashTypes + 8; #define REG_CFG9_CARDCTL *((vu16*)0x1000000C) #define CARDCTL_SPICARD (1u<<8) -int CardSPIWriteRead(CardSPIType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize) { +int CardSPIWriteRead(bool infrared, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize) { u32 headerFooterVal = 0; REG_CFG9_CARDCTL |= CARDCTL_SPICARD; - if (type.infrared) { + if (infrared) { SPI_XferInfo irXfer = { &headerFooterVal, 1, false }; SPI_DoXfer(SPI_DEV_CART_IR, &irXfer, 1, false); } @@ -123,13 +123,13 @@ int CardSPIWriteRead(CardSPIType type, const void* cmd, u32 cmdSize, void* answe return 0; } -int CardSPIWaitWriteEnd(CardSPIType type, u32 timeout) { +int CardSPIWaitWriteEnd(bool infrared, u32 timeout) { u8 cmd = SPI_CMD_RDSR, statusReg = 0; int res = 0; u64 time_start = timer_start(); do { - res = CardSPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); + res = CardSPIWriteRead(infrared, &cmd, 1, &statusReg, 1, 0, 0); if (res) return res; if (timer_msec(time_start) > timeout) return 1; } while(statusReg & SPI_FLG_WIP); @@ -139,18 +139,18 @@ int CardSPIWaitWriteEnd(CardSPIType type, u32 timeout) { int CardSPIEnableWriting_512B(CardSPIType type) { u8 cmd = SPI_CMD_WREN; - return CardSPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); + return CardSPIWriteRead(type.infrared, &cmd, 1, NULL, 0, 0, 0); } int CardSPIEnableWriting_regular(CardSPIType type) { u8 cmd = SPI_CMD_WREN, statusReg = 0; - int res = CardSPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); + int res = CardSPIWriteRead(type.infrared, &cmd, 1, NULL, 0, 0, 0); if (res) return res; cmd = SPI_CMD_RDSR; do { - res = CardSPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); + res = CardSPIWriteRead(type.infrared, &cmd, 1, &statusReg, 1, 0, 0); if (res) return res; } while(statusReg & ~SPI_FLG_WEL); @@ -165,24 +165,24 @@ int CardSPIEnableWriting(CardSPIType type) { int _SPIWriteTransaction(CardSPIType type, void* cmd, u32 cmdSize, const void* data, u32 dataSize) { int res; if ((res = CardSPIEnableWriting(type))) return res; - if ((res = CardSPIWriteRead(type, cmd, cmdSize, NULL, 0, (void*) ((u8*) data), dataSize))) return res; - return CardSPIWaitWriteEnd(type, 1000); + if ((res = CardSPIWriteRead(type.infrared, cmd, cmdSize, NULL, 0, (void*) ((u8*) data), dataSize))) return res; + return CardSPIWaitWriteEnd(type.infrared, 1000); } -int CardSPIReadJEDECIDAndStatusReg(CardSPIType type, u32* id, u8* statusReg) { +int CardSPIReadJEDECIDAndStatusReg(bool infrared, u32* id, u8* statusReg) { u8 cmd = SPI_FLASH_CMD_RDID; u8 reg = 0; u8 idbuf[3] = { 0 }; u32 id_ = 0; - int res = CardSPIWaitWriteEnd(type, 0); + int res = CardSPIWaitWriteEnd(infrared, 0); if (res) return res; - if ((res = CardSPIWriteRead(type, &cmd, 1, idbuf, 3, 0, 0))) return res; + if ((res = CardSPIWriteRead(infrared, &cmd, 1, idbuf, 3, 0, 0))) return res; id_ = (idbuf[0] << 16) | (idbuf[1] << 8) | idbuf[2]; cmd = SPI_CMD_RDSR; - if ((res = CardSPIWriteRead(type, &cmd, 1, ®, 1, 0, 0))) return res; + if ((res = CardSPIWriteRead(infrared, &cmd, 1, ®, 1, 0, 0))) return res; if (id) *id = id_; if (statusReg) *statusReg = reg; @@ -256,7 +256,7 @@ int CardSPIWriteSaveData_24bit_erase_program(CardSPIType type, u32 offset, const if (!(res = _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data - offset + pos), pageSize))) { break; } - CardSPIWriteRead(type, "\x04", 1, NULL, 0, NULL, 0); + CardSPIWriteRead(type.infrared, "\x04", 1, NULL, 0, NULL, 0); } if(res) { free(newData); @@ -278,7 +278,7 @@ int CardSPIWriteSaveData(CardSPIType type, u32 offset, const void* data, u32 siz u32 writeSize = type.chip->writeSize; if (writeSize == 0) return 0xC8E13404; - int res = CardSPIWaitWriteEnd(type, 1000); + int res = CardSPIWaitWriteEnd(type.infrared, 1000); if (res) return res; while(pos < end) { @@ -307,7 +307,7 @@ int CardSPIReadSaveData_9bit(CardSPIType type, u32 pos, void* data, u32 size) { cmd[0] = SPI_512B_EEPROM_CMD_RDLO; cmd[1] = (u8) pos; - int res = CardSPIWriteRead(type, cmd, cmdSize, data, len, NULL, 0); + int res = CardSPIWriteRead(type.infrared, cmd, cmdSize, data, len, NULL, 0); if (res) return res; read += len; @@ -319,7 +319,7 @@ int CardSPIReadSaveData_9bit(CardSPIType type, u32 pos, void* data, u32 size) { cmd[0] = SPI_512B_EEPROM_CMD_RDHI; cmd[1] = (u8)(pos + read); - int res = CardSPIWriteRead(type, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0); + int res = CardSPIWriteRead(type.infrared, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0); if (res) return res; } @@ -330,13 +330,13 @@ int CardSPIReadSaveData_9bit(CardSPIType type, u32 pos, void* data, u32 size) { int CardSPIReadSaveData_16bit(CardSPIType type, u32 offset, void* data, u32 size) { u8 cmd[3] = { SPI_CMD_READ, (u8)(offset >> 8), (u8) offset }; - return CardSPIWriteRead(type, cmd, 3, data, size, NULL, 0); + return CardSPIWriteRead(type.infrared, cmd, 3, data, size, NULL, 0); } int CardSPIReadSaveData_24bit(CardSPIType type, u32 offset, void* data, u32 size) { u8 cmd[4] = { SPI_CMD_READ, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; - return CardSPIWriteRead(type, cmd, 4, data, size, NULL, 0); + return CardSPIWriteRead(type.infrared, cmd, 4, data, size, NULL, 0); } int CardSPIReadSaveData(CardSPIType type, u32 offset, void* data, u32 size) { @@ -344,7 +344,7 @@ int CardSPIReadSaveData(CardSPIType type, u32 offset, void* data, u32 size) { if (size == 0) return 0; - int res = CardSPIWaitWriteEnd(type, 1000); + int res = CardSPIWaitWriteEnd(type.infrared, 1000); if (res) return res; size = (size <= CardSPIGetCapacity(type) - offset) ? size : CardSPIGetCapacity(type) - offset; @@ -367,7 +367,7 @@ int CardSPIEraseSector_emulated(CardSPIType type, u32 offset) { int CardSPIEraseSector_real(CardSPIType type, u32 offset) { u8 cmd[4] = { type.chip->eraseCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; - int res = CardSPIWaitWriteEnd(type, 10000); + int res = CardSPIWaitWriteEnd(type.infrared, 10000); if (res) return res; return _SPIWriteTransaction(type, cmd, 4, NULL, 0); @@ -438,7 +438,7 @@ int _SPIIsDataMirrored(CardSPIType type, int size, bool* mirrored) { return 0; } -int CardSPIGetCardSPIType(CardSPIType* type, bool infrared) { +CardSPIType CardSPIGetCardSPIType(bool infrared) { u8 sr = 0; u32 jedec = 0; CardSPIType t = {NO_CHIP, infrared}; @@ -446,20 +446,18 @@ int CardSPIGetCardSPIType(CardSPIType* type, bool infrared) { if(infrared) { // Infrared carts currently not supported, need additional handling! - *type = (CardSPIType) {NO_CHIP, true}; - return 0; + return (CardSPIType) {NO_CHIP, true}; } - res = CardSPIReadJEDECIDAndStatusReg(t, &jedec, &sr); - if (res) return res; + res = CardSPIReadJEDECIDAndStatusReg(infrared, &jedec, &sr); + if (res) return (CardSPIType) {NO_CHIP, false}; if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { t.chip = &FLASH_DUMMY; } - if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { *type = (CardSPIType) { EEPROM_512B, false }; return 0; } + if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { return (CardSPIType) { EEPROM_512B, false }; } if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) { t = (CardSPIType) { &EEPROM_DUMMY, false }; } if(t.chip == NO_CHIP) { - *type = (CardSPIType) {NO_CHIP, false}; - return 0; + return (CardSPIType) {NO_CHIP, false}; } if (t.chip == &EEPROM_DUMMY) { @@ -467,24 +465,20 @@ int CardSPIGetCardSPIType(CardSPIType* type, bool infrared) { size_t i; for(i = 0; i < sizeof(EEPROMTypes) / sizeof(CardSPITypeData) - 1; i++) { - if ((res = _SPIIsDataMirrored(t, CardSPIGetCapacity((CardSPIType) {EEPROMTypes + i, false}), &mirrored))) return res; + if ((res = _SPIIsDataMirrored(t, CardSPIGetCapacity((CardSPIType) {EEPROMTypes + i, false}), &mirrored))) return (CardSPIType) {NO_CHIP, false}; if (mirrored) { - *type = (CardSPIType) {EEPROMTypes + i, false}; - return 0; + return (CardSPIType) {EEPROMTypes + i, false}; } } - *type = (CardSPIType) { EEPROMTypes + i, false }; - return 0; + return (CardSPIType) { EEPROMTypes + i, false }; } for(size_t i = 0; i < sizeof(flashTypes) / sizeof(CardSPITypeData); i++) { if (flashTypes[i].jedecId == jedec) { - *type = (CardSPIType) { flashTypes + i, infrared }; - return 0; + return (CardSPIType) { flashTypes + i, infrared }; } } - *type = (CardSPIType) { NO_CHIP, infrared }; - return 0; + return (CardSPIType) { NO_CHIP, infrared }; } diff --git a/arm9/source/gamecart/card_spi.h b/arm9/source/gamecart/card_spi.h index 359a7be..0c60a5a 100644 --- a/arm9/source/gamecart/card_spi.h +++ b/arm9/source/gamecart/card_spi.h @@ -66,11 +66,11 @@ extern const CardSPITypeData * const FLASH_128KB_CTR; // Most common, including extern const CardSPITypeData * const FLASH_512KB_CTR; // Also common, including Detective Pikachu extern const CardSPITypeData * const FLASH_1MB_CTR; // For example Pokemon Ultra Sun -int CardSPIWriteRead(CardSPIType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize); -int CardSPIWaitWriteEnd(CardSPIType type, u32 timeout); +int CardSPIWriteRead(bool infrared, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize); +int CardSPIWaitWriteEnd(bool infrared, u32 timeout); int CardSPIEnableWriting(CardSPIType type); -int CardSPIReadJEDECIDAndStatusReg(CardSPIType type, u32* id, u8* statusReg); -int CardSPIGetCardSPIType(CardSPIType* type, bool infrared); +int CardSPIReadJEDECIDAndStatusReg(bool infrared, u32* id, u8* statusReg); +CardSPIType CardSPIGetCardSPIType(bool infrared); u32 CardSPIGetPageSize(CardSPIType type); u32 CardSPIGetCapacity(CardSPIType type); u32 CardSPIGetEraseSize(CardSPIType type); diff --git a/arm9/source/gamecart/gamecart.c b/arm9/source/gamecart/gamecart.c index 5ad4acb..a6b34db 100644 --- a/arm9/source/gamecart/gamecart.c +++ b/arm9/source/gamecart/gamecart.c @@ -152,10 +152,18 @@ u32 InitCartRead(CartData* cdata) { // save data u32 card2_offset = getle32(cdata->header + 0x200); - if ((card2_offset != 0xFFFFFFFF) || (CardSPIGetCardSPIType(&(cdata->save_type), 0) != 0)) { - cdata->save_type = (CardSPIType) { NO_CHIP, false }; + if (card2_offset != 0xFFFFFFFF) { + cdata->save_type = CARD_SAVE_CARD2; + cdata->save_size = cdata->cart_size - card2_offset * NCSD_MEDIA_UNIT; + } else { + cdata->spi_save_type = CardSPIGetCardSPIType(false); + if (cdata->spi_save_type.chip == NO_CHIP) { + cdata->save_type = CARD_SAVE_NONE; + } else { + cdata->save_type = CARD_SAVE_SPI; + cdata->save_size = CardSPIGetCapacity(cdata->spi_save_type); + } } - cdata->save_size = CardSPIGetCapacity(cdata->save_type); } else { // NTR/TWL cartridges // NTR header TwlHeader* nds_header = (void*)cdata->header; @@ -198,10 +206,13 @@ u32 InitCartRead(CartData* cdata) { // save data bool infrared = *(nds_header->game_code) == 'I'; - if (CardSPIGetCardSPIType(&(cdata->save_type), infrared) != 0) { - cdata->save_type = (CardSPIType) { NO_CHIP, false }; + cdata->spi_save_type = CardSPIGetCardSPIType(infrared); + if (cdata->spi_save_type.chip == NO_CHIP) { + cdata->save_type = CARD_SAVE_NONE; + } else { + cdata->save_type = CARD_SAVE_SPI; + cdata->save_size = CardSPIGetCapacity(cdata->spi_save_type); } - cdata->save_size = CardSPIGetCapacity(cdata->save_type); } return 0; } @@ -336,27 +347,11 @@ u32 ReadCartInfo(u8* buffer, u64 offset, u64 count, CartData* cdata) { u32 ReadCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata) { if (offset >= cdata->save_size) return 1; if (offset + count > cdata->save_size) count = cdata->save_size - offset; - return (CardSPIReadSaveData(cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; + return (CardSPIReadSaveData(cdata->spi_save_type, offset, buffer, count) == 0) ? 0 : 1; } u32 WriteCartSave(const u8* buffer, u64 offset, u64 count, CartData* cdata) { if (offset >= cdata->save_size) return 1; if (offset + count > cdata->save_size) count = cdata->save_size - offset; - return (CardSPIWriteSaveData(cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; -} - -u32 ReadCartSaveJedecId(u8* buffer, u64 offset, u64 count, CartData* cdata) { - u8 ownBuf[JEDECID_AND_SREG_SIZE] = { 0 }; - u32 id; - u8 sReg; - if (offset >= JEDECID_AND_SREG_SIZE) return 1; - if (offset + count > JEDECID_AND_SREG_SIZE) count = JEDECID_AND_SREG_SIZE - offset; - int res = CardSPIReadJEDECIDAndStatusReg(cdata->save_type, &id, &sReg); - if (res) return res; - ownBuf[0] = (id >> 16) & 0xff; - ownBuf[1] = (id >> 8) & 0xff; - ownBuf[2] = id & 0xff; - ownBuf[JEDECID_AND_SREG_SIZE - 1] = sReg; - memcpy(buffer, ownBuf + offset, count); - return 0; + return (CardSPIWriteSaveData(cdata->spi_save_type, offset, buffer, count) == 0) ? 0 : 1; } diff --git a/arm9/source/gamecart/gamecart.h b/arm9/source/gamecart/gamecart.h index cafc407..cf485e1 100644 --- a/arm9/source/gamecart/gamecart.h +++ b/arm9/source/gamecart/gamecart.h @@ -12,6 +12,13 @@ #define PRIV_HDR_SIZE 0x50 #define JEDECID_AND_SREG_SIZE 0x4 +typedef enum CardSaveType { + CARD_SAVE_NONE, + CARD_SAVE_SPI, + CARD_SAVE_CARD2, + CARD_SAVE_RETAIL_NAND, +} CardSaveType; + typedef struct { u8 header[0x8000]; // NTR header + secure area / CTR header + private header u8 storage[0x8000]; // encrypted secure area + modcrypt area / unused @@ -20,7 +27,8 @@ typedef struct { u64 cart_size; u64 data_size; u32 save_size; - CardSPIType save_type; + CardSaveType save_type; + CardSPIType spi_save_type; // Specific data for SPI save u32 arm9i_rom_offset; // TWL specific } PACKED_ALIGN(16) CartData; @@ -34,4 +42,3 @@ u32 ReadCartPrivateHeader(void* buffer, u64 offset, u64 count, CartData* cdata); u32 ReadCartInfo(u8* buffer, u64 offset, u64 count, CartData* cdata); u32 ReadCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata); u32 WriteCartSave(const u8* buffer, u64 offset, u64 count, CartData* cdata); -u32 ReadCartSaveJedecId(u8* buffer, u64 offset, u64 count, CartData* cdata); diff --git a/arm9/source/virtual/vcart.c b/arm9/source/virtual/vcart.c index 709c5c3..481470a 100644 --- a/arm9/source/virtual/vcart.c +++ b/arm9/source/virtual/vcart.c @@ -2,9 +2,8 @@ #include "gamecart.h" #define FAT_LIMIT 0x100000000 -#define VFLAG_SECURE_AREA_ENC (1UL<<27) -#define VFLAG_GAMECART_NFO (1UL<<28) -#define VFLAG_JEDECID_AND_SRFG (1UL<<29) +#define VFLAG_SECURE_AREA_ENC (1UL<<28) +#define VFLAG_GAMECART_NFO (1UL<<29) #define VFLAG_SAVEGAME (1UL<<30) #define VFLAG_PRIV_HDR (1UL<<31) @@ -66,7 +65,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) { vfile->size = PRIV_HDR_SIZE; vfile->flags |= VFLAG_PRIV_HDR; return true; - } else if ((vdir->index == 7) && (cdata->save_size > 0)) { // savegame + } else if ((vdir->index == 7) && (cdata->save_type != CARD_SAVE_NONE)) { // savegame snprintf(vfile->name, 32, "%s.sav", name); vfile->size = cdata->save_size; vfile->flags = VFLAG_SAVEGAME; @@ -78,11 +77,6 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) { vfile->size = strnlen(info, 255); vfile->flags |= VFLAG_GAMECART_NFO; return true; - } else if ((vdir->index == 9) && cdata->save_type.chip) { // JEDEC id and status register - strcpy(vfile->name, "jedecid_and_sreg.bin"); - vfile->size = JEDECID_AND_SREG_SIZE; - vfile->flags |= VFLAG_JEDECID_AND_SRFG; - return true; } } @@ -99,8 +93,6 @@ int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) return ReadCartSave(buffer, foffset, count, cdata); else if (vfile->flags & VFLAG_GAMECART_NFO) return ReadCartInfo(buffer, foffset, count, cdata); - else if (vfile->flags & VFLAG_JEDECID_AND_SRFG) - return ReadCartSaveJedecId(buffer, foffset, count, cdata); SetSecureAreaEncryption(vfile->flags & VFLAG_SECURE_AREA_ENC); return ReadCartBytes(buffer, foffset, count, cdata);