mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 21:52:48 +00:00
Handle GBA VC save in virtual NAND drives
This commit is contained in:
parent
9432723791
commit
d0785b12d1
48
source/nand/agbsave.c
Normal file
48
source/nand/agbsave.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "agbsave.h"
|
||||
#include "sha.h"
|
||||
#include "aes.h"
|
||||
|
||||
u32 GetAgbSaveSize(u32 nand_src) {
|
||||
AgbSave* agbsave = (AgbSave*) NAND_BUFFER;
|
||||
if (ReadNandSectors((u8*) agbsave, SECTOR_AGBSAVE, 1, 0x07, nand_src) != 0)
|
||||
return 0;
|
||||
return agbsave->save_size; // it's recommended to also check the CMAC
|
||||
}
|
||||
|
||||
u32 CheckAgbSaveCmac(u32 nand_src) {
|
||||
u8 magic[] = { AGBSAVE_MAGIC };
|
||||
|
||||
AgbSave* agbsave = (AgbSave*) NAND_BUFFER;
|
||||
if ((ReadNandSectors((u8*) agbsave, SECTOR_AGBSAVE, 1, 0x07, nand_src) != 0) ||
|
||||
(memcmp(agbsave->magic, magic, sizeof(magic)) != 0) ||
|
||||
(ReadNandBytes(agbsave->savegame, (SECTOR_AGBSAVE+1) * 0x200, agbsave->save_size, 0x07, nand_src) != 0))
|
||||
return 1;
|
||||
|
||||
u8 cmac[16] __attribute__((aligned(32)));
|
||||
u8 shasum[32];
|
||||
sha_quick(shasum, ((u8*) agbsave) + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE);
|
||||
use_aeskey(0x24);
|
||||
aes_cmac(shasum, cmac, 2);
|
||||
|
||||
return (memcmp(cmac, agbsave->cmac, 16) == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
u32 FixAgbSaveCmac(u32 nand_dst) {
|
||||
AgbSave* agbsave = (AgbSave*) NAND_BUFFER;
|
||||
if ((ReadNandSectors((u8*) agbsave, SECTOR_AGBSAVE, 1, 0x07, nand_dst) != 0) ||
|
||||
(ReadNandBytes(agbsave->savegame, (SECTOR_AGBSAVE+1) * 0x200, agbsave->save_size, 0x07, nand_dst) != 0))
|
||||
return 1;
|
||||
|
||||
u8 cmac[16] __attribute__((aligned(32)));
|
||||
u8 shasum[32];
|
||||
sha_quick(shasum, ((u8*) agbsave) + 0x30, (0x200 - 0x30) + agbsave->save_size, SHA256_MODE);
|
||||
use_aeskey(0x24);
|
||||
aes_cmac(shasum, cmac, 2);
|
||||
memcpy(agbsave->cmac, cmac, 16);
|
||||
|
||||
// set CFG_BOOTENV = 0x7 so the save is taken over
|
||||
// https://www.3dbrew.org/wiki/CONFIG_Registers#CFG_BOOTENV
|
||||
*(u32*) 0x10010000 = 0x7;
|
||||
|
||||
return (WriteNandSectors((u8*) agbsave, SECTOR_AGBSAVE, 1, 0x07, nand_dst) != 0) ? 0 : 1;
|
||||
}
|
29
source/nand/agbsave.h
Normal file
29
source/nand/agbsave.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "nand.h"
|
||||
|
||||
#define AGBSAVE_MAGIC '.', 'S', 'A', 'V'
|
||||
|
||||
// see: http://3dbrew.org/wiki/3DS_Virtual_Console#NAND_Savegame
|
||||
typedef struct {
|
||||
u8 magic[4]; // ".SAV"
|
||||
u8 reserved0[0xC]; // always 0xFF
|
||||
u8 cmac[0x10];
|
||||
u8 reserved1[0x10]; // always 0xFF
|
||||
u32 unknown0; // always 0x01
|
||||
u32 times_saved;
|
||||
u64 title_id;
|
||||
u8 sd_cid[0x10];
|
||||
u32 save_start; // always 0x200
|
||||
u32 save_size;
|
||||
u8 reserved2[0x8]; // always 0xFF
|
||||
u32 unknown1; // has to do with ARM7?
|
||||
u32 unknown2; // has to do with ARM7?
|
||||
u8 reserved3[0x198]; // always 0xFF
|
||||
u8 savegame[(SIZE_AGBSAVE-1)*0x200];
|
||||
} __attribute__((packed)) AgbSave;
|
||||
|
||||
u32 GetAgbSaveSize(u32 nand_src);
|
||||
u32 CheckAgbSaveCmac(u32 nand_src);
|
||||
u32 FixAgbSaveCmac(u32 nand_dst);
|
@ -11,12 +11,16 @@
|
||||
#define NAND_MIN_SECTORS ((GetUnitPlatform() == PLATFORM_N3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS)
|
||||
|
||||
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file
|
||||
static u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
|
||||
static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
|
||||
0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86,
|
||||
0x62, 0x22, 0x5C, 0xFD, 0x6F, 0xAE, 0x9B, 0x0A, 0x85, 0xA5, 0xCE, 0x21, 0xAA, 0xB6, 0xC8, 0x4D
|
||||
};
|
||||
static const u8 slot0x24KeyY_sha256[0x20] = { // hash for slot0x24KeyY (16 byte)
|
||||
0x5F, 0x04, 0x01, 0x22, 0x95, 0xB2, 0x23, 0x70, 0x12, 0x40, 0x53, 0x30, 0xC0, 0xA7, 0xBF, 0x7C,
|
||||
0xD4, 0x40, 0x92, 0x25, 0xD1, 0x9D, 0xA2, 0xDE, 0xCD, 0xC7, 0x12, 0x97, 0x08, 0x46, 0x54, 0xB7
|
||||
};
|
||||
|
||||
static u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
|
||||
static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
|
||||
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
|
||||
@ -25,7 +29,7 @@ static u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
|
||||
static const u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
|
||||
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
|
||||
@ -34,7 +38,7 @@ static u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND header (@0x1BE)
|
||||
static const u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND header (@0x1BE)
|
||||
0x00, 0x04, 0x18, 0x00, 0x06, 0x01, 0xA0, 0x3F, 0x97, 0x00, 0x00, 0x00, 0xA9, 0x7D, 0x04, 0x00,
|
||||
0x00, 0x04, 0x8E, 0x40, 0x06, 0x01, 0xA0, 0xC3, 0x8D, 0x80, 0x04, 0x00, 0xB3, 0x05, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
@ -48,13 +52,50 @@ static u8 OtpSha256[32] = { 0 };
|
||||
|
||||
static u32 emunand_base_sector = 0x000000;
|
||||
|
||||
|
||||
u32 LoadKeyYFromP9(u8* key, const u8* keyhash, u32 offset, u32 keyslot)
|
||||
{
|
||||
static u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81
|
||||
u8 ctr0x15[16] __attribute__((aligned(32)));
|
||||
u8 keyY0x15[16] __attribute__((aligned(32)));
|
||||
u8 keyY[16] __attribute__((aligned(32)));
|
||||
u8 header[0x200];
|
||||
|
||||
// check arm9loaderhax
|
||||
if (!CheckA9lh() || (offset < (offsetA9l + 0x0800))) return 1;
|
||||
|
||||
// section 2 (arm9loader) header of FIRM
|
||||
// this is @0x066A00 in FIRM90 & FIRM81
|
||||
ReadNandBytes(header, (SECTOR_FIRM0 * 0x200) + offsetA9l, 0x200, 0x06, NAND_SYSNAND);
|
||||
memcpy(keyY0x15, header + 0x10, 0x10); // 0x15 keyY
|
||||
memcpy(ctr0x15, header + 0x20, 0x10); // 0x15 counter
|
||||
|
||||
// read and decrypt the encrypted keyY
|
||||
ReadNandBytes(keyY, (SECTOR_FIRM0 * 0x200) + offset, 0x10, 0x06, NAND_SYSNAND);
|
||||
setup_aeskeyY(0x15, keyY0x15);
|
||||
use_aeskey(0x15);
|
||||
ctr_decrypt_byte(keyY, keyY, 0x10, offset - (offsetA9l + 0x800), AES_CNT_CTRNAND_MODE, ctr0x15);
|
||||
if (key) memcpy(key, keyY, 0x10);
|
||||
|
||||
// check the key
|
||||
u8 shasum[0x32];
|
||||
sha_quick(shasum, keyY, 16, SHA256_MODE);
|
||||
if (memcmp(shasum, keyhash, 32) == 0) {
|
||||
setup_aeskeyY(keyslot, keyY);
|
||||
use_aeskey(keyslot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool InitNandCrypto(void)
|
||||
{
|
||||
// part #0: KeyX / KeyY for secret sector 0x96
|
||||
// on a9lh this MUST be run before accessing the SHA register in any other way
|
||||
if (CheckA9lh()) { // for a9lh
|
||||
// store the current SHA256 from register
|
||||
memcpy(OtpSha256, (void*)REG_SHAHASH, 32);
|
||||
memcpy(OtpSha256, (void*) REG_SHAHASH, 32);
|
||||
} else {
|
||||
const char* base[] = { INPUT_PATHS };
|
||||
char path[64];
|
||||
@ -105,55 +146,21 @@ bool InitNandCrypto(void)
|
||||
use_aeskey(0x03);
|
||||
}
|
||||
|
||||
// part #3: CTRNAND N3DS KEY
|
||||
// part #3: CTRNAND N3DS KEY / AGBSAVE CMAC KEY
|
||||
// thanks AuroraWright and Gelex for advice on this
|
||||
// see: https://github.com/AuroraWright/Luma3DS/blob/master/source/crypto.c#L347
|
||||
if (CheckA9lh()) { // only for a9lh
|
||||
u8 ctr[16] __attribute__((aligned(32)));
|
||||
u8 keyY[16] __attribute__((aligned(32)));
|
||||
u8 header[0x200];
|
||||
|
||||
// section 2 header of FIRM0
|
||||
// this is @0x066A00 in FIRM90 & FIRM81
|
||||
static u32 offsetSection2 = 0x066A00;
|
||||
ReadNandSectors(header, 0x58980 + (offsetSection2 / 0x200), 1, 0x06, NAND_SYSNAND);
|
||||
memcpy(keyY, header + 0x10, 0x10); // 0x15 keyY
|
||||
|
||||
// try FIRM90 & FIRM81 offsets, search for the key
|
||||
for (u32 fver = 0; fver < 2; fver++) {
|
||||
static u32 offset0x05KeyY[2] = { 0x0EB014, 0x0EB24C };
|
||||
u32 offset = offset0x05KeyY[fver];
|
||||
u8 sector[0x200];
|
||||
|
||||
// sector containing the slot0x05 keyY
|
||||
// key is encrypted @0x0EB014 in the FIRM90
|
||||
// key is encrypted @0x0EB24C in the FIRM81
|
||||
ReadNandSectors(sector, 0x58980 + (offset / 0x200), 1, 0x06, NAND_SYSNAND);
|
||||
|
||||
// decrypt the sector, get the key
|
||||
memcpy(ctr, header + 0x20, 0x10); // 0x15 counter
|
||||
add_ctr(ctr, (offset - (offset % 0x200) - (offsetSection2 + 0x800)) / 16);
|
||||
for (u32 i = 0x0; i < 0x200; i += 0x10) {
|
||||
setup_aeskeyY(0x15, keyY);
|
||||
use_aeskey(0x15);
|
||||
set_ctr(ctr);
|
||||
aes_decrypt(sector + i, sector + i, 1, AES_CNT_CTRNAND_MODE);
|
||||
add_ctr(ctr, 0x1);
|
||||
}
|
||||
memcpy(slot0x05KeyY, sector + (offset % 0x200), 16);
|
||||
|
||||
// check the key
|
||||
sha_quick(shasum, slot0x05KeyY, 16, SHA256_MODE);
|
||||
if (memcmp(shasum, slot0x05KeyY_sha256, 32) == 0) {
|
||||
setup_aeskeyY(0x05, slot0x05KeyY);
|
||||
use_aeskey(0x05);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((memcmp(shasum, slot0x05KeyY_sha256, 32) != 0) && // last resort
|
||||
(LoadKeyFromFile(slot0x05KeyY, 0x05, 'Y', NULL) != 0)) {};
|
||||
}
|
||||
|
||||
// keyY 0x05 is encrypted @0x0EB014 in the FIRM90
|
||||
// keyY 0x05 is encrypted @0x0EB24C in the FIRM81
|
||||
if ((LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB014, 0x05) != 0) &&
|
||||
(LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB24C, 0x05) != 0))
|
||||
LoadKeyFromFile(slot0x05KeyY, 0x05, 'Y', NULL);
|
||||
|
||||
// keyY 0x24 is encrypted @0x0E62DC in the FIRM90
|
||||
// keyY 0x24 is encrypted @0x0E6514 in the FIRM81
|
||||
if ((LoadKeyYFromP9(NULL, slot0x24KeyY_sha256, 0x0E62DC, 0x24) != 0) &&
|
||||
(LoadKeyYFromP9(NULL, slot0x24KeyY_sha256, 0x0E6514, 0x24) != 0))
|
||||
LoadKeyFromFile(NULL, 0x24, 'Y', NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -191,13 +198,13 @@ bool CheckA9lh(void)
|
||||
|
||||
void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot)
|
||||
{
|
||||
u32 mode = (sector >= (0x0B100000 / 0x200)) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE;
|
||||
u32 mode = (sector >= SECTOR_TWL + SIZE_TWL) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE;
|
||||
u8 ctr[16] __attribute__((aligned(32)));
|
||||
u32 blocks = count * (0x200 / 0x10);
|
||||
|
||||
// copy NAND CTR and increment it
|
||||
memcpy(ctr, (sector >= (0x0B100000 / 0x200)) ? CtrNandCtr : TwlNandCtr, 16);
|
||||
add_ctr(ctr, sector * (0x200/0x10));
|
||||
memcpy(ctr, (sector >= SECTOR_TWL + SIZE_TWL) ? CtrNandCtr : TwlNandCtr, 16);
|
||||
add_ctr(ctr, sector * (0x200 / 0x10));
|
||||
|
||||
// decrypt the data
|
||||
use_aeskey(keyslot);
|
||||
@ -214,8 +221,7 @@ void CryptSector0x96(u8* buffer, bool encrypt)
|
||||
|
||||
// decrypt the sector
|
||||
use_aeskey(0x11);
|
||||
for (u32 b = 0x0; b < 0x200; b += 0x10, buffer += 0x10)
|
||||
aes_decrypt((void*) buffer, (void*) buffer, 1, mode);
|
||||
ecb_decrypt((void*) buffer, (void*) buffer, 0x200 / AES_BLOCK_SIZE, mode);
|
||||
}
|
||||
|
||||
int ReadNandBytes(u8* buffer, u32 offset, u32 count, u32 keyslot, u32 nand_src)
|
||||
|
@ -10,9 +10,30 @@
|
||||
#define NAND_TYPE_N3DS (1<<5)
|
||||
#define NAND_TYPE_NO3DS (1<<6)
|
||||
|
||||
// minimum size of NAND memory
|
||||
#define NAND_MIN_SECTORS_O3DS 0x1D7800
|
||||
#define NAND_MIN_SECTORS_N3DS 0x26C000
|
||||
|
||||
// start sectors of partitions
|
||||
#define SECTOR_TWL 0x000000
|
||||
#define SECTOR_SECRET 0x000096
|
||||
#define SECTOR_TWLN 0x000097
|
||||
#define SECTOR_TWLP 0x04808D
|
||||
#define SECTOR_AGBSAVE 0x058800
|
||||
#define SECTOR_FIRM0 0x058980
|
||||
#define SECTOR_FIRM1 0x05A980
|
||||
#define SECTOR_CTR 0x05C980
|
||||
|
||||
// sizes of partitions (in sectors)
|
||||
#define SIZE_TWL 0x058800
|
||||
#define SIZE_TWLN 0x047DA9
|
||||
#define SIZE_TWLP 0x0105B3
|
||||
#define SIZE_AGBSAVE 0x000180
|
||||
#define SIZE_FIRM0 0x002000
|
||||
#define SIZE_FIRM1 0x002000
|
||||
#define SIZE_CTR_O3DS 0x17AE80
|
||||
#define SIZE_CTR_N3DS 0x20F680
|
||||
|
||||
bool InitNandCrypto(void);
|
||||
bool CheckSlot0x05Crypto(void);
|
||||
bool CheckSector0x96Crypto(void);
|
||||
|
@ -20,8 +20,8 @@ u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) {
|
||||
}
|
||||
|
||||
u32 ValidateNandDump(const char* path) {
|
||||
const u32 mbr_sectors[] = { TWL_OFFSET, CTR_OFFSET };
|
||||
const u32 firm_sectors[] = { FIRM_OFFSETS };
|
||||
const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR };
|
||||
const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
|
||||
u8 buffer[0x200];
|
||||
FirmHeader firm;
|
||||
MbrHeader mbr;
|
||||
|
@ -2,11 +2,8 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define TWL_OFFSET 0x000000
|
||||
#define CTR_OFFSET 0x05C980
|
||||
#define FIRM_OFFSETS 0x058980, 0x05A980
|
||||
|
||||
#define SAFE_SECTORS 0x000001, 0x000096, 0x000097, 0x058980, 0x05C980, 0x000000 // last one is a placeholder
|
||||
#define SAFE_SECTORS 0x000000 + 0x1, SECTOR_SECRET, SECTOR_SECRET + 0x1, \
|
||||
SECTOR_FIRM0, SECTOR_CTR, 0x000000 // last one is a placeholder
|
||||
|
||||
u32 ValidateNandDump(const char* path);
|
||||
u32 SafeRestoreNandDump(const char* path);
|
||||
|
@ -1,15 +1,18 @@
|
||||
#include "vnand.h"
|
||||
#include "nand.h"
|
||||
#include "agbsave.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define VFLAG_ON_O3DS NAND_TYPE_O3DS
|
||||
#define VFLAG_ON_N3DS NAND_TYPE_N3DS
|
||||
#define VFLAG_ON_NO3DS NAND_TYPE_NO3DS
|
||||
#define VFLAG_ON_NAND (VFLAG_ON_O3DS | VFLAG_ON_N3DS | VFLAG_ON_NO3DS)
|
||||
#define VFLAG_GBA_VC (1<<29)
|
||||
#define VFLAG_NEEDS_OTP (1<<30)
|
||||
#define VFLAG_NAND_SIZE (1<<31)
|
||||
|
||||
// see: http://3dbrew.org/wiki/Flash_Filesystem#NAND_structure
|
||||
// too much hardcoding, but more readable this way
|
||||
static const VirtualFile vNandFileTemplates[] = {
|
||||
{ "twln.bin" , 0x00012E00, 0x08FB5200, 0x03, VFLAG_ON_NAND },
|
||||
{ "twlp.bin" , 0x09011A00, 0x020B6600, 0x03, VFLAG_ON_NAND },
|
||||
@ -28,7 +31,8 @@ static const VirtualFile vNandFileTemplates[] = {
|
||||
{ "nand_minsize.bin" , 0x00000000, 0x4D800000, 0xFF, VFLAG_ON_N3DS | VFLAG_ON_NO3DS | VFLAG_A9LH_AREA },
|
||||
{ "nand_hdr.bin" , 0x00000000, 0x00000200, 0xFF, VFLAG_ON_NAND | VFLAG_A9LH_AREA },
|
||||
{ "twlmbr.bin" , 0x000001BE, 0x00000042, 0x03, VFLAG_ON_NAND | VFLAG_A9LH_AREA },
|
||||
{ "unused.bin" , 0x00000200, 0x00012A00, 0xFF, VFLAG_ON_NAND }
|
||||
{ "unused.bin" , 0x00000200, 0x00012A00, 0xFF, VFLAG_ON_NAND },
|
||||
{ "gbavc.sav" , 0x0B100200, 0x00000000, 0x07, VFLAG_ON_NAND | VFLAG_GBA_VC },
|
||||
};
|
||||
|
||||
bool CheckVNandDrive(u32 nand_src) {
|
||||
@ -51,7 +55,8 @@ bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir
|
||||
// XORpad drive handling
|
||||
if (nand_src == VRT_XORPAD) {
|
||||
snprintf(vfile->name, 32, "%s.xorpad", templates[vdir->index].name);
|
||||
if ((vfile->keyslot == 0x11) || (vfile->keyslot >= 0x40)) continue;
|
||||
if ((vfile->keyslot == 0x11) || (vfile->keyslot >= 0x40) || (vfile->flags & VFLAG_GBA_VC))
|
||||
continue;
|
||||
}
|
||||
|
||||
// process / check special flags
|
||||
@ -68,6 +73,10 @@ bool ReadVNandDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir
|
||||
continue; // EmuNAND/ImgNAND is too small
|
||||
vfile->size = GetNandSizeSectors(NAND_SYSNAND) * 0x200;
|
||||
}
|
||||
if (vfile->flags & VFLAG_GBA_VC) {
|
||||
if (CheckAgbSaveCmac(nand_src) != 0) continue;
|
||||
vfile->size = GetAgbSaveSize(nand_src);
|
||||
}
|
||||
|
||||
// found if arriving here
|
||||
vfile->flags |= nand_src;
|
||||
@ -84,5 +93,7 @@ int ReadVNandFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) {
|
||||
|
||||
int WriteVNandFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count) {
|
||||
u32 nand_dst = vfile->flags & (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD);
|
||||
return WriteNandBytes(buffer, vfile->offset + offset, count, vfile->keyslot, nand_dst);
|
||||
int res = WriteNandBytes(buffer, vfile->offset + offset, count, vfile->keyslot, nand_dst);
|
||||
if ((res == 0) && (vfile->flags & VFLAG_GBA_VC)) res = FixAgbSaveCmac(nand_dst);
|
||||
return res;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user