mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 21:52:48 +00:00
Initial support of cartridge savegame reads
This commit is contained in:
parent
beb2a881cd
commit
01bc082ca0
@ -3,6 +3,7 @@
|
||||
#include "command_ctr.h"
|
||||
#include "command_ntr.h"
|
||||
#include "card_eeprom.h"
|
||||
#include "spi.h"
|
||||
#include "nds.h"
|
||||
#include "ncch.h"
|
||||
#include "ncsd.h"
|
||||
@ -23,6 +24,8 @@ typedef struct {
|
||||
u32 cart_id;
|
||||
u64 cart_size;
|
||||
u64 data_size;
|
||||
u32 save_size;
|
||||
int save_type;
|
||||
u32 unused_offset;
|
||||
} PACKED_ALIGN(16) CartDataCtr;
|
||||
|
||||
@ -37,6 +40,8 @@ typedef struct {
|
||||
u32 cart_id;
|
||||
u64 cart_size;
|
||||
u64 data_size;
|
||||
u32 save_size;
|
||||
int save_type;
|
||||
u32 arm9i_rom_offset;
|
||||
} PACKED_ALIGN(16) CartDataNtrTwl;
|
||||
|
||||
@ -55,14 +60,14 @@ u32 GetCartName(char* name, CartData* cdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 InitCardRead(CartData* cdata) {
|
||||
u32 InitCartRead(CartData* cdata) {
|
||||
memset(cdata, 0x00, sizeof(CartData));
|
||||
cdata->cart_type = CART_NONE;
|
||||
if (!CART_INSERTED) return 1;
|
||||
Cart_Init();
|
||||
cdata->cart_id = Cart_GetID();
|
||||
cdata->cart_type = (cdata->cart_id & 0x10000000) ? CART_CTR : CART_NTR;
|
||||
if (cdata->cart_type & CART_CTR) {
|
||||
if (cdata->cart_type & CART_CTR) { // CTR cartridges
|
||||
memset(cdata, 0xFF, 0x4000 + PRIV_HDR_SIZE); // switch the padding to 0xFF
|
||||
|
||||
// init, NCCH header
|
||||
@ -93,7 +98,14 @@ u32 InitCardRead(CartData* cdata) {
|
||||
memcpy(priv_header + 0x40, &(cdata->cart_id), 4);
|
||||
memset(priv_header + 0x44, 0x00, 4);
|
||||
memset(priv_header + 0x48, 0xFF, 8);
|
||||
} else {
|
||||
|
||||
// save data
|
||||
u32 card2_offset = getle32(cdata->header + 0x200);
|
||||
if ((card2_offset != 0xFFFFFFFF) || (SPIGetCardType((CardType*) (CardType*) &(cdata->save_type), 0) != 0) || (cdata->save_type < 0)) {
|
||||
cdata->save_type = -1;
|
||||
cdata->save_size = 0;
|
||||
} else cdata->save_size = SPIGetCapacity((CardType) cdata->save_type);
|
||||
} else { // NTR/TWL cartridges
|
||||
// NTR header
|
||||
TwlHeader* nds_header = (void*)cdata->header;
|
||||
NTR_CmdReadHeader(cdata->header);
|
||||
@ -121,6 +133,13 @@ u32 InitCardRead(CartData* cdata) {
|
||||
|
||||
// last safety check
|
||||
if (cdata->data_size > cdata->cart_size) return 1;
|
||||
|
||||
// save data
|
||||
u32 infrared = (*(nds_header->game_code) == 'I') ? 1 : 0;
|
||||
if ((SPIGetCardType((CardType*) &(cdata->save_type), infrared) != 0) || (cdata->save_type < 0)) {
|
||||
cdata->save_type = -1;
|
||||
cdata->save_size = 0;
|
||||
} else cdata->save_size = SPIGetCapacity((CardType) cdata->save_type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -221,3 +240,15 @@ u32 ReadCartPrivateHeader(void* buffer, u64 offset, u64 count, CartData* cdata)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 (SPIReadSaveData((CardType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
u32 WriteCartSave(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 (SPIWriteSaveData((CardType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1;
|
||||
}
|
||||
|
@ -17,11 +17,15 @@ typedef struct {
|
||||
u32 cart_id;
|
||||
u64 cart_size;
|
||||
u64 data_size;
|
||||
u32 save_size;
|
||||
int save_type;
|
||||
u32 arm9i_rom_offset; // TWL specific
|
||||
} PACKED_ALIGN(16) CartData;
|
||||
|
||||
u32 GetCartName(char* name, CartData* cdata);
|
||||
u32 InitCardRead(CartData* cdata);
|
||||
u32 InitCartRead(CartData* cdata);
|
||||
u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata);
|
||||
u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata);
|
||||
u32 ReadCartPrivateHeader(void* buffer, u64 offset, u64 count, CartData* cdata);
|
||||
u32 ReadCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata);
|
||||
u32 WriteCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata);
|
||||
|
453
arm9/source/gamecart/spi.c
Normal file
453
arm9/source/gamecart/spi.c
Normal file
@ -0,0 +1,453 @@
|
||||
/*
|
||||
* This file is part of TWLSaveTool.
|
||||
* Copyright (C) 2015-2016 TuxSH
|
||||
*
|
||||
* TWLSaveTool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "spi.h"
|
||||
|
||||
// Deliberately written in C! (except for a few lines)
|
||||
|
||||
// u8* fill_buf = NULL;
|
||||
|
||||
#define CFG_CARDCONF *(vu16 *)0x1000000C
|
||||
|
||||
#define REG_SPICARDCNT *(vu32 *)0x1000D800
|
||||
#define REG_SPICARDSIZE *(vu32 *)0x1000D808
|
||||
#define REG_SPICARDFIFO *(vu32 *)0x1000D80C
|
||||
#define REG_SPICARDFIFOSTAT *(vu32 *)0x1000D810
|
||||
#define REG_UNK_AT_0x18 *(vu32 *)0x1000D818
|
||||
|
||||
#define SPICARD_START_IS_BUSY 0x8000
|
||||
|
||||
//Thanks @Steveice10 for giving me his P9 symbols (IDB file).
|
||||
//This code is compatible with SPI.c/SPI.h from TWLSaveTool/// Card SPI baud rate.
|
||||
|
||||
//taken over from ctrulib fs.h
|
||||
typedef enum {
|
||||
BAUDRATE_512KHZ = 0, ///< 512KHz.
|
||||
BAUDRATE_1MHZ = 1, ///< 1MHz.
|
||||
BAUDRATE_2MHZ = 2, ///< 2MHz.
|
||||
BAUDRATE_4MHZ = 3, ///< 4MHz.
|
||||
BAUDRATE_8MHZ = 4, ///< 8MHz.
|
||||
BAUDRATE_16MHZ = 5, ///< 16MHz.
|
||||
} FS_CardSpiBaudRate;
|
||||
|
||||
|
||||
void _SPITransferData(void *data, u32 len, FS_CardSpiBaudRate baudRate, bool write)
|
||||
{
|
||||
REG_SPICARDCNT = (((write) ? 1 : 0) << 13) | (1 << 12) | (u32)baudRate;
|
||||
REG_UNK_AT_0x18 = 6;
|
||||
REG_SPICARDCNT |= SPICARD_START_IS_BUSY; //start
|
||||
|
||||
u32 wordCount = (len + 3) >> 2;
|
||||
|
||||
for(u32 i = 0; i < wordCount; i++)
|
||||
{
|
||||
u32 nbBytes = (len <= 4) ? len : 4;
|
||||
|
||||
if(write)
|
||||
{
|
||||
u32 word = 0;
|
||||
memcpy(&word, (u32 *)data + i, nbBytes);
|
||||
while(!(REG_SPICARDFIFOSTAT & 1));
|
||||
REG_SPICARDFIFOSTAT = word;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
while(!(REG_SPICARDFIFOSTAT & 1));
|
||||
u32 word = REG_SPICARDFIFOSTAT;
|
||||
memcpy((u32 *)data + i, &word, nbBytes);
|
||||
}
|
||||
|
||||
while(REG_SPICARDCNT & SPICARD_START_IS_BUSY);
|
||||
len -= nbBytes;
|
||||
}
|
||||
}
|
||||
|
||||
int SPIWriteRead(CardType type, void* cmd, u32 cmdSize, void* answer, u32 answerSize, void* data, u32 dataSize)
|
||||
{
|
||||
bool infra = type == FLASH_512KB_INFRARED || type == FLASH_256KB_INFRARED;
|
||||
|
||||
CFG_CARDCONF |= 0x100; //wake card
|
||||
|
||||
u32 zero = 0;
|
||||
if(infra) _SPITransferData(&zero, 1, BAUDRATE_1MHZ, true); //header
|
||||
|
||||
if(cmd != NULL) _SPITransferData(cmd, cmdSize, BAUDRATE_4MHZ, true);
|
||||
if(answer != NULL) _SPITransferData(answer, answerSize, BAUDRATE_4MHZ, false);
|
||||
if(data != NULL) _SPITransferData(data, dataSize, BAUDRATE_4MHZ, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIWaitWriteEnd(CardType type) {
|
||||
u8 cmd = SPI_CMD_RDSR, statusReg = 0;
|
||||
int res = 0;
|
||||
|
||||
do{
|
||||
res = SPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0);
|
||||
if(res) return res;
|
||||
} while(statusReg & SPI_FLG_WIP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIEnableWriting(CardType type) {
|
||||
u8 cmd = SPI_CMD_WREN, statusReg = 0;
|
||||
int res = SPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0);
|
||||
|
||||
if(res || type == EEPROM_512B) return res; // Weird, but works (otherwise we're getting an infinite loop for that chip type).
|
||||
cmd = SPI_CMD_RDSR;
|
||||
|
||||
do{
|
||||
res = SPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0);
|
||||
if(res) return res;
|
||||
} while(statusReg & ~SPI_FLG_WEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg) {
|
||||
u8 cmd = SPI_FLASH_CMD_RDID;
|
||||
u8 reg = 0;
|
||||
u8 idbuf[3] = { 0 };
|
||||
u32 id_ = 0;
|
||||
int res = SPIWaitWriteEnd(type);
|
||||
if(res) return res;
|
||||
|
||||
if((res = SPIWriteRead(type, &cmd, 1, idbuf, 3, 0, 0))) return res;
|
||||
|
||||
id_ = (idbuf[0] << 16) | (idbuf[1] << 8) | idbuf[2];
|
||||
cmd = SPI_CMD_RDSR;
|
||||
|
||||
if((res = SPIWriteRead(type, &cmd, 1, ®, 1, 0, 0))) return res;
|
||||
|
||||
if(id) *id = id_;
|
||||
if(statusReg) *statusReg = reg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 SPIGetPageSize(CardType type) {
|
||||
u32 EEPROMSizes[] = { 16, 32, 128, 256 };
|
||||
if(type == NO_CHIP || type > CHIP_LAST) return 0;
|
||||
else if(type < FLASH_256KB_1) return EEPROMSizes[(int) type];
|
||||
else return 256;
|
||||
}
|
||||
|
||||
u32 SPIGetCapacity(CardType type) {
|
||||
u32 sz[] = { 9, 13, 16, 17, 18, 18, 19, 19, 20, 23, 19, 19 };
|
||||
|
||||
if(type == NO_CHIP || type > CHIP_LAST) return 0;
|
||||
else return 1 << sz[(int) type];
|
||||
}
|
||||
|
||||
int SPIWriteSaveData(CardType type, u32 offset, void* data, u32 size) {
|
||||
u8 cmd[4] = { 0 };
|
||||
u32 cmdSize = 4;
|
||||
|
||||
u32 end = offset + size;
|
||||
u32 pos = offset;
|
||||
if(size == 0) return 0;
|
||||
u32 pageSize = SPIGetPageSize(type);
|
||||
if(pageSize == 0) return 0xC8E13404;
|
||||
|
||||
int res = SPIWaitWriteEnd(type);
|
||||
if(res) return res;
|
||||
|
||||
size = (size <= SPIGetCapacity(type) - offset) ? size : SPIGetCapacity(type) - offset;
|
||||
|
||||
while(pos < end) {
|
||||
switch(type) {
|
||||
case EEPROM_512B:
|
||||
cmdSize = 2;
|
||||
cmd[0] = (pos >= 0x100) ? SPI_512B_EEPROM_CMD_WRHI : SPI_512B_EEPROM_CMD_WRLO;
|
||||
cmd[1] = (u8) pos;
|
||||
break;
|
||||
case EEPROM_8KB:
|
||||
case EEPROM_64KB:
|
||||
cmdSize = 3;
|
||||
cmd[0] = SPI_EEPROM_CMD_WRITE;
|
||||
cmd[1] = (u8)(pos >> 8);
|
||||
cmd[2] = (u8) pos;
|
||||
break;
|
||||
case EEPROM_128KB:
|
||||
cmdSize = 4;
|
||||
cmd[0] = SPI_EEPROM_CMD_WRITE;
|
||||
cmd[1] = (u8)(pos >> 16);
|
||||
cmd[2] = (u8)(pos >> 8);
|
||||
cmd[3] = (u8) pos;
|
||||
break;
|
||||
case FLASH_256KB_1:
|
||||
/*
|
||||
This is what is done in the official implementation, but I think it's wrong
|
||||
cmdSize = 4;
|
||||
cmd[0] = SPI_CMD_PP;
|
||||
cmd[1] = (u8)(pos >> 16);
|
||||
cmd[2] = (u8)(pos >> 8);
|
||||
cmd[3] = (u8) pos;
|
||||
break;
|
||||
*/
|
||||
case FLASH_256KB_2:
|
||||
case FLASH_512KB_1:
|
||||
case FLASH_512KB_2:
|
||||
case FLASH_1MB:
|
||||
case FLASH_512KB_INFRARED:
|
||||
case FLASH_256KB_INFRARED:
|
||||
cmdSize = 4;
|
||||
cmd[0] = SPI_FLASH_CMD_PW;
|
||||
cmd[1] = (u8)(pos >> 16);
|
||||
cmd[2] = (u8)(pos >> 8);
|
||||
cmd[3] = (u8) pos;
|
||||
break;
|
||||
case FLASH_8MB:
|
||||
return 0xC8E13404; // writing is unsupported (so is reading? need to test)
|
||||
default:
|
||||
return 0; // never happens
|
||||
}
|
||||
|
||||
u32 remaining = end - pos;
|
||||
u32 nb = pageSize - (pos % pageSize);
|
||||
|
||||
u32 dataSize = (remaining < nb) ? remaining : nb;
|
||||
|
||||
if( (res = SPIEnableWriting(type)) ) return res;
|
||||
if( (res = SPIWriteRead(type, cmd, cmdSize, NULL, 0, (void*) ((u8*) data - offset + pos), dataSize)) ) return res;
|
||||
if( (res = SPIWaitWriteEnd(type)) ) return res;
|
||||
|
||||
pos = ((pos / pageSize) + 1) * pageSize; // truncate
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _SPIReadSaveData_512B_impl(u32 pos, void* data, u32 size) {
|
||||
u8 cmd[4];
|
||||
u32 cmdSize = 2;
|
||||
|
||||
u32 end = pos + size;
|
||||
|
||||
u32 read = 0;
|
||||
if(pos < 0x100) {
|
||||
u32 len = 0x100 - pos;
|
||||
cmd[0] = SPI_512B_EEPROM_CMD_RDLO;
|
||||
cmd[1] = (u8) pos;
|
||||
|
||||
int res = SPIWriteRead(EEPROM_512B, cmd, cmdSize, data, len, NULL, 0);
|
||||
if(res) return res;
|
||||
|
||||
read += len;
|
||||
}
|
||||
|
||||
if(end >= 0x100) {
|
||||
u32 len = end - 0x100;
|
||||
|
||||
cmd[0] = SPI_512B_EEPROM_CMD_RDHI;
|
||||
cmd[1] = (u8)(pos + read);
|
||||
|
||||
int res = SPIWriteRead(EEPROM_512B, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0);
|
||||
|
||||
if(res) return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIReadSaveData(CardType type, u32 offset, void* data, u32 size) {
|
||||
u8 cmd[4] = { SPI_CMD_READ };
|
||||
u32 cmdSize = 4;
|
||||
if(size == 0) return 0;
|
||||
if(type == NO_CHIP) return 0xC8E13404;
|
||||
|
||||
int res = SPIWaitWriteEnd(type);
|
||||
if(res) return res;
|
||||
|
||||
size = (size <= SPIGetCapacity(type) - offset) ? size : SPIGetCapacity(type) - offset;
|
||||
u32 pos = offset;
|
||||
switch(type) {
|
||||
case EEPROM_512B:
|
||||
return _SPIReadSaveData_512B_impl(offset, data, size);
|
||||
break;
|
||||
case EEPROM_8KB:
|
||||
case EEPROM_64KB:
|
||||
cmdSize = 3;
|
||||
cmd[1] = (u8)(pos >> 8);
|
||||
cmd[2] = (u8) pos;
|
||||
break;
|
||||
case EEPROM_128KB:
|
||||
cmdSize = 4;
|
||||
cmd[1] = (u8)(pos >> 16);
|
||||
cmd[2] = (u8)(pos >> 8);
|
||||
cmd[3] = (u8) pos;
|
||||
break;
|
||||
case FLASH_256KB_1:
|
||||
case FLASH_256KB_2:
|
||||
case FLASH_512KB_1:
|
||||
case FLASH_512KB_2:
|
||||
case FLASH_1MB:
|
||||
case FLASH_8MB:
|
||||
case FLASH_512KB_INFRARED:
|
||||
case FLASH_256KB_INFRARED:
|
||||
cmdSize = 4;
|
||||
cmd[1] = (u8)(pos >> 16);
|
||||
cmd[2] = (u8)(pos >> 8);
|
||||
cmd[3] = (u8) pos;
|
||||
break;
|
||||
default:
|
||||
return 0; // never happens
|
||||
}
|
||||
|
||||
return SPIWriteRead(type, cmd, cmdSize, data, size, NULL, 0);
|
||||
}
|
||||
|
||||
/* int SPIEraseSector(CardType type, u32 offset) {
|
||||
u8 cmd[4] = { SPI_FLASH_CMD_SE, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset };
|
||||
if(type == NO_CHIP || type == FLASH_8MB) return 0xC8E13404;
|
||||
|
||||
if(type < FLASH_256KB_1 && fill_buf == NULL) {
|
||||
fill_buf = new u8[0x10000];
|
||||
memset(fill_buf, 0xff, 0x10000);
|
||||
}
|
||||
|
||||
int res = SPIWaitWriteEnd(type);
|
||||
|
||||
if(type >= FLASH_256KB_1) {
|
||||
if( (res = SPIEnableWriting(type)) ) return res;
|
||||
if( (res = SPIWriteRead(type, cmd, 4, NULL, 0, NULL, 0)) ) return res;
|
||||
if( (res = SPIWaitWriteEnd(type)) ) return res;
|
||||
}
|
||||
// Simulate the same behavior on EEPROM chips.
|
||||
else {
|
||||
u32 sz = SPIGetCapacity(type);
|
||||
int res = SPIWriteSaveData(type, 0, fill_buf, (sz < 0x10000) ? sz : 0x10000);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
} */
|
||||
|
||||
|
||||
// The following routine use code from savegame-manager:
|
||||
|
||||
/*
|
||||
* savegame_manager: a tool to backup and restore savegames from Nintendo
|
||||
* DS cartridges. Nintendo DS and all derivative names are trademarks
|
||||
* by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash.
|
||||
*
|
||||
* auxspi.cpp: A thin reimplementation of the AUXSPI protocol
|
||||
* (high level functions)
|
||||
*
|
||||
* Copyright (C) Pokedoc (2010)
|
||||
*/
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
int _SPIIsDataMirrored(CardType type, int size, bool* mirrored) {
|
||||
u32 offset0 = (size-1); // n KB
|
||||
u32 offset1 = (2*size-1); // 2n KB
|
||||
|
||||
u8 buf1; // +0k data read -> write
|
||||
u8 buf2; // +n k data read -> read
|
||||
u8 buf3; // +0k ~data write
|
||||
u8 buf4; // +n k data new comp buf2
|
||||
|
||||
int res;
|
||||
|
||||
if( (res = SPIReadSaveData(type, offset0, &buf1, 1)) ) return res;
|
||||
if( (res = SPIReadSaveData(type, offset1, &buf2, 1)) ) return res;
|
||||
buf3=~buf1;
|
||||
if( (res = SPIWriteSaveData(type, offset0, &buf3, 1)) ) return res;
|
||||
if( (res = SPIReadSaveData(type, offset1, &buf4, 1)) ) return res;
|
||||
if( (res = SPIWriteSaveData(type, offset0, &buf1, 1)) ) return res;
|
||||
|
||||
*mirrored = buf2 != buf4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SPIGetCardType(CardType* type, int infrared) {
|
||||
u8 sr = 0;
|
||||
u32 jedec = 0;
|
||||
u32 tries = 0;
|
||||
CardType t = (infrared == 1) ? FLASH_INFRARED_DUMMY : FLASH_STD_DUMMY;
|
||||
int res;
|
||||
u32 jedecOrderedList[] = { 0x204012, 0x621600, 0x204013, 0x621100, 0x204014, 0x202017};
|
||||
|
||||
u32 maxTries = (infrared == -1) ? 2 : 1; // note: infrared = -1 fails 1/3 of the time
|
||||
while(tries < maxTries){
|
||||
res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy
|
||||
if(res) return res;
|
||||
|
||||
if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { break; }
|
||||
if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { t = EEPROM_512B; break; }
|
||||
if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) { t = EEPROM_STD_DUMMY; break; }
|
||||
|
||||
++tries;
|
||||
t = FLASH_INFRARED_DUMMY;
|
||||
}
|
||||
|
||||
if(t == EEPROM_512B) { *type = t; return 0; }
|
||||
else if(t == EEPROM_STD_DUMMY) {
|
||||
bool mirrored = false;
|
||||
|
||||
if( (res = _SPIIsDataMirrored(t, 8192, &mirrored)) ) return res;
|
||||
if(mirrored) t = EEPROM_8KB;
|
||||
else{
|
||||
if( (res = _SPIIsDataMirrored(t, 65536, &mirrored)) ) return res;
|
||||
if(mirrored) t = EEPROM_64KB;
|
||||
else t = EEPROM_128KB;
|
||||
}
|
||||
|
||||
*type = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if(t == FLASH_INFRARED_DUMMY) {
|
||||
if(infrared == 0) *type = NO_CHIP; // did anything go wrong?
|
||||
|
||||
if(jedec == jedecOrderedList[0] || jedec == jedecOrderedList[1]) *type = FLASH_256KB_INFRARED;
|
||||
else *type = FLASH_512KB_INFRARED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
else {
|
||||
if(infrared == 1) *type = NO_CHIP; // did anything go wrong?
|
||||
if(jedec == 0x204017) { *type = FLASH_8MB; return 0; } // 8MB. savegame-manager: which one? (more work is required to unlock this save chip!)
|
||||
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 6; ++i) {
|
||||
if(jedec == jedecOrderedList[i]) { *type = (CardType)((int) FLASH_256KB_1 + i); return 0; }
|
||||
}
|
||||
|
||||
*type = NO_CHIP;
|
||||
return 0;
|
||||
}
|
||||
}
|
89
arm9/source/gamecart/spi.h
Normal file
89
arm9/source/gamecart/spi.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of TWLSaveTool.
|
||||
* Copyright (C) 2015-2016 TuxSH
|
||||
*
|
||||
* TWLSaveTool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SPI_CMD_RDSR 5
|
||||
#define SPI_CMD_WREN 6
|
||||
|
||||
#define SPI_512B_EEPROM_CMD_WRLO 2
|
||||
#define SPI_512B_EEPROM_CMD_WRHI 10
|
||||
#define SPI_512B_EEPROM_CMD_RDLO 3
|
||||
#define SPI_512B_EEPROM_CMD_RDHI 11
|
||||
|
||||
#define SPI_EEPROM_CMD_WRITE 2
|
||||
|
||||
#define SPI_CMD_READ 3
|
||||
|
||||
#define SPI_CMD_PP 2
|
||||
#define SPI_FLASH_CMD_PW 10
|
||||
#define SPI_FLASH_CMD_RDID 0x9f
|
||||
#define SPI_FLASH_CMD_SE 0xd8
|
||||
|
||||
|
||||
#define SPI_FLG_WIP 1
|
||||
#define SPI_FLG_WEL 2
|
||||
|
||||
// extern u8* fill_buf;
|
||||
typedef enum {
|
||||
NO_CHIP = -1,
|
||||
|
||||
EEPROM_512B = 0,
|
||||
|
||||
EEPROM_8KB = 1,
|
||||
EEPROM_64KB = 2,
|
||||
EEPROM_128KB = 3,
|
||||
EEPROM_STD_DUMMY = 1,
|
||||
|
||||
FLASH_256KB_1 = 4,
|
||||
FLASH_256KB_2 = 5,
|
||||
FLASH_512KB_1 = 6,
|
||||
FLASH_512KB_2 = 7,
|
||||
FLASH_1MB = 8,
|
||||
FLASH_8MB = 9, // <- can't restore savegames, and maybe not read them atm
|
||||
FLASH_STD_DUMMY = 4,
|
||||
|
||||
FLASH_512KB_INFRARED = 10,
|
||||
FLASH_256KB_INFRARED = 11, // AFAIK, only "Active Health with Carol Vorderman" has such a flash save memory
|
||||
FLASH_INFRARED_DUMMY = 9,
|
||||
|
||||
CHIP_LAST = 11,
|
||||
} CardType;
|
||||
|
||||
int SPIWriteRead(CardType type, void* cmd, u32 cmdSize, void* answer, u32 answerSize, void* data, u32 dataSize);
|
||||
int SPIWaitWriteEnd(CardType type);
|
||||
int SPIEnableWriting(CardType type);
|
||||
int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg);
|
||||
int SPIGetCardType(CardType* type, int infrared);
|
||||
u32 SPIGetPageSize(CardType type);
|
||||
u32 SPIGetCapacity(CardType type);
|
||||
|
||||
int SPIWriteSaveData(CardType type, u32 offset, void* data, u32 size);
|
||||
int SPIReadSaveData(CardType type, u32 offset, void* data, u32 size);
|
||||
|
||||
// int SPIEraseSector(CardType type, u32 offset);
|
||||
// int SPIErase(CardType type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -2,6 +2,7 @@
|
||||
#include "gamecart.h"
|
||||
|
||||
#define FAT_LIMIT 0x100000000
|
||||
#define VFLAG_SAVEGAME (1UL<<30)
|
||||
#define VFLAG_PRIV_HDR (1UL<<31)
|
||||
|
||||
static CartData* cdata = NULL;
|
||||
@ -11,7 +12,7 @@ static bool cart_checked = false;
|
||||
u32 InitVCartDrive(void) {
|
||||
if (!cart_checked) cart_checked = true;
|
||||
if (!cdata) cdata = (CartData*) malloc(sizeof(CartData));
|
||||
cart_init = (cdata && (InitCardRead(cdata) == 0) && (cdata->cart_size <= FAT_LIMIT));
|
||||
cart_init = (cdata && (InitCartRead(cdata) == 0) && (cdata->cart_size <= FAT_LIMIT));
|
||||
if (!cart_init && cdata) {
|
||||
free(cdata);
|
||||
cdata = NULL;
|
||||
@ -31,7 +32,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||
vfile->keyslot = 0xFF; // unused
|
||||
vfile->flags = VFLAG_READONLY;
|
||||
|
||||
while (++vdir->index <= 5) {
|
||||
while (++vdir->index <= 6) {
|
||||
if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom
|
||||
snprintf(vfile->name, 32, "%s.%s", name, ext);
|
||||
vfile->size = cdata->cart_size;
|
||||
@ -55,6 +56,11 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||
vfile->size = PRIV_HDR_SIZE;
|
||||
vfile->flags |= VFLAG_PRIV_HDR;
|
||||
return true;
|
||||
} else if ((vdir->index == 6) && (cdata->save_size > 0)) { // savegame
|
||||
snprintf(vfile->name, 32, "%s.sav", name);
|
||||
vfile->size = cdata->save_size;
|
||||
vfile->flags = VFLAG_SAVEGAME;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +72,8 @@ int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count)
|
||||
if (!cdata) return -1;
|
||||
if (vfile->flags & VFLAG_PRIV_HDR)
|
||||
return ReadCartPrivateHeader(buffer, foffset, count, cdata);
|
||||
else if (vfile->flags & VFLAG_SAVEGAME)
|
||||
return ReadCartSave(buffer, foffset, count, cdata);
|
||||
else return ReadCartBytes(buffer, foffset, count, cdata);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user