mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
[NCSD/NCCH] More NCSD parsing
This commit is contained in:
parent
6b6cc7ca2f
commit
71cc187f93
3 changed files with 75 additions and 10 deletions
|
@ -1,8 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include "io_file.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
struct NCCH {
|
struct NCCH {
|
||||||
|
struct FSInfo { // Info on the ExeFS/RomFS
|
||||||
|
u64 offset = 0;
|
||||||
|
u64 size = 0;
|
||||||
|
u64 hashRegionSize = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 partitionIndex = 0;
|
||||||
|
u64 fileOffset = 0;
|
||||||
|
|
||||||
bool isNew3DS = false;
|
bool isNew3DS = false;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
bool compressExeFS = false;
|
bool compressExeFS = false;
|
||||||
|
@ -16,10 +26,17 @@ struct NCCH {
|
||||||
u32 bssSize = 0;
|
u32 bssSize = 0;
|
||||||
u32 exheaderSize = 0;
|
u32 exheaderSize = 0;
|
||||||
|
|
||||||
|
FSInfo exeFS;
|
||||||
|
FSInfo romFS;
|
||||||
|
|
||||||
// Header: 0x200 + 0x800 byte NCCH header + exheadr
|
// Header: 0x200 + 0x800 byte NCCH header + exheadr
|
||||||
// Returns true on success, false on failure
|
// Returns true on success, false on failure
|
||||||
bool loadFromHeader(u8* header);
|
// Partition index/offset/size must have been set before this
|
||||||
|
bool loadFromHeader(u8* header, IOFile& file);
|
||||||
|
|
||||||
bool hasExtendedHeader() { return exheaderSize != 0; }
|
bool hasExtendedHeader() { return exheaderSize != 0; }
|
||||||
|
bool hasExeFS() { return exeFS.size != 0; }
|
||||||
|
bool hasRomFS() { return romFS.size != 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<u8, 16> primaryKey = {}; // For exheader, ExeFS header and icons
|
std::array<u8, 16> primaryKey = {}; // For exheader, ExeFS header and icons
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "loader/ncch.hpp"
|
#include "loader/ncch.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
bool NCCH::loadFromHeader(u8* header) {
|
bool NCCH::loadFromHeader(u8* header, IOFile& file) {
|
||||||
if (header[0x100] != 'N' || header[0x101] != 'C' || header[0x102] != 'C' || header[0x103] != 'H') {
|
if (header[0x100] != 'N' || header[0x101] != 'C' || header[0x102] != 'C' || header[0x103] != 'H') {
|
||||||
printf("Invalid header on NCCH\n");
|
printf("Invalid header on NCCH\n");
|
||||||
return false;
|
return false;
|
||||||
|
@ -18,8 +18,15 @@ bool NCCH::loadFromHeader(u8* header) {
|
||||||
fixedCryptoKey = (header[0x188 + 7] & 0x1) == 0x1;
|
fixedCryptoKey = (header[0x188 + 7] & 0x1) == 0x1;
|
||||||
mountRomFS = (header[0x188 + 7] & 0x2) != 0x2;
|
mountRomFS = (header[0x188 + 7] & 0x2) != 0x2;
|
||||||
encrypted = (header[0x188 + 7] & 0x4) != 0x4;
|
encrypted = (header[0x188 + 7] & 0x4) != 0x4;
|
||||||
|
|
||||||
|
// Read ExeFS and RomFS info
|
||||||
|
exeFS.offset = u64(*(u32*)&header[0x1A0]) * mediaUnit;
|
||||||
|
exeFS.size = u64(*(u32*)&header[0x1A4]) * mediaUnit;
|
||||||
|
exeFS.hashRegionSize = u64(*(u32*)&header[0x1A8]) * mediaUnit;
|
||||||
|
|
||||||
bool encryptedExheader = encrypted;
|
romFS.offset = u64(*(u32*)&header[0x1B0]) * mediaUnit;
|
||||||
|
romFS.size = u64(*(u32*)&header[0x1B4]) * mediaUnit;
|
||||||
|
romFS.hashRegionSize = u64(*(u32*)&header[0x1B8]) * mediaUnit;
|
||||||
|
|
||||||
if (fixedCryptoKey) {
|
if (fixedCryptoKey) {
|
||||||
Helpers::panic("Fixed crypto keys for NCCH");
|
Helpers::panic("Fixed crypto keys for NCCH");
|
||||||
|
@ -29,12 +36,13 @@ bool NCCH::loadFromHeader(u8* header) {
|
||||||
const u8* exheader = &header[0x200]; // Extended NCCH header
|
const u8* exheader = &header[0x200]; // Extended NCCH header
|
||||||
const u64 jumpID = *(u64*)&exheader[0x1C0 + 0x8];
|
const u64 jumpID = *(u64*)&exheader[0x1C0 + 0x8];
|
||||||
|
|
||||||
// TODO: How does this even work
|
// It seems like some decryption tools will decrypt the file, without actually setting the NoCrypto flag in the NCCH header
|
||||||
if (u32(programID) == u32(jumpID) && encryptedExheader) {
|
// This is a nice and easy hack to see if a file is pretending to be encrypted, taken from 3DMoo and Citra
|
||||||
printf("Exheader is supposedly ecrypted but not actually encrypted\n");
|
if (u32(programID) == u32(jumpID) && encrypted) {
|
||||||
encryptedExheader = false;
|
printf("NCSD is supposedly ecrypted but not actually encrypted\n");
|
||||||
} else if (encryptedExheader) {
|
encrypted = false;
|
||||||
Helpers::panic("Encrypted exheader");
|
} else if (encrypted) {
|
||||||
|
Helpers::panic("Encrypted NCSD file");
|
||||||
}
|
}
|
||||||
|
|
||||||
compressExeFS = (exheader[0xD] & 1) != 0;
|
compressExeFS = (exheader[0xD] & 1) != 0;
|
||||||
|
@ -44,6 +52,42 @@ bool NCCH::loadFromHeader(u8* header) {
|
||||||
|
|
||||||
printf("Stack size: %08X\nBSS size: %08X\n", stackSize, bssSize);
|
printf("Stack size: %08X\nBSS size: %08X\n", stackSize, bssSize);
|
||||||
|
|
||||||
|
// Read ExeFS
|
||||||
|
if (hasExeFS()) {
|
||||||
|
printf("ExeFS offset: %08llX, size: %08llX\n", exeFS.offset, exeFS.size);
|
||||||
|
auto exeFSOffset = fileOffset + exeFS.offset;
|
||||||
|
constexpr size_t exeFSHeaderSize = 0x200;
|
||||||
|
|
||||||
|
u8 exeFSHeader[exeFSHeaderSize];
|
||||||
|
|
||||||
|
file.seek(exeFSOffset);
|
||||||
|
auto [success, bytes] = file.readBytes(exeFSHeader, exeFSHeaderSize);
|
||||||
|
if (!success || bytes != exeFSHeaderSize) {
|
||||||
|
printf("Failed to parse ExeFS header\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExeFS format allows up to 10 files
|
||||||
|
for (int file = 0; file < 10; file++) {
|
||||||
|
u8* fileInfo = &exeFSHeader[file * 16];
|
||||||
|
|
||||||
|
char name[9];
|
||||||
|
std::memcpy(name, fileInfo, 8); // Get file name as a string
|
||||||
|
name[8] = '\0'; // Add null terminator to it just in case there's none
|
||||||
|
|
||||||
|
u32 fileOffset = *(u32*)&fileInfo[0x8];
|
||||||
|
u32 fileSize = *(u32*)&fileInfo[0xC];
|
||||||
|
|
||||||
|
if (fileSize != 0) {
|
||||||
|
printf("File %d. Name: %s, Size: %08X, Offset: %08X\n", file, name, fileSize, fileOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasRomFS()) {
|
||||||
|
printf("RomFS offset: %08llX, size: %08llX\n", romFS.offset, romFS.size);
|
||||||
|
}
|
||||||
|
|
||||||
if (stackSize != VirtualAddrs::DefaultStackSize) {
|
if (stackSize != VirtualAddrs::DefaultStackSize) {
|
||||||
Helpers::panic("Stack size != 0x4000");
|
Helpers::panic("Stack size != 0x4000");
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,13 @@ std::optional<NCSD> Memory::loadNCSD(const std::filesystem::path& path) {
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
auto& partition = ncsd.partitions[i];
|
auto& partition = ncsd.partitions[i];
|
||||||
|
NCCH& ncch = partition.ncch;
|
||||||
partition.offset = u64(partitionData[i * 2]) * NCSD::mediaUnit;
|
partition.offset = u64(partitionData[i * 2]) * NCSD::mediaUnit;
|
||||||
partition.length = u64(partitionData[i * 2 + 1]) * NCSD::mediaUnit;
|
partition.length = u64(partitionData[i * 2 + 1]) * NCSD::mediaUnit;
|
||||||
|
|
||||||
|
ncch.partitionIndex = i;
|
||||||
|
ncch.fileOffset = partition.offset;
|
||||||
|
|
||||||
if (partition.length != 0) { // Initialize the NCCH of each partition
|
if (partition.length != 0) { // Initialize the NCCH of each partition
|
||||||
ncsd.file.seek(partition.offset);
|
ncsd.file.seek(partition.offset);
|
||||||
|
|
||||||
|
@ -57,7 +61,7 @@ std::optional<NCSD> Memory::loadNCSD(const std::filesystem::path& path) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!partition.ncch.loadFromHeader(ncchHeader)) {
|
if (!ncch.loadFromHeader(ncchHeader, ncsd.file)) {
|
||||||
printf("Invalid NCCH partition\n");
|
printf("Invalid NCCH partition\n");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue