Added virtual gamecart drive

This commit is contained in:
d0k3 2017-01-13 14:20:42 +01:00
parent 1e4be5fe61
commit fa9a2c2fb5
37 changed files with 2382 additions and 18 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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");

View File

@ -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)

View File

@ -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;

View File

@ -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);

View 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;
}

View 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
View 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);
}

View 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();

View 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;
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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
View 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
View 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;
}

View 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
View 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
View 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();

View 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
View 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);
}

View 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);

View 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
}

View 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);

View 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
}

View 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);

View 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;
}

View 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);

View File

@ -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
View 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
View 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

View File

@ -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;
} }

View File

@ -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