mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 21:52:48 +00:00
Added virtual gamecart drive
This commit is contained in:
parent
1e4be5fe61
commit
fa9a2c2fb5
4
Makefile
4
Makefile
@ -21,9 +21,9 @@ ifeq ($(MODE),safe)
|
|||||||
export TARGET := SafeMode9
|
export TARGET := SafeMode9
|
||||||
endif
|
endif
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source source/common source/fs source/crypto source/fatfs source/nand source/virtual source/game source/abstraction
|
SOURCES := source source/common source/fs source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/abstraction
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := source source/common source/font source/fs source/crypto source/fatfs source/nand source/virtual source/game
|
INCLUDES := source source/common source/font source/fs source/crypto source/fatfs source/nand source/virtual source/game source/gamecart
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
|
@ -66,6 +66,9 @@
|
|||||||
// buffer area defines (in use by vgame.c)
|
// buffer area defines (in use by vgame.c)
|
||||||
#define VGAME_BUFFER ((u8*)0x21500000)
|
#define VGAME_BUFFER ((u8*)0x21500000)
|
||||||
#define VGAME_BUFFER_SIZE (0x200000) // 2MB, big RomFS
|
#define VGAME_BUFFER_SIZE (0x200000) // 2MB, big RomFS
|
||||||
|
// buffer area defines (in use by vcart.c)
|
||||||
|
#define VCART_BUFFER ((u8*)0x21600000)
|
||||||
|
#define VCART_BUFFER_SIZE (0x20000) // 128kB, this is more than enough
|
||||||
// buffer area defines (in use by image.c, for RAMdrive)
|
// buffer area defines (in use by image.c, for RAMdrive)
|
||||||
#define RAMDRV_BUFFER_O3DS ((u8*)0x22200000) // in O3DS FCRAM
|
#define RAMDRV_BUFFER_O3DS ((u8*)0x22200000) // in O3DS FCRAM
|
||||||
#define RAMDRV_SIZE_O3DS (0x01C00000) // 28MB
|
#define RAMDRV_SIZE_O3DS (0x01C00000) // 28MB
|
||||||
|
@ -45,6 +45,8 @@ int DriveType(const char* path) {
|
|||||||
type = DRV_VIRTUAL | DRV_MEMORY;
|
type = DRV_VIRTUAL | DRV_MEMORY;
|
||||||
} else if (vsrc == VRT_GAME) {
|
} else if (vsrc == VRT_GAME) {
|
||||||
type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE;
|
type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE;
|
||||||
|
} else if (vsrc == VRT_CART) {
|
||||||
|
type = DRV_VIRTUAL | DRV_CART;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#define NORM_FS 10
|
#define NORM_FS 10
|
||||||
#define IMGN_FS 3 // image normal filesystems
|
#define IMGN_FS 3 // image normal filesystems
|
||||||
#define VIRT_FS 9
|
#define VIRT_FS 10
|
||||||
|
|
||||||
// primary drive types
|
// primary drive types
|
||||||
#define DRV_UNKNOWN (0<<0)
|
#define DRV_UNKNOWN (0<<0)
|
||||||
@ -20,9 +20,10 @@
|
|||||||
#define DRV_RAMDRIVE (1<<7)
|
#define DRV_RAMDRIVE (1<<7)
|
||||||
#define DRV_MEMORY (1<<8)
|
#define DRV_MEMORY (1<<8)
|
||||||
#define DRV_GAME (1<<9)
|
#define DRV_GAME (1<<9)
|
||||||
#define DRV_ALIAS (1<<10)
|
#define DRV_CART (1<<10)
|
||||||
#define DRV_SEARCH (1<<11)
|
#define DRV_ALIAS (1<<11)
|
||||||
#define DRV_STDFAT (1<<12) // standard FAT drive without limitations
|
#define DRV_SEARCH (1<<12)
|
||||||
|
#define DRV_STDFAT (1<<13) // standard FAT drive without limitations
|
||||||
|
|
||||||
#define FS_DRVNAME \
|
#define FS_DRVNAME \
|
||||||
"SDCARD", \
|
"SDCARD", \
|
||||||
@ -31,13 +32,14 @@
|
|||||||
"IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", \
|
"IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", \
|
||||||
"GAME IMAGE", \
|
"GAME IMAGE", \
|
||||||
"SYSNAND SD", "EMUNAND SD", \
|
"SYSNAND SD", "EMUNAND SD", \
|
||||||
|
"GAMECART", \
|
||||||
"SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL", \
|
"SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL", \
|
||||||
"NAND XORPADS", \
|
"NAND XORPADS", \
|
||||||
"MEMORY VIRTUAL", \
|
"MEMORY VIRTUAL", \
|
||||||
"LAST SEARCH" \
|
"LAST SEARCH" \
|
||||||
|
|
||||||
#define FS_DRVNUM \
|
#define FS_DRVNUM \
|
||||||
"0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:", "G:", "A:", "B:", "S:", "E:", "I:", "X:", "M:", "Z:"
|
"0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:", "G:", "A:", "B:", "C:", "S:", "E:", "I:", "X:", "M:", "Z:"
|
||||||
|
|
||||||
/** Function to identify the type of a drive **/
|
/** Function to identify the type of a drive **/
|
||||||
int DriveType(const char* path);
|
int DriveType(const char* path);
|
||||||
|
@ -32,6 +32,9 @@ bool CheckWritePermissions(const char* path) {
|
|||||||
} else if (drvtype & DRV_GAME) {
|
} else if (drvtype & DRV_GAME) {
|
||||||
perm = PERM_GAME;
|
perm = PERM_GAME;
|
||||||
snprintf(area_name, 16, "game images");
|
snprintf(area_name, 16, "game images");
|
||||||
|
} else if (drvtype & DRV_CART) {
|
||||||
|
perm = PERM_CART;
|
||||||
|
snprintf(area_name, 16, "gamecarts");
|
||||||
} else if (drvtype & DRV_XORPAD) {
|
} else if (drvtype & DRV_XORPAD) {
|
||||||
perm = PERM_XORPAD;
|
perm = PERM_XORPAD;
|
||||||
snprintf(area_name, 16, "XORpads");
|
snprintf(area_name, 16, "XORpads");
|
||||||
|
@ -11,8 +11,9 @@
|
|||||||
#define PERM_MEMORY (1<<5)
|
#define PERM_MEMORY (1<<5)
|
||||||
#define PERM_GAME (1<<6) // can't be enabled, placeholder
|
#define PERM_GAME (1<<6) // can't be enabled, placeholder
|
||||||
#define PERM_XORPAD (1<<7) // can't be enabled, placeholder
|
#define PERM_XORPAD (1<<7) // can't be enabled, placeholder
|
||||||
#define PERM_A9LH ((1<<8) | PERM_SYSNAND)
|
#define PERM_CART (1<<8) // can't be enabled, placeholder
|
||||||
#define PERM_SDDATA ((1<<9) | PERM_SDCARD)
|
#define PERM_A9LH ((1<<9) | PERM_SYSNAND)
|
||||||
|
#define PERM_SDDATA ((1<<10) | PERM_SDCARD)
|
||||||
#define PERM_BASE (PERM_SDCARD | PERM_RAMDRIVE)
|
#define PERM_BASE (PERM_SDCARD | PERM_RAMDRIVE)
|
||||||
#define PERM_ALL (PERM_SDCARD | PERM_RAMDRIVE | PERM_EMUNAND | PERM_SYSNAND | PERM_IMAGE | PERM_MEMORY | PERM_SDDATA)
|
#define PERM_ALL (PERM_SDCARD | PERM_RAMDRIVE | PERM_EMUNAND | PERM_SYSNAND | PERM_IMAGE | PERM_MEMORY | PERM_SDDATA)
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ u32 ValidateNcsdHeader(NcsdHeader* header) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetNcsdTrimmedSize(NcsdHeader* header) {
|
u64 GetNcsdTrimmedSize(NcsdHeader* header) {
|
||||||
u32 data_units = 0;
|
u32 data_units = 0;
|
||||||
for (u32 i = 0; i < 8; i++) {
|
for (u32 i = 0; i < 8; i++) {
|
||||||
NcchPartition* partition = header->partitions + i;
|
NcchPartition* partition = header->partitions + i;
|
||||||
|
@ -33,5 +33,5 @@ typedef struct {
|
|||||||
} __attribute__((packed)) NcsdHeader;
|
} __attribute__((packed)) NcsdHeader;
|
||||||
|
|
||||||
u32 ValidateNcsdHeader(NcsdHeader* header);
|
u32 ValidateNcsdHeader(NcsdHeader* header);
|
||||||
u32 GetNcsdTrimmedSize(NcsdHeader* header);
|
u64 GetNcsdTrimmedSize(NcsdHeader* header);
|
||||||
u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data);
|
u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data);
|
||||||
|
307
source/gamecart/card_eeprom.c
Normal file
307
source/gamecart/card_eeprom.c
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (C) 2005 - 2010
|
||||||
|
Michael Noland (joat)
|
||||||
|
Jason Rogers (dovoto)
|
||||||
|
Dave Murphy (WinterMute)
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any
|
||||||
|
damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you
|
||||||
|
must not claim that you wrote the original software. If you use
|
||||||
|
this software in a product, an acknowledgment in the product
|
||||||
|
documentation would be appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------*/
|
||||||
|
#include "ndscard.h"
|
||||||
|
#include "card_eeprom.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u8 cardEepromCommand(u8 command) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u8 retval;
|
||||||
|
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
|
||||||
|
REG_AUXSPIDATA = command;
|
||||||
|
|
||||||
|
eepromWaitBusy();
|
||||||
|
|
||||||
|
REG_AUXSPIDATA = 0;
|
||||||
|
eepromWaitBusy();
|
||||||
|
retval = REG_AUXSPIDATA;
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u32 cardEepromReadID() {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
int i;
|
||||||
|
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
|
||||||
|
REG_AUXSPIDATA = SPI_EEPROM_RDID;
|
||||||
|
|
||||||
|
eepromWaitBusy();
|
||||||
|
u32 id = 0;
|
||||||
|
for ( i=0; i<3; i++) {
|
||||||
|
REG_AUXSPIDATA = 0;
|
||||||
|
eepromWaitBusy();
|
||||||
|
id = (id << 8) | REG_AUXSPIDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
int cardEepromGetType(void) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
int sr = cardEepromCommand(SPI_EEPROM_RDSR);
|
||||||
|
int id = cardEepromReadID();
|
||||||
|
|
||||||
|
if (( sr == 0xff && id == 0xffffff) || ( sr == 0 && id == 0 )) return -1;
|
||||||
|
if ( sr == 0xf0 && id == 0xffffff ) return 1;
|
||||||
|
if ( sr == 0x00 && id == 0xffffff ) return 2;
|
||||||
|
if ( id != 0xffffff) return 3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u32 cardEepromGetSize() {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int type = cardEepromGetType();
|
||||||
|
|
||||||
|
if(type == -1)
|
||||||
|
return 0;
|
||||||
|
if(type == 0)
|
||||||
|
return 8192;
|
||||||
|
if(type == 1)
|
||||||
|
return 512;
|
||||||
|
if(type == 2) {
|
||||||
|
u32 buf1,buf2,buf3;
|
||||||
|
cardReadEeprom(0,(u8*)&buf1,4,type);
|
||||||
|
if ( !(buf1 != 0 || buf1 != 0xffffffff) ) {
|
||||||
|
buf3 = ~buf1;
|
||||||
|
cardWriteEeprom(0,(u8*)&buf3,4,type);
|
||||||
|
} else {
|
||||||
|
buf3 = buf1;
|
||||||
|
}
|
||||||
|
int size = 8192;
|
||||||
|
while (1) {
|
||||||
|
cardReadEeprom(size,(u8*)&buf2,4,type);
|
||||||
|
if ( buf2 == buf3 ) break;
|
||||||
|
size += 8192;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( buf1 != buf3 ) cardWriteEeprom(0,(u8*)&buf1,4,type);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int device;
|
||||||
|
|
||||||
|
if(type == 3) {
|
||||||
|
int id = cardEepromReadID();
|
||||||
|
|
||||||
|
device = id & 0xffff;
|
||||||
|
|
||||||
|
if ( ((id >> 16) & 0xff) == 0x20 ) { // ST
|
||||||
|
|
||||||
|
switch(device) {
|
||||||
|
|
||||||
|
case 0x4014:
|
||||||
|
return 1024*1024; // 8Mbit(1 meg)
|
||||||
|
break;
|
||||||
|
case 0x4013:
|
||||||
|
case 0x8013: // M25PE40
|
||||||
|
return 512*1024; // 4Mbit(512KByte)
|
||||||
|
break;
|
||||||
|
case 0x2017:
|
||||||
|
return 8*1024*1024; // 64Mbit(8 meg)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ((id >> 16) & 0xff) == 0x62 ) { // Sanyo
|
||||||
|
|
||||||
|
if (device == 0x1100)
|
||||||
|
return 512*1024; // 4Mbit(512KByte)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ((id >> 16) & 0xff) == 0xC2 ) { // Macronix
|
||||||
|
|
||||||
|
if (device == 0x2211)
|
||||||
|
return 128*1024; // 1Mbit(128KByte) - MX25L1021E
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 256*1024; // 2Mbit(256KByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardReadEeprom(u32 address, u8 *data, u32 length, u32 addrtype) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
REG_AUXSPIDATA = 0x03 | ((addrtype == 1) ? address>>8<<3 : 0);
|
||||||
|
eepromWaitBusy();
|
||||||
|
|
||||||
|
if (addrtype == 3) {
|
||||||
|
REG_AUXSPIDATA = (address >> 16) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addrtype >= 2) {
|
||||||
|
REG_AUXSPIDATA = (address >> 8) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
REG_AUXSPIDATA = (address) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
REG_AUXSPIDATA = 0;
|
||||||
|
eepromWaitBusy();
|
||||||
|
*data++ = REG_AUXSPIDATA;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardWriteEeprom(u32 address, u8 *data, u32 length, u32 addrtype) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
u32 address_end = address + length;
|
||||||
|
int i;
|
||||||
|
int maxblocks = 32;
|
||||||
|
if(addrtype == 1) maxblocks = 16;
|
||||||
|
if(addrtype == 2) maxblocks = 32;
|
||||||
|
if(addrtype == 3) maxblocks = 256;
|
||||||
|
|
||||||
|
while (address < address_end) {
|
||||||
|
// set WEL (Write Enable Latch)
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
REG_AUXSPIDATA = 0x06; eepromWaitBusy();
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
|
||||||
|
// program maximum of 32 bytes
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
|
||||||
|
if(addrtype == 1) {
|
||||||
|
// WRITE COMMAND 0x02 + A8 << 3
|
||||||
|
REG_AUXSPIDATA = 0x02 | (address & (1 << (8))) >> (8-3) ;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = address & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
}
|
||||||
|
else if(addrtype == 2) {
|
||||||
|
REG_AUXSPIDATA = 0x02;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = address >> 8;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = address & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
}
|
||||||
|
else if(addrtype == 3) {
|
||||||
|
REG_AUXSPIDATA = 0x02;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = (address >> 16) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = (address >> 8) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = address & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; address<address_end && i<maxblocks; i++, address++) {
|
||||||
|
REG_AUXSPIDATA = *data++;
|
||||||
|
eepromWaitBusy();
|
||||||
|
}
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
|
||||||
|
// wait programming to finish
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
REG_AUXSPIDATA = 0x05; eepromWaitBusy();
|
||||||
|
do { REG_AUXSPIDATA = 0; eepromWaitBusy(); } while (REG_AUXSPIDATA & 0x01); // WIP (Write In Progress) ?
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
// Chip Erase : clear FLASH MEMORY (TYPE 3 ONLY)
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardEepromChipErase(void) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
int sz, sector;
|
||||||
|
sz=cardEepromGetSize();
|
||||||
|
|
||||||
|
for ( sector = 0; sector < sz; sector+=0x10000) {
|
||||||
|
cardEepromSectorErase(sector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
// COMMAND Sec.erase 0xD8
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardEepromSectorErase(u32 address) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
// set WEL (Write Enable Latch)
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
REG_AUXSPIDATA = 0x06;
|
||||||
|
eepromWaitBusy();
|
||||||
|
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
|
||||||
|
// SectorErase 0xD8
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
REG_AUXSPIDATA = 0xD8;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = (address >> 16) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = (address >> 8) & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
REG_AUXSPIDATA = address & 0xFF;
|
||||||
|
eepromWaitBusy();
|
||||||
|
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
|
||||||
|
// wait erase to finish
|
||||||
|
REG_AUXSPICNT = /*E*/0x8000 | /*SEL*/0x2000 | /*MODE*/0x40;
|
||||||
|
REG_AUXSPIDATA = 0x05;
|
||||||
|
eepromWaitBusy();
|
||||||
|
|
||||||
|
do {
|
||||||
|
REG_AUXSPIDATA = 0;
|
||||||
|
eepromWaitBusy();
|
||||||
|
} while (REG_AUXSPIDATA & 0x01); // WIP (Write In Progress) ?
|
||||||
|
REG_AUXSPICNT = /*MODE*/0x40;
|
||||||
|
}
|
38
source/gamecart/card_eeprom.h
Normal file
38
source/gamecart/card_eeprom.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "ndscard.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
static inline void eepromWaitBusy() {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
while (REG_AUXSPICNT & CARD_SPI_BUSY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads from the EEPROM
|
||||||
|
void cardReadEeprom(u32 address, u8 *data, u32 length, u32 addrtype);
|
||||||
|
|
||||||
|
// Writes to the EEPROM. TYPE 3 EEPROM must be erased first (I think?)
|
||||||
|
void cardWriteEeprom(u32 address, u8 *data, u32 length, u32 addrtype);
|
||||||
|
|
||||||
|
// Returns the ID of the EEPROM chip? Doesn't work well, most chips give ff,ff
|
||||||
|
// i = 0 or 1
|
||||||
|
u32 cardEepromReadID();
|
||||||
|
|
||||||
|
// Sends a command to the EEPROM
|
||||||
|
u8 cardEepromCommand(u8 command);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -1:no card or no EEPROM
|
||||||
|
* 0:unknown PassMe?
|
||||||
|
* 1:TYPE 1 4Kbit(512Byte) EEPROM
|
||||||
|
* 2:TYPE 2 64Kbit(8KByte)or 512kbit(64Kbyte) EEPROM
|
||||||
|
* 3:TYPE 3 2Mbit(256KByte) FLASH MEMORY (some rare 4Mbit and 8Mbit chips also)
|
||||||
|
*/
|
||||||
|
int cardEepromGetType(void);
|
||||||
|
|
||||||
|
// Returns the size in bytes of EEPROM
|
||||||
|
u32 cardEepromGetSize();
|
||||||
|
|
||||||
|
// Erases the entire chip. TYPE 3 chips MUST be erased before writing to them. (I think?)
|
||||||
|
void cardEepromChipErase(void);
|
||||||
|
|
||||||
|
// Erases a single sector of the TYPE 3 chip
|
||||||
|
void cardEepromSectorErase(u32 address);
|
142
source/gamecart/card_ntr.c
Normal file
142
source/gamecart/card_ntr.c
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (C) 2005
|
||||||
|
Michael Noland (joat)
|
||||||
|
Jason Rogers (dovoto)
|
||||||
|
Dave Murphy (WinterMute)
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any
|
||||||
|
damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you
|
||||||
|
must not claim that you wrote the original software. If you use
|
||||||
|
this software in a product, an acknowledgment in the product
|
||||||
|
documentation would be appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------*/
|
||||||
|
#include "ndscard.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardWriteCommand(const u8 *command) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
int index;
|
||||||
|
|
||||||
|
REG_AUXSPICNT = CARD_CR1_ENABLE | CARD_CR1_IRQ;
|
||||||
|
|
||||||
|
for (index = 0; index < 8; index++) {
|
||||||
|
CARD_COMMAND[7-index] = command[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardPolledTransfer(u32 flags, u32 *destination, u32 length, const u8 *command) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u32 data;
|
||||||
|
cardWriteCommand(command);
|
||||||
|
REG_ROMCTRL = flags;
|
||||||
|
u32 * target = destination + length;
|
||||||
|
do {
|
||||||
|
// Read data if available
|
||||||
|
if (REG_ROMCTRL & CARD_DATA_READY) {
|
||||||
|
data=CARD_DATA_RD;
|
||||||
|
if (destination < target)
|
||||||
|
*destination = data;
|
||||||
|
destination++;
|
||||||
|
}
|
||||||
|
} while (REG_ROMCTRL & CARD_BUSY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardStartTransfer(const u8 *command, u32 *destination, int channel, u32 flags) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
cardWriteCommand(command);
|
||||||
|
|
||||||
|
// Set up a DMA channel to transfer a word every time the card makes one
|
||||||
|
DMA_SRC(channel) = (u32)&CARD_DATA_RD;
|
||||||
|
DMA_DEST(channel) = (u32)destination;
|
||||||
|
DMA_CR(channel) = DMA_ENABLE | DMA_START_CARD | DMA_32_BIT | DMA_REPEAT | DMA_SRC_FIX | 0x0001;
|
||||||
|
|
||||||
|
REG_ROMCTRL = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u32 cardWriteAndRead(const u8 *command, u32 flags) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
cardWriteCommand(command);
|
||||||
|
REG_ROMCTRL = flags | CARD_ACTIVATE | CARD_nRESET | CARD_BLK_SIZE(7);
|
||||||
|
while (!(REG_ROMCTRL & CARD_DATA_READY)) ;
|
||||||
|
return CARD_DATA_RD;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardParamCommand (u8 command, u32 parameter, u32 flags, u32 *destination, u32 length) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u8 cmdData[8];
|
||||||
|
|
||||||
|
cmdData[7] = (u8) command;
|
||||||
|
cmdData[6] = (u8) (parameter >> 24);
|
||||||
|
cmdData[5] = (u8) (parameter >> 16);
|
||||||
|
cmdData[4] = (u8) (parameter >> 8);
|
||||||
|
cmdData[3] = (u8) (parameter >> 0);
|
||||||
|
cmdData[2] = 0;
|
||||||
|
cmdData[1] = 0;
|
||||||
|
cmdData[0] = 0;
|
||||||
|
|
||||||
|
cardPolledTransfer(flags, destination, length, cmdData);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardReadHeader(u8 *header) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
REG_ROMCTRL=0;
|
||||||
|
REG_AUXSPICNT=0;
|
||||||
|
swiDelay(167550);
|
||||||
|
REG_AUXSPICNT=CARD_CR1_ENABLE|CARD_CR1_IRQ;
|
||||||
|
REG_ROMCTRL=CARD_nRESET|CARD_SEC_SEED;
|
||||||
|
while(REG_ROMCTRL&CARD_BUSY) ;
|
||||||
|
cardReset();
|
||||||
|
while(REG_ROMCTRL&CARD_BUSY) ;
|
||||||
|
|
||||||
|
cardParamCommand(CARD_CMD_HEADER_READ,0,CARD_ACTIVATE|CARD_nRESET|CARD_CLK_SLOW|CARD_BLK_SIZE(1)|CARD_DELAY1(0x1FFF)|CARD_DELAY2(0x3F),(u32*)(void*)header,512/4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
u32 cardReadID(u32 flags) {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
const u8 command[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, CARD_CMD_HEADER_CHIPID};
|
||||||
|
return cardWriteAndRead(command, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
void cardReset() {
|
||||||
|
//---------------------------------------------------------------------------------
|
||||||
|
const u8 cmdData[8]={0,0,0,0,0,0,0,CARD_CMD_DUMMY};
|
||||||
|
cardWriteCommand(cmdData);
|
||||||
|
REG_ROMCTRL=CARD_ACTIVATE|CARD_nRESET|CARD_CLK_SLOW|CARD_BLK_SIZE(5)|CARD_DELAY2(0x18);
|
||||||
|
u32 read=0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(REG_ROMCTRL&CARD_DATA_READY) {
|
||||||
|
if(read<0x2000) {
|
||||||
|
u32 data=CARD_DATA_RD;
|
||||||
|
(void)data;
|
||||||
|
read+=4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(REG_ROMCTRL&CARD_BUSY);
|
||||||
|
}
|
11
source/gamecart/card_ntr.h
Normal file
11
source/gamecart/card_ntr.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void cardWriteCommand(const u8 *command);
|
||||||
|
void cardPolledTransfer(u32 flags, u32 *destination, u32 length, const u8 *command);
|
||||||
|
void cardStartTransfer(const u8 *command, u32 *destination, int channel, u32 flags);
|
||||||
|
u32 cardWriteAndRead(const u8 *command, u32 flags);
|
||||||
|
void cardParamCommand (u8 command, u32 parameter, u32 flags, u32 *destination, u32 length);
|
||||||
|
void cardReadHeader(u8 *header);
|
||||||
|
u32 cardReadID(u32 flags);
|
||||||
|
void cardReset();
|
151
source/gamecart/command_ak2i.c
Normal file
151
source/gamecart/command_ak2i.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
//
|
||||||
|
// modifyed by osilloscopion (2 Jul 2016)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "command_ntr.h"
|
||||||
|
#include "command_ak2i.h"
|
||||||
|
#include "protocol_ntr.h"
|
||||||
|
#include "card_ntr.h"
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
|
u32 AK2I_CmdGetHardwareVersion(void)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xD1000000, 0x00000000};
|
||||||
|
u32 ver = 0;
|
||||||
|
|
||||||
|
NTR_SendCommand(cmd, 4, 0, &ver);
|
||||||
|
return ver & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdReadRom(u32 address, u8 *buffer, u32 length)
|
||||||
|
{
|
||||||
|
length &= ~(0x03);
|
||||||
|
u32 cmd[2] = {0xB7000000 | (address >> 8), (address & 0xff) << 24};
|
||||||
|
NTR_SendCommand(cmd, length, 2, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdReadFlash(u32 address, u8 *buffer, u32 length)
|
||||||
|
{
|
||||||
|
length &= ~(0x03);
|
||||||
|
u32 cmd[2] = { 0xB7000000 | (address >> 8), (address & 0xff) << 24 | 0x00100000 };
|
||||||
|
NTR_SendCommand(cmd, length, 2, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdSetMapTableAddress(u32 tableName, u32 tableInRamAddress)
|
||||||
|
{
|
||||||
|
tableName &= 0x0F;
|
||||||
|
u32 cmd[2] = {0xD0000000 | (tableInRamAddress >> 8),
|
||||||
|
((tableInRamAddress & 0xff) << 24) | ((u8)tableName << 16) };
|
||||||
|
|
||||||
|
NTR_SendCommand(cmd, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdSetFlash1681_81(void)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xD8000000 , 0x0000c606};
|
||||||
|
NTR_SendCommand(cmd, 0, 20, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdUnlockFlash(void)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xC2AA55AA, 0x55000000};
|
||||||
|
NTR_SendCommand(cmd, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdUnlockASIC(void)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = { 0xC2AA5555, 0xAA000000 };
|
||||||
|
NTR_SendCommand(cmd, 4, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2i_CmdLockFlash(void) {
|
||||||
|
u32 cmd[2] = { 0xC2AAAA55, 0x55000000 };
|
||||||
|
NTR_SendCommand(cmd, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdActiveFatMap(void)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xC255AA55, 0xAA000000};
|
||||||
|
NTR_SendCommand(cmd, 4, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waitFlashBusy()
|
||||||
|
{
|
||||||
|
u32 state = 0;
|
||||||
|
u32 cmd[2] = {0xC0000000, 0x00000000};
|
||||||
|
do {
|
||||||
|
//ioAK2Delay( 16 * 10 );
|
||||||
|
NTR_SendCommand(cmd, 4, 4, &state);
|
||||||
|
state &= 1;
|
||||||
|
} while(state != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdEraseFlashBlock_44(u32 address)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xD4000000 | (address & 0x001fffff), (u32)(1<<16)};
|
||||||
|
NTR_SendCommand(cmd, 0, 0, NULL);
|
||||||
|
waitFlashBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdEraseFlashBlock_81(u32 address)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xD4000000 | (address & 0x001fffff), (u32)((0x30<<24) | (0x80<<16) | (0<<8) | (0x35))};
|
||||||
|
NTR_SendCommand(cmd, 0, 20, NULL);
|
||||||
|
waitFlashBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdWriteFlashByte_44(u32 address, u8 data)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = {0xD4000000 | (address & 0x001fffff), (u32)((data<<24) | (3<<16))};
|
||||||
|
NTR_SendCommand(cmd, 0, 20, NULL);
|
||||||
|
waitFlashBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdWriteFlash_44(u32 address, const void *data, u32 length)
|
||||||
|
{
|
||||||
|
u8 * pbuffer = (u8 *)data;
|
||||||
|
for(u32 i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
AK2I_CmdWriteFlashByte_44(address, *(pbuffer + i));
|
||||||
|
address++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdWriteFlashByte_81(u32 address, u8 data)
|
||||||
|
{
|
||||||
|
u32 cmd[2] = { 0xD4000000 | (address & 0x001fffff), (u32)((data<<24) | (0xa0<<16) | (0<<8) | (0x63)) };
|
||||||
|
NTR_SendCommand(cmd, 0, 20, NULL);
|
||||||
|
waitFlashBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AK2I_CmdWriteFlash_81(u32 address, const void *data, u32 length)
|
||||||
|
{
|
||||||
|
u8 * pbuffer = (u8 *)data;
|
||||||
|
for (u32 i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
AK2I_CmdWriteFlashByte_81(address, *(pbuffer + i));
|
||||||
|
address++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AK2I_CmdVerifyFlash(void *src, u32 dest, u32 length)
|
||||||
|
{
|
||||||
|
u8 verifyBuffer[512];
|
||||||
|
u8 * pSrc = (u8 *)src;
|
||||||
|
for (u32 i = 0; i < length; i += 512) {
|
||||||
|
u32 toRead = 512;
|
||||||
|
if (toRead > length - i)
|
||||||
|
toRead = length - i;
|
||||||
|
AK2I_CmdReadFlash(dest + i, verifyBuffer, toRead);
|
||||||
|
|
||||||
|
for (u32 j = 0; j < toRead; ++j) {
|
||||||
|
if(verifyBuffer[j] != *(pSrc + i + j))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
29
source/gamecart/command_ak2i.h
Normal file
29
source/gamecart/command_ak2i.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
//
|
||||||
|
// modifyed by osilloscopion (2 Jul 2016)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AK2I_MTN_NOR_OFFSET = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 AK2I_CmdGetHardwareVersion(void);
|
||||||
|
void AK2I_CmdReadRom(u32 address, u8 *buffer, u32 length);
|
||||||
|
void AK2I_CmdReadFlash(u32 address, u8 *buffer, u32 length);
|
||||||
|
void AK2I_CmdSetMapTableAddress(u32 tableName, u32 tableInRamAddress);
|
||||||
|
void AK2I_CmdSetFlash1681_81(void);
|
||||||
|
void AK2I_CmdUnlockFlash(void);
|
||||||
|
void AK2I_CmdUnlockASIC(void);
|
||||||
|
void AK2i_CmdLockFlash(void);
|
||||||
|
void AK2I_CmdActiveFatMap(void);
|
||||||
|
void AK2I_CmdEraseFlashBlock_44(u32 address);
|
||||||
|
void AK2I_CmdEraseFlashBlock_81(u32 address);
|
||||||
|
void AK2I_CmdWriteFlash_44(u32 address, const void *data, u32 length);
|
||||||
|
void AK2I_CmdWriteFlash_81(u32 address, const void *data, u32 length);
|
||||||
|
bool AK2I_CmdVerifyFlash(void *src, u32 dest, u32 length);
|
57
source/gamecart/command_ctr.c
Normal file
57
source/gamecart/command_ctr.c
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "command_ctr.h"
|
||||||
|
|
||||||
|
#include "protocol_ctr.h"
|
||||||
|
|
||||||
|
static int read_count = 0;
|
||||||
|
|
||||||
|
static void CTR_CmdC5()
|
||||||
|
{
|
||||||
|
static const u32 c5_cmd[4] = { 0xC5000000, 0x00000000, 0x00000000, 0x00000000 };
|
||||||
|
CTR_SendCommand(c5_cmd, 0, 1, 0x100002C, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTR_CmdReadData(u32 sector, u32 length, u32 blocks, void* buffer)
|
||||||
|
{
|
||||||
|
if(read_count++ > 10000)
|
||||||
|
{
|
||||||
|
CTR_CmdC5();
|
||||||
|
read_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 read_cmd[4] = {
|
||||||
|
(0xBF000000 | (u32)(sector >> 23)),
|
||||||
|
(u32)((sector << 9) & 0xFFFFFFFF),
|
||||||
|
0x00000000, 0x00000000
|
||||||
|
};
|
||||||
|
CTR_SendCommand(read_cmd, length, blocks, 0x704822C, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTR_CmdReadHeader(void* buffer)
|
||||||
|
{
|
||||||
|
static const u32 readheader_cmd[4] = { 0x82000000, 0x00000000, 0x00000000, 0x00000000 };
|
||||||
|
CTR_SendCommand(readheader_cmd, 0x200, 1, 0x704802C, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTR_CmdReadUniqueID(void* buffer)
|
||||||
|
{
|
||||||
|
static const u32 readheader_cmd[4] = { 0xC6000000, 0x00000000, 0x00000000, 0x00000000 };
|
||||||
|
CTR_SendCommand(readheader_cmd, 0x40, 1, 0x903002C, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CTR_CmdGetSecureId(u32 rand1, u32 rand2)
|
||||||
|
{
|
||||||
|
u32 id = 0;
|
||||||
|
const u32 getid_cmd[4] = { 0xA2000000, 0x00000000, rand1, rand2 };
|
||||||
|
CTR_SendCommand(getid_cmd, 0x4, 1, 0x701002C, &id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTR_CmdSeed(u32 rand1, u32 rand2)
|
||||||
|
{
|
||||||
|
const u32 seed_cmd[4] = { 0x83000000, 0x00000000, rand1, rand2 };
|
||||||
|
CTR_SendCommand(seed_cmd, 0, 1, 0x700822C, NULL);
|
||||||
|
}
|
14
source/gamecart/command_ctr.h
Normal file
14
source/gamecart/command_ctr.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void CTR_CmdReadSectorSD(u8* aBuffer, u32 aSector);
|
||||||
|
void CTR_CmdReadData(u32 sector, u32 length, u32 blocks, void* buffer);
|
||||||
|
void CTR_CmdReadHeader(void* buffer);
|
||||||
|
void CTR_CmdReadUniqueID(void* buffer);
|
||||||
|
u32 CTR_CmdGetSecureId(u32 rand1, u32 rand2);
|
||||||
|
void CTR_CmdSeed(u32 rand1, u32 rand2);
|
67
source/gamecart/command_ntr.c
Normal file
67
source/gamecart/command_ntr.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
//
|
||||||
|
// modifyed by osilloscopion (2 Jul 2016)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "command_ntr.h"
|
||||||
|
#include "protocol_ntr.h"
|
||||||
|
#include "card_ntr.h"
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
|
|
||||||
|
u32 ReadDataFlags = 0;
|
||||||
|
|
||||||
|
void NTR_CmdReset(void)
|
||||||
|
{
|
||||||
|
cardReset ();
|
||||||
|
ioDelay(0xF000);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 NTR_CmdGetCartId(void)
|
||||||
|
{
|
||||||
|
return cardReadID (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_CmdEnter16ByteMode(void)
|
||||||
|
{
|
||||||
|
static const u32 enter16bytemode_cmd[2] = { 0x3E000000, 0x00000000 };
|
||||||
|
NTR_SendCommand(enter16bytemode_cmd, 0x0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_CmdReadHeader (u8* buffer)
|
||||||
|
{
|
||||||
|
REG_NTRCARDROMCNT=0;
|
||||||
|
REG_NTRCARDMCNT=0;
|
||||||
|
ioDelay(167550);
|
||||||
|
REG_NTRCARDMCNT=NTRCARD_CR1_ENABLE|NTRCARD_CR1_IRQ;
|
||||||
|
REG_NTRCARDROMCNT=NTRCARD_nRESET|NTRCARD_SEC_SEED;
|
||||||
|
while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ;
|
||||||
|
cardReset();
|
||||||
|
while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ;
|
||||||
|
u32 iCardId=cardReadID(NTRCARD_CLK_SLOW);
|
||||||
|
while(REG_NTRCARDROMCNT&NTRCARD_BUSY) ;
|
||||||
|
|
||||||
|
u32 iCheapCard=iCardId&0x80000000;
|
||||||
|
|
||||||
|
if(iCheapCard)
|
||||||
|
{
|
||||||
|
//this is magic of wood goblins
|
||||||
|
for(size_t ii=0;ii<8;++ii)
|
||||||
|
cardParamCommand(NTRCARD_CMD_HEADER_READ,ii*0x200,NTRCARD_ACTIVATE|NTRCARD_nRESET|NTRCARD_CLK_SLOW|NTRCARD_BLK_SIZE(1)|NTRCARD_DELAY1(0x1FFF)|NTRCARD_DELAY2(0x3F),(u32*)(void*)(buffer+ii*0x200),0x200/sizeof(u32));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//0xac3f1fff
|
||||||
|
cardParamCommand(NTRCARD_CMD_HEADER_READ,0,NTRCARD_ACTIVATE|NTRCARD_nRESET|NTRCARD_CLK_SLOW|NTRCARD_BLK_SIZE(4)|NTRCARD_DELAY1(0x1FFF)|NTRCARD_DELAY2(0x3F),(u32*)(void*)buffer,0x1000/sizeof(u32));
|
||||||
|
}
|
||||||
|
//cardReadHeader (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_CmdReadData (u32 offset, void* buffer)
|
||||||
|
{
|
||||||
|
cardParamCommand (NTRCARD_CMD_DATA_READ, offset, ReadDataFlags | NTRCARD_ACTIVATE | NTRCARD_nRESET | NTRCARD_BLK_SIZE(1), (u32*)buffer, 0x200 / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
19
source/gamecart/command_ntr.h
Normal file
19
source/gamecart/command_ntr.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
//
|
||||||
|
// modifyed by osilloscopion (2 Jul 2016)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void NTR_CmdReset(void);
|
||||||
|
u32 NTR_CmdGetCartId(void);
|
||||||
|
void NTR_CmdEnter16ByteMode(void);
|
||||||
|
void NTR_CmdReadHeader (u8* buffer);
|
||||||
|
void NTR_CmdReadData (u32 offset, void* buffer);
|
||||||
|
|
||||||
|
bool NTR_Secure_Init (u8* buffer, u32 CartID, int iCardDevice);
|
||||||
|
|
9
source/gamecart/delay.h
Normal file
9
source/gamecart/delay.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void ioDelay(u32 us);
|
218
source/gamecart/gamecart.c
Normal file
218
source/gamecart/gamecart.c
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
#include "gamecart.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
#include "command_ctr.h"
|
||||||
|
#include "command_ntr.h"
|
||||||
|
#include "card_eeprom.h"
|
||||||
|
#include "ndsheader.h"
|
||||||
|
#include "ncch.h"
|
||||||
|
#include "ncsd.h"
|
||||||
|
|
||||||
|
#define CART_INSERTED (!(REG_CARDCONF2 & 0x1))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NcsdHeader ncsd;
|
||||||
|
u32 card2_offset;
|
||||||
|
u8 cinfo[0x1000 - (0x200 + sizeof(u32))];
|
||||||
|
NcchHeader ncch;
|
||||||
|
u8 padding[0x3000 - 0x200];
|
||||||
|
u8 private[PRIV_HDR_SIZE];
|
||||||
|
u8 unused[0x4000 + 0x8000 - PRIV_HDR_SIZE]; // 0xFF
|
||||||
|
u32 cart_type;
|
||||||
|
u32 cart_id;
|
||||||
|
u64 cart_size;
|
||||||
|
u64 data_size;
|
||||||
|
u32 unused_offset;
|
||||||
|
} __attribute__((packed)) CartDataCtr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TwlHeader ntr_header;
|
||||||
|
u8 ntr_padding[0x3000]; // 0x00
|
||||||
|
u8 secure_area[0x4000];
|
||||||
|
TwlHeader twl_header;
|
||||||
|
u8 twl_padding[0x3000]; // 0x00
|
||||||
|
u8 modcrypt_area[0x4000];
|
||||||
|
u32 cart_type;
|
||||||
|
u32 cart_id;
|
||||||
|
u64 cart_size;
|
||||||
|
u64 data_size;
|
||||||
|
u32 arm9i_rom_offset;
|
||||||
|
} __attribute__((packed)) CartDataNtrTwl;
|
||||||
|
|
||||||
|
u32 GetCartName(char* name, CartData* cdata) {
|
||||||
|
if (cdata->cart_type & CART_CTR) {
|
||||||
|
CartDataCtr* cdata_i = (CartDataCtr*)(void*) cdata;
|
||||||
|
NcsdHeader* ncsd = &(cdata_i->ncsd);
|
||||||
|
snprintf(name, 24, "%016llX", ncsd->mediaId);
|
||||||
|
return 0;
|
||||||
|
} else if (cdata->cart_type & CART_NTR) {
|
||||||
|
CartDataNtrTwl* cdata_i = (CartDataNtrTwl*)(void*) cdata;
|
||||||
|
TwlHeader* nds = &(cdata_i->ntr_header);
|
||||||
|
snprintf(name, 24, "%.12s.%.6s%02X", nds->game_title, nds->game_code, nds->rom_version);
|
||||||
|
return 0;
|
||||||
|
} else return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CheckCartId(u32 cart_id) {
|
||||||
|
if (!CART_INSERTED) return 0xFFFFFFFF;
|
||||||
|
u32 curr_cart_id = Cart_GetID();
|
||||||
|
return (curr_cart_id == cart_id) ? 0 : curr_cart_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 InitCardRead(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) {
|
||||||
|
memset(cdata, 0xFF, 0x4000 + PRIV_HDR_SIZE); // switch the padding to 0xFF
|
||||||
|
|
||||||
|
// init, NCCH header
|
||||||
|
static u32 sec_keys[4];
|
||||||
|
u8* ncch_header = cdata->header + 0x1000;
|
||||||
|
CTR_CmdReadHeader(ncch_header);
|
||||||
|
Cart_Secure_Init((u32*) (void*) ncch_header, sec_keys);
|
||||||
|
|
||||||
|
// NCSD header and CINFO
|
||||||
|
Cart_Dummy();
|
||||||
|
Cart_Dummy();
|
||||||
|
CTR_CmdReadData(0, 0x200, 8, cdata->header);
|
||||||
|
|
||||||
|
// safety checks, cart size
|
||||||
|
NcsdHeader* ncsd = (NcsdHeader*) (void*) cdata->header;
|
||||||
|
NcchHeader* ncch = (NcchHeader*) (void*) ncch_header;
|
||||||
|
if ((ValidateNcsdHeader(ncsd) != 0) || (ValidateNcchHeader(ncch) != 0))
|
||||||
|
return 1;
|
||||||
|
cdata->cart_size = (u64) ncsd->size * NCSD_MEDIA_UNIT;
|
||||||
|
cdata->data_size = GetNcsdTrimmedSize(ncsd);
|
||||||
|
if (cdata->cart_size > 0x100000000) return 1; // can't support carts > 4GB
|
||||||
|
else if (cdata->cart_size == 0x100000000) cdata->cart_size -= 0x200; // silent 4GB fix
|
||||||
|
if (cdata->data_size > cdata->cart_size) return 1;
|
||||||
|
|
||||||
|
// private header
|
||||||
|
u8* priv_header = cdata->header + 0x4000;
|
||||||
|
CTR_CmdReadUniqueID(priv_header);
|
||||||
|
memcpy(priv_header + 0x40, &(cdata->cart_id), 4);
|
||||||
|
memset(priv_header + 0x44, 0x00, 4);
|
||||||
|
memset(priv_header + 0x48, 0xFF, 8);
|
||||||
|
} else {
|
||||||
|
// NTR header
|
||||||
|
TwlHeader* nds_header = (TwlHeader*) cdata->header;
|
||||||
|
NTR_CmdReadHeader(cdata->header);
|
||||||
|
if (!(*(cdata->header))) return 1; // error reading the header
|
||||||
|
if (!NTR_Secure_Init(cdata->header, Cart_GetID(), 0)) return 1;
|
||||||
|
|
||||||
|
// cartridge size, trimmed size, twl presets
|
||||||
|
if (nds_header->device_capacity >= 15) return 1; // too big, not valid
|
||||||
|
cdata->cart_size = (128 * 1024) << nds_header->device_capacity;
|
||||||
|
cdata->data_size = nds_header->ntr_rom_size;
|
||||||
|
cdata->arm9i_rom_offset = 0;
|
||||||
|
|
||||||
|
// TWL header
|
||||||
|
if (nds_header->unit_code != 0x00) { // DSi or NDS+DSi
|
||||||
|
cdata->cart_type |= CART_TWL;
|
||||||
|
cdata->data_size = nds_header->ntr_twl_rom_size;
|
||||||
|
cdata->arm9i_rom_offset = nds_header->arm9i_rom_offset;
|
||||||
|
if ((cdata->arm9i_rom_offset < nds_header->ntr_rom_size) ||
|
||||||
|
(cdata->arm9i_rom_offset + MODC_AREA_SIZE > cdata->data_size))
|
||||||
|
return 1; // safety first
|
||||||
|
Cart_Init();
|
||||||
|
NTR_CmdReadHeader(cdata->twl_header);
|
||||||
|
if (!NTR_Secure_Init(cdata->twl_header, Cart_GetID(), 1)) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// last safety check
|
||||||
|
if (cdata->data_size > cdata->cart_size) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ReadCartSectors(u8* buffer, u32 sector, u32 count, CartData* cdata) {
|
||||||
|
if (!CART_INSERTED) return 1;
|
||||||
|
// header
|
||||||
|
u32 header_sectors = (cdata->cart_type & CART_CTR) ? 0x4000/0x200 : 0x8000/0x200;
|
||||||
|
if (sector < header_sectors) {
|
||||||
|
u32 header_count = (sector + count > header_sectors) ? header_sectors - sector : count;
|
||||||
|
memcpy(buffer, cdata->header + (sector * 0x200), header_count * 0x200);
|
||||||
|
buffer += header_count * 0x200;
|
||||||
|
sector += header_count;
|
||||||
|
count -= header_count;
|
||||||
|
}
|
||||||
|
if (!count) return 0;
|
||||||
|
// actual cart reads
|
||||||
|
if (cdata->cart_type & CART_CTR) {
|
||||||
|
Cart_Dummy();
|
||||||
|
Cart_Dummy();
|
||||||
|
CTR_CmdReadData(sector, 0x200, count, buffer);
|
||||||
|
// overwrite the card2 savegame with 0xFF
|
||||||
|
u32 card2_offset = getle32(cdata->header + 0x200);
|
||||||
|
if ((card2_offset != 0xFFFFFFFF) &&
|
||||||
|
(card2_offset >= cdata->data_size) &&
|
||||||
|
(sector + count > card2_offset)) {
|
||||||
|
if (sector > card2_offset)
|
||||||
|
memset(buffer, 0xFF, (count * 0x200));
|
||||||
|
else memset(buffer + (card2_offset - sector) * 0x200, 0xFF,
|
||||||
|
(count - (card2_offset - sector)) * 0x200);
|
||||||
|
}
|
||||||
|
} else if (cdata->cart_type & CART_NTR) {
|
||||||
|
u8* buff = buffer;
|
||||||
|
u32 off = sector * 0x200;
|
||||||
|
for (u32 i = 0; i < count; i++, off += 0x200, buff += 0x200)
|
||||||
|
NTR_CmdReadData(off, buff);
|
||||||
|
// modcrypt area handling
|
||||||
|
if ((cdata->cart_type & CART_TWL) &&
|
||||||
|
((sector+count) * 0x200 > cdata->arm9i_rom_offset) &&
|
||||||
|
(sector * 0x200 < cdata->arm9i_rom_offset + MODC_AREA_SIZE)) {
|
||||||
|
u32 arm9i_rom_offset = cdata->arm9i_rom_offset;
|
||||||
|
u8* buffer_arm9i = buffer;
|
||||||
|
u32 offset_i = 0;
|
||||||
|
u32 size_i = MODC_AREA_SIZE;
|
||||||
|
if (arm9i_rom_offset < (sector * 0x200))
|
||||||
|
offset_i = (sector * 0x200) - arm9i_rom_offset;
|
||||||
|
else buffer_arm9i = buffer + (arm9i_rom_offset - (sector * 0x200));
|
||||||
|
size_i = MODC_AREA_SIZE - offset_i;
|
||||||
|
if (size_i > (count * 0x200) - (buffer_arm9i - buffer))
|
||||||
|
size_i = (count * 0x200) - (buffer_arm9i - buffer);
|
||||||
|
if (size_i) memcpy(buffer_arm9i, cdata->twl_header + 0x4000 + offset_i, size_i);
|
||||||
|
}
|
||||||
|
} else return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ReadCartBytes(u8* buffer, u32 offset, u32 count, CartData* cdata) {
|
||||||
|
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
|
||||||
|
// simple wrapper function for ReadCartSectors(...)
|
||||||
|
return ReadCartSectors(buffer, offset / 0x200, count / 0x200, cdata);
|
||||||
|
} else { // misaligned data -> -___-
|
||||||
|
u8 l_buffer[0x200];
|
||||||
|
if (offset % 0x200) { // handle misaligned offset
|
||||||
|
u32 offset_fix = 0x200 - (offset % 0x200);
|
||||||
|
if (ReadCartSectors(l_buffer, offset / 0x200, 1, cdata) != 0) return 1;
|
||||||
|
memcpy(buffer, l_buffer + 0x200 - offset_fix, min(offset_fix, count));
|
||||||
|
if (count <= offset_fix) return 0;
|
||||||
|
offset += offset_fix;
|
||||||
|
buffer += offset_fix;
|
||||||
|
count -= offset_fix;
|
||||||
|
} // offset is now aligned and part of the data is read
|
||||||
|
if (count >= 0x200) { // otherwise this is misaligned and will be handled below
|
||||||
|
if (ReadCartSectors(buffer, offset / 0x200, count / 0x200, cdata) != 0) return 1;
|
||||||
|
}
|
||||||
|
if (count % 0x200) { // handle misaligned count
|
||||||
|
u32 count_fix = count % 0x200;
|
||||||
|
if (ReadCartSectors(l_buffer, (offset + count) / 0x200, 1, cdata) != 0) return 1;
|
||||||
|
memcpy(buffer + count - count_fix, l_buffer, count_fix);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ReadCartPrivateHeader(u8* buffer, u32 offset, u32 count, CartData* cdata) {
|
||||||
|
if (!(cdata->cart_type & CART_CTR)) return 1;
|
||||||
|
if (offset < PRIV_HDR_SIZE) {
|
||||||
|
u8* priv_hdr = cdata->header + 0x4000;
|
||||||
|
if (offset + count > PRIV_HDR_SIZE) count = PRIV_HDR_SIZE - offset;
|
||||||
|
memcpy(buffer, priv_hdr + offset, count);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
28
source/gamecart/gamecart.h
Normal file
28
source/gamecart/gamecart.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define CART_NONE 0
|
||||||
|
#define CART_CTR (1<<0)
|
||||||
|
#define CART_NTR (1<<1)
|
||||||
|
#define CART_TWL (1<<2)
|
||||||
|
|
||||||
|
#define MODC_AREA_SIZE 0x4000
|
||||||
|
#define PRIV_HDR_SIZE 0x50
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 header[0x8000]; // NTR header + secure area / CTR header + private header
|
||||||
|
u8 twl_header[0x8000]; // TWL header + modcrypt area / unused
|
||||||
|
u32 cart_type;
|
||||||
|
u32 cart_id;
|
||||||
|
u64 cart_size;
|
||||||
|
u64 data_size;
|
||||||
|
u32 arm9i_rom_offset; // TWL specific
|
||||||
|
} __attribute__((packed)) CartData;
|
||||||
|
|
||||||
|
u32 GetCartName(char* name, CartData* cdata);
|
||||||
|
u32 CheckCartId(u32 cart_id);
|
||||||
|
u32 InitCardRead(CartData* cdata);
|
||||||
|
u32 ReadCartSectors(u8* buffer, u32 sector, u32 count, CartData* cdata);
|
||||||
|
u32 ReadCartBytes(u8* buffer, u32 offset, u32 count, CartData* cdata);
|
||||||
|
u32 ReadCartPrivateHeader(u8* buffer, u32 offset, u32 count, CartData* cdata);
|
17
source/gamecart/iodelay.s
Normal file
17
source/gamecart/iodelay.s
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
.arm
|
||||||
|
.global ioDelay
|
||||||
|
.type ioDelay STT_FUNC
|
||||||
|
|
||||||
|
@ioDelay ( u32 us )
|
||||||
|
ioDelay:
|
||||||
|
ldr r1, =0x18000000 @ VRAM
|
||||||
|
1:
|
||||||
|
@ Loop doing uncached reads from VRAM to make loop timing more reliable
|
||||||
|
ldr r2, [r1]
|
||||||
|
subs r0, #1
|
||||||
|
bgt 1b
|
||||||
|
bx lr
|
100
source/gamecart/ndscard.h
Normal file
100
source/gamecart/ndscard.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
//
|
||||||
|
// modifyed by osilloscopion (2 Jul 2016)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
|
#define u8 uint8_t
|
||||||
|
#define u16 uint16_t
|
||||||
|
#define u32 uint32_t
|
||||||
|
#define u64 uint64_t
|
||||||
|
|
||||||
|
#define vu8 volatile u8
|
||||||
|
#define vu16 volatile u16
|
||||||
|
#define vu32 volatile u32
|
||||||
|
#define vu64 volatile u64
|
||||||
|
|
||||||
|
#define REG_ROMCTRL (*(vu32*)0x10164004)
|
||||||
|
#define REG_AUXSPICNT (*(vu16*)0x10164000)
|
||||||
|
#define REG_AUXSPICNTH (*(vu16*)0x10164001)
|
||||||
|
#define REG_AUXSPIDATA (*(vu16*)0x10164002)
|
||||||
|
|
||||||
|
#define CARD_COMMAND ((vu8*)0x10164008)
|
||||||
|
#define CARD_DATA_RD (*(vu32*)0x1016401C)
|
||||||
|
|
||||||
|
#define CARD_CR1_ENABLE 0x8000u
|
||||||
|
#define CARD_CR1_IRQ 0x4000u
|
||||||
|
|
||||||
|
// SPI EEPROM COMMANDS
|
||||||
|
#define SPI_EEPROM_WRSR 0x01
|
||||||
|
#define SPI_EEPROM_PP 0x02 // Page Program
|
||||||
|
#define SPI_EEPROM_READ 0x03
|
||||||
|
#define SPI_EEPROM_WRDI 0x04 // Write disable
|
||||||
|
#define SPI_EEPROM_RDSR 0x05 // Read status register
|
||||||
|
#define SPI_EEPROM_WREN 0x06 // Write enable
|
||||||
|
#define SPI_EEPROM_PW 0x0a // Page Write
|
||||||
|
#define SPI_EEPROM_FAST 0x0b // Fast Read
|
||||||
|
#define SPI_EEPROM_RDID 0x9f
|
||||||
|
#define SPI_EEPROM_RDP 0xab // Release from deep power down
|
||||||
|
#define SPI_EEPROM_DPD 0xb9 // Deep power down
|
||||||
|
|
||||||
|
#define CARD_ACTIVATE (1u<<31) // when writing, get the ball rolling
|
||||||
|
#define CARD_WR (1u<<30) // Card write enable
|
||||||
|
#define CARD_nRESET (1u<<29) // value on the /reset pin (1 = high out, not a reset state, 0 = low out = in reset)
|
||||||
|
#define CARD_SEC_LARGE (1u<<28) // Use "other" secure area mode, which tranfers blocks of 0x1000 bytes at a time
|
||||||
|
#define CARD_CLK_SLOW (1u<<27) // Transfer clock rate (0 = 6.7MHz, 1 = 4.2MHz)
|
||||||
|
#define CARD_BLK_SIZE(n) (((n)&0x7u)<<24) // Transfer block size, (0 = None, 1..6 = (0x100 << n) bytes, 7 = 4 bytes)
|
||||||
|
#define CARD_SEC_CMD (1u<<22) // The command transfer will be hardware encrypted (KEY2)
|
||||||
|
#define CARD_DELAY2(n) (((n)&0x3Fu)<<16) // Transfer delay length part 2
|
||||||
|
#define CARD_SEC_SEED (1u<<15) // Apply encryption (KEY2) seed to hardware registers
|
||||||
|
#define CARD_SEC_EN (1u<<14) // Security enable
|
||||||
|
#define CARD_SEC_DAT (1u<<13) // The data transfer will be hardware encrypted (KEY2)
|
||||||
|
#define CARD_DELAY1(n) ((n)&0x1FFFu) // Transfer delay length part 1
|
||||||
|
|
||||||
|
// 3 bits in b10..b8 indicate something
|
||||||
|
// read bits
|
||||||
|
#define CARD_BUSY (1u<<31) // when reading, still expecting incomming data?
|
||||||
|
#define CARD_DATA_READY (1u<<23) // when reading, REG_NTRCARDFIFO has another word of data and is good to go
|
||||||
|
|
||||||
|
// Card commands
|
||||||
|
#define CARD_CMD_DUMMY 0x9Fu
|
||||||
|
#define CARD_CMD_HEADER_READ 0x00u
|
||||||
|
#define CARD_CMD_HEADER_CHIPID 0x90u
|
||||||
|
#define CARD_CMD_ACTIVATE_BF 0x3Cu // Go into blowfish (KEY1) encryption mode
|
||||||
|
#define CARD_CMD_ACTIVATE_BF2 0x3Du // Go into blowfish (KEY1) encryption mode
|
||||||
|
#define CARD_CMD_ACTIVATE_SEC 0x40u // Go into hardware (KEY2) encryption mode
|
||||||
|
#define CARD_CMD_SECURE_CHIPID 0x10u
|
||||||
|
#define CARD_CMD_SECURE_READ 0x20u
|
||||||
|
#define CARD_CMD_DISABLE_SEC 0x60u // Leave hardware (KEY2) encryption mode
|
||||||
|
#define CARD_CMD_DATA_MODE 0xA0u
|
||||||
|
#define CARD_CMD_DATA_READ 0xB7u
|
||||||
|
#define CARD_CMD_DATA_CHIPID 0xB8u
|
||||||
|
|
||||||
|
//REG_AUXSPICNT
|
||||||
|
#define CARD_ENABLE (1<<15)
|
||||||
|
#define CARD_SPI_ENABLE (1<<13)
|
||||||
|
#define CARD_SPI_BUSY (1<<7)
|
||||||
|
#define CARD_SPI_HOLD (1<<6)
|
||||||
|
|
||||||
|
#define CARD_SPICNTH_ENABLE (1<<7) // in byte 1, i.e. 0x8000
|
||||||
|
#define CARD_SPICNTH_IRQ (1<<6) // in byte 1, i.e. 0x4000
|
||||||
|
|
||||||
|
#define swiDelay(n) ioDelay(n)
|
||||||
|
|
||||||
|
#define DMA_SRC(n) (*(vu32*)(0x10002004 + (n * 0x1c)))
|
||||||
|
#define DMA_DEST(n) (*(vu32*)(0x10002008 + (n * 0x1c)))
|
||||||
|
#define DMA_CR(n) (*(vu32*)(0x1000201C + (n * 0x1c)))
|
||||||
|
|
||||||
|
#define DMA_ENABLE (1u << 31)
|
||||||
|
#define DMA_START_CARD (5u << 27)
|
||||||
|
#define DMA_32_BIT (1u << 26)
|
||||||
|
#define DMA_REPEAT (1u << 25)
|
||||||
|
#define DMA_SRC_FIX (1u << 24)
|
||||||
|
|
||||||
|
void cardReset();
|
47
source/gamecart/ndsheader.h
Normal file
47
source/gamecart/ndsheader.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
// very limited, information taken from here:
|
||||||
|
// https://github.com/devkitPro/ndstool/blob/dsi-support/source/header.h
|
||||||
|
typedef struct {
|
||||||
|
// common stuff (DS + DSi)
|
||||||
|
char game_title[12];
|
||||||
|
char game_code[4];
|
||||||
|
char maker_code[2];
|
||||||
|
u8 unit_code; // (0x00=NDS, 0x02=NDS+DSi, 0x03=DSi)
|
||||||
|
u8 seed_select;
|
||||||
|
u8 device_capacity; // cartridge size: (128 * 1024) << this
|
||||||
|
u8 reserved0[7];
|
||||||
|
u8 unknown0[2];
|
||||||
|
u8 rom_version;
|
||||||
|
u8 flags;
|
||||||
|
u8 ignored0[0x60]; // ignored
|
||||||
|
u32 ntr_rom_size; // in byte
|
||||||
|
u32 header_size;
|
||||||
|
u8 reserved1[56];
|
||||||
|
u8 logo[156];
|
||||||
|
u16 logo_crc;
|
||||||
|
u16 header_crc;
|
||||||
|
u8 debugger_reserved[0x20];
|
||||||
|
// extended mode stuff (DSi only)
|
||||||
|
u8 ignored1[0x40]; // ignored
|
||||||
|
u32 arm9i_rom_offset;
|
||||||
|
u32 reserved2;
|
||||||
|
u32 arm9i_load_adress;
|
||||||
|
u32 arm9i_size;
|
||||||
|
u32 arm7i_rom_offset;
|
||||||
|
u32 unknown1;
|
||||||
|
u32 arm7i_load_adress;
|
||||||
|
u32 arm7i_size;
|
||||||
|
u8 ignored2[0x30]; // ignored
|
||||||
|
u32 ntr_twl_rom_size;
|
||||||
|
u8 unknown2[12];
|
||||||
|
u8 ignored3[0x10]; // ignored
|
||||||
|
u64 title_id;
|
||||||
|
u32 pubsav_size;
|
||||||
|
u32 prvsav_size;
|
||||||
|
u8 reserved3[176];
|
||||||
|
u8 unknown3[0x10];
|
||||||
|
u8 ignored4[0xD00]; // ignored
|
||||||
|
} __attribute__((packed)) TwlHeader;
|
239
source/gamecart/protocol.c
Normal file
239
source/gamecart/protocol.c
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "protocol.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "protocol_ctr.h"
|
||||||
|
#include "protocol_ntr.h"
|
||||||
|
#include "command_ctr.h"
|
||||||
|
#include "command_ntr.h"
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
|
// could have been done better, but meh...
|
||||||
|
#define REG_AESCNT (*(vu32*)0x10009000)
|
||||||
|
#define REG_AESBLKCNT (*(vu32*)0x10009004)
|
||||||
|
#define REG_AESWRFIFO (*(vu32*)0x10009008)
|
||||||
|
#define REG_AESRDFIFO (*(vu32*)0x1000900C)
|
||||||
|
#define REG_AESKEYSEL (*(vu8*)0x10009010)
|
||||||
|
#define REG_AESKEYCNT (*(vu8*)0x10009011)
|
||||||
|
#define REG_AESCTR ((vu32*)0x10009020) // 16
|
||||||
|
#define REG_AESMAC ((vu32*)0x10009030) // 16
|
||||||
|
#define REG_AESKEYFIFO (*(vu32*)0x10009100)
|
||||||
|
#define REG_AESKEYXFIFO (*(vu32*)0x10009104)
|
||||||
|
#define REG_AESKEYYFIFO (*(vu32*)0x10009108)
|
||||||
|
|
||||||
|
extern u8* bottomScreen;
|
||||||
|
|
||||||
|
u32 CartID = 0xFFFFFFFFu;
|
||||||
|
u32 CartType = 0;
|
||||||
|
|
||||||
|
static u32 A0_Response = 0xFFFFFFFFu;
|
||||||
|
static u32 rand1 = 0;
|
||||||
|
static u32 rand2 = 0;
|
||||||
|
|
||||||
|
u32 BSWAP32(u32 val) {
|
||||||
|
return (((val >> 24) & 0xFF)) |
|
||||||
|
(((val >> 16) & 0xFF) << 8) |
|
||||||
|
(((val >> 8) & 0xFF) << 16) |
|
||||||
|
((val & 0xFF) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Verify
|
||||||
|
static void ResetCartSlot(void)
|
||||||
|
{
|
||||||
|
REG_CARDCONF2 = 0x0C;
|
||||||
|
REG_CARDCONF &= ~3;
|
||||||
|
|
||||||
|
if (REG_CARDCONF2 == 0xC) {
|
||||||
|
while (REG_CARDCONF2 != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (REG_CARDCONF2 != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
REG_CARDCONF2 = 0x4;
|
||||||
|
while(REG_CARDCONF2 != 0x4);
|
||||||
|
|
||||||
|
REG_CARDCONF2 = 0x8;
|
||||||
|
while(REG_CARDCONF2 != 0x8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SwitchToNTRCARD(void)
|
||||||
|
{
|
||||||
|
REG_NTRCARDROMCNT = 0x20000000;
|
||||||
|
REG_CARDCONF &= ~3;
|
||||||
|
REG_CARDCONF &= ~0x100;
|
||||||
|
REG_NTRCARDMCNT = NTRCARD_CR1_ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SwitchToCTRCARD(void)
|
||||||
|
{
|
||||||
|
REG_CTRCARDCNT = 0x10000000;
|
||||||
|
REG_CARDCONF = (REG_CARDCONF & ~3) | 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Cart_IsInserted(void)
|
||||||
|
{
|
||||||
|
return (0x9000E2C2 == CTR_CmdGetSecureId(rand1, rand2) );
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Cart_GetID(void)
|
||||||
|
{
|
||||||
|
return CartID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cart_Init(void)
|
||||||
|
{
|
||||||
|
ResetCartSlot(); //Seems to reset the cart slot?
|
||||||
|
|
||||||
|
REG_CTRCARDSECCNT &= 0xFFFFFFFB;
|
||||||
|
ioDelay(0x40000);
|
||||||
|
|
||||||
|
SwitchToNTRCARD();
|
||||||
|
ioDelay(0x40000);
|
||||||
|
|
||||||
|
REG_NTRCARDROMCNT = 0;
|
||||||
|
REG_NTRCARDMCNT &= 0xFF;
|
||||||
|
ioDelay(0x40000);
|
||||||
|
|
||||||
|
REG_NTRCARDMCNT |= (NTRCARD_CR1_ENABLE | NTRCARD_CR1_IRQ);
|
||||||
|
REG_NTRCARDROMCNT = NTRCARD_nRESET | NTRCARD_SEC_SEED;
|
||||||
|
while (REG_NTRCARDROMCNT & NTRCARD_BUSY);
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
NTR_CmdReset();
|
||||||
|
ioDelay(0x40000);
|
||||||
|
CartID = NTR_CmdGetCartId();
|
||||||
|
|
||||||
|
// 3ds
|
||||||
|
if (CartID & 0x10000000) {
|
||||||
|
u32 unknowna0_cmd[2] = { 0xA0000000, 0x00000000 };
|
||||||
|
NTR_SendCommand(unknowna0_cmd, 0x4, 0, &A0_Response);
|
||||||
|
|
||||||
|
NTR_CmdEnter16ByteMode();
|
||||||
|
SwitchToCTRCARD();
|
||||||
|
ioDelay(0xF000);
|
||||||
|
|
||||||
|
REG_CTRCARDBLKCNT = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AES_SetKeyControl(u32 a) {
|
||||||
|
REG_AESKEYCNT = (REG_AESKEYCNT & 0xC0) | a | 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns 1 if MAC valid otherwise 0
|
||||||
|
static u8 card_aes(u32 *out, u32 *buff, size_t size) { // note size param ignored
|
||||||
|
(void) size;
|
||||||
|
REG_AESCNT = 0x10C00; //flush r/w fifo macsize = 001
|
||||||
|
|
||||||
|
(*(vu8*)0x10000008) |= 0x0C; //???
|
||||||
|
|
||||||
|
REG_AESCNT |= 0x2800000;
|
||||||
|
|
||||||
|
//const u8 is_dev_unit = *(vu8*)0x10010010;
|
||||||
|
//if(is_dev_unit) //Dev unit
|
||||||
|
const u8 is_dev_cart = (A0_Response&3)==3;
|
||||||
|
if(is_dev_cart) //Dev unit
|
||||||
|
{
|
||||||
|
AES_SetKeyControl(0x11);
|
||||||
|
REG_AESKEYFIFO = 0;
|
||||||
|
REG_AESKEYFIFO = 0;
|
||||||
|
REG_AESKEYFIFO = 0;
|
||||||
|
REG_AESKEYFIFO = 0;
|
||||||
|
REG_AESKEYSEL = 0x11;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AES_SetKeyControl(0x3B);
|
||||||
|
REG_AESKEYYFIFO = buff[0];
|
||||||
|
REG_AESKEYYFIFO = buff[1];
|
||||||
|
REG_AESKEYYFIFO = buff[2];
|
||||||
|
REG_AESKEYYFIFO = buff[3];
|
||||||
|
REG_AESKEYSEL = 0x3B;
|
||||||
|
}
|
||||||
|
|
||||||
|
REG_AESCNT = 0x4000000;
|
||||||
|
REG_AESCNT &= 0xFFF7FFFF;
|
||||||
|
REG_AESCNT |= 0x2970000;
|
||||||
|
REG_AESMAC[0] = buff[11];
|
||||||
|
REG_AESMAC[1] = buff[10];
|
||||||
|
REG_AESMAC[2] = buff[9];
|
||||||
|
REG_AESMAC[3] = buff[8];
|
||||||
|
REG_AESCNT |= 0x2800000;
|
||||||
|
REG_AESCTR[0] = buff[14];
|
||||||
|
REG_AESCTR[1] = buff[13];
|
||||||
|
REG_AESCTR[2] = buff[12];
|
||||||
|
REG_AESBLKCNT = 0x10000;
|
||||||
|
|
||||||
|
u32 v11 = ((REG_AESCNT | 0x80000000) & 0xC7FFFFFF); //Start and clear mode (ccm decrypt)
|
||||||
|
u32 v12 = v11 & 0xBFFFFFFF; //Disable Interrupt
|
||||||
|
REG_AESCNT = ((((v12 | 0x3000) & 0xFD7F3FFF) | (5 << 23)) & 0xFEBFFFFF) | (5 << 22);
|
||||||
|
|
||||||
|
//REG_AESCNT = 0x83D73C00;
|
||||||
|
REG_AESWRFIFO = buff[4];
|
||||||
|
REG_AESWRFIFO = buff[5];
|
||||||
|
REG_AESWRFIFO = buff[6];
|
||||||
|
REG_AESWRFIFO = buff[7];
|
||||||
|
while (((REG_AESCNT >> 5) & 0x1F) <= 3);
|
||||||
|
out[0] = REG_AESRDFIFO;
|
||||||
|
out[1] = REG_AESRDFIFO;
|
||||||
|
out[2] = REG_AESRDFIFO;
|
||||||
|
out[3] = REG_AESRDFIFO;
|
||||||
|
return ((REG_AESCNT >> 21) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cart_Secure_Init(u32 *buf, u32 *out)
|
||||||
|
{
|
||||||
|
card_aes(out, buf, 0x200);
|
||||||
|
// u8 mac_valid = card_aes(out, buf, 0x200);
|
||||||
|
|
||||||
|
// if (!mac_valid)
|
||||||
|
// ClearScreen(bottomScreen, RGB(255, 0, 0));
|
||||||
|
|
||||||
|
ioDelay(0xF0000);
|
||||||
|
|
||||||
|
CTR_SetSecKey(A0_Response);
|
||||||
|
CTR_SetSecSeed(out, true);
|
||||||
|
|
||||||
|
rand1 = 0x42434445;//*((vu32*)0x10011000);
|
||||||
|
rand2 = 0x46474849;//*((vu32*)0x10011010);
|
||||||
|
|
||||||
|
CTR_CmdSeed(rand1, rand2);
|
||||||
|
|
||||||
|
out[3] = BSWAP32(rand2);
|
||||||
|
out[2] = BSWAP32(rand1);
|
||||||
|
CTR_SetSecSeed(out, false);
|
||||||
|
|
||||||
|
u32 test = 0;
|
||||||
|
const u32 A2_cmd[4] = { 0xA2000000, 0x00000000, rand1, rand2 };
|
||||||
|
CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test);
|
||||||
|
|
||||||
|
u32 test2 = 0;
|
||||||
|
const u32 A3_cmd[4] = { 0xA3000000, 0x00000000, rand1, rand2 };
|
||||||
|
CTR_SendCommand(A3_cmd, 4, 1, 0x701002C, &test2);
|
||||||
|
|
||||||
|
if(test==CartID && test2==A0_Response)
|
||||||
|
{
|
||||||
|
const u32 C5_cmd[4] = { 0xC5000000, 0x00000000, rand1, rand2 };
|
||||||
|
CTR_SendCommand(C5_cmd, 0, 1, 0x100002C, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test);
|
||||||
|
ioDelay(0xF0000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cart_Dummy(void) {
|
||||||
|
// Sends a dummy command to skip encrypted responses some problematic carts send.
|
||||||
|
u32 test;
|
||||||
|
const u32 A2_cmd[4] = { 0xA2000000, 0x00000000, rand1, rand2 };
|
||||||
|
CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test);
|
||||||
|
}
|
19
source/gamecart/protocol.h
Normal file
19
source/gamecart/protocol.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define REG_CARDCONF (*(vu16*)0x1000000C)
|
||||||
|
#define REG_CARDCONF2 (*(vu8*)0x10000010)
|
||||||
|
|
||||||
|
#define LATENCY 0x822Cu
|
||||||
|
|
||||||
|
u32 BSWAP32(u32 val);
|
||||||
|
|
||||||
|
void Cart_Init(void);
|
||||||
|
int Cart_IsInserted(void);
|
||||||
|
u32 Cart_GetID(void);
|
||||||
|
void Cart_Secure_Init(u32* buf, u32* out);
|
||||||
|
void Cart_Dummy(void);
|
183
source/gamecart/protocol_ctr.c
Normal file
183
source/gamecart/protocol_ctr.c
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "protocol_ctr.h"
|
||||||
|
|
||||||
|
#include "protocol.h"
|
||||||
|
#include "delay.h"
|
||||||
|
#ifdef VERBOSE_COMMANDS
|
||||||
|
#include "draw.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void CTR_SetSecKey(u32 value) {
|
||||||
|
REG_CTRCARDSECCNT |= ((value & 3) << 8) | 4;
|
||||||
|
while (!(REG_CTRCARDSECCNT & 0x4000));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTR_SetSecSeed(const u32* seed, bool flag) {
|
||||||
|
REG_CTRCARDSECSEED = BSWAP32(seed[3]);
|
||||||
|
REG_CTRCARDSECSEED = BSWAP32(seed[2]);
|
||||||
|
REG_CTRCARDSECSEED = BSWAP32(seed[1]);
|
||||||
|
REG_CTRCARDSECSEED = BSWAP32(seed[0]);
|
||||||
|
REG_CTRCARDSECCNT |= 0x8000;
|
||||||
|
|
||||||
|
while (!(REG_CTRCARDSECCNT & 0x4000));
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
(*(vu32*)0x1000400C) = 0x00000001; // Enable cart command encryption?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTR_SendCommand(const u32 command[4], u32 pageSize, u32 blocks, u32 latency, void* buffer)
|
||||||
|
{
|
||||||
|
#ifdef VERBOSE_COMMANDS
|
||||||
|
Debug("C> %08X %08X %08X %08X", command[0], command[1], command[2], command[3]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
REG_CTRCARDCMD[0] = command[3];
|
||||||
|
REG_CTRCARDCMD[1] = command[2];
|
||||||
|
REG_CTRCARDCMD[2] = command[1];
|
||||||
|
REG_CTRCARDCMD[3] = command[0];
|
||||||
|
|
||||||
|
//Make sure this never happens
|
||||||
|
if(blocks == 0) blocks = 1;
|
||||||
|
|
||||||
|
pageSize -= pageSize & 3; // align to 4 byte
|
||||||
|
u32 pageParam = CTRCARD_PAGESIZE_4K;
|
||||||
|
u32 transferLength = 4096;
|
||||||
|
// make zero read and 4 byte read a little special for timing optimization(and 512 too)
|
||||||
|
switch(pageSize) {
|
||||||
|
case 0:
|
||||||
|
transferLength = 0;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_0;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
transferLength = 4;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_4;
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
transferLength = 64;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_64;
|
||||||
|
break;
|
||||||
|
case 512:
|
||||||
|
transferLength = 512;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_512;
|
||||||
|
break;
|
||||||
|
case 1024:
|
||||||
|
transferLength = 1024;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_1K;
|
||||||
|
break;
|
||||||
|
case 2048:
|
||||||
|
transferLength = 2048;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_2K;
|
||||||
|
break;
|
||||||
|
case 4096:
|
||||||
|
transferLength = 4096;
|
||||||
|
pageParam = CTRCARD_PAGESIZE_4K;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; //Defaults already set
|
||||||
|
}
|
||||||
|
|
||||||
|
REG_CTRCARDBLKCNT = blocks - 1;
|
||||||
|
transferLength *= blocks;
|
||||||
|
|
||||||
|
// go
|
||||||
|
REG_CTRCARDCNT = 0x10000000;
|
||||||
|
REG_CTRCARDCNT = /*CTRKEY_PARAM | */CTRCARD_ACTIVATE | CTRCARD_nRESET | pageParam | latency;
|
||||||
|
|
||||||
|
u8 * pbuf = (u8 *)buffer;
|
||||||
|
u32 * pbuf32 = (u32 * )buffer;
|
||||||
|
bool useBuf = ( NULL != pbuf );
|
||||||
|
bool useBuf32 = (useBuf && (0 == (3 & ((u32)buffer))));
|
||||||
|
|
||||||
|
u32 count = 0;
|
||||||
|
u32 cardCtrl = REG_CTRCARDCNT;
|
||||||
|
|
||||||
|
if(useBuf32)
|
||||||
|
{
|
||||||
|
while( (cardCtrl & CTRCARD_BUSY) && count < transferLength)
|
||||||
|
{
|
||||||
|
cardCtrl = REG_CTRCARDCNT;
|
||||||
|
if( cardCtrl & CTRCARD_DATA_READY ) {
|
||||||
|
u32 data = REG_CTRCARDFIFO;
|
||||||
|
*pbuf32++ = data;
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(useBuf)
|
||||||
|
{
|
||||||
|
while( (cardCtrl & CTRCARD_BUSY) && count < transferLength)
|
||||||
|
{
|
||||||
|
cardCtrl = REG_CTRCARDCNT;
|
||||||
|
if( cardCtrl & CTRCARD_DATA_READY ) {
|
||||||
|
u32 data = REG_CTRCARDFIFO;
|
||||||
|
pbuf[0] = (unsigned char) (data >> 0);
|
||||||
|
pbuf[1] = (unsigned char) (data >> 8);
|
||||||
|
pbuf[2] = (unsigned char) (data >> 16);
|
||||||
|
pbuf[3] = (unsigned char) (data >> 24);
|
||||||
|
pbuf += sizeof (unsigned int);
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while( (cardCtrl & CTRCARD_BUSY) && count < transferLength)
|
||||||
|
{
|
||||||
|
cardCtrl = REG_CTRCARDCNT;
|
||||||
|
if( cardCtrl & CTRCARD_DATA_READY ) {
|
||||||
|
u32 data = REG_CTRCARDFIFO;
|
||||||
|
(void)data;
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if read is not finished, ds will not pull ROM CS to high, we pull it high manually
|
||||||
|
if( count != transferLength ) {
|
||||||
|
// MUST wait for next data ready,
|
||||||
|
// if ds pull ROM CS to high during 4 byte data transfer, something will mess up
|
||||||
|
// so we have to wait next data ready
|
||||||
|
do { cardCtrl = REG_CTRCARDCNT; } while(!(cardCtrl & CTRCARD_DATA_READY));
|
||||||
|
// and this tiny delay is necessary
|
||||||
|
ioDelay(33);
|
||||||
|
// pull ROM CS high
|
||||||
|
REG_CTRCARDCNT = 0x10000000;
|
||||||
|
REG_CTRCARDCNT = CTRKEY_PARAM | CTRCARD_ACTIVATE | CTRCARD_nRESET;
|
||||||
|
}
|
||||||
|
// wait rom cs high
|
||||||
|
do { cardCtrl = REG_CTRCARDCNT; } while( cardCtrl & CTRCARD_BUSY );
|
||||||
|
//lastCmd[0] = command[0];lastCmd[1] = command[1];
|
||||||
|
|
||||||
|
#ifdef VERBOSE_COMMANDS
|
||||||
|
if (!useBuf) {
|
||||||
|
Debug("C< NULL");
|
||||||
|
} else if (!useBuf32) {
|
||||||
|
Debug("C< non32");
|
||||||
|
} else {
|
||||||
|
u32* p = (u32*)buffer;
|
||||||
|
int transferWords = count / 4;
|
||||||
|
for (int i = 0; i < transferWords && i < 4*4; i += 4) {
|
||||||
|
switch (transferWords - i) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Debug("C< %08X", p[i+0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Debug("C< %08X %08X", p[i+0], p[i+1]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Debug("C< %08X %08X %08X", p[i+0], p[i+1], p[i+2]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug("C< %08X %08X %08X %08X", p[i+0], p[i+1], p[i+2], p[i+3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
42
source/gamecart/protocol_ctr.h
Normal file
42
source/gamecart/protocol_ctr.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define REG_CTRCARDCNT (*(vu32*)0x10004000)
|
||||||
|
#define REG_CTRCARDBLKCNT (*(vu32*)0x10004004)
|
||||||
|
#define REG_CTRCARDSECCNT (*(vu32*)0x10004008)
|
||||||
|
#define REG_CTRCARDSECSEED (*(vu32*)0x10004010)
|
||||||
|
#define REG_CTRCARDCMD ((vu32*)0x10004020)
|
||||||
|
#define REG_CTRCARDFIFO (*(vu32*)0x10004030)
|
||||||
|
|
||||||
|
#define CTRCARD_PAGESIZE_0 (0<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_4 (1u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_16 (2u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_64 (3u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_512 (4u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_1K (5u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_2K (6u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_4K (7u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_16K (8u<<16)
|
||||||
|
#define CTRCARD_PAGESIZE_64K (9u<<16)
|
||||||
|
|
||||||
|
#define CTRCARD_CRC_ERROR (1u<<4)
|
||||||
|
#define CTRCARD_ACTIVATE (1u<<31) // when writing, get the ball rolling
|
||||||
|
#define CTRCARD_IE (1u<<30) // Interrupt enable
|
||||||
|
#define CTRCARD_WR (1u<<29) // Card write enable
|
||||||
|
#define CTRCARD_nRESET (1u<<28) // value on the /reset pin (1 = high out, not a reset state, 0 = low out = in reset)
|
||||||
|
#define CTRCARD_BLK_SIZE(n) (((n)&0xFu)<<16) // Transfer block size
|
||||||
|
|
||||||
|
#define CTRCARD_BUSY (1u<<31) // when reading, still expecting incomming data?
|
||||||
|
#define CTRCARD_DATA_READY (1u<<27) // when reading, REG_CTRCARDFIFO has another word of data and is good to go
|
||||||
|
|
||||||
|
#define CTRKEY_PARAM 0x1000000u
|
||||||
|
|
||||||
|
void CTR_SetSecKey(u32 value);
|
||||||
|
void CTR_SetSecSeed(const u32* seed, bool flag);
|
||||||
|
|
||||||
|
void CTR_SendCommand(const u32 command[4], u32 pageSize, u32 blocks, u32 latency, void* buffer);
|
154
source/gamecart/protocol_ntr.c
Normal file
154
source/gamecart/protocol_ntr.c
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "protocol_ntr.h"
|
||||||
|
#ifdef VERBOSE_COMMANDS
|
||||||
|
#include "draw.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void NTR_SendCommand(const u32 command[2], u32 pageSize, u32 latency, void* buffer)
|
||||||
|
{
|
||||||
|
#ifdef VERBOSE_COMMANDS
|
||||||
|
Debug("N> %08X %08X", command[0], command[1]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
REG_NTRCARDMCNT = NTRCARD_CR1_ENABLE;
|
||||||
|
|
||||||
|
for( u32 i=0; i<2; ++i )
|
||||||
|
{
|
||||||
|
REG_NTRCARDCMD[i*4+0] = command[i]>>24;
|
||||||
|
REG_NTRCARDCMD[i*4+1] = command[i]>>16;
|
||||||
|
REG_NTRCARDCMD[i*4+2] = command[i]>>8;
|
||||||
|
REG_NTRCARDCMD[i*4+3] = command[i]>>0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageSize -= pageSize & 3; // align to 4 byte
|
||||||
|
|
||||||
|
u32 pageParam = NTRCARD_PAGESIZE_4K;
|
||||||
|
u32 transferLength = 4096;
|
||||||
|
|
||||||
|
// make zero read and 4 byte read a little special for timing optimization(and 512 too)
|
||||||
|
switch (pageSize) {
|
||||||
|
case 0:
|
||||||
|
transferLength = 0;
|
||||||
|
pageParam = NTRCARD_PAGESIZE_0;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
transferLength = 4;
|
||||||
|
pageParam = NTRCARD_PAGESIZE_4;
|
||||||
|
break;
|
||||||
|
case 512:
|
||||||
|
transferLength = 512;
|
||||||
|
pageParam = NTRCARD_PAGESIZE_512;
|
||||||
|
break;
|
||||||
|
case 8192:
|
||||||
|
transferLength = 8192;
|
||||||
|
pageParam = NTRCARD_PAGESIZE_8K;
|
||||||
|
break;
|
||||||
|
case 16384:
|
||||||
|
transferLength = 16384;
|
||||||
|
pageParam = NTRCARD_PAGESIZE_16K;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; //Using 4K pagesize and transfer length by default
|
||||||
|
}
|
||||||
|
|
||||||
|
// go
|
||||||
|
REG_NTRCARDROMCNT = 0x10000000;
|
||||||
|
REG_NTRCARDROMCNT = NTRKEY_PARAM | NTRCARD_ACTIVATE | NTRCARD_nRESET | pageParam | latency;
|
||||||
|
|
||||||
|
u8 * pbuf = (u8 *)buffer;
|
||||||
|
u32 * pbuf32 = (u32 * )buffer;
|
||||||
|
bool useBuf = ( NULL != pbuf );
|
||||||
|
bool useBuf32 = (useBuf && (0 == (3 & ((u32)buffer))));
|
||||||
|
|
||||||
|
u32 count = 0;
|
||||||
|
u32 cardCtrl = REG_NTRCARDROMCNT;
|
||||||
|
|
||||||
|
if(useBuf32)
|
||||||
|
{
|
||||||
|
while( (cardCtrl & NTRCARD_BUSY) && count < pageSize)
|
||||||
|
{
|
||||||
|
cardCtrl = REG_NTRCARDROMCNT;
|
||||||
|
if( cardCtrl & NTRCARD_DATA_READY ) {
|
||||||
|
u32 data = REG_NTRCARDFIFO;
|
||||||
|
*pbuf32++ = data;
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(useBuf)
|
||||||
|
{
|
||||||
|
while( (cardCtrl & NTRCARD_BUSY) && count < pageSize)
|
||||||
|
{
|
||||||
|
cardCtrl = REG_NTRCARDROMCNT;
|
||||||
|
if( cardCtrl & NTRCARD_DATA_READY ) {
|
||||||
|
u32 data = REG_NTRCARDFIFO;
|
||||||
|
pbuf[0] = (unsigned char) (data >> 0);
|
||||||
|
pbuf[1] = (unsigned char) (data >> 8);
|
||||||
|
pbuf[2] = (unsigned char) (data >> 16);
|
||||||
|
pbuf[3] = (unsigned char) (data >> 24);
|
||||||
|
pbuf += sizeof (unsigned int);
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while( (cardCtrl & NTRCARD_BUSY) && count < pageSize)
|
||||||
|
{
|
||||||
|
cardCtrl = REG_NTRCARDROMCNT;
|
||||||
|
if( cardCtrl & NTRCARD_DATA_READY ) {
|
||||||
|
u32 data = REG_NTRCARDFIFO;
|
||||||
|
(void)data;
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if read is not finished, ds will not pull ROM CS to high, we pull it high manually
|
||||||
|
if( count != transferLength ) {
|
||||||
|
// MUST wait for next data ready,
|
||||||
|
// if ds pull ROM CS to high during 4 byte data transfer, something will mess up
|
||||||
|
// so we have to wait next data ready
|
||||||
|
do { cardCtrl = REG_NTRCARDROMCNT; } while(!(cardCtrl & NTRCARD_DATA_READY));
|
||||||
|
// and this tiny delay is necessary
|
||||||
|
//ioAK2Delay(33);
|
||||||
|
// pull ROM CS high
|
||||||
|
REG_NTRCARDROMCNT = 0x10000000;
|
||||||
|
REG_NTRCARDROMCNT = NTRKEY_PARAM | NTRCARD_ACTIVATE | NTRCARD_nRESET/* | 0 | 0x0000*/;
|
||||||
|
}
|
||||||
|
// wait rom cs high
|
||||||
|
do { cardCtrl = REG_NTRCARDROMCNT; } while( cardCtrl & NTRCARD_BUSY );
|
||||||
|
//lastCmd[0] = command[0];lastCmd[1] = command[1];
|
||||||
|
|
||||||
|
#ifdef VERBOSE_COMMANDS
|
||||||
|
if (!useBuf) {
|
||||||
|
Debug("N< NULL");
|
||||||
|
} else if (!useBuf32) {
|
||||||
|
Debug("N< non32");
|
||||||
|
} else {
|
||||||
|
u32* p = (u32*)buffer;
|
||||||
|
int transferWords = count / 4;
|
||||||
|
for (int i = 0; i < transferWords && i < 4*4; i += 4) {
|
||||||
|
switch (transferWords - i) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Debug("N< %08X", p[i+0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Debug("N< %08X %08X", p[i+0], p[i+1]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Debug("N< %08X %08X %08X", p[i+0], p[i+1], p[i+2]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug("N< %08X %08X %08X %08X", p[i+0], p[i+1], p[i+2], p[i+3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
65
source/gamecart/protocol_ntr.h
Normal file
65
source/gamecart/protocol_ntr.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2014 Normmatt
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define REG_NTRCARDMCNT (*(vu16*)0x10164000)
|
||||||
|
#define REG_NTRCARDMDATA (*(vu16*)0x10164002)
|
||||||
|
#define REG_NTRCARDROMCNT (*(vu32*)0x10164004)
|
||||||
|
#define REG_NTRCARDCMD ((vu8*)0x10164008)
|
||||||
|
#define REG_NTRCARDSEEDX_L (*(vu32*)0x10164010)
|
||||||
|
#define REG_NTRCARDSEEDY_L (*(vu32*)0x10164014)
|
||||||
|
#define REG_NTRCARDSEEDX_H (*(vu16*)0x10164018)
|
||||||
|
#define REG_NTRCARDSEEDY_H (*(vu16*)0x1016401A)
|
||||||
|
#define REG_NTRCARDFIFO (*(vu32*)0x1016401C)
|
||||||
|
|
||||||
|
#define NTRCARD_PAGESIZE_0 (0<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_4 (7u<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_512 (1u<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_1K (2u<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_2K (3u<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_4K (4u<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_8K (5u<<24)
|
||||||
|
#define NTRCARD_PAGESIZE_16K (6u<<24)
|
||||||
|
|
||||||
|
#define NTRCARD_ACTIVATE (1u<<31) // when writing, get the ball rolling
|
||||||
|
#define NTRCARD_WR (1u<<30) // Card write enable
|
||||||
|
#define NTRCARD_nRESET (1u<<29) // value on the /reset pin (1 = high out, not a reset state, 0 = low out = in reset)
|
||||||
|
#define NTRCARD_SEC_LARGE (1u<<28) // Use "other" secure area mode, which tranfers blocks of 0x1000 bytes at a time
|
||||||
|
#define NTRCARD_CLK_SLOW (1u<<27) // Transfer clock rate (0 = 6.7MHz, 1 = 4.2MHz)
|
||||||
|
#define NTRCARD_BLK_SIZE(n) (((n)&0x7u)<<24) // Transfer block size, (0 = None, 1..6 = (0x100 << n) bytes, 7 = 4 bytes)
|
||||||
|
#define NTRCARD_SEC_CMD (1u<<22) // The command transfer will be hardware encrypted (KEY2)
|
||||||
|
#define NTRCARD_DELAY2(n) (((n)&0x3Fu)<<16) // Transfer delay length part 2
|
||||||
|
#define NTRCARD_SEC_SEED (1u<<15) // Apply encryption (KEY2) seed to hardware registers
|
||||||
|
#define NTRCARD_SEC_EN (1u<<14) // Security enable
|
||||||
|
#define NTRCARD_SEC_DAT (1u<<13) // The data transfer will be hardware encrypted (KEY2)
|
||||||
|
#define NTRCARD_DELAY1(n) ((n)&0x1FFFu) // Transfer delay length part 1
|
||||||
|
|
||||||
|
// 3 bits in b10..b8 indicate something
|
||||||
|
// read bits
|
||||||
|
#define NTRCARD_BUSY (1u<<31) // when reading, still expecting incomming data?
|
||||||
|
#define NTRCARD_DATA_READY (1u<<23) // when reading, REG_NTRCARDFIFO has another word of data and is good to go
|
||||||
|
|
||||||
|
// Card commands
|
||||||
|
#define NTRCARD_CMD_DUMMY 0x9Fu
|
||||||
|
#define NTRCARD_CMD_HEADER_READ 0x00u
|
||||||
|
#define NTRCARD_CMD_HEADER_CHIPID 0x90u
|
||||||
|
#define NTRCARD_CMD_ACTIVATE_BF 0x3Cu // Go into blowfish (KEY1) encryption mode
|
||||||
|
#define NTRCARD_CMD_ACTIVATE_BF2 0x3Du // Go into blowfish (KEY1) encryption mode
|
||||||
|
#define NTRCARD_CMD_ACTIVATE_SEC 0x40u // Go into hardware (KEY2) encryption mode
|
||||||
|
#define NTRCARD_CMD_SECURE_CHIPID 0x10u
|
||||||
|
#define NTRCARD_CMD_SECURE_READ 0x20u
|
||||||
|
#define NTRCARD_CMD_DISABLE_SEC 0x60u // Leave hardware (KEY2) encryption mode
|
||||||
|
#define NTRCARD_CMD_DATA_MODE 0xA0u
|
||||||
|
#define NTRCARD_CMD_DATA_READ 0xB7u
|
||||||
|
#define NTRCARD_CMD_DATA_CHIPID 0xB8u
|
||||||
|
|
||||||
|
#define NTRCARD_CR1_ENABLE 0x8000u
|
||||||
|
#define NTRCARD_CR1_IRQ 0x4000u
|
||||||
|
|
||||||
|
#define NTRKEY_PARAM 0x3F1FFFu
|
||||||
|
|
||||||
|
void NTR_SendCommand(const u32 command[2], u32 pageSize, u32 latency, void* buffer);
|
321
source/gamecart/secure_ntr.c
Normal file
321
source/gamecart/secure_ntr.c
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
card_access.cpp
|
||||||
|
Copyright (C) 2010 yellow wood goblin
|
||||||
|
|
||||||
|
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 3 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// modifyed by osilloscopion (2 Jul 2016)
|
||||||
|
|
||||||
|
#include "protocol_ntr.h"
|
||||||
|
#include "secure_ntr.h"
|
||||||
|
#include "card_ntr.h"
|
||||||
|
// #include "draw.h"
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define BSWAP32(val) ((((val >> 24) & 0xFF)) | (((val >> 16) & 0xFF) << 8) | (((val >> 8) & 0xFF) << 16) | ((val & 0xFF) << 24))
|
||||||
|
|
||||||
|
extern u32 ReadDataFlags;
|
||||||
|
|
||||||
|
void NTR_CryptUp (u32* pCardHash, u32* aPtr)
|
||||||
|
{
|
||||||
|
u32 x = aPtr[1];
|
||||||
|
u32 y = aPtr[0];
|
||||||
|
u32 z;
|
||||||
|
|
||||||
|
for(int ii=0;ii<0x10;++ii)
|
||||||
|
{
|
||||||
|
z = pCardHash[ii] ^ x;
|
||||||
|
x = pCardHash[0x012 + ((z >> 24) & 0xff)];
|
||||||
|
x = pCardHash[0x112 + ((z >> 16) & 0xff)] + x;
|
||||||
|
x = pCardHash[0x212 + ((z >> 8) & 0xff)] ^ x;
|
||||||
|
x = pCardHash[0x312 + ((z >> 0) & 0xff)] + x;
|
||||||
|
x = y ^ x;
|
||||||
|
y = z;
|
||||||
|
}
|
||||||
|
aPtr[0] = x ^ pCardHash[0x10];
|
||||||
|
aPtr[1] = y ^ pCardHash[0x11];
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_CryptDown(u32* pCardHash, u32* aPtr)
|
||||||
|
{
|
||||||
|
u32 x = aPtr[1];
|
||||||
|
u32 y = aPtr[0];
|
||||||
|
u32 z;
|
||||||
|
|
||||||
|
for(int ii=0x11;ii>0x01;--ii)
|
||||||
|
{
|
||||||
|
z = pCardHash[ii] ^ x;
|
||||||
|
x = pCardHash[0x012 + ((z >> 24) & 0xff)];
|
||||||
|
x = pCardHash[0x112 + ((z >> 16) & 0xff)] + x;
|
||||||
|
x = pCardHash[0x212 + ((z >> 8) & 0xff)] ^ x;
|
||||||
|
x = pCardHash[0x312 + ((z >> 0) & 0xff)] + x;
|
||||||
|
x = y ^ x;
|
||||||
|
y = z;
|
||||||
|
}
|
||||||
|
aPtr[0] = x ^ pCardHash[0x01];
|
||||||
|
aPtr[1] = y ^ pCardHash[0x00];
|
||||||
|
}
|
||||||
|
|
||||||
|
// chosen by fair dice roll.
|
||||||
|
// guaranteed to be random.
|
||||||
|
#define getRandomNumber() (4)
|
||||||
|
|
||||||
|
void NTR_InitKey1 (u8* aCmdData, IKEY1* pKey1, int iCardDevice)
|
||||||
|
{
|
||||||
|
pKey1->iii = getRandomNumber() & 0x00000fff;
|
||||||
|
pKey1->jjj = getRandomNumber() & 0x00000fff;
|
||||||
|
pKey1->kkkkk = getRandomNumber() & 0x000fffff;
|
||||||
|
pKey1->llll = getRandomNumber() & 0x0000ffff;
|
||||||
|
pKey1->mmm = getRandomNumber() & 0x00000fff;
|
||||||
|
pKey1->nnn = getRandomNumber() & 0x00000fff;
|
||||||
|
|
||||||
|
if(iCardDevice) //DSi
|
||||||
|
aCmdData[7]=NTRCARD_CMD_ACTIVATE_BF2; //0x3D
|
||||||
|
else
|
||||||
|
aCmdData[7]=NTRCARD_CMD_ACTIVATE_BF;
|
||||||
|
|
||||||
|
aCmdData[6] = (u8)(pKey1->iii >> 4);
|
||||||
|
aCmdData[5] = (u8)((pKey1->iii << 4) | (pKey1->jjj >> 8));
|
||||||
|
aCmdData[4] = (u8)pKey1->jjj;
|
||||||
|
aCmdData[3] = (u8)(pKey1->kkkkk >> 16);
|
||||||
|
aCmdData[2] = (u8)(pKey1->kkkkk >> 8);
|
||||||
|
aCmdData[1] = (u8)pKey1->kkkkk;
|
||||||
|
aCmdData[0] = (u8)getRandomNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_ApplyKey (u32* pCardHash, int nCardHash, u32* pKeyCode)
|
||||||
|
{
|
||||||
|
u32 scratch[2];
|
||||||
|
|
||||||
|
NTR_CryptUp (pCardHash, &pKeyCode[1]);
|
||||||
|
NTR_CryptUp (pCardHash, &pKeyCode[0]);
|
||||||
|
memset(scratch, 0, sizeof (scratch));
|
||||||
|
|
||||||
|
for(int ii=0;ii<0x12;++ii)
|
||||||
|
{
|
||||||
|
pCardHash[ii] = pCardHash[ii] ^ BSWAP32 (pKeyCode[ii%2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int ii=0;ii<nCardHash;ii+=2)
|
||||||
|
{
|
||||||
|
NTR_CryptUp (pCardHash, scratch);
|
||||||
|
pCardHash[ii] = scratch[1];
|
||||||
|
pCardHash[ii+1] = scratch[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_InitKey (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, int level, int iCardDevice)
|
||||||
|
{
|
||||||
|
if(iCardDevice)
|
||||||
|
{
|
||||||
|
const u8* BlowfishTwl = (const u8*)0x01FFD3E0;
|
||||||
|
memcpy (pCardHash, BlowfishTwl, 0x1048);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const u8* BlowfishNtr = (const u8*)0x01FFE428;
|
||||||
|
memcpy (pCardHash, BlowfishNtr, 0x1048);
|
||||||
|
}
|
||||||
|
|
||||||
|
pKeyCode[0] = aGameCode;
|
||||||
|
pKeyCode[1] = aGameCode/2;
|
||||||
|
pKeyCode[2] = aGameCode*2;
|
||||||
|
|
||||||
|
if (level >= 1) NTR_ApplyKey (pCardHash, nCardHash, pKeyCode);
|
||||||
|
if (level >= 2) NTR_ApplyKey (pCardHash, nCardHash, pKeyCode);
|
||||||
|
|
||||||
|
pKeyCode[1] = pKeyCode[1]*2;
|
||||||
|
pKeyCode[2] = pKeyCode[2]/2;
|
||||||
|
|
||||||
|
if (level >= 3) NTR_ApplyKey (pCardHash, nCardHash, pKeyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_CreateEncryptedCommand (u8 aCommand, u32* pCardHash, u8* aCmdData, IKEY1* pKey1, u32 aBlock)
|
||||||
|
{
|
||||||
|
u32 iii,jjj;
|
||||||
|
if(aCommand!=NTRCARD_CMD_SECURE_READ) aBlock=pKey1->llll;
|
||||||
|
if(aCommand==NTRCARD_CMD_ACTIVATE_SEC)
|
||||||
|
{
|
||||||
|
iii=pKey1->mmm;
|
||||||
|
jjj=pKey1->nnn;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iii=pKey1->iii;
|
||||||
|
jjj=pKey1->jjj;
|
||||||
|
}
|
||||||
|
aCmdData[7]=(u8)(aCommand|(aBlock>>12));
|
||||||
|
aCmdData[6]=(u8)(aBlock>>4);
|
||||||
|
aCmdData[5]=(u8)((aBlock<<4)|(iii>>8));
|
||||||
|
aCmdData[4]=(u8)iii;
|
||||||
|
aCmdData[3]=(u8)(jjj>>4);
|
||||||
|
aCmdData[2]=(u8)((jjj<<4)|(pKey1->kkkkk>>16));
|
||||||
|
aCmdData[1]=(u8)(pKey1->kkkkk>>8);
|
||||||
|
aCmdData[0]=(u8)pKey1->kkkkk;
|
||||||
|
|
||||||
|
NTR_CryptUp(pCardHash, (u32*)(void*)aCmdData);
|
||||||
|
|
||||||
|
pKey1->kkkkk+=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_DecryptSecureArea (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, u32* pSecureArea, int iCardDevice)
|
||||||
|
{
|
||||||
|
NTR_InitKey (aGameCode, pCardHash, nCardHash, pKeyCode, 2, iCardDevice);
|
||||||
|
NTR_CryptDown(pCardHash, pSecureArea);
|
||||||
|
NTR_InitKey(aGameCode, pCardHash, nCardHash, pKeyCode, 3, iCardDevice);
|
||||||
|
for(int ii=0;ii<0x200;ii+=2) NTR_CryptDown (pCardHash, pSecureArea + ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u32 NTR_GetIDSafe (u32 flags, const u8* command, u32 Delay)
|
||||||
|
{
|
||||||
|
u32 data = 0;
|
||||||
|
Delay = ((Delay & 0x3fff) * 1000) / 0x83;
|
||||||
|
ioDelay (Delay);
|
||||||
|
cardWriteCommand(command);
|
||||||
|
REG_NTRCARDROMCNT = flags | NTRCARD_BLK_SIZE(7);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (REG_NTRCARDROMCNT & NTRCARD_DATA_READY)
|
||||||
|
{
|
||||||
|
data = REG_NTRCARDFIFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(REG_NTRCARDROMCNT & NTRCARD_BUSY);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NTR_CmdSecure (u32 flags, void* buffer, u32 length, u8* pcmd, u32 Delay)
|
||||||
|
{
|
||||||
|
Delay = ((Delay & 0x3fff) * 1000) / 0x83;
|
||||||
|
ioDelay (Delay);
|
||||||
|
cardPolledTransfer (flags, buffer, length, pcmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NTR_Secure_Init (u8* header, u32 CartID, int iCardDevice)
|
||||||
|
{
|
||||||
|
u32 iGameCode;
|
||||||
|
u32 iCardHash[0x412] = {0};
|
||||||
|
u32 iKeyCode[3] = {0};
|
||||||
|
u32* secureArea=(u32*)(void*)(header + 0x4000);
|
||||||
|
u8 cmdData[8] __attribute__((aligned(32)));
|
||||||
|
const u8 cardSeedBytes[]={0xE8,0x4D,0x5A,0xB1,0x17,0x8F,0x99,0xD5};
|
||||||
|
IKEY1 iKey1 ={0};
|
||||||
|
bool iCheapCard = (CartID & 0x80000000) != 0;
|
||||||
|
u32 cardControl13 = *((u32*)(void*)&header[0x60]);
|
||||||
|
u32 cardControlBF = *((u32*)(void*)&header[0x64]);
|
||||||
|
u16 readTimeout = *((u16*)(void*)&header[0x6E]); readTimeout*=8;
|
||||||
|
u8 deviceType = header[0x13];
|
||||||
|
int nCardHash = sizeof (iCardHash) / sizeof (iCardHash[0]);
|
||||||
|
u32 flagsKey1=NTRCARD_ACTIVATE|NTRCARD_nRESET|(cardControl13&(NTRCARD_WR|NTRCARD_CLK_SLOW))|((cardControlBF&(NTRCARD_CLK_SLOW|NTRCARD_DELAY1(0x1FFF)))+((cardControlBF&NTRCARD_DELAY2(0x3F))>>16));
|
||||||
|
u32 flagsSec=(cardControlBF&(NTRCARD_CLK_SLOW|NTRCARD_DELAY1(0x1FFF)|NTRCARD_DELAY2(0x3F)))|NTRCARD_ACTIVATE|NTRCARD_nRESET|NTRCARD_SEC_EN|NTRCARD_SEC_DAT;
|
||||||
|
|
||||||
|
iGameCode = *((u32*)(void*)&header[0x0C]);
|
||||||
|
ReadDataFlags = cardControl13 & ~ NTRCARD_BLK_SIZE(7);
|
||||||
|
NTR_InitKey (iGameCode, iCardHash, nCardHash, iKeyCode, iCardDevice?1:2, iCardDevice);
|
||||||
|
|
||||||
|
if(!iCheapCard) flagsKey1 |= NTRCARD_SEC_LARGE;
|
||||||
|
//Debug("iCheapCard=%d, readTimeout=%d", iCheapCard, readTimeout);
|
||||||
|
|
||||||
|
NTR_InitKey1 (cmdData, &iKey1, iCardDevice);
|
||||||
|
//Debug("cmdData=%02X %02X %02X %02X %02X %02X %02X %02X ", cmdData[0], cmdData[1], cmdData[2], cmdData[3], cmdData[4], cmdData[5], cmdData[6], cmdData[7]);
|
||||||
|
//Debug("iKey1=%08X %08X %08X", iKey1.iii, iKey1. jjj, iKey1. kkkkk);
|
||||||
|
//Debug("iKey1=%08X %08X %08X", iKey1. llll, iKey1. mmm, iKey1. nnn);
|
||||||
|
|
||||||
|
NTR_CmdSecure ((cardControl13 & (NTRCARD_WR | NTRCARD_nRESET | NTRCARD_CLK_SLOW)) | NTRCARD_ACTIVATE, NULL, 0, cmdData, 0);
|
||||||
|
|
||||||
|
NTR_CreateEncryptedCommand (NTRCARD_CMD_ACTIVATE_SEC, iCardHash, cmdData, &iKey1, 0);
|
||||||
|
//Debug("cmdData=%02X %02X %02X %02X %02X %02X %02X %02X ", cmdData[0], cmdData[1], cmdData[2], cmdData[3], cmdData[4], cmdData[5], cmdData[6], cmdData[7]);
|
||||||
|
if(iCheapCard)
|
||||||
|
{
|
||||||
|
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData, 0);
|
||||||
|
}
|
||||||
|
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData, readTimeout);
|
||||||
|
|
||||||
|
REG_NTRCARDROMCNT = 0;
|
||||||
|
REG_NTRCARDSEEDX_L = cardSeedBytes[deviceType & 0x07] | (iKey1.nnn << 15) | (iKey1.mmm << 27) | 0x6000;
|
||||||
|
REG_NTRCARDSEEDY_L = 0x879b9b05;
|
||||||
|
REG_NTRCARDSEEDX_H = iKey1.mmm >> 5;
|
||||||
|
REG_NTRCARDSEEDY_H = 0x5c;
|
||||||
|
REG_NTRCARDROMCNT = NTRCARD_nRESET | NTRCARD_SEC_SEED | NTRCARD_SEC_EN | NTRCARD_SEC_DAT;
|
||||||
|
|
||||||
|
flagsKey1 |= NTRCARD_SEC_EN | NTRCARD_SEC_DAT;
|
||||||
|
NTR_CreateEncryptedCommand(NTRCARD_CMD_SECURE_CHIPID, iCardHash, cmdData, &iKey1, 0);
|
||||||
|
//Debug("cmdData=%02X %02X %02X %02X %02X %02X %02X %02X ", cmdData[0], cmdData[1], cmdData[2], cmdData[3], cmdData[4], cmdData[5], cmdData[6], cmdData[7]);
|
||||||
|
u32 SecureCartID = 0;
|
||||||
|
if(iCheapCard)
|
||||||
|
{
|
||||||
|
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//NTR_CmdSecure (flagsKey1, &SecureCartID, sizeof (SecureCartID), cmdData, readTimeout);
|
||||||
|
SecureCartID = NTR_GetIDSafe (flagsKey1, cmdData, readTimeout);
|
||||||
|
|
||||||
|
if (SecureCartID != CartID)
|
||||||
|
{
|
||||||
|
// Debug("Invalid SecureCartID\n(%08X != %08X)", SecureCartID, CartID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int secureAreaOffset = 0;
|
||||||
|
for(int secureBlockNumber=4;secureBlockNumber<8;++secureBlockNumber)
|
||||||
|
{
|
||||||
|
NTR_CreateEncryptedCommand (NTRCARD_CMD_SECURE_READ, iCardHash, cmdData, &iKey1, secureBlockNumber);
|
||||||
|
if (iCheapCard)
|
||||||
|
{
|
||||||
|
NTR_CmdSecure (flagsSec, NULL, 0, cmdData, 0);
|
||||||
|
for(int ii=8;ii>0;--ii)
|
||||||
|
{
|
||||||
|
NTR_CmdSecure (flagsSec | NTRCARD_BLK_SIZE(1), secureArea + secureAreaOffset, 0x200, cmdData, readTimeout);
|
||||||
|
secureAreaOffset += 0x200 / sizeof (u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NTR_CmdSecure (flagsSec | NTRCARD_BLK_SIZE(4) | NTRCARD_SEC_LARGE, secureArea + secureAreaOffset, 0x1000, cmdData, readTimeout);
|
||||||
|
secureAreaOffset += 0x1000 / sizeof (u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTR_CreateEncryptedCommand (NTRCARD_CMD_DATA_MODE, iCardHash, cmdData, &iKey1, 0);
|
||||||
|
if(iCheapCard)
|
||||||
|
{
|
||||||
|
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData, 0);
|
||||||
|
}
|
||||||
|
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData, readTimeout);
|
||||||
|
|
||||||
|
if(!iCardDevice) //CycloDS doesn't like the dsi secure area being decrypted
|
||||||
|
{
|
||||||
|
NTR_DecryptSecureArea (iGameCode, iCardHash, nCardHash, iKeyCode, secureArea, iCardDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug("secure area %08X %08X", secureArea[0], secureArea[1]);
|
||||||
|
if(secureArea[0] == 0x72636e65/*'encr'*/ && secureArea[1] == 0x6a624f79/*'yObj'*/)
|
||||||
|
{
|
||||||
|
secureArea[0] = 0xe7ffdeff;
|
||||||
|
secureArea[1] = 0xe7ffdeff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Debug("Invalid secure area (%08X %08X)", secureArea[0], secureArea[1]);
|
||||||
|
//dragon quest 5 has invalid secure area. really.
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
19
source/gamecart/secure_ntr.h
Normal file
19
source/gamecart/secure_ntr.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _IKEY1{
|
||||||
|
u32 iii;
|
||||||
|
u32 jjj;
|
||||||
|
u32 kkkkk;
|
||||||
|
u32 llll;
|
||||||
|
u32 mmm;
|
||||||
|
u32 nnn;
|
||||||
|
} IKEY1, *PIKEY1;
|
||||||
|
|
||||||
|
void NTR_InitKey (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, int level, int iCardDevice);
|
||||||
|
void NTR_InitKey1 (u8* aCmdData, IKEY1* pKey1, int iCardDevice);
|
||||||
|
|
||||||
|
void NTR_CreateEncryptedCommand (u8 aCommand, u32* pCardHash, u8* aCmdData, IKEY1* pKey1, u32 aBlock);
|
||||||
|
void NTR_DecryptSecureArea (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, u32* pSecureArea, int iCardDevice);
|
@ -93,7 +93,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, DirStruct* c
|
|||||||
((drvtype & DRV_SDCARD) ? "SD" : (drvtype & DRV_RAMDRIVE) ? "RAMdrive" : (drvtype & DRV_GAME) ? "Game" :
|
((drvtype & DRV_SDCARD) ? "SD" : (drvtype & DRV_RAMDRIVE) ? "RAMdrive" : (drvtype & DRV_GAME) ? "Game" :
|
||||||
(drvtype & DRV_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" :
|
(drvtype & DRV_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" :
|
||||||
(drvtype & DRV_XORPAD) ? "XORpad" : (drvtype & DRV_MEMORY) ? "Memory" : (drvtype & DRV_ALIAS) ? "Alias" :
|
(drvtype & DRV_XORPAD) ? "XORpad" : (drvtype & DRV_MEMORY) ? "Memory" : (drvtype & DRV_ALIAS) ? "Alias" :
|
||||||
(drvtype & DRV_SEARCH) ? "Search" : ""),
|
(drvtype & DRV_CART) ? "Gamecart" : (drvtype & DRV_SEARCH) ? "Search" : ""),
|
||||||
((drvtype & DRV_FAT) ? " FAT" : (drvtype & DRV_VIRTUAL) ? " Virtual" : ""));
|
((drvtype & DRV_FAT) ? " FAT" : (drvtype & DRV_VIRTUAL) ? " Virtual" : ""));
|
||||||
ResizeString(tempstr, drvstr, 160 / FONT_WIDTH_EXT, 8, false);
|
ResizeString(tempstr, drvstr, 160 / FONT_WIDTH_EXT, 8, false);
|
||||||
}else {
|
}else {
|
||||||
@ -1046,6 +1046,8 @@ u32 GodMode() {
|
|||||||
ShowPrompt(false, "Not allowed in virtual game path");
|
ShowPrompt(false, "Not allowed in virtual game path");
|
||||||
} else if ((curr_drvtype & DRV_XORPAD) && (pad_state & BUTTON_Y)) {
|
} else if ((curr_drvtype & DRV_XORPAD) && (pad_state & BUTTON_Y)) {
|
||||||
ShowPrompt(false, "Not allowed in XORpad drive");
|
ShowPrompt(false, "Not allowed in XORpad drive");
|
||||||
|
} else if ((curr_drvtype & DRV_CART) && (pad_state & BUTTON_Y)) {
|
||||||
|
ShowPrompt(false, "Not allowed in gamecart drive");
|
||||||
}else if (pad_state & BUTTON_Y) { // paste files
|
}else if (pad_state & BUTTON_Y) { // paste files
|
||||||
const char* optionstr[2] = { "Copy path(s)", "Move path(s)" };
|
const char* optionstr[2] = { "Copy path(s)", "Move path(s)" };
|
||||||
char promptstr[64];
|
char promptstr[64];
|
||||||
|
41
source/virtual/vcart.c
Normal file
41
source/virtual/vcart.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "vcart.h"
|
||||||
|
#include "gamecart.h"
|
||||||
|
|
||||||
|
#define VFLAG_PRIV_HDR (1<<31)
|
||||||
|
|
||||||
|
static CartData* cdata = (CartData*) VCART_BUFFER; // 128kB reserved (~64kB required)
|
||||||
|
|
||||||
|
bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||||
|
if ((vdir->index < 0) &&
|
||||||
|
(CheckCartId(cdata->cart_id) != 0) &&
|
||||||
|
(InitCardRead(cdata) != 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (++vdir->index < 3) {
|
||||||
|
char name[24];
|
||||||
|
GetCartName(name, cdata);
|
||||||
|
memset(vfile, 0, sizeof(VirtualFile));
|
||||||
|
vfile->keyslot = 0xFF; // unused
|
||||||
|
|
||||||
|
if (vdir->index == 2) { // private header
|
||||||
|
if (!(cdata->cart_type & CART_CTR)) return false;
|
||||||
|
snprintf(vfile->name, 32, "%s-private.bin", name);
|
||||||
|
vfile->size = PRIV_HDR_SIZE;
|
||||||
|
vfile->flags = VFLAG_PRIV_HDR;
|
||||||
|
} else {
|
||||||
|
const char* ext = (cdata->cart_type & CART_CTR) ? "3ds" : "nds";
|
||||||
|
snprintf(vfile->name, 32, "%s%s.%s", name, (vdir->index == 1) ? ".trim" : "", ext);
|
||||||
|
vfile->size = (vdir->index == 1) ? cdata->data_size : cdata->cart_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadVCartFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) {
|
||||||
|
if (vfile->flags & VFLAG_PRIV_HDR)
|
||||||
|
return ReadCartPrivateHeader(buffer, offset, count, cdata);
|
||||||
|
else return ReadCartBytes(buffer, offset, count, cdata);
|
||||||
|
}
|
8
source/virtual/vcart.h
Normal file
8
source/virtual/vcart.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "virtual.h"
|
||||||
|
|
||||||
|
bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir);
|
||||||
|
int ReadVCartFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
|
||||||
|
// int WriteVCartFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count); // no writes
|
@ -2,6 +2,7 @@
|
|||||||
#include "vnand.h"
|
#include "vnand.h"
|
||||||
#include "vmem.h"
|
#include "vmem.h"
|
||||||
#include "vgame.h"
|
#include "vgame.h"
|
||||||
|
#include "vcart.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char drv_letter;
|
char drv_letter;
|
||||||
@ -30,7 +31,7 @@ bool CheckVirtualDrive(const char* path) {
|
|||||||
return CheckVNandDrive(virtual_src); // check virtual NAND drive for EmuNAND / ImgNAND
|
return CheckVNandDrive(virtual_src); // check virtual NAND drive for EmuNAND / ImgNAND
|
||||||
else if (virtual_src & VRT_GAME)
|
else if (virtual_src & VRT_GAME)
|
||||||
return CheckVGameDrive();
|
return CheckVGameDrive();
|
||||||
return virtual_src; // this is safe for SysNAND & memory
|
return virtual_src; // this is safe for SysNAND, memory & cart
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
|
bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
|
||||||
@ -42,6 +43,8 @@ bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
|
|||||||
ret = ReadVMemDir(vfile, vdir);
|
ret = ReadVMemDir(vfile, vdir);
|
||||||
} else if (virtual_src & VRT_GAME) {
|
} else if (virtual_src & VRT_GAME) {
|
||||||
ret = ReadVGameDir(vfile, vdir);
|
ret = ReadVGameDir(vfile, vdir);
|
||||||
|
} else if (virtual_src & VRT_CART) {
|
||||||
|
ret = ReadVCartDir(vfile, vdir);
|
||||||
}
|
}
|
||||||
vfile->flags |= virtual_src; // add source flag
|
vfile->flags |= virtual_src; // add source flag
|
||||||
return ret;
|
return ret;
|
||||||
@ -167,6 +170,8 @@ int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count,
|
|||||||
return ReadVMemFile(vfile, buffer, offset, count);
|
return ReadVMemFile(vfile, buffer, offset, count);
|
||||||
} else if (vfile->flags & VRT_GAME) {
|
} else if (vfile->flags & VRT_GAME) {
|
||||||
return ReadVGameFile(vfile, buffer, offset, count);
|
return ReadVGameFile(vfile, buffer, offset, count);
|
||||||
|
} else if (vfile->flags & VRT_CART) {
|
||||||
|
return ReadVCartFile(vfile, buffer, offset, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@ -184,7 +189,7 @@ int WriteVirtualFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32
|
|||||||
return WriteVNandFile(vfile, buffer, offset, count);
|
return WriteVNandFile(vfile, buffer, offset, count);
|
||||||
} else if (vfile->flags & VRT_MEMORY) {
|
} else if (vfile->flags & VRT_MEMORY) {
|
||||||
return WriteVMemFile(vfile, buffer, offset, count);
|
return WriteVMemFile(vfile, buffer, offset, count);
|
||||||
} // no write support for virtual game files
|
} // no write support for virtual game / cart files
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,17 @@
|
|||||||
#define VRT_XORPAD NAND_ZERONAND
|
#define VRT_XORPAD NAND_ZERONAND
|
||||||
#define VRT_MEMORY (1<<10)
|
#define VRT_MEMORY (1<<10)
|
||||||
#define VRT_GAME (1<<11)
|
#define VRT_GAME (1<<11)
|
||||||
|
#define VRT_CART (1<<12)
|
||||||
|
|
||||||
#define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME)
|
#define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_CART)
|
||||||
|
|
||||||
#define VFLAG_DIR (1<<16)
|
#define VFLAG_DIR (1<<16)
|
||||||
#define VFLAG_ROOT (1<<17)
|
#define VFLAG_ROOT (1<<17)
|
||||||
#define VFLAG_A9LH_AREA (1<<18)
|
#define VFLAG_A9LH_AREA (1<<18)
|
||||||
#define VFLAG_LV3 (1<<19)
|
#define VFLAG_LV3 (1<<19)
|
||||||
|
|
||||||
#define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, \
|
#define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, {'X', VRT_XORPAD }, \
|
||||||
{'X', VRT_XORPAD }, {'M', VRT_MEMORY}, {'G', VRT_GAME}
|
{'M', VRT_MEMORY}, {'G', VRT_GAME}, {'C', VRT_CART}
|
||||||
|
|
||||||
// virtual file flag (subject to change):
|
// virtual file flag (subject to change):
|
||||||
// bits 0...9 : reserved for NAND virtual sources and info
|
// bits 0...9 : reserved for NAND virtual sources and info
|
||||||
|
Loading…
x
Reference in New Issue
Block a user