mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Enabled embedded backups for NAND images
This commit is contained in:
parent
1764a1b271
commit
89b987b1c0
@ -38,7 +38,7 @@
|
||||
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
||||
|
||||
// GodMode9 version
|
||||
#define VERSION "0.9.9.1"
|
||||
#define VERSION "0.9.9.2"
|
||||
|
||||
// input / output paths
|
||||
#define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9"
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD)))
|
||||
#define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))
|
||||
#define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND))
|
||||
#define FTYPE_EBACKUP(tp) (tp&(IMG_NAND))
|
||||
#define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO))
|
||||
#define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH))
|
||||
|
||||
|
@ -616,10 +616,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
bool buildable_legit = (FTYPE_BUILDABLE_L(filetype));
|
||||
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
|
||||
bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND));
|
||||
bool ebackupable = (FTYPE_EBACKUP(filetype));
|
||||
bool xorpadable = (FTYPE_XORPAD(filetype));
|
||||
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
|
||||
bool special_opt = mountable || verificable || decryptable || encryptable ||
|
||||
buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable;
|
||||
buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable || ebackupable;
|
||||
|
||||
char pathstr[48];
|
||||
TruncateString(pathstr, curr_entry->path, 32, 8);
|
||||
@ -770,6 +771,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
n_opt = 0;
|
||||
int mount = (mountable) ? ++n_opt : -1;
|
||||
int restore = (restorable) ? ++n_opt : -1;
|
||||
int ebackup = (ebackupable) ? ++n_opt : -1;
|
||||
int decrypt = (decryptable) ? ++n_opt : -1;
|
||||
int encrypt = (encryptable) ? ++n_opt : -1;
|
||||
int build = (buildable) ? ++n_opt : -1;
|
||||
@ -781,6 +783,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
int launch = (launchable) ? ++n_opt : -1;
|
||||
if (mount > 0) optionstr[mount-1] = "Mount image to drive";
|
||||
if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)";
|
||||
if (ebackup > 0) optionstr[ebackup-1] = "Update embedded backup";
|
||||
if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")";
|
||||
if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")";
|
||||
if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
|
||||
@ -987,9 +990,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
ShowPrompt(false, "%s\nH&S inject %s", pathstr,
|
||||
(InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed");
|
||||
}
|
||||
return 0;
|
||||
} else if (user_select == restore) { // -> restore SysNAND (A9LH preserving)
|
||||
ShowPrompt(false, "%s\nNAND restore %s", pathstr,
|
||||
(SafeRestoreNandDump(curr_entry->path) == 0) ? "success" : "failed");
|
||||
return 0;
|
||||
} else if ((user_select == xorpad) || (user_select == xorpad_inplace)) {
|
||||
bool inplace = (user_select == xorpad_inplace);
|
||||
bool success = (BuildNcchInfoXorpads((inplace) ? current_path : OUTPUT_PATH, curr_entry->path) == 0);
|
||||
@ -1005,6 +1010,14 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
*scroll = 0;
|
||||
*cursor = 1;
|
||||
}
|
||||
return 0;
|
||||
} else if (user_select == ebackup) {
|
||||
ShowString("%s\nUpdating embedded backup...", pathstr);
|
||||
bool required = (CheckEmbeddedBackup(curr_entry->path) != 0);
|
||||
bool success = (required && (EmbedEssentialBackup(curr_entry->path) == 0));
|
||||
ShowPrompt(false, "%s\nBackup update: %s", pathstr, (!required) ? "not required" :
|
||||
(success) ? "completed" : "failed!");
|
||||
return 0;
|
||||
} else if ((user_select == launch)) {
|
||||
size_t payload_size = FileGetSize(curr_entry->path);
|
||||
if (ShowUnlockSequence(3, "%s (%dkB)\nLaunch as arm9 payload?", pathstr, payload_size / 1024)) {
|
||||
@ -1013,6 +1026,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
while(1);
|
||||
} // failed load is basically impossible here
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return FileHandlerMenu(current_path, cursor, scroll, current_dir, clipboard);
|
||||
|
@ -2,11 +2,26 @@
|
||||
#include "nand.h"
|
||||
#include "firm.h"
|
||||
#include "fatmbr.h"
|
||||
#include "sysfiles.h" // for essential backup struct
|
||||
#include "exefs.h" // for essential backup struct
|
||||
#include "image.h"
|
||||
#include "fsinit.h"
|
||||
#include "fsperm.h"
|
||||
#include "sha.h"
|
||||
#include "ui.h"
|
||||
#include "vff.h"
|
||||
|
||||
typedef struct {
|
||||
ExeFsHeader header;
|
||||
u8 nand_hdr[0x200];
|
||||
SecureInfo secinfo;
|
||||
u8 padding_secinfo[0x200 - sizeof(SecureInfo)];
|
||||
MovableSed movable;
|
||||
u8 padding_movable[0x200 - sizeof(MovableSed)];
|
||||
LocalFriendCodeSeed frndseed;
|
||||
u8 padding_frndseed[0x200 - sizeof(LocalFriendCodeSeed)];
|
||||
} __attribute__((packed)) EssentialBackup;
|
||||
|
||||
u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) {
|
||||
u32 offset = sector * 0x200;
|
||||
u32 size = count * 0x200;
|
||||
@ -19,6 +34,79 @@ u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
|
||||
// prepare essential backup struct
|
||||
const ExeFsFileHeader filelist[] = {
|
||||
{ "nand_hdr", 0x000, 0x200 },
|
||||
{ "secinfo" , 0x200, 0x111 },
|
||||
{ "movable" , 0x400, 0x140 },
|
||||
{ "frndseed", 0x600, 0x110 }
|
||||
};
|
||||
memset(essential, 0, sizeof(EssentialBackup));
|
||||
memcpy(essential, filelist, sizeof(filelist));
|
||||
|
||||
// backup current mount path, mount new path
|
||||
char path_store[256] = { 0 };
|
||||
char* path_bak = NULL;
|
||||
strncpy(path_store, GetMountPath(), 256);
|
||||
if (*path_store) path_bak = path_store;
|
||||
if (!InitImgFS(path)) {
|
||||
InitImgFS(path_bak);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// read four files
|
||||
ExeFsFileHeader* files = essential->header.files;
|
||||
if ((fvx_qread("I:/nand_hdr.bin", &(essential->nand_hdr), 0, 0x200, (UINT*) &(files[0].size)) != FR_OK) ||
|
||||
((fvx_qread("7:/rw/sys/SecureInfo_A", &(essential->secinfo), 0, 0x200, (UINT*) &(files[1].size)) != FR_OK) &&
|
||||
(fvx_qread("7:/rw/sys/SecureInfo_B", &(essential->secinfo), 0, 0x200, (UINT*) &(files[1].size)) != FR_OK)) ||
|
||||
(fvx_qread("7:/private/movable.sed", &(essential->movable), 0, 0x200, (UINT*) &(files[2].size)) != FR_OK) ||
|
||||
((fvx_qread("7:/rw/sys/LocalFriendCodeSeed_B", &(essential->frndseed), 0, 0x200, (UINT*) &(files[3].size)) != FR_OK) &&
|
||||
(fvx_qread("7:/rw/sys/LocalFriendCodeSeed_A", &(essential->frndseed), 0, 0x200, (UINT*) &(files[3].size)) != FR_OK))) {
|
||||
InitImgFS(path_bak);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// mount original file
|
||||
InitImgFS(path_bak);
|
||||
|
||||
// check sizes
|
||||
if ((files[0].size != 0x200) || (files[1].size != 0x111) ||
|
||||
((files[2].size != 0x120) && (files[2].size != 0x140)) || (files[3].size != 0x110))
|
||||
return 1;
|
||||
|
||||
// calculate hashes
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
sha_quick(essential->header.hashes[9-i],
|
||||
((u8*) essential) + files[i].offset + sizeof(ExeFsHeader),
|
||||
files[i].size, SHA256_MODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 CheckEmbeddedBackup(const char* path) {
|
||||
EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER;
|
||||
EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup));
|
||||
UINT btr;
|
||||
if ((BuildEssentialBackup(path, essential) != 0) ||
|
||||
(fvx_qread(path, embedded, 0x200, sizeof(EssentialBackup), &btr) != FR_OK) ||
|
||||
(memcmp(embedded, essential, sizeof(EssentialBackup)) != 0))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 EmbedEssentialBackup(const char* path) {
|
||||
EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER;
|
||||
UINT btw;
|
||||
// leaving out the write permissions check here, it's okay
|
||||
if ((BuildEssentialBackup(path, essential) != 0) ||
|
||||
(CheckNandHeader(essential->nand_hdr) == 0) || // 0 -> header not recognized
|
||||
(fvx_qwrite(path, essential, 0x200, sizeof(EssentialBackup), &btw) != FR_OK) ||
|
||||
(btw != sizeof(EssentialBackup)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ValidateNandDump(const char* path) {
|
||||
const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR };
|
||||
const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
|
||||
@ -114,10 +202,9 @@ u32 SafeRestoreNandDump(const char* path) {
|
||||
u32 safe_sectors[] = { SAFE_SECTORS };
|
||||
FIL file;
|
||||
|
||||
/* if (ValidateNandDump(path) != 0) { // NAND dump validation
|
||||
ShowPrompt(false, "NAND dump corrupt or not from console.\nYou can still try mount and copy.");
|
||||
if ((ValidateNandDump(path) != 0) && // NAND dump validation
|
||||
!ShowPrompt(true, "NAND dump corrupt or not from console.\nStill continue?"))
|
||||
return 1;
|
||||
}*/
|
||||
if (!CheckA9lh()) {
|
||||
ShowPrompt(false, "Error: A9LH not detected.");
|
||||
return 1;
|
||||
|
@ -5,5 +5,7 @@
|
||||
#define SAFE_SECTORS 0x000000 + 0x1, SECTOR_SECRET, SECTOR_SECRET + 0x1, \
|
||||
SECTOR_FIRM0, SECTOR_CTR, 0x000000 // last one is a placeholder
|
||||
|
||||
u32 CheckEmbeddedBackup(const char* path);
|
||||
u32 EmbedEssentialBackup(const char* path);
|
||||
u32 ValidateNandDump(const char* path);
|
||||
u32 SafeRestoreNandDump(const char* path);
|
||||
|
31
source/nand/sysfiles.h
Normal file
31
source/nand/sysfiles.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// /rw/sys/LocalFriendCodeSeed_B (/_A) file
|
||||
// see: http://3dbrew.org/wiki/Nandrw/sys/LocalFriendCodeSeed_B
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u8 unknown[0x8]; // normally zero
|
||||
u8 codeseed[0x8]; // the actual data
|
||||
} __attribute__((packed)) LocalFriendCodeSeed;
|
||||
|
||||
// /private/movable.sed file
|
||||
// see: http://3dbrew.org/wiki/Nand/private/movable.sed
|
||||
typedef struct {
|
||||
u8 magic[0x4]; // "SEED"
|
||||
u8 indicator[0x4]; // uninitialized all zero, otherwise u8[1] nonzero
|
||||
LocalFriendCodeSeed codeseed_data;
|
||||
u8 keyy_high[8];
|
||||
u8 unknown[0x10];
|
||||
u8 cmac[0x10];
|
||||
} __attribute__((packed)) MovableSed;
|
||||
|
||||
// /rw/sys/Secure_A (/_B) file
|
||||
// see: http://3dbrew.org/wiki/Nandrw/sys/SecureInfo_A
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u8 region;
|
||||
u8 unknown;
|
||||
char serial[0xF];
|
||||
} __attribute__((packed)) SecureInfo;
|
Loading…
x
Reference in New Issue
Block a user