mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
Remove recursion from RomFS parse, clang-format
This commit is contained in:
parent
2de35bd39d
commit
fb6ec3aa5f
4 changed files with 202 additions and 194 deletions
|
@ -1,22 +1,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <vector>
|
||||||
#include <cstring>
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
namespace IVFC {
|
namespace IVFC {
|
||||||
|
|
||||||
struct IVFCLevel {
|
struct IVFCLevel {
|
||||||
u64 logicalOffset;
|
u64 logicalOffset;
|
||||||
u64 size;
|
u64 size;
|
||||||
u64 blockSize;
|
u64 blockSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IVFC {
|
struct IVFC {
|
||||||
u64 masterHashSize;
|
u64 masterHashSize;
|
||||||
std::vector<IVFCLevel> levels;
|
std::vector<IVFCLevel> levels;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc);
|
size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc);
|
||||||
|
|
||||||
} // namespace IVFC
|
} // namespace IVFC
|
|
@ -1,22 +1,23 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
namespace RomFS {
|
namespace RomFS {
|
||||||
|
|
||||||
struct RomFSNode {
|
struct RomFSNode {
|
||||||
std::u16string name {};
|
std::u16string name;
|
||||||
// The file/directory offset relative to the start of the RomFS
|
// The file/directory offset relative to the start of the RomFS
|
||||||
u64 offset { 0 };
|
u64 offset = 0;
|
||||||
u64 size { 0 };
|
u64 size = 0;
|
||||||
bool isDirectory { false };
|
bool isDirectory = false;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RomFSNode>> directories {};
|
std::vector<std::unique_ptr<RomFSNode>> directories;
|
||||||
std::vector<std::unique_ptr<RomFSNode>> files {};
|
std::vector<std::unique_ptr<RomFSNode>> files;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<RomFSNode> parseRomFSTree(uintptr_t romFS, u64 romFSSize);
|
std::unique_ptr<RomFSNode> parseRomFSTree(uintptr_t romFS, u64 romFSSize);
|
||||||
|
|
||||||
} // namespace RomFS
|
} // namespace RomFS
|
|
@ -2,77 +2,77 @@
|
||||||
|
|
||||||
namespace IVFC {
|
namespace IVFC {
|
||||||
|
|
||||||
size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc) {
|
size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc) {
|
||||||
uintptr_t ivfcPointer = ivfcStart;
|
uintptr_t ivfcPointer = ivfcStart;
|
||||||
std::string magicIVFC((char*)ivfcPointer, 4);
|
|
||||||
ivfcPointer += 4;
|
|
||||||
|
|
||||||
if (magicIVFC != "IVFC") {
|
char* ivfcCharPtr = (char*)ivfcPointer;
|
||||||
printf("Invalid IVFC magic: %s\n", magicIVFC.c_str());
|
if (ivfcCharPtr[0] != 'I' || ivfcCharPtr[1] != 'V' || ivfcCharPtr[2] != 'F' || ivfcCharPtr[3] != 'C') {
|
||||||
return 0;
|
printf("Invalid header on IVFC\n");
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
ivfcPointer += 4;
|
||||||
|
|
||||||
uint32_t magicIdentifier = *(u32*)ivfcPointer;
|
u32 magicIdentifier = *(u32*)ivfcPointer;
|
||||||
ivfcPointer += 4;
|
ivfcPointer += 4;
|
||||||
|
|
||||||
// RomFS IVFC uses 0x10000, DISA/DIFF IVFC uses 0x20000 here
|
// RomFS IVFC uses 0x10000, DISA/DIFF IVFC uses 0x20000 here
|
||||||
if (magicIdentifier != 0x10000 && magicIdentifier != 0x20000) {
|
if (magicIdentifier != 0x10000 && magicIdentifier != 0x20000) {
|
||||||
printf("Invalid IVFC magic identifier: %08X\n", magicIdentifier);
|
printf("Invalid IVFC magic identifier: %08X\n", magicIdentifier);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (magicIdentifier == 0x10000) {
|
if (magicIdentifier == 0x10000) {
|
||||||
ivfc.masterHashSize = *(u32*)ivfcPointer;
|
ivfc.masterHashSize = *(u32*)ivfcPointer;
|
||||||
ivfcPointer += 4;
|
ivfcPointer += 4;
|
||||||
// RomFS IVFC uses 3 levels
|
// RomFS IVFC uses 3 levels
|
||||||
ivfc.levels.resize(3);
|
ivfc.levels.resize(3);
|
||||||
} else {
|
} else {
|
||||||
ivfc.masterHashSize = *(u64*)ivfcPointer;
|
ivfc.masterHashSize = *(u64*)ivfcPointer;
|
||||||
ivfcPointer += 8;
|
ivfcPointer += 8;
|
||||||
// DISA/DIFF IVFC uses 4 levels
|
// DISA/DIFF IVFC uses 4 levels
|
||||||
ivfc.levels.resize(4);
|
ivfc.levels.resize(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < ivfc.levels.size(); i++) {
|
for (size_t i = 0; i < ivfc.levels.size(); i++) {
|
||||||
IVFCLevel level;
|
IVFCLevel level;
|
||||||
|
|
||||||
level.logicalOffset = *(u64*)ivfcPointer;
|
|
||||||
ivfcPointer += 8;
|
|
||||||
|
|
||||||
level.size = *(u64*)ivfcPointer;
|
level.logicalOffset = *(u64*)ivfcPointer;
|
||||||
ivfcPointer += 8;
|
ivfcPointer += 8;
|
||||||
|
|
||||||
// This field is in log2
|
level.size = *(u64*)ivfcPointer;
|
||||||
level.blockSize = 1 << *(u32*)ivfcPointer;
|
ivfcPointer += 8;
|
||||||
ivfcPointer += 4;
|
|
||||||
|
|
||||||
// Skip 4 reserved bytes
|
// This field is in log2
|
||||||
ivfcPointer += 4;
|
level.blockSize = 1 << *(u32*)ivfcPointer;
|
||||||
|
ivfcPointer += 4;
|
||||||
|
|
||||||
ivfc.levels[i] = level;
|
// Skip 4 reserved bytes
|
||||||
}
|
ivfcPointer += 4;
|
||||||
|
|
||||||
u64 ivfcDescriptorSize = *(u64*)ivfcPointer;
|
ivfc.levels[i] = level;
|
||||||
ivfcPointer += 8;
|
}
|
||||||
|
|
||||||
uintptr_t ivfcActualSize = ivfcPointer - ivfcStart;
|
u64 ivfcDescriptorSize = *(u64*)ivfcPointer;
|
||||||
|
ivfcPointer += 8;
|
||||||
|
|
||||||
// According to 3DBrew, this is usually the case but not guaranteed
|
uintptr_t ivfcActualSize = ivfcPointer - ivfcStart;
|
||||||
if (ivfcActualSize != ivfcDescriptorSize) {
|
|
||||||
printf("IVFC descriptor size mismatch: %lx != %lx\n", ivfcActualSize, ivfcDescriptorSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (magicIdentifier == 0x10000 && ivfcActualSize != 0x5C) {
|
// According to 3DBrew, this is usually the case but not guaranteed
|
||||||
// This is always 0x5C bytes long
|
if (ivfcActualSize != ivfcDescriptorSize) {
|
||||||
printf("Invalid IVFC size: %08x\n", (u32)ivfcActualSize);
|
printf("IVFC descriptor size mismatch: %lx != %lx\n", ivfcActualSize, ivfcDescriptorSize);
|
||||||
return 0;
|
}
|
||||||
} else if (magicIdentifier == 0x20000 && ivfcActualSize != 0x78) {
|
|
||||||
// This is always 0x78 bytes long
|
|
||||||
printf("Invalid IVFC size: %08x\n", (u32)ivfcActualSize);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ivfcActualSize;
|
if (magicIdentifier == 0x10000 && ivfcActualSize != 0x5C) {
|
||||||
}
|
// This is always 0x5C bytes long
|
||||||
|
printf("Invalid IVFC size: %08x\n", (u32)ivfcActualSize);
|
||||||
|
return 0;
|
||||||
|
} else if (magicIdentifier == 0x20000 && ivfcActualSize != 0x78) {
|
||||||
|
// This is always 0x78 bytes long
|
||||||
|
printf("Invalid IVFC size: %08x\n", (u32)ivfcActualSize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace IVFC
|
return ivfcActualSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IVFC
|
|
@ -1,136 +1,143 @@
|
||||||
#include "fs/romfs.hpp"
|
#include "fs/romfs.hpp"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <queue>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "fs/ivfc.hpp"
|
#include "fs/ivfc.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include <cstdio>
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace RomFS {
|
namespace RomFS {
|
||||||
|
|
||||||
constexpr u32 metadataInvalidEntry = 0xFFFFFFFF;
|
constexpr u32 metadataInvalidEntry = 0xFFFFFFFF;
|
||||||
|
|
||||||
struct Level3Header {
|
struct Level3Header {
|
||||||
uint32_t headerSize;
|
u32 headerSize;
|
||||||
uint32_t directoryHashTableOffset;
|
u32 directoryHashTableOffset;
|
||||||
uint32_t directoryHashTableSize;
|
u32 directoryHashTableSize;
|
||||||
uint32_t directoryMetadataOffset;
|
u32 directoryMetadataOffset;
|
||||||
uint32_t directoryMetadataSize;
|
u32 directoryMetadataSize;
|
||||||
uint32_t fileHashTableOffset;
|
u32 fileHashTableOffset;
|
||||||
uint32_t fileHashTableSize;
|
u32 fileHashTableSize;
|
||||||
uint32_t fileMetadataOffset;
|
u32 fileMetadataOffset;
|
||||||
uint32_t fileMetadataSize;
|
u32 fileMetadataSize;
|
||||||
uint32_t fileDataOffset;
|
u32 fileDataOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline uintptr_t align(uintptr_t value, uintptr_t alignment) {
|
inline uintptr_t align(uintptr_t value, uintptr_t alignment) {
|
||||||
if (value % alignment == 0)
|
if (value % alignment == 0) return value;
|
||||||
return value;
|
|
||||||
|
|
||||||
return value + (alignment - (value % alignment));
|
return value + (alignment - (value % alignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void printNode(const RomFSNode& node, int indentation, std::string path) {
|
inline void printNode(const RomFSNode& node, int indentation, std::string path) {
|
||||||
for (int i = 0; i < indentation; i++) {
|
for (int i = 0; i < indentation; i++) {
|
||||||
printf(" ");
|
printf(" ");
|
||||||
}
|
}
|
||||||
printf("%s%s\n", path.c_str(), std::string(node.name.begin(), node.name.end()).c_str());
|
printf("%s%s\n", path.c_str(), std::string(node.name.begin(), node.name.end()).c_str());
|
||||||
path += std::string(node.name.begin(), node.name.end()) + "/";
|
path += std::string(node.name.begin(), node.name.end()) + "/";
|
||||||
indentation++;
|
indentation++;
|
||||||
for (auto& directory : node.directories) {
|
for (auto& directory : node.directories) {
|
||||||
printNode(*directory, indentation, path);
|
printNode(*directory, indentation, path);
|
||||||
}
|
}
|
||||||
indentation--;
|
indentation--;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<RomFSNode>> parseDirectory(const uintptr_t metadataBase, const uintptr_t metadataOffset) {
|
std::unique_ptr<RomFSNode> parseRootDirectory(uintptr_t metadataBase) {
|
||||||
std::vector<std::unique_ptr<RomFSNode>> directories {};
|
std::unique_ptr<RomFSNode> rootDirectory = std::make_unique<RomFSNode>();
|
||||||
|
rootDirectory->isDirectory = true;
|
||||||
|
rootDirectory->name = u"romfs:";
|
||||||
|
rootDirectory->offset = 0;
|
||||||
|
|
||||||
// Get offset of first child directory
|
std::queue<RomFSNode*> directoryOffsets;
|
||||||
u32* metadataPtr = (u32*)(metadataBase + metadataOffset);
|
directoryOffsets.push(rootDirectory.get());
|
||||||
metadataPtr += 2;
|
|
||||||
u32 currentDirectoryOffset = *metadataPtr;
|
|
||||||
|
|
||||||
// Loop over all the sibling directories of the first child to get all the children directories
|
while (!directoryOffsets.empty()) {
|
||||||
while (currentDirectoryOffset != metadataInvalidEntry) {
|
RomFSNode* currentNode = directoryOffsets.front();
|
||||||
metadataPtr = (u32*)(metadataBase + currentDirectoryOffset);
|
directoryOffsets.pop();
|
||||||
metadataPtr++; // Skip the parent offset
|
|
||||||
u32 siblingDirectoryOffset = *metadataPtr++;
|
|
||||||
metadataPtr++; // Skip the child offset
|
|
||||||
metadataPtr++; // Skip the first file offset
|
|
||||||
metadataPtr++; // Skip the next directory in hash table offset
|
|
||||||
u32 nameLength = (*metadataPtr++) / 2;
|
|
||||||
|
|
||||||
// Arbitrary limit
|
u32* metadataPtr = (u32*)(metadataBase + currentNode->offset);
|
||||||
if (nameLength > 128) {
|
metadataPtr += 2;
|
||||||
printf("Invalid directory name length: %08X\n", nameLength);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
char16_t* namePtr = (char16_t*)metadataPtr;
|
// Offset of first child directory
|
||||||
std::u16string name(namePtr, nameLength);
|
u32 currentDirectoryOffset = *metadataPtr;
|
||||||
|
|
||||||
std::unique_ptr directory = std::make_unique<RomFSNode>();
|
// Loop over all the sibling directories of the first child to get all the children directories
|
||||||
directory->isDirectory = true;
|
// of the current directory
|
||||||
directory->name = name;
|
while (currentDirectoryOffset != metadataInvalidEntry) {
|
||||||
directory->offset = currentDirectoryOffset;
|
metadataPtr = (u32*)(metadataBase + currentDirectoryOffset);
|
||||||
directories.push_back(std::move(directory));
|
metadataPtr++; // Skip the parent offset
|
||||||
|
u32 siblingDirectoryOffset = *metadataPtr;
|
||||||
|
// Skip the rest of the fields
|
||||||
|
metadataPtr += 4;
|
||||||
|
u32 nameLength = *metadataPtr++ / 2;
|
||||||
|
|
||||||
currentDirectoryOffset = siblingDirectoryOffset;
|
// Arbitrary limit
|
||||||
}
|
if (nameLength > 128) {
|
||||||
|
printf("Invalid directory name length: %08X\n", nameLength);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Loop over all the children directories to get their children
|
char16_t* namePtr = (char16_t*)metadataPtr;
|
||||||
for (auto& directory : directories) {
|
std::u16string name(namePtr, nameLength);
|
||||||
directory->directories = parseDirectory(metadataBase, directory->offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return directories;
|
std::unique_ptr directory = std::make_unique<RomFSNode>();
|
||||||
}
|
directory->isDirectory = true;
|
||||||
|
directory->name = name;
|
||||||
|
directory->offset = currentDirectoryOffset;
|
||||||
|
currentNode->directories.push_back(std::move(directory));
|
||||||
|
|
||||||
std::unique_ptr<RomFSNode> parseRomFSTree(uintptr_t romFS, u64 romFSSize) {
|
currentDirectoryOffset = siblingDirectoryOffset;
|
||||||
IVFC::IVFC ivfc;
|
}
|
||||||
size_t ivfcSize = IVFC::parseIVFC((uintptr_t)romFS, ivfc);
|
|
||||||
|
|
||||||
if (ivfcSize == 0) {
|
for (auto& directory : currentNode->directories) {
|
||||||
printf("Failed to parse IVFC\n");
|
directoryOffsets.push(directory.get());
|
||||||
return {};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t masterHashOffset = RomFS::align(ivfcSize, 0x10);
|
return rootDirectory;
|
||||||
// For a weird reason, the level 3 offset is not the one in the IVFC, instead it's
|
}
|
||||||
// the first block after the master hash
|
|
||||||
// TODO: Find out why and explain in the comment
|
|
||||||
uintptr_t level3Offset = RomFS::align(masterHashOffset + ivfc.masterHashSize, ivfc.levels[2].blockSize);
|
|
||||||
uintptr_t const level3Base = (uintptr_t)romFS + level3Offset;
|
|
||||||
u32* level3Ptr = (u32*)level3Base;
|
|
||||||
|
|
||||||
Level3Header header;
|
std::unique_ptr<RomFSNode> parseRomFSTree(uintptr_t romFS, u64 romFSSize) {
|
||||||
header.headerSize = *level3Ptr++;
|
IVFC::IVFC ivfc;
|
||||||
header.directoryHashTableOffset = *level3Ptr++;
|
size_t ivfcSize = IVFC::parseIVFC((uintptr_t)romFS, ivfc);
|
||||||
header.directoryHashTableSize = *level3Ptr++;
|
|
||||||
header.directoryMetadataOffset = *level3Ptr++;
|
|
||||||
header.directoryMetadataSize = *level3Ptr++;
|
|
||||||
header.fileHashTableOffset = *level3Ptr++;
|
|
||||||
header.fileHashTableSize = *level3Ptr++;
|
|
||||||
header.fileMetadataOffset = *level3Ptr++;
|
|
||||||
header.fileMetadataSize = *level3Ptr++;
|
|
||||||
header.fileDataOffset = *level3Ptr;
|
|
||||||
|
|
||||||
if (header.headerSize != 0x28) {
|
if (ivfcSize == 0) {
|
||||||
printf("Invalid level 3 header size: %08X\n", header.headerSize);
|
printf("Failed to parse IVFC\n");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<RomFSNode> root = std::make_unique<RomFSNode>();
|
uintptr_t masterHashOffset = RomFS::align(ivfcSize, 0x10);
|
||||||
root->isDirectory = true;
|
// For a weird reason, the level 3 offset is not the one in the IVFC, instead it's
|
||||||
root->name = u"";
|
// the first block after the master hash
|
||||||
root->offset = 0;
|
// TODO: Find out why and explain in the comment
|
||||||
root->directories = parseDirectory(level3Base + header.directoryMetadataOffset, 0);
|
uintptr_t level3Offset = RomFS::align(masterHashOffset + ivfc.masterHashSize, ivfc.levels[2].blockSize);
|
||||||
|
uintptr_t level3Base = (uintptr_t)romFS + level3Offset;
|
||||||
|
u32* level3Ptr = (u32*)level3Base;
|
||||||
|
|
||||||
// If you want to print the tree, uncomment this
|
Level3Header header;
|
||||||
// printNode(*root, 0, "");
|
header.headerSize = *level3Ptr++;
|
||||||
|
header.directoryHashTableOffset = *level3Ptr++;
|
||||||
|
header.directoryHashTableSize = *level3Ptr++;
|
||||||
|
header.directoryMetadataOffset = *level3Ptr++;
|
||||||
|
header.directoryMetadataSize = *level3Ptr++;
|
||||||
|
header.fileHashTableOffset = *level3Ptr++;
|
||||||
|
header.fileHashTableSize = *level3Ptr++;
|
||||||
|
header.fileMetadataOffset = *level3Ptr++;
|
||||||
|
header.fileMetadataSize = *level3Ptr++;
|
||||||
|
header.fileDataOffset = *level3Ptr;
|
||||||
|
|
||||||
return root;
|
if (header.headerSize != 0x28) {
|
||||||
}
|
printf("Invalid level 3 header size: %08X\n", header.headerSize);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace RomFS
|
std::unique_ptr<RomFSNode> root = parseRootDirectory(level3Base + header.directoryMetadataOffset);
|
||||||
|
|
||||||
|
// If you want to print the tree, uncomment this
|
||||||
|
// printNode(*root, 0, "");
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RomFS
|
Loading…
Add table
Reference in a new issue