mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 21:52:48 +00:00
First cleanup & adaptions
This commit is contained in:
parent
0df734a76a
commit
256327a8c9
4
Makefile
4
Makefile
@ -18,9 +18,9 @@ include $(DEVKITARM)/ds_rules
|
||||
#---------------------------------------------------------------------------------
|
||||
export TARGET := GodMode9
|
||||
BUILD := build
|
||||
SOURCES := source source/fatfs source/fatfs/crypto source/abstraction
|
||||
SOURCES := source source/fatfs source/decryptor source/abstraction
|
||||
DATA := data
|
||||
INCLUDES := include source source/fatfs source/fatfs/crypto
|
||||
INCLUDES := include source source/fatfs source/decryptor
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
|
220
source/fatfs/3dsnand.c
Normal file
220
source/fatfs/3dsnand.c
Normal file
@ -0,0 +1,220 @@
|
||||
#include "platform.h"
|
||||
#include "fatfs/aes.h"
|
||||
#include "fatfs/3dsnand.h"
|
||||
#include "fatfs/sdmmc.h"
|
||||
|
||||
// see: http://3dbrew.org/wiki/Flash_Filesystem
|
||||
static PartitionInfo partitions[] = {
|
||||
{ "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE },
|
||||
{ "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE },
|
||||
{ "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE },
|
||||
{ "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
|
||||
{ "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
|
||||
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS
|
||||
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS
|
||||
};
|
||||
|
||||
static u32 emunand_header = 0;
|
||||
static u32 emunand_offset = 0;
|
||||
|
||||
|
||||
u32 CheckEmuNand(void)
|
||||
{
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 nand_size_sectors = getMMCDevice(0)->total_size;
|
||||
u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS;
|
||||
u32 ret = EMUNAND_NOT_READY;
|
||||
|
||||
// check the MBR for presence of a hidden partition
|
||||
sdmmc_sdcard_readsectors(0, 1, buffer);
|
||||
u32 hidden_sectors = getle32(buffer + 0x1BE + 0x8);
|
||||
|
||||
for (u32 offset_sector = 0; offset_sector + nand_size_sectors < hidden_sectors; offset_sector += multi_sectors) {
|
||||
// check for Gateway type EmuNAND
|
||||
sdmmc_sdcard_readsectors(offset_sector + nand_size_sectors, 1, buffer);
|
||||
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
|
||||
ret |= EMUNAND_GATEWAY << (2 * (offset_sector / multi_sectors));
|
||||
continue;
|
||||
}
|
||||
// check for RedNAND type EmuNAND
|
||||
sdmmc_sdcard_readsectors(offset_sector + 1, 1, buffer);
|
||||
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
|
||||
ret |= EMUNAND_REDNAND << (2 * (offset_sector / multi_sectors));
|
||||
continue;
|
||||
}
|
||||
// EmuNAND ready but not set up
|
||||
ret |= EMUNAND_READY << (2 * (offset_sector / multi_sectors));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SetNand(bool set_emunand, u32 base_sector)
|
||||
{
|
||||
// no checks here AT ALL - be careful!!!
|
||||
if (set_emunand) {
|
||||
emunand_header = base_sector + getMMCDevice(0)->total_size;
|
||||
emunand_offset = base_sector;
|
||||
} else {
|
||||
emunand_header = 0;
|
||||
emunand_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out)
|
||||
{
|
||||
if (emunand_header) {
|
||||
if (sector_no == 0) {
|
||||
int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out);
|
||||
if (errorcode) return errorcode;
|
||||
sector_no = 1;
|
||||
numsectors--;
|
||||
out += 0x200;
|
||||
}
|
||||
return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out);
|
||||
} else return sdmmc_nand_readsectors(sector_no, numsectors, out);
|
||||
}
|
||||
|
||||
int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in)
|
||||
{
|
||||
if (emunand_header) {
|
||||
if (sector_no == 0) {
|
||||
int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in);
|
||||
if (errorcode) return errorcode;
|
||||
sector_no = 1;
|
||||
numsectors--;
|
||||
in += 0x200;
|
||||
}
|
||||
return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in);
|
||||
} else return sdmmc_nand_writesectors(sector_no, numsectors, in);
|
||||
}
|
||||
|
||||
u32 GetNandSize(void)
|
||||
{
|
||||
return getMMCDevice(0)->total_size * NAND_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
PartitionInfo* GetPartitionInfo(u32 partition_id)
|
||||
{
|
||||
u32 partition_num = 0;
|
||||
|
||||
if (partition_id == P_CTRNAND) {
|
||||
partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6;
|
||||
} else {
|
||||
for(; !(partition_id & (1<<partition_num)) && (partition_num < 32); partition_num++);
|
||||
}
|
||||
|
||||
return (partition_num >= 32) ? NULL : &(partitions[partition_num]);
|
||||
}
|
||||
|
||||
u32 GetNandCtr(u8* ctr, u32 offset)
|
||||
{
|
||||
static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"};
|
||||
static const u8* version_ctrs[] = {
|
||||
(u8*)0x080D7CAC,
|
||||
(u8*)0x080D858C,
|
||||
(u8*)0x080D748C,
|
||||
(u8*)0x080D740C,
|
||||
(u8*)0x080D74CC,
|
||||
(u8*)0x080D794C
|
||||
};
|
||||
static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32);
|
||||
static u8* ctr_start = NULL;
|
||||
|
||||
if (ctr_start == NULL) {
|
||||
for (u32 i = 0; i < version_ctrs_len; i++) {
|
||||
if (*(u32*)version_ctrs[i] == 0x5C980) {
|
||||
ctr_start = (u8*) version_ctrs[i] + 0x30;
|
||||
}
|
||||
}
|
||||
|
||||
// If value not in previous list start memory scanning (test range)
|
||||
if (ctr_start == NULL) {
|
||||
for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) {
|
||||
if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) {
|
||||
ctr_start = c + 0x30;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctr_start == NULL) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// the ctr is stored backwards in memory
|
||||
if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
ctr[i] = *(ctr_start + (0xF - i));
|
||||
} else { // TWL region
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
ctr[i] = *(ctr_start + 0x88 + (0xF - i));
|
||||
}
|
||||
|
||||
// increment counter
|
||||
add_ctr(ctr, offset / 0x10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 CryptBuffer(CryptBufferInfo *info)
|
||||
{
|
||||
u8 ctr[16] __attribute__((aligned(32)));
|
||||
memcpy(ctr, info->ctr, 16);
|
||||
|
||||
u8* buffer = info->buffer;
|
||||
u32 size = info->size;
|
||||
u32 mode = info->mode;
|
||||
|
||||
if (info->setKeyY) {
|
||||
u8 keyY[16] __attribute__((aligned(32)));
|
||||
memcpy(keyY, info->keyY, 16);
|
||||
setup_aeskeyY(info->keyslot, keyY);
|
||||
info->setKeyY = 0;
|
||||
}
|
||||
use_aeskey(info->keyslot);
|
||||
|
||||
for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) {
|
||||
set_ctr(ctr);
|
||||
if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE)
|
||||
memcpy(ctr, buffer, 0x10);
|
||||
aes_decrypt((void*) buffer, (void*) buffer, 1, mode);
|
||||
if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE)
|
||||
memcpy(ctr, buffer, 0x10);
|
||||
else if ((mode & (0x7 << 27)) == AES_CTR_MODE)
|
||||
add_ctr(ctr, 0x1);
|
||||
}
|
||||
|
||||
memcpy(info->ctr, ctr, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
|
||||
{
|
||||
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
|
||||
if(GetNandCtr(info.ctr, offset) != 0)
|
||||
return 1;
|
||||
|
||||
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
|
||||
u32 start_sector = offset / NAND_SECTOR_SIZE;
|
||||
ReadNandSectors(start_sector, n_sectors, buffer);
|
||||
CryptBuffer(&info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
|
||||
{
|
||||
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
|
||||
if(GetNandCtr(info.ctr, offset) != 0)
|
||||
return 1;
|
||||
|
||||
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
|
||||
u32 start_sector = offset / NAND_SECTOR_SIZE;
|
||||
CryptBuffer(&info);
|
||||
WriteNandSectors(start_sector, n_sectors, buffer);
|
||||
|
||||
return 0;
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define BUFFER_ADDRESS ((u8*) 0x21000000) // buffer must not be used for anything else!
|
||||
#define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64)
|
||||
|
||||
#define NAND_SECTOR_SIZE 0x200
|
||||
#define SECTORS_PER_READ (BUFFER_MAX_SIZE / NAND_SECTOR_SIZE)
|
||||
|
||||
@ -12,12 +15,6 @@
|
||||
#define P_FIRM1 (1<<4)
|
||||
#define P_CTRNAND (1<<5)
|
||||
|
||||
// these three are not handled by the feature functions
|
||||
// they have to be handled by the menu system
|
||||
#define N_EMUNAND (1<<29)
|
||||
#define N_FORCENAND (1<<30)
|
||||
#define N_NANDWRITE (1<<31)
|
||||
|
||||
// return values for the CheckEmuNAND() function
|
||||
#define EMUNAND_NOT_READY 0 // must be zero
|
||||
#define EMUNAND_READY 1
|
||||
@ -28,6 +25,16 @@
|
||||
#define EMUNAND_MULTI_OFFSET_O3DS 0x00200000
|
||||
#define EMUNAND_MULTI_OFFSET_N3DS 0x00400000
|
||||
|
||||
typedef struct {
|
||||
u32 keyslot;
|
||||
u32 setKeyY;
|
||||
u8 ctr[16];
|
||||
u8 keyY[16];
|
||||
u32 size;
|
||||
u32 mode;
|
||||
u8* buffer;
|
||||
} __attribute__((packed)) CryptBufferInfo;
|
||||
|
||||
typedef struct {
|
||||
char name[16];
|
||||
u8 magic[8];
|
||||
@ -38,25 +45,16 @@ typedef struct {
|
||||
} __attribute__((packed)) PartitionInfo;
|
||||
|
||||
PartitionInfo* GetPartitionInfo(u32 partition_id);
|
||||
u32 GetNandCtr(u8* ctr, u32 offset);
|
||||
|
||||
u32 OutputFileNameSelector(char* filename, const char* basename, char* extension);
|
||||
u32 InputFileNameSelector(char* filename, const char* basename, char* extension, u8* magic, u32 msize, u32 fsize);
|
||||
u32 CheckEmuNand(void);
|
||||
void SetNand(bool set_emunand, u32 base_sector);
|
||||
|
||||
u32 GetNandCtr(u8* ctr, u32 offset);
|
||||
u32 CryptBuffer(CryptBufferInfo *info);
|
||||
|
||||
int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out);
|
||||
int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in);
|
||||
u32 GetNandSize(void);
|
||||
|
||||
u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition);
|
||||
u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition);
|
||||
u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition);
|
||||
u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition);
|
||||
|
||||
// --> FEATURE FUNCTIONS <--
|
||||
u32 CheckEmuNand(void);
|
||||
u32 SetNand(bool set_emunand, bool force_emunand);
|
||||
|
||||
u32 CtrNandPadgen(u32 param);
|
||||
u32 TwlNandPadgen(u32 param);
|
||||
u32 Firm0Firm1Padgen(u32 param);
|
||||
|
||||
u32 DumpNand(u32 param);
|
||||
u32 RestoreNand(u32 param);
|
||||
u32 DecryptNandPartition(u32 param);
|
||||
u32 InjectNandPartition(u32 param);
|
@ -1,68 +0,0 @@
|
||||
#include "fs.h"
|
||||
#include "draw.h"
|
||||
#include "decryptor/decryptor.h"
|
||||
#include "decryptor/aes.h"
|
||||
|
||||
|
||||
u32 CryptBuffer(CryptBufferInfo *info)
|
||||
{
|
||||
u8 ctr[16] __attribute__((aligned(32)));
|
||||
memcpy(ctr, info->ctr, 16);
|
||||
|
||||
u8* buffer = info->buffer;
|
||||
u32 size = info->size;
|
||||
u32 mode = info->mode;
|
||||
|
||||
if (info->setKeyY) {
|
||||
u8 keyY[16] __attribute__((aligned(32)));
|
||||
memcpy(keyY, info->keyY, 16);
|
||||
setup_aeskeyY(info->keyslot, keyY);
|
||||
info->setKeyY = 0;
|
||||
}
|
||||
use_aeskey(info->keyslot);
|
||||
|
||||
for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) {
|
||||
set_ctr(ctr);
|
||||
if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE)
|
||||
memcpy(ctr, buffer, 0x10);
|
||||
aes_decrypt((void*) buffer, (void*) buffer, 1, mode);
|
||||
if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE)
|
||||
memcpy(ctr, buffer, 0x10);
|
||||
else if ((mode & (0x7 << 27)) == AES_CTR_MODE)
|
||||
add_ctr(ctr, 0x1);
|
||||
}
|
||||
|
||||
memcpy(info->ctr, ctr, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 CreatePad(PadInfo *info)
|
||||
{
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 result = 0;
|
||||
|
||||
if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given
|
||||
return 1;
|
||||
|
||||
CryptBufferInfo decryptInfo = {.keyslot = info->keyslot, .setKeyY = info->setKeyY, .mode = info->mode, .buffer = buffer};
|
||||
memcpy(decryptInfo.ctr, info->ctr, 16);
|
||||
memcpy(decryptInfo.keyY, info->keyY, 16);
|
||||
u32 size_bytes = info->size_mb * 1024*1024;
|
||||
for (u32 i = 0; i < size_bytes; i += BUFFER_MAX_SIZE) {
|
||||
u32 curr_block_size = min(BUFFER_MAX_SIZE, size_bytes - i);
|
||||
decryptInfo.size = curr_block_size;
|
||||
memset(buffer, 0x00, curr_block_size);
|
||||
ShowProgress(i, size_bytes);
|
||||
CryptBuffer(&decryptInfo);
|
||||
if (!DebugFileWrite((void*)buffer, curr_block_size, i)) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ShowProgress(0, 0);
|
||||
FileClose();
|
||||
|
||||
return result;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define BUFFER_ADDRESS ((u8*) 0x21000000)
|
||||
#define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64)
|
||||
|
||||
typedef struct {
|
||||
u32 keyslot;
|
||||
u32 setKeyY;
|
||||
u8 ctr[16];
|
||||
u8 keyY[16];
|
||||
u32 size;
|
||||
u32 mode;
|
||||
u8* buffer;
|
||||
} __attribute__((packed)) CryptBufferInfo;
|
||||
|
||||
typedef struct {
|
||||
u32 keyslot;
|
||||
u32 setKeyY;
|
||||
u8 ctr[16];
|
||||
u8 keyY[16];
|
||||
u32 size_mb;
|
||||
u32 mode;
|
||||
char filename[180];
|
||||
} __attribute__((packed, aligned(16))) PadInfo;
|
||||
|
||||
|
||||
u32 CryptBuffer(CryptBufferInfo *info);
|
||||
u32 CreatePad(PadInfo *info);
|
File diff suppressed because it is too large
Load Diff
@ -1,107 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "decryptor/decryptor.h"
|
||||
|
||||
#define GC_NCCH_PROCESS (1<<0)
|
||||
#define GC_CIA_PROCESS (1<<1)
|
||||
#define GC_CIA_DEEP (1<<2)
|
||||
#define GC_NCCH_ENCRYPT (1<<3)
|
||||
#define GC_CIA_ENCRYPT (1<<4)
|
||||
#define GC_CXI_ONLY (1<<5)
|
||||
|
||||
#define MAX_ENTRIES 1024
|
||||
|
||||
typedef struct {
|
||||
u64 titleId;
|
||||
u8 external_seed[16];
|
||||
u8 reserved[8];
|
||||
} __attribute__((packed)) SeedInfoEntry;
|
||||
|
||||
typedef struct {
|
||||
u32 n_entries;
|
||||
u8 padding[12];
|
||||
SeedInfoEntry entries[MAX_ENTRIES];
|
||||
} __attribute__((packed)) SeedInfo;
|
||||
|
||||
typedef struct {
|
||||
u8 ctr[16];
|
||||
u32 size_mb;
|
||||
char filename[180];
|
||||
} __attribute__((packed)) SdInfoEntry;
|
||||
|
||||
typedef struct {
|
||||
u32 n_entries;
|
||||
SdInfoEntry entries[MAX_ENTRIES];
|
||||
} __attribute__((packed, aligned(16))) SdInfo;
|
||||
|
||||
typedef struct {
|
||||
u8 ctr[16];
|
||||
u8 keyY[16];
|
||||
u32 size_mb;
|
||||
u8 reserved[4];
|
||||
u32 usesSeedCrypto;
|
||||
u32 uses7xCrypto;
|
||||
u64 titleId;
|
||||
char filename[112];
|
||||
} __attribute__((packed)) NcchInfoEntry;
|
||||
|
||||
typedef struct {
|
||||
u32 padding;
|
||||
u32 ncch_info_version;
|
||||
u32 n_entries;
|
||||
u8 reserved[4];
|
||||
NcchInfoEntry entries[MAX_ENTRIES];
|
||||
} __attribute__((packed, aligned(16))) NcchInfo;
|
||||
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u8 magic[0x4];
|
||||
u32 size;
|
||||
u64 partitionId;
|
||||
u16 makercode;
|
||||
u16 version;
|
||||
u8 reserved0[0x4];
|
||||
u64 programId;
|
||||
u8 reserved1[0x10];
|
||||
u8 hash_logo[0x20];
|
||||
char productCode[0x10];
|
||||
u8 hash_exthdr[0x20];
|
||||
u32 size_exthdr;
|
||||
u8 reserved2[0x4];
|
||||
u8 flags[0x8];
|
||||
u32 offset_plain;
|
||||
u32 size_plain;
|
||||
u32 offset_logo;
|
||||
u32 size_logo;
|
||||
u32 offset_exefs;
|
||||
u32 size_exefs;
|
||||
u32 size_exefs_hash;
|
||||
u8 reserved3[0x4];
|
||||
u32 offset_romfs;
|
||||
u32 size_romfs;
|
||||
u32 size_romfs_hash;
|
||||
u8 reserved4[0x4];
|
||||
u8 hash_exefs[0x20];
|
||||
u8 hash_romfs[0x20];
|
||||
} __attribute__((packed, aligned(16))) NcchHeader;
|
||||
|
||||
|
||||
u32 GetSdCtr(u8* ctr, const char* path);
|
||||
u32 GetSd0x34KeyY(u8* movable_keyY, bool from_nand);
|
||||
u32 SdFolderSelector(char* path, u8* keyY);
|
||||
u32 SdInfoGen(SdInfo* info, const char* base_path);
|
||||
u32 CryptSdToSd(const char* filename, u32 offset, u32 size, CryptBufferInfo* info);
|
||||
u32 GetHashFromFile(const char* filename, u32 offset, u32 size, u8* hash);
|
||||
u32 CheckHashFromFile(const char* filename, u32 offset, u32 size, u8* hash);
|
||||
u32 CryptNcch(const char* filename, u32 offset, u32 size, u64 seedId, u8* encrypt_flags);
|
||||
u32 CryptCia(const char* filename, u8* ncch_crypt, bool cia_encrypt, bool cxi_only);
|
||||
|
||||
// --> FEATURE FUNCTIONS <--
|
||||
u32 NcchPadgen(u32 param);
|
||||
u32 SdPadgen(u32 param);
|
||||
u32 SdPadgenDirect(u32 param);
|
||||
u32 UpdateSeedDb(u32 param);
|
||||
u32 CryptGameFiles(u32 param);
|
||||
u32 CryptSdFiles(u32 param);
|
||||
u32 DecryptSdFilesDirect(u32 param);
|
@ -1,682 +0,0 @@
|
||||
#include "fs.h"
|
||||
#include "draw.h"
|
||||
#include "hid.h"
|
||||
#include "platform.h"
|
||||
#include "decryptor/aes.h"
|
||||
#include "decryptor/decryptor.h"
|
||||
#include "decryptor/nand.h"
|
||||
#include "fatfs/sdmmc.h"
|
||||
|
||||
// see: http://3dbrew.org/wiki/Flash_Filesystem
|
||||
static PartitionInfo partitions[] = {
|
||||
{ "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE },
|
||||
{ "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE },
|
||||
{ "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE },
|
||||
{ "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
|
||||
{ "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
|
||||
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS
|
||||
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS
|
||||
};
|
||||
|
||||
static u32 emunand_header = 0;
|
||||
static u32 emunand_offset = 0;
|
||||
|
||||
|
||||
u32 CheckEmuNand(void)
|
||||
{
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 nand_size_sectors = getMMCDevice(0)->total_size;
|
||||
u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS;
|
||||
u32 ret = EMUNAND_NOT_READY;
|
||||
|
||||
// check the MBR for presence of a hidden partition
|
||||
sdmmc_sdcard_readsectors(0, 1, buffer);
|
||||
u32 hidden_sectors = getle32(buffer + 0x1BE + 0x8);
|
||||
|
||||
for (u32 offset_sector = 0; offset_sector + nand_size_sectors < hidden_sectors; offset_sector += multi_sectors) {
|
||||
// check for Gateway type EmuNAND
|
||||
sdmmc_sdcard_readsectors(offset_sector + nand_size_sectors, 1, buffer);
|
||||
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
|
||||
ret |= EMUNAND_GATEWAY << (2 * (offset_sector / multi_sectors));
|
||||
continue;
|
||||
}
|
||||
// check for RedNAND type EmuNAND
|
||||
sdmmc_sdcard_readsectors(offset_sector + 1, 1, buffer);
|
||||
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
|
||||
ret |= EMUNAND_REDNAND << (2 * (offset_sector / multi_sectors));
|
||||
continue;
|
||||
}
|
||||
// EmuNAND ready but not set up
|
||||
ret |= EMUNAND_READY << (2 * (offset_sector / multi_sectors));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 SetNand(bool set_emunand, bool force_emunand)
|
||||
{
|
||||
if (set_emunand) {
|
||||
u32 emunand_state = CheckEmuNand();
|
||||
u32 emunand_count = 0;
|
||||
u32 offset_sector = 0;
|
||||
|
||||
for (emunand_count = 0; (emunand_state >> (2 * emunand_count)) & 0x3; emunand_count++);
|
||||
if (emunand_count > 1) { // multiple EmuNANDs -> use selector
|
||||
u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS;
|
||||
u32 emunand_no = 0;
|
||||
Debug("Use arrow keys and <A> to choose EmuNAND");
|
||||
while (true) {
|
||||
u32 emunandn_state = (emunand_state >> (2 * emunand_no)) & 0x3;
|
||||
offset_sector = emunand_no * multi_sectors;
|
||||
Debug("\rEmuNAND #%u: %s", emunand_no, (emunandn_state == EMUNAND_READY) ? "EmuNAND ready" : (emunandn_state == EMUNAND_GATEWAY) ? "GW EmuNAND" : "RedNAND");
|
||||
// user input routine
|
||||
u32 pad_state = InputWait();
|
||||
if (pad_state & BUTTON_DOWN) {
|
||||
emunand_no = (emunand_no + 1) % emunand_count;
|
||||
} else if (pad_state & BUTTON_UP) {
|
||||
emunand_no = (emunand_no) ? emunand_no - 1 : emunand_count - 1;
|
||||
} else if (pad_state & BUTTON_A) {
|
||||
Debug("EmuNAND #%u", emunand_no);
|
||||
emunand_state = emunandn_state;
|
||||
break;
|
||||
} else if (pad_state & BUTTON_B) {
|
||||
Debug("(cancelled by user)");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((emunand_state == EMUNAND_READY) && force_emunand)
|
||||
emunand_state = EMUNAND_GATEWAY;
|
||||
switch (emunand_state) {
|
||||
case EMUNAND_NOT_READY:
|
||||
Debug("SD is not formatted for EmuNAND");
|
||||
return 1;
|
||||
case EMUNAND_GATEWAY:
|
||||
emunand_header = offset_sector + getMMCDevice(0)->total_size;
|
||||
emunand_offset = offset_sector;
|
||||
Debug("Using EmuNAND @ %06X/%06X", emunand_header, emunand_offset);
|
||||
return 0;
|
||||
case EMUNAND_REDNAND:
|
||||
emunand_header = offset_sector + 1;
|
||||
emunand_offset = offset_sector + 1;
|
||||
Debug("Using RedNAND @ %06X/%06X", emunand_header, emunand_offset);
|
||||
return 0;
|
||||
default:
|
||||
Debug("EmuNAND is not available");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
emunand_header = 0;
|
||||
emunand_offset = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out)
|
||||
{
|
||||
if (emunand_header) {
|
||||
if (sector_no == 0) {
|
||||
int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out);
|
||||
if (errorcode) return errorcode;
|
||||
sector_no = 1;
|
||||
numsectors--;
|
||||
out += 0x200;
|
||||
}
|
||||
return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out);
|
||||
} else return sdmmc_nand_readsectors(sector_no, numsectors, out);
|
||||
}
|
||||
|
||||
static inline int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in)
|
||||
{
|
||||
if (emunand_header) {
|
||||
if (sector_no == 0) {
|
||||
int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in);
|
||||
if (errorcode) return errorcode;
|
||||
sector_no = 1;
|
||||
numsectors--;
|
||||
in += 0x200;
|
||||
}
|
||||
return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in);
|
||||
} else return sdmmc_nand_writesectors(sector_no, numsectors, in);
|
||||
}
|
||||
|
||||
u32 OutputFileNameSelector(char* filename, const char* basename, char* extension) {
|
||||
char bases[3][64] = { 0 };
|
||||
char* dotpos = NULL;
|
||||
|
||||
// build first base name and extension
|
||||
strncpy(bases[0], basename, 63);
|
||||
dotpos = strrchr(bases[0], '.');
|
||||
|
||||
if (dotpos) {
|
||||
*dotpos = '\0';
|
||||
if (!extension)
|
||||
extension = dotpos + 1;
|
||||
}
|
||||
|
||||
// build other two base names
|
||||
snprintf(bases[1], 63, "%s_%s", bases[0], (emunand_header) ? "emu" : "sys");
|
||||
snprintf(bases[2], 63, "%s%s" , (emunand_header) ? "emu" : "sys", bases[0]);
|
||||
|
||||
u32 fn_id = (emunand_header) ? 1 : 0;
|
||||
u32 fn_num = (emunand_header) ? (emunand_offset / ((GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS)) : 0;
|
||||
bool exists = false;
|
||||
char extstr[16] = { 0 };
|
||||
if (extension)
|
||||
snprintf(extstr, 15, ".%s", extension);
|
||||
Debug("Use arrow keys and <A> to choose a name");
|
||||
while (true) {
|
||||
char numstr[2] = { 0 };
|
||||
// build and output file name (plus "(!)" if existing)
|
||||
numstr[0] = (fn_num > 0) ? '0' + fn_num : '\0';
|
||||
snprintf(filename, 63, "%s%s%s", bases[fn_id], numstr, extstr);
|
||||
if ((exists = FileOpen(filename)))
|
||||
FileClose();
|
||||
Debug("\r%s%s", filename, (exists) ? " (!)" : "");
|
||||
// user input routine
|
||||
u32 pad_state = InputWait();
|
||||
if (pad_state & BUTTON_DOWN) { // increment filename id
|
||||
fn_id = (fn_id + 1) % 3;
|
||||
} else if (pad_state & BUTTON_UP) { // decrement filename id
|
||||
fn_id = (fn_id > 0) ? fn_id - 1 : 2;
|
||||
} else if ((pad_state & BUTTON_RIGHT) && (fn_num < 9)) { // increment number
|
||||
fn_num++;
|
||||
} else if ((pad_state & BUTTON_LEFT) && (fn_num > 0)) { // decrement number
|
||||
fn_num--;
|
||||
} else if (pad_state & BUTTON_A) {
|
||||
Debug("%s%s", filename, (exists) ? " (!)" : "");
|
||||
break;
|
||||
} else if (pad_state & BUTTON_B) {
|
||||
Debug("(cancelled by user)");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite confirmation
|
||||
if (exists) {
|
||||
Debug("Press <A> to overwrite existing file");
|
||||
while (true) {
|
||||
u32 pad_state = InputWait();
|
||||
if (pad_state & BUTTON_A) {
|
||||
break;
|
||||
} else if (pad_state & BUTTON_B) {
|
||||
Debug("(cancelled by user)");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 InputFileNameSelector(char* filename, const char* basename, char* extension, u8* magic, u32 msize, u32 fsize) {
|
||||
char** fnptr = (char**) 0x20400000; // allow using 0x8000 byte
|
||||
char* fnlist = (char*) 0x20408000; // allow using 0x80000 byte
|
||||
u32 n_names = 0;
|
||||
|
||||
// get the file list - try work directory first
|
||||
if (!GetFileList(WORK_DIR, fnlist, 0x80000, false, true, false) && !GetFileList("/", fnlist, 0x800000, false, true, false)) {
|
||||
Debug("Failed retrieving the file names list");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get base name, extension
|
||||
char base[64] = { 0 };
|
||||
if (basename != NULL) {
|
||||
// build base name and extension
|
||||
strncpy(base, basename, 63);
|
||||
char* dotpos = strrchr(base, '.');
|
||||
if (dotpos) {
|
||||
*dotpos = '\0';
|
||||
if (!extension)
|
||||
extension = dotpos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// limit magic number size
|
||||
if (msize > 0x200)
|
||||
msize = 0x200;
|
||||
|
||||
// parse the file names list for usable entries
|
||||
for (char* fn = strtok(fnlist, "\n"); fn != NULL; fn = strtok(NULL, "\n")) {
|
||||
u8 data[0x200];
|
||||
char* dotpos = strrchr(fn, '.');
|
||||
if (strrchr(fn, '/'))
|
||||
fn = strrchr(fn, '/') + 1;
|
||||
if (strnlen(fn, 128) > 63)
|
||||
continue; // file name too long
|
||||
if ((basename != NULL) && !strstr(fn, base))
|
||||
continue; // basename check failed
|
||||
if ((extension != NULL) && (dotpos != NULL) && (strncmp(dotpos + 1, extension, strnlen(extension, 16))))
|
||||
continue; // extension check failed
|
||||
else if ((extension == NULL) != (dotpos == NULL))
|
||||
continue; // extension check failed
|
||||
if (!FileOpen(fn))
|
||||
continue; // file can't be opened
|
||||
if (fsize && (FileGetSize() != fsize)) {
|
||||
FileClose();
|
||||
continue; // file size check failed
|
||||
}
|
||||
if (msize) {
|
||||
if (FileRead(data, msize, 0) != msize) {
|
||||
FileClose();
|
||||
continue; // can't be read
|
||||
}
|
||||
if (memcmp(data, magic, msize) != 0) {
|
||||
FileClose();
|
||||
continue; // magic number does not match
|
||||
}
|
||||
}
|
||||
FileClose();
|
||||
// this is a match - keep it
|
||||
fnptr[n_names++] = fn;
|
||||
if (n_names * sizeof(char**) >= 0x8000)
|
||||
return 1;
|
||||
}
|
||||
if (n_names == 0) {
|
||||
Debug("No usable file found");
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 index = 0;
|
||||
Debug("Use arrow keys and <A> to choose a file");
|
||||
while (true) {
|
||||
snprintf(filename, 63, "%s", fnptr[index]);
|
||||
Debug("\r%s", filename);
|
||||
u32 pad_state = InputWait();
|
||||
if (pad_state & BUTTON_DOWN) { // next filename
|
||||
index = (index + 1) % n_names;
|
||||
} else if (pad_state & BUTTON_UP) { // previous filename
|
||||
index = (index > 0) ? index - 1 : n_names - 1;
|
||||
} else if (pad_state & BUTTON_A) {
|
||||
Debug("%s", filename);
|
||||
break;
|
||||
} else if (pad_state & BUTTON_B) {
|
||||
Debug("(cancelled by user)");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PartitionInfo* GetPartitionInfo(u32 partition_id)
|
||||
{
|
||||
u32 partition_num = 0;
|
||||
|
||||
if (partition_id == P_CTRNAND) {
|
||||
partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6;
|
||||
} else {
|
||||
for(; !(partition_id & (1<<partition_num)) && (partition_num < 32); partition_num++);
|
||||
}
|
||||
|
||||
return (partition_num >= 32) ? NULL : &(partitions[partition_num]);
|
||||
}
|
||||
|
||||
u32 CtrNandPadgen(u32 param)
|
||||
{
|
||||
u32 keyslot;
|
||||
u32 nand_size;
|
||||
|
||||
// legacy sizes & offset, to work with 3DSFAT16Tool
|
||||
if (GetUnitPlatform() == PLATFORM_3DS) {
|
||||
keyslot = 0x4;
|
||||
nand_size = 758;
|
||||
} else {
|
||||
keyslot = 0x5;
|
||||
nand_size = 1055;
|
||||
}
|
||||
|
||||
Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size);
|
||||
Debug("Filename: nand.fat16.xorpad");
|
||||
|
||||
PadInfo padInfo = {
|
||||
.keyslot = keyslot,
|
||||
.setKeyY = 0,
|
||||
.size_mb = nand_size,
|
||||
.filename = "nand.fat16.xorpad",
|
||||
.mode = AES_CNT_CTRNAND_MODE
|
||||
};
|
||||
if(GetNandCtr(padInfo.ctr, 0xB930000) != 0)
|
||||
return 1;
|
||||
|
||||
return CreatePad(&padInfo);
|
||||
}
|
||||
|
||||
u32 TwlNandPadgen(u32 param)
|
||||
{
|
||||
u32 size_mb = (partitions[0].size + (1024 * 1024) - 1) / (1024 * 1024);
|
||||
Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb);
|
||||
Debug("Filename: twlnand.fat16.xorpad");
|
||||
|
||||
PadInfo padInfo = {
|
||||
.keyslot = partitions[0].keyslot,
|
||||
.setKeyY = 0,
|
||||
.size_mb = size_mb,
|
||||
.filename = "twlnand.fat16.xorpad",
|
||||
.mode = AES_CNT_TWLNAND_MODE
|
||||
};
|
||||
if(GetNandCtr(padInfo.ctr, partitions[0].offset) != 0)
|
||||
return 1;
|
||||
|
||||
return CreatePad(&padInfo);
|
||||
}
|
||||
|
||||
u32 Firm0Firm1Padgen(u32 param)
|
||||
{
|
||||
u32 size_mb = (partitions[3].size + partitions[4].size + (1024 * 1024) - 1) / (1024 * 1024);
|
||||
Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb);
|
||||
Debug("Filename: firm0firm1.xorpad");
|
||||
|
||||
PadInfo padInfo = {
|
||||
.keyslot = partitions[3].keyslot,
|
||||
.setKeyY = 0,
|
||||
.size_mb = size_mb,
|
||||
.filename = "firm0firm1.xorpad",
|
||||
.mode = AES_CNT_CTRNAND_MODE
|
||||
};
|
||||
if(GetNandCtr(padInfo.ctr, partitions[3].offset) != 0)
|
||||
return 1;
|
||||
|
||||
return CreatePad(&padInfo);
|
||||
}
|
||||
|
||||
u32 GetNandCtr(u8* ctr, u32 offset)
|
||||
{
|
||||
static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"};
|
||||
static const u8* version_ctrs[] = {
|
||||
(u8*)0x080D7CAC,
|
||||
(u8*)0x080D858C,
|
||||
(u8*)0x080D748C,
|
||||
(u8*)0x080D740C,
|
||||
(u8*)0x080D74CC,
|
||||
(u8*)0x080D794C
|
||||
};
|
||||
static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32);
|
||||
static u8* ctr_start = NULL;
|
||||
|
||||
if (ctr_start == NULL) {
|
||||
for (u32 i = 0; i < version_ctrs_len; i++) {
|
||||
if (*(u32*)version_ctrs[i] == 0x5C980) {
|
||||
Debug("System version %s", versions[i]);
|
||||
ctr_start = (u8*) version_ctrs[i] + 0x30;
|
||||
}
|
||||
}
|
||||
|
||||
// If value not in previous list start memory scanning (test range)
|
||||
if (ctr_start == NULL) {
|
||||
for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) {
|
||||
if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) {
|
||||
ctr_start = c + 0x30;
|
||||
Debug("CTR start 0x%08X", ctr_start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctr_start == NULL) {
|
||||
Debug("CTR start not found!");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// the ctr is stored backwards in memory
|
||||
if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
ctr[i] = *(ctr_start + (0xF - i));
|
||||
} else { // TWL region
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
ctr[i] = *(ctr_start + 0x88 + (0xF - i));
|
||||
}
|
||||
|
||||
// increment counter
|
||||
add_ctr(ctr, offset / 0x10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
|
||||
{
|
||||
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
|
||||
if(GetNandCtr(info.ctr, offset) != 0)
|
||||
return 1;
|
||||
|
||||
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
|
||||
u32 start_sector = offset / NAND_SECTOR_SIZE;
|
||||
ReadNandSectors(start_sector, n_sectors, buffer);
|
||||
CryptBuffer(&info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition)
|
||||
{
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 result = 0;
|
||||
|
||||
if (!DebugFileCreate(filename, true))
|
||||
return 1;
|
||||
|
||||
for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) {
|
||||
u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i));
|
||||
ShowProgress(i, size);
|
||||
DecryptNandToMem(buffer, offset + i, read_bytes, partition);
|
||||
if(!DebugFileWrite(buffer, read_bytes, i)) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ShowProgress(0, 0);
|
||||
FileClose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 DumpNand(u32 param)
|
||||
{
|
||||
char filename[64];
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE;
|
||||
u32 result = 0;
|
||||
|
||||
Debug("Dumping %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024));
|
||||
|
||||
if (OutputFileNameSelector(filename, "NAND.bin", NULL) != 0)
|
||||
return 1;
|
||||
if (!DebugFileCreate(filename, true))
|
||||
return 1;
|
||||
|
||||
u32 n_sectors = nand_size / NAND_SECTOR_SIZE;
|
||||
for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) {
|
||||
u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i));
|
||||
ShowProgress(i, n_sectors);
|
||||
ReadNandSectors(i, read_sectors, buffer);
|
||||
if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ShowProgress(0, 0);
|
||||
FileClose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 DecryptNandPartition(u32 param)
|
||||
{
|
||||
PartitionInfo* p_info = NULL;
|
||||
char filename[64];
|
||||
u8 magic[NAND_SECTOR_SIZE];
|
||||
|
||||
for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) {
|
||||
if (param & partition_id) {
|
||||
p_info = GetPartitionInfo(partition_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p_info == NULL) {
|
||||
Debug("No partition to dump");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Debug("Dumping & Decrypting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024));
|
||||
if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0)
|
||||
return 1;
|
||||
if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) {
|
||||
Debug("Decryption error, please contact us");
|
||||
return 1;
|
||||
}
|
||||
if (OutputFileNameSelector(filename, p_info->name, "bin") != 0)
|
||||
return 1;
|
||||
|
||||
return DecryptNandToFile(filename, p_info->offset, p_info->size, p_info);
|
||||
}
|
||||
|
||||
u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
|
||||
{
|
||||
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
|
||||
if(GetNandCtr(info.ctr, offset) != 0)
|
||||
return 1;
|
||||
|
||||
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
|
||||
u32 start_sector = offset / NAND_SECTOR_SIZE;
|
||||
CryptBuffer(&info);
|
||||
WriteNandSectors(start_sector, n_sectors, buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition)
|
||||
{
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 result = 0;
|
||||
|
||||
if (!DebugFileOpen(filename))
|
||||
return 1;
|
||||
|
||||
if (FileGetSize() != size) {
|
||||
Debug("%s has wrong size", filename);
|
||||
FileClose();
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) {
|
||||
u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i));
|
||||
ShowProgress(i, size);
|
||||
if(!DebugFileRead(buffer, read_bytes, i)) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
EncryptMemToNand(buffer, offset + i, read_bytes, partition);
|
||||
}
|
||||
|
||||
ShowProgress(0, 0);
|
||||
FileClose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 RestoreNand(u32 param)
|
||||
{
|
||||
char filename[64];
|
||||
u8* buffer = BUFFER_ADDRESS;
|
||||
u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE;
|
||||
u32 result = 0;
|
||||
u8 magic[4];
|
||||
|
||||
if (!(param & N_NANDWRITE)) // developer screwup protection
|
||||
return 1;
|
||||
|
||||
// User file select
|
||||
if (InputFileNameSelector(filename, "NAND.bin", NULL, NULL, 0, nand_size) != 0)
|
||||
return 1;
|
||||
|
||||
if (!DebugFileOpen(filename))
|
||||
return 1;
|
||||
if (nand_size != FileGetSize()) {
|
||||
FileClose();
|
||||
Debug("NAND backup has the wrong size!");
|
||||
return 1;
|
||||
};
|
||||
if(!DebugFileRead(magic, 4, 0x100)) {
|
||||
FileClose();
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(magic, "NCSD", 4) != 0) {
|
||||
FileClose();
|
||||
Debug("Not a proper NAND backup!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Debug("Restoring %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024));
|
||||
|
||||
u32 n_sectors = nand_size / NAND_SECTOR_SIZE;
|
||||
for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) {
|
||||
u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i));
|
||||
ShowProgress(i, n_sectors);
|
||||
if(!DebugFileRead(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
WriteNandSectors(i, read_sectors, buffer);
|
||||
}
|
||||
|
||||
ShowProgress(0, 0);
|
||||
FileClose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 InjectNandPartition(u32 param)
|
||||
{
|
||||
PartitionInfo* p_info = NULL;
|
||||
char filename[64];
|
||||
u8 magic[NAND_SECTOR_SIZE];
|
||||
|
||||
if (!(param & N_NANDWRITE)) // developer screwup protection
|
||||
return 1;
|
||||
|
||||
for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) {
|
||||
if (param & partition_id) {
|
||||
p_info = GetPartitionInfo(partition_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p_info == NULL) {
|
||||
Debug("No partition to inject to");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Debug("Encrypting & Injecting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024));
|
||||
// User file select
|
||||
if (InputFileNameSelector(filename, p_info->name, "bin",
|
||||
p_info->magic, (p_info->magic[0] != 0xFF) ? 8 : 0, p_info->size) != 0)
|
||||
return 1;
|
||||
|
||||
// Encryption check
|
||||
if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0)
|
||||
return 1;
|
||||
if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) {
|
||||
Debug("Decryption error, please contact us");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// File check
|
||||
if (FileOpen(filename)) {
|
||||
if(!DebugFileRead(magic, 8, 0)) {
|
||||
FileClose();
|
||||
return 1;
|
||||
}
|
||||
if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) {
|
||||
Debug("Bad file content, won't inject");
|
||||
FileClose();
|
||||
return 1;
|
||||
}
|
||||
FileClose();
|
||||
}
|
||||
|
||||
return EncryptFileToNand(filename, p_info->offset, p_info->size, p_info);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#include "sha.h"
|
||||
|
||||
void sha_init(u32 mode)
|
||||
{
|
||||
while(*REG_SHACNT & 1);
|
||||
*REG_SHACNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
||||
}
|
||||
|
||||
void sha_update(const void* src, u32 size)
|
||||
{
|
||||
const u32* src32 = (const u32*)src;
|
||||
|
||||
while(size >= 0x40) {
|
||||
while(*REG_SHACNT & 1);
|
||||
for(u32 i = 0; i < 4; i++) {
|
||||
*REG_SHAINFIFO = *src32++;
|
||||
*REG_SHAINFIFO = *src32++;
|
||||
*REG_SHAINFIFO = *src32++;
|
||||
*REG_SHAINFIFO = *src32++;
|
||||
}
|
||||
size -= 0x40;
|
||||
}
|
||||
while(*REG_SHACNT & 1);
|
||||
memcpy((void*)REG_SHAINFIFO, src32, size);
|
||||
}
|
||||
|
||||
void sha_get(void* res) {
|
||||
*REG_SHACNT = (*REG_SHACNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND;
|
||||
while(*REG_SHACNT & SHA_FINAL_ROUND);
|
||||
while(*REG_SHACNT & 1);
|
||||
memcpy(res, (void*)REG_SHAHASH, (256 / 8));
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define REG_SHACNT ((volatile uint32_t*)0x1000A000)
|
||||
#define REG_SHABLKCNT ((volatile uint32_t*)0x1000A004)
|
||||
#define REG_SHAHASH ((volatile uint32_t*)0x1000A040)
|
||||
#define REG_SHAINFIFO ((volatile uint32_t*)0x1000A080)
|
||||
|
||||
#define SHA_CNT_STATE 0x00000003
|
||||
#define SHA_CNT_OUTPUT_ENDIAN 0x00000008
|
||||
#define SHA_CNT_MODE 0x00000030
|
||||
#define SHA_CNT_ENABLE 0x00010000
|
||||
#define SHA_CNT_ACTIVE 0x00020000
|
||||
|
||||
#define SHA_HASH_READY 0x00000000
|
||||
#define SHA_NORMAL_ROUND 0x00000001
|
||||
#define SHA_FINAL_ROUND 0x00000002
|
||||
|
||||
#define SHA256_MODE 0
|
||||
#define SHA224_MODE 0x00000010
|
||||
#define SHA1_MODE 0x00000020
|
||||
|
||||
|
||||
void sha_init(u32 mode);
|
||||
void sha_update(const void* src, u32 size);
|
||||
void sha_get(void* res);
|
@ -8,8 +8,52 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "platform.h"
|
||||
#include "sdmmc.h"
|
||||
#include "decryptor/nand.h"
|
||||
|
||||
#define TYPE_SDCARD 0
|
||||
#define TYPE_SYSNAND 1
|
||||
#define TYPE_EMUNAND 2
|
||||
|
||||
typedef struct {
|
||||
DWORD offset;
|
||||
DWORD subtype;
|
||||
BYTE type;
|
||||
} FATpartition;
|
||||
|
||||
FATpartition DriveInfo[28] = {
|
||||
{ 0x000000, TYPE_SCARD, 0 }, // 0 - SDCARD
|
||||
{ 0x000000, TYPE_SYSNAND, P_CTRNAND }, // 1 - SYSNAND CTRNAND
|
||||
{ 0x000000, TYPE_SYSNAND, P_TWLN }, // 2 - SYSNAND TWLN
|
||||
{ 0x000000, TYPE_SYSNAND, P_TWLP }, // 3 - SYSNAND TWLP
|
||||
{ 0x000000, TYPE_EMUNAND, P_CTRNAND }, // 4 - EMUNAND0 O3DS CTRNAND
|
||||
{ 0x000000, TYPE_EMUNAND, P_TWLN }, // 5 - EMUNAND0 O3DS TWLN
|
||||
{ 0x000000, TYPE_EMUNAND, P_TWLP }, // 6 - EMUNAND0 O3DS TWLP
|
||||
{ 0x200000, TYPE_EMUNAND, P_CTRNAND }, // 7 - EMUNAND1 O3DS CTRNAND
|
||||
{ 0x200000, TYPE_EMUNAND, P_TWLN }, // 8 - EMUNAND1 O3DS TWLN
|
||||
{ 0x200000, TYPE_EMUNAND, P_TWLP }, // 9 - EMUNAND1 O3DS TWLP
|
||||
{ 0x400000, TYPE_EMUNAND, P_CTRNAND }, // 10 - EMUNAND2 O3DS CTRNAND
|
||||
{ 0x400000, TYPE_EMUNAND, P_TWLN }, // 11 - EMUNAND2 O3DS TWLN
|
||||
{ 0x400000, TYPE_EMUNAND, P_TWLP }, // 12 - EMUNAND2 O3DS TWLP
|
||||
{ 0x600000, TYPE_EMUNAND, P_CTRNAND }, // 13 - EMUNAND3 O3DS CTRNAND
|
||||
{ 0x600000, TYPE_EMUNAND, P_TWLN }, // 14 - EMUNAND3 O3DS TWLN
|
||||
{ 0x600000, TYPE_EMUNAND, P_TWLP }, // 15 - EMUNAND3 O3DS TWLP
|
||||
{ 0x000000, TYPE_EMUNAND, P_CTRNAND }, // 16 - EMUNAND0 N3DS CTRNAND
|
||||
{ 0x000000, TYPE_EMUNAND, P_TWLN }, // 17 - EMUNAND0 N3DS TWLN
|
||||
{ 0x000000, TYPE_EMUNAND, P_TWLP }, // 18 - EMUNAND0 N3DS TWLP
|
||||
{ 0x400000, TYPE_EMUNAND, P_CTRNAND }, // 19 - EMUNAND1 N3DS CTRNAND
|
||||
{ 0x400000, TYPE_EMUNAND, P_TWLN }, // 20 - EMUNAND1 N3DS TWLN
|
||||
{ 0x400000, TYPE_EMUNAND, P_TWLP }, // 21 - EMUNAND1 N3DS TWLP
|
||||
{ 0x800000, TYPE_EMUNAND, P_CTRNAND }, // 22 - EMUNAND2 N3DS CTRNAND
|
||||
{ 0x800000, TYPE_EMUNAND, P_TWLN }, // 23 - EMUNAND2 N3DS TWLN
|
||||
{ 0x800000, TYPE_EMUNAND, P_TWLP }, // 24 - EMUNAND2 N3DS TWLP
|
||||
{ 0xC00000, TYPE_EMUNAND, P_CTRNAND }, // 25 - EMUNAND3 N3DS CTRNAND
|
||||
{ 0xC00000, TYPE_EMUNAND, P_TWLN }, // 26 - EMUNAND3 N3DS TWLN
|
||||
{ 0xC00000, TYPE_EMUNAND, P_TWLP } // 27 - EMUNAND3 N3DS TWLP
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
@ -52,9 +96,18 @@ DRESULT disk_read (
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_readsectors(sector, count, buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (DriveInfo[pdrv].type == TYPE_SCARD) {
|
||||
if (sdmmc_sdcard_readsectors(sector, count, buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
} else {
|
||||
PartitionInfo* partition = GetPartitionInfo(DriveInfo[pdrv].subtype);
|
||||
if (partition == NULL) return RES_PARERR;
|
||||
u32 offset = (sector * 0x200) + partition->offset;
|
||||
SetNand(DriveInfo[pdrv].type == TYPE_EMUNAND, DriveInfo[pdrv].offset);
|
||||
if (DecryptNandToMem(buff, offset, count * 0x200, partition) != 0)
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
@ -74,9 +127,19 @@ DRESULT disk_write (
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (DriveInfo[pdrv].type == TYPE_SCARD) {
|
||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
} else {
|
||||
PartitionInfo* partition = GetPartitionInfo(DriveInfo[pdrv].subtype);
|
||||
if (partition == NULL) return RES_PARERR;
|
||||
u32 offset = (sector * 0x200) + partition->offset;
|
||||
SetNand(DriveInfo[pdrv].type == TYPE_EMUNAND, DriveInfo[pdrv].offset);
|
||||
// if (EncryptMemToNand(buff, offset, count * 0x200, partition) != 0)
|
||||
return RES_PARERR;
|
||||
// NO, not yet!
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
@ -103,11 +166,11 @@ DRESULT disk_ioctl (
|
||||
*((DWORD*) buff) = 0x200;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD*) buff) = getMMCDevice(1)->total_size;
|
||||
*((DWORD*) buff) = getMMCDevice((DriveInfo[pdrv].type == TYPE_SCARD) ? 1 : 0)->total_size;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
*((DWORD*) buff) = 0x2000;
|
||||
return RES_OK;
|
||||
return (DriveInfo[pdrv].type == TYPE_SCARD) ? RES_OK : RES_PARERR;
|
||||
case CTRL_SYNC:
|
||||
// nothing to do here - the disk_write function handles that
|
||||
return RES_OK;
|
||||
|
16
source/fs.h
16
source/fs.h
@ -2,6 +2,22 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef enum {
|
||||
T_NAND_BASE,
|
||||
T_NONFAT_ROOT,
|
||||
T_FAT_ROOT,
|
||||
T_FAT_FILE,
|
||||
T_FAT_DIR
|
||||
} EntryType;
|
||||
|
||||
typedef struct {
|
||||
char* name; // should point to the correct portion of the path
|
||||
char path[256];
|
||||
u32 size;
|
||||
u32 offset;
|
||||
EntryType entryType;
|
||||
} DirEntry;
|
||||
|
||||
bool InitFS();
|
||||
void DeinitFS();
|
||||
|
||||
|
186
source/main.c
186
source/main.c
@ -5,188 +5,6 @@
|
||||
#include "i2c.h"
|
||||
#include "decryptor/game.h"
|
||||
#include "decryptor/nand.h"
|
||||
#include "decryptor/nandfat.h"
|
||||
#include "decryptor/titlekey.h"
|
||||
|
||||
#define SUBMENU_START 5
|
||||
|
||||
|
||||
MenuInfo menu[] =
|
||||
{
|
||||
{
|
||||
"XORpad Generator Options", 7,
|
||||
{
|
||||
{ "NCCH Padgen", &NcchPadgen, 0 },
|
||||
{ "SD Padgen (SDinfo.bin)", &SdPadgen, 0 },
|
||||
{ "SD Padgen (SysNAND dir)", &SdPadgenDirect, 0 },
|
||||
{ "SD Padgen (EmuNAND dir)", &SdPadgenDirect, N_EMUNAND },
|
||||
{ "CTRNAND Padgen", &CtrNandPadgen, 0 },
|
||||
{ "TWLNAND Padgen", &TwlNandPadgen, 0 },
|
||||
{ "FIRM0FIRM1 Padgen", &Firm0Firm1Padgen, 0 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Titlekey Decrypt Options", 3,
|
||||
{
|
||||
{ "Titlekey Decrypt (file)", &DecryptTitlekeysFile, 0 },
|
||||
{ "Titlekey Decrypt (SysNAND)", &DecryptTitlekeysNand, 0 },
|
||||
{ "Titlekey Decrypt (EmuNAND)", &DecryptTitlekeysNand, N_EMUNAND }
|
||||
}
|
||||
},
|
||||
{
|
||||
"SysNAND Options", 8,
|
||||
{
|
||||
{ "NAND Backup", &DumpNand, 0 },
|
||||
{ "NAND Restore", &RestoreNand, N_NANDWRITE },
|
||||
{ "Partition Dump...", NULL, SUBMENU_START + 0 },
|
||||
{ "Partition Inject...", NULL, SUBMENU_START + 2 },
|
||||
{ "File Dump...", NULL, SUBMENU_START + 4 },
|
||||
{ "File Inject...", NULL, SUBMENU_START + 6 },
|
||||
{ "Health&Safety Dump", &DumpHealthAndSafety, 0 },
|
||||
{ "Health&Safety Inject", &InjectHealthAndSafety, N_NANDWRITE }
|
||||
}
|
||||
},
|
||||
{
|
||||
"EmuNAND Options", 9,
|
||||
{
|
||||
{ "EmuNAND Backup", &DumpNand, N_EMUNAND },
|
||||
{ "EmuNAND Restore", &RestoreNand, N_NANDWRITE | N_EMUNAND | N_FORCENAND },
|
||||
{ "Partition Dump...", NULL, SUBMENU_START + 1 },
|
||||
{ "Partition Inject...", NULL, SUBMENU_START + 3 },
|
||||
{ "File Dump...", NULL, SUBMENU_START + 5 },
|
||||
{ "File Inject...", NULL, SUBMENU_START + 7 },
|
||||
{ "Health&Safety Dump", &DumpHealthAndSafety, N_EMUNAND },
|
||||
{ "Health&Safety Inject", &InjectHealthAndSafety, N_NANDWRITE | N_EMUNAND },
|
||||
{ "Update SeedDB", &UpdateSeedDb, N_EMUNAND }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Game Decryptor Options", 10,
|
||||
{
|
||||
{ "NCCH/NCSD Decryptor", &CryptGameFiles, GC_NCCH_PROCESS },
|
||||
{ "NCCH/NCSD Encryptor", &CryptGameFiles, GC_NCCH_PROCESS | GC_NCCH_ENCRYPT },
|
||||
{ "CIA Decryptor (shallow)", &CryptGameFiles, GC_CIA_PROCESS },
|
||||
{ "CIA Decryptor (deep)", &CryptGameFiles, GC_CIA_PROCESS | GC_CIA_DEEP },
|
||||
{ "CIA Decryptor (CXI only)", &CryptGameFiles, GC_CIA_PROCESS | GC_CIA_DEEP | GC_CXI_ONLY },
|
||||
{ "CIA Encryptor (NCCH)", &CryptGameFiles, GC_CIA_PROCESS | GC_NCCH_ENCRYPT },
|
||||
{ "CIA Encryptor (CXI only)", &CryptGameFiles, GC_CIA_PROCESS | GC_NCCH_ENCRYPT | GC_CXI_ONLY },
|
||||
{ "SD Decryptor/Encryptor", &CryptSdFiles, 0 },
|
||||
{ "SD Decryptor (SysNAND dir)", &DecryptSdFilesDirect, 0 },
|
||||
{ "SD Decryptor (EmuNAND dir)", &DecryptSdFilesDirect, N_EMUNAND }
|
||||
}
|
||||
},
|
||||
// everything below is not contained in the main menu
|
||||
{
|
||||
"Partition Dump... (SysNAND)", 6, // ID 0
|
||||
{
|
||||
{ "Dump TWLN Partition", &DecryptNandPartition, P_TWLN },
|
||||
{ "Dump TWLP Partition", &DecryptNandPartition, P_TWLP },
|
||||
{ "Dump AGBSAVE Partition", &DecryptNandPartition, P_AGBSAVE },
|
||||
{ "Dump FIRM0 Partition", &DecryptNandPartition, P_FIRM0 },
|
||||
{ "Dump FIRM1 Partition", &DecryptNandPartition, P_FIRM1 },
|
||||
{ "Dump CTRNAND Partition", &DecryptNandPartition, P_CTRNAND }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Partition Dump...(EmuNAND)", 6, // ID 1
|
||||
{
|
||||
{ "Dump TWLN Partition", &DecryptNandPartition, N_EMUNAND | P_TWLN },
|
||||
{ "Dump TWLP Partition", &DecryptNandPartition, N_EMUNAND | P_TWLP },
|
||||
{ "Dump AGBSAVE Partition", &DecryptNandPartition, N_EMUNAND | P_AGBSAVE },
|
||||
{ "Dump FIRM0 Partition", &DecryptNandPartition, N_EMUNAND | P_FIRM0 },
|
||||
{ "Dump FIRM1 Partition", &DecryptNandPartition, N_EMUNAND | P_FIRM1 },
|
||||
{ "Dump CTRNAND Partition", &DecryptNandPartition, N_EMUNAND | P_CTRNAND }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Partition Inject... (SysNAND)", 6, // ID 2
|
||||
{
|
||||
{ "Inject TWLN Partition", &InjectNandPartition, N_NANDWRITE | P_TWLN },
|
||||
{ "Inject TWLP Partition", &InjectNandPartition, N_NANDWRITE | P_TWLP },
|
||||
{ "Inject AGBSAVE Partition", &InjectNandPartition, N_NANDWRITE | P_AGBSAVE },
|
||||
{ "Inject FIRM0 Partition", &InjectNandPartition, N_NANDWRITE | P_FIRM0 },
|
||||
{ "Inject FIRM1 Partition", &InjectNandPartition, N_NANDWRITE | P_FIRM1 },
|
||||
{ "Inject CTRNAND Partition", &InjectNandPartition, N_NANDWRITE | P_CTRNAND }
|
||||
}
|
||||
},
|
||||
{
|
||||
"Partition Inject... (EmuNAND)", 6, // ID 3
|
||||
{
|
||||
{ "Inject TWLN Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_TWLN },
|
||||
{ "Inject TWLP Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_TWLP },
|
||||
{ "Inject AGBSAVE Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_AGBSAVE },
|
||||
{ "Inject FIRM0 Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_FIRM0 },
|
||||
{ "Inject FIRM1 Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_FIRM1 },
|
||||
{ "Inject CTRNAND Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_CTRNAND }
|
||||
}
|
||||
},
|
||||
{
|
||||
"File Dump... (SysNAND)", 10, // ID 4
|
||||
{
|
||||
{ "Dump ticket.db", &DumpFile, F_TICKET },
|
||||
{ "Dump title.db", &DumpFile, F_TITLE },
|
||||
{ "Dump import.db", &DumpFile, F_IMPORT },
|
||||
{ "Dump certs.db", &DumpFile, F_CERTS },
|
||||
{ "Dump SecureInfo_A", &DumpFile, F_SECUREINFO },
|
||||
{ "Dump LocalFriendCodeSeed_B", &DumpFile, F_LOCALFRIEND },
|
||||
{ "Dump rand_seed", &DumpFile, F_RANDSEED },
|
||||
{ "Dump movable.sed", &DumpFile, F_MOVABLE },
|
||||
{ "Dump nagsave.bin", &DumpFile, F_NAGSAVE },
|
||||
{ "Dump nnidsave.bin", &DumpFile, F_NNIDSAVE }
|
||||
}
|
||||
},
|
||||
{
|
||||
"File Dump... (EmuNAND)", 11, // ID 5
|
||||
{
|
||||
{ "Dump ticket.db", &DumpFile, N_EMUNAND | F_TICKET },
|
||||
{ "Dump title.db", &DumpFile, N_EMUNAND | F_TITLE },
|
||||
{ "Dump import.db", &DumpFile, N_EMUNAND | F_IMPORT },
|
||||
{ "Dump certs.db", &DumpFile, N_EMUNAND | F_CERTS },
|
||||
{ "Dump SecureInfo_A", &DumpFile, N_EMUNAND | F_SECUREINFO },
|
||||
{ "Dump LocalFriendCodeSeed_B", &DumpFile, N_EMUNAND | F_LOCALFRIEND },
|
||||
{ "Dump rand_seed", &DumpFile, N_EMUNAND | F_RANDSEED },
|
||||
{ "Dump movable.sed", &DumpFile, N_EMUNAND | F_MOVABLE },
|
||||
{ "Dump seedsave.bin", &DumpFile, N_EMUNAND | F_SEEDSAVE },
|
||||
{ "Dump nagsave.bin", &DumpFile, N_EMUNAND | F_NAGSAVE },
|
||||
{ "Dump nnidsave.bin", &DumpFile, N_EMUNAND | F_NNIDSAVE }
|
||||
}
|
||||
},
|
||||
{
|
||||
"File Inject... (SysNAND)", 10, // ID 6
|
||||
{
|
||||
{ "Inject ticket.db", &InjectFile, N_NANDWRITE | F_TICKET },
|
||||
{ "Inject title.db", &InjectFile, N_NANDWRITE | F_TITLE },
|
||||
{ "Inject import.db", &InjectFile, N_NANDWRITE | F_IMPORT },
|
||||
{ "Inject certs.db", &InjectFile, N_NANDWRITE | F_CERTS },
|
||||
{ "Inject SecureInfo_A", &InjectFile, N_NANDWRITE | F_SECUREINFO },
|
||||
{ "Inject LocalFriendCodeSeed_B", &InjectFile, N_NANDWRITE | F_LOCALFRIEND },
|
||||
{ "Inject rand_seed", &InjectFile, N_NANDWRITE | F_RANDSEED },
|
||||
{ "Inject movable.sed", &InjectFile, N_NANDWRITE | F_MOVABLE },
|
||||
{ "Inject nagsave.bin", &InjectFile, N_NANDWRITE | F_NAGSAVE },
|
||||
{ "Inject nnidsave.bin", &InjectFile, N_NANDWRITE | F_NNIDSAVE }
|
||||
}
|
||||
},
|
||||
{
|
||||
"File Inject... (EmuNAND)", 11, // ID 7
|
||||
{
|
||||
{ "Inject ticket.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_TICKET },
|
||||
{ "Inject title.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_TITLE },
|
||||
{ "Inject import.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_IMPORT },
|
||||
{ "Inject certs.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_CERTS },
|
||||
{ "Inject SecureInfo_A", &InjectFile, N_NANDWRITE | N_EMUNAND | F_SECUREINFO },
|
||||
{ "Inject LocalFriendCodeSeed_B", &InjectFile, N_NANDWRITE | N_EMUNAND | F_LOCALFRIEND },
|
||||
{ "Inject rand_seed", &InjectFile, N_NANDWRITE | N_EMUNAND | F_RANDSEED },
|
||||
{ "Inject movable.sed", &InjectFile, N_NANDWRITE | N_EMUNAND | F_MOVABLE },
|
||||
{ "Inject seedsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_SEEDSAVE },
|
||||
{ "Inject nagsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_NAGSAVE },
|
||||
{ "Inject nnidsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_NNIDSAVE }
|
||||
}
|
||||
},
|
||||
{
|
||||
NULL, 0, {}, // empty menu to signal end
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Reboot()
|
||||
{
|
||||
@ -207,9 +25,9 @@ int main()
|
||||
ClearScreenFull(true, true);
|
||||
InitFS();
|
||||
|
||||
u32 menu_exit = ProcessMenu(menu, SUBMENU_START);
|
||||
u32 godmode_exit = 0;
|
||||
|
||||
DeinitFS();
|
||||
(menu_exit == MENU_EXIT_REBOOT) ? Reboot() : PowerOff();
|
||||
(godmode_exit == 0) ? Reboot() : PowerOff();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user