diff --git a/Makefile b/Makefile index 5fb9494..e229c71 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,9 @@ ifeq ($(MODE),safe) export TARGET := SafeMode9 endif 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 -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 diff --git a/source/common/common.h b/source/common/common.h index 6e9c03c..3740a05 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -66,6 +66,9 @@ // buffer area defines (in use by vgame.c) #define VGAME_BUFFER ((u8*)0x21500000) #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) #define RAMDRV_BUFFER_O3DS ((u8*)0x22200000) // in O3DS FCRAM #define RAMDRV_SIZE_O3DS (0x01C00000) // 28MB diff --git a/source/fs/fsdrive.c b/source/fs/fsdrive.c index a35e0c3..cc06e7b 100644 --- a/source/fs/fsdrive.c +++ b/source/fs/fsdrive.c @@ -45,6 +45,8 @@ int DriveType(const char* path) { type = DRV_VIRTUAL | DRV_MEMORY; } else if (vsrc == VRT_GAME) { type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE; + } else if (vsrc == VRT_CART) { + type = DRV_VIRTUAL | DRV_CART; } } diff --git a/source/fs/fsdrive.h b/source/fs/fsdrive.h index 3239a96..dee9efd 100644 --- a/source/fs/fsdrive.h +++ b/source/fs/fsdrive.h @@ -5,7 +5,7 @@ #define NORM_FS 10 #define IMGN_FS 3 // image normal filesystems -#define VIRT_FS 9 +#define VIRT_FS 10 // primary drive types #define DRV_UNKNOWN (0<<0) @@ -20,9 +20,10 @@ #define DRV_RAMDRIVE (1<<7) #define DRV_MEMORY (1<<8) #define DRV_GAME (1<<9) -#define DRV_ALIAS (1<<10) -#define DRV_SEARCH (1<<11) -#define DRV_STDFAT (1<<12) // standard FAT drive without limitations +#define DRV_CART (1<<10) +#define DRV_ALIAS (1<<11) +#define DRV_SEARCH (1<<12) +#define DRV_STDFAT (1<<13) // standard FAT drive without limitations #define FS_DRVNAME \ "SDCARD", \ @@ -31,13 +32,14 @@ "IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", \ "GAME IMAGE", \ "SYSNAND SD", "EMUNAND SD", \ + "GAMECART", \ "SYSNAND VIRTUAL", "EMUNAND VIRTUAL", "IMGNAND VIRTUAL", \ "NAND XORPADS", \ "MEMORY VIRTUAL", \ "LAST SEARCH" \ #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 **/ int DriveType(const char* path); diff --git a/source/fs/fsperm.c b/source/fs/fsperm.c index 7729d99..dd8e109 100644 --- a/source/fs/fsperm.c +++ b/source/fs/fsperm.c @@ -32,6 +32,9 @@ bool CheckWritePermissions(const char* path) { } else if (drvtype & DRV_GAME) { perm = PERM_GAME; snprintf(area_name, 16, "game images"); + } else if (drvtype & DRV_CART) { + perm = PERM_CART; + snprintf(area_name, 16, "gamecarts"); } else if (drvtype & DRV_XORPAD) { perm = PERM_XORPAD; snprintf(area_name, 16, "XORpads"); diff --git a/source/fs/fsperm.h b/source/fs/fsperm.h index 97524f6..8c537d7 100644 --- a/source/fs/fsperm.h +++ b/source/fs/fsperm.h @@ -11,8 +11,9 @@ #define PERM_MEMORY (1<<5) #define PERM_GAME (1<<6) // can't be enabled, placeholder #define PERM_XORPAD (1<<7) // can't be enabled, placeholder -#define PERM_A9LH ((1<<8) | PERM_SYSNAND) -#define PERM_SDDATA ((1<<9) | PERM_SDCARD) +#define PERM_CART (1<<8) // can't be enabled, placeholder +#define PERM_A9LH ((1<<9) | PERM_SYSNAND) +#define PERM_SDDATA ((1<<10) | PERM_SDCARD) #define PERM_BASE (PERM_SDCARD | PERM_RAMDRIVE) #define PERM_ALL (PERM_SDCARD | PERM_RAMDRIVE | PERM_EMUNAND | PERM_SYSNAND | PERM_IMAGE | PERM_MEMORY | PERM_SDDATA) diff --git a/source/game/ncsd.c b/source/game/ncsd.c index 84a8eac..eec28d1 100644 --- a/source/game/ncsd.c +++ b/source/game/ncsd.c @@ -22,7 +22,7 @@ u32 ValidateNcsdHeader(NcsdHeader* header) { return 0; } -u32 GetNcsdTrimmedSize(NcsdHeader* header) { +u64 GetNcsdTrimmedSize(NcsdHeader* header) { u32 data_units = 0; for (u32 i = 0; i < 8; i++) { NcchPartition* partition = header->partitions + i; diff --git a/source/game/ncsd.h b/source/game/ncsd.h index 04ad738..add5be9 100644 --- a/source/game/ncsd.h +++ b/source/game/ncsd.h @@ -33,5 +33,5 @@ typedef struct { } __attribute__((packed)) NcsdHeader; u32 ValidateNcsdHeader(NcsdHeader* header); -u32 GetNcsdTrimmedSize(NcsdHeader* header); +u64 GetNcsdTrimmedSize(NcsdHeader* header); u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data); diff --git a/source/gamecart/card_eeprom.c b/source/gamecart/card_eeprom.c new file mode 100644 index 0000000..88b04be --- /dev/null +++ b/source/gamecart/card_eeprom.c @@ -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> 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; +} diff --git a/source/gamecart/card_eeprom.h b/source/gamecart/card_eeprom.h new file mode 100644 index 0000000..d81b6a4 --- /dev/null +++ b/source/gamecart/card_eeprom.h @@ -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); diff --git a/source/gamecart/card_ntr.c b/source/gamecart/card_ntr.c new file mode 100644 index 0000000..fdcc5f7 --- /dev/null +++ b/source/gamecart/card_ntr.c @@ -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); +} diff --git a/source/gamecart/card_ntr.h b/source/gamecart/card_ntr.h new file mode 100644 index 0000000..5921bcb --- /dev/null +++ b/source/gamecart/card_ntr.h @@ -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(); diff --git a/source/gamecart/command_ak2i.c b/source/gamecart/command_ak2i.c new file mode 100644 index 0000000..02ce979 --- /dev/null +++ b/source/gamecart/command_ak2i.c @@ -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; +} + diff --git a/source/gamecart/command_ak2i.h b/source/gamecart/command_ak2i.h new file mode 100644 index 0000000..8cf2a9e --- /dev/null +++ b/source/gamecart/command_ak2i.h @@ -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); diff --git a/source/gamecart/command_ctr.c b/source/gamecart/command_ctr.c new file mode 100644 index 0000000..49c8829 --- /dev/null +++ b/source/gamecart/command_ctr.c @@ -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); +} diff --git a/source/gamecart/command_ctr.h b/source/gamecart/command_ctr.h new file mode 100644 index 0000000..b2a5a43 --- /dev/null +++ b/source/gamecart/command_ctr.h @@ -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); diff --git a/source/gamecart/command_ntr.c b/source/gamecart/command_ntr.c new file mode 100644 index 0000000..db2dff6 --- /dev/null +++ b/source/gamecart/command_ntr.c @@ -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); +} + + diff --git a/source/gamecart/command_ntr.h b/source/gamecart/command_ntr.h new file mode 100644 index 0000000..559dc33 --- /dev/null +++ b/source/gamecart/command_ntr.h @@ -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); + diff --git a/source/gamecart/delay.h b/source/gamecart/delay.h new file mode 100644 index 0000000..afad438 --- /dev/null +++ b/source/gamecart/delay.h @@ -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); diff --git a/source/gamecart/gamecart.c b/source/gamecart/gamecart.c new file mode 100644 index 0000000..ced8a83 --- /dev/null +++ b/source/gamecart/gamecart.c @@ -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; +} diff --git a/source/gamecart/gamecart.h b/source/gamecart/gamecart.h new file mode 100644 index 0000000..487d92e --- /dev/null +++ b/source/gamecart/gamecart.h @@ -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); diff --git a/source/gamecart/iodelay.s b/source/gamecart/iodelay.s new file mode 100644 index 0000000..b3baccd --- /dev/null +++ b/source/gamecart/iodelay.s @@ -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 diff --git a/source/gamecart/ndscard.h b/source/gamecart/ndscard.h new file mode 100644 index 0000000..106b32e --- /dev/null +++ b/source/gamecart/ndscard.h @@ -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 +#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(); diff --git a/source/gamecart/ndsheader.h b/source/gamecart/ndsheader.h new file mode 100644 index 0000000..8db89be --- /dev/null +++ b/source/gamecart/ndsheader.h @@ -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; diff --git a/source/gamecart/protocol.c b/source/gamecart/protocol.c new file mode 100644 index 0000000..c78b520 --- /dev/null +++ b/source/gamecart/protocol.c @@ -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 +#include +#include + +#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); +} diff --git a/source/gamecart/protocol.h b/source/gamecart/protocol.h new file mode 100644 index 0000000..2a6c4a9 --- /dev/null +++ b/source/gamecart/protocol.h @@ -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); diff --git a/source/gamecart/protocol_ctr.c b/source/gamecart/protocol_ctr.c new file mode 100644 index 0000000..0f24952 --- /dev/null +++ b/source/gamecart/protocol_ctr.c @@ -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 +} diff --git a/source/gamecart/protocol_ctr.h b/source/gamecart/protocol_ctr.h new file mode 100644 index 0000000..cba469e --- /dev/null +++ b/source/gamecart/protocol_ctr.h @@ -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); diff --git a/source/gamecart/protocol_ntr.c b/source/gamecart/protocol_ntr.c new file mode 100644 index 0000000..7a57d69 --- /dev/null +++ b/source/gamecart/protocol_ntr.c @@ -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 +} diff --git a/source/gamecart/protocol_ntr.h b/source/gamecart/protocol_ntr.h new file mode 100644 index 0000000..536e7fc --- /dev/null +++ b/source/gamecart/protocol_ntr.h @@ -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); diff --git a/source/gamecart/secure_ntr.c b/source/gamecart/secure_ntr.c new file mode 100644 index 0000000..5e1823e --- /dev/null +++ b/source/gamecart/secure_ntr.c @@ -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 . +*/ + +// 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= 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; +} diff --git a/source/gamecart/secure_ntr.h b/source/gamecart/secure_ntr.h new file mode 100644 index 0000000..7ef5779 --- /dev/null +++ b/source/gamecart/secure_ntr.h @@ -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); diff --git a/source/godmode.c b/source/godmode.c index 4ab80b8..2098435 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -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_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" : (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" : "")); ResizeString(tempstr, drvstr, 160 / FONT_WIDTH_EXT, 8, false); }else { @@ -1046,7 +1046,9 @@ u32 GodMode() { ShowPrompt(false, "Not allowed in virtual game path"); } else if ((curr_drvtype & DRV_XORPAD) && (pad_state & BUTTON_Y)) { ShowPrompt(false, "Not allowed in XORpad drive"); - } else if (pad_state & BUTTON_Y) { // paste files + } 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 const char* optionstr[2] = { "Copy path(s)", "Move path(s)" }; char promptstr[64]; u32 flags = 0; diff --git a/source/virtual/vcart.c b/source/virtual/vcart.c new file mode 100644 index 0000000..b179f80 --- /dev/null +++ b/source/virtual/vcart.c @@ -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); +} diff --git a/source/virtual/vcart.h b/source/virtual/vcart.h new file mode 100644 index 0000000..93f6880 --- /dev/null +++ b/source/virtual/vcart.h @@ -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 diff --git a/source/virtual/virtual.c b/source/virtual/virtual.c index 5d8ee80..ef628bf 100644 --- a/source/virtual/virtual.c +++ b/source/virtual/virtual.c @@ -2,6 +2,7 @@ #include "vnand.h" #include "vmem.h" #include "vgame.h" +#include "vcart.h" typedef struct { char drv_letter; @@ -30,7 +31,7 @@ bool CheckVirtualDrive(const char* path) { return CheckVNandDrive(virtual_src); // check virtual NAND drive for EmuNAND / ImgNAND else if (virtual_src & VRT_GAME) 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) { @@ -42,6 +43,8 @@ bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) { ret = ReadVMemDir(vfile, vdir); } else if (virtual_src & VRT_GAME) { ret = ReadVGameDir(vfile, vdir); + } else if (virtual_src & VRT_CART) { + ret = ReadVCartDir(vfile, vdir); } vfile->flags |= virtual_src; // add source flag return ret; @@ -167,6 +170,8 @@ int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count, return ReadVMemFile(vfile, buffer, offset, count); } else if (vfile->flags & VRT_GAME) { return ReadVGameFile(vfile, buffer, offset, count); + } else if (vfile->flags & VRT_CART) { + return ReadVCartFile(vfile, buffer, offset, count); } return -1; @@ -184,7 +189,7 @@ int WriteVirtualFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 return WriteVNandFile(vfile, buffer, offset, count); } else if (vfile->flags & VRT_MEMORY) { return WriteVMemFile(vfile, buffer, offset, count); - } // no write support for virtual game files + } // no write support for virtual game / cart files return -1; } diff --git a/source/virtual/virtual.h b/source/virtual/virtual.h index 2301eb4..c2924cf 100644 --- a/source/virtual/virtual.h +++ b/source/virtual/virtual.h @@ -10,16 +10,17 @@ #define VRT_XORPAD NAND_ZERONAND #define VRT_MEMORY (1<<10) #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_ROOT (1<<17) #define VFLAG_A9LH_AREA (1<<18) #define VFLAG_LV3 (1<<19) -#define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, \ - {'X', VRT_XORPAD }, {'M', VRT_MEMORY}, {'G', VRT_GAME} +#define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, {'X', VRT_XORPAD }, \ + {'M', VRT_MEMORY}, {'G', VRT_GAME}, {'C', VRT_CART} // virtual file flag (subject to change): // bits 0...9 : reserved for NAND virtual sources and info