From fb6ec3aa5fd1f2a65f9ae55ebf505931f133d4c7 Mon Sep 17 00:00:00 2001 From: offtkp Date: Sat, 29 Jul 2023 17:01:44 +0300 Subject: [PATCH] Remove recursion from RomFS parse, clang-format --- include/fs/ivfc.hpp | 26 ++--- include/fs/romfs.hpp | 29 +++--- src/core/fs/ivfc.cpp | 118 +++++++++++----------- src/core/fs/romfs.cpp | 223 ++++++++++++++++++++++-------------------- 4 files changed, 202 insertions(+), 194 deletions(-) diff --git a/include/fs/ivfc.hpp b/include/fs/ivfc.hpp index b8d72380..7c3b8c0e 100644 --- a/include/fs/ivfc.hpp +++ b/include/fs/ivfc.hpp @@ -1,22 +1,22 @@ #pragma once -#include -#include +#include + #include "helpers.hpp" namespace IVFC { - struct IVFCLevel { - u64 logicalOffset; - u64 size; - u64 blockSize; - }; + struct IVFCLevel { + u64 logicalOffset; + u64 size; + u64 blockSize; + }; - struct IVFC { - u64 masterHashSize; - std::vector levels; - }; + struct IVFC { + u64 masterHashSize; + std::vector levels; + }; - size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc); + size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc); -} // namespace IVFC \ No newline at end of file +} // namespace IVFC \ No newline at end of file diff --git a/include/fs/romfs.hpp b/include/fs/romfs.hpp index b42e873c..cdc7dcdc 100644 --- a/include/fs/romfs.hpp +++ b/include/fs/romfs.hpp @@ -1,22 +1,23 @@ #pragma once -#include "helpers.hpp" +#include #include #include -#include + +#include "helpers.hpp" namespace RomFS { - struct RomFSNode { - std::u16string name {}; - // The file/directory offset relative to the start of the RomFS - u64 offset { 0 }; - u64 size { 0 }; - bool isDirectory { false }; + struct RomFSNode { + std::u16string name; + // The file/directory offset relative to the start of the RomFS + u64 offset = 0; + u64 size = 0; + bool isDirectory = false; - std::vector> directories {}; - std::vector> files {}; - }; + std::vector> directories; + std::vector> files; + }; - std::unique_ptr parseRomFSTree(uintptr_t romFS, u64 romFSSize); - -} // namespace RomFS \ No newline at end of file + std::unique_ptr parseRomFSTree(uintptr_t romFS, u64 romFSSize); + +} // namespace RomFS \ No newline at end of file diff --git a/src/core/fs/ivfc.cpp b/src/core/fs/ivfc.cpp index db633550..65cc2e61 100644 --- a/src/core/fs/ivfc.cpp +++ b/src/core/fs/ivfc.cpp @@ -2,77 +2,77 @@ namespace IVFC { - size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc) { - uintptr_t ivfcPointer = ivfcStart; - std::string magicIVFC((char*)ivfcPointer, 4); - ivfcPointer += 4; + size_t parseIVFC(uintptr_t ivfcStart, IVFC& ivfc) { + uintptr_t ivfcPointer = ivfcStart; - if (magicIVFC != "IVFC") { - printf("Invalid IVFC magic: %s\n", magicIVFC.c_str()); - return 0; - } + char* ivfcCharPtr = (char*)ivfcPointer; + if (ivfcCharPtr[0] != 'I' || ivfcCharPtr[1] != 'V' || ivfcCharPtr[2] != 'F' || ivfcCharPtr[3] != 'C') { + printf("Invalid header on IVFC\n"); + return 0; + } + ivfcPointer += 4; - uint32_t magicIdentifier = *(u32*)ivfcPointer; - ivfcPointer += 4; + u32 magicIdentifier = *(u32*)ivfcPointer; + ivfcPointer += 4; - // RomFS IVFC uses 0x10000, DISA/DIFF IVFC uses 0x20000 here - if (magicIdentifier != 0x10000 && magicIdentifier != 0x20000) { - printf("Invalid IVFC magic identifier: %08X\n", magicIdentifier); - return 0; - } + // RomFS IVFC uses 0x10000, DISA/DIFF IVFC uses 0x20000 here + if (magicIdentifier != 0x10000 && magicIdentifier != 0x20000) { + printf("Invalid IVFC magic identifier: %08X\n", magicIdentifier); + return 0; + } - if (magicIdentifier == 0x10000) { - ivfc.masterHashSize = *(u32*)ivfcPointer; - ivfcPointer += 4; - // RomFS IVFC uses 3 levels - ivfc.levels.resize(3); - } else { - ivfc.masterHashSize = *(u64*)ivfcPointer; - ivfcPointer += 8; - // DISA/DIFF IVFC uses 4 levels - ivfc.levels.resize(4); - } + if (magicIdentifier == 0x10000) { + ivfc.masterHashSize = *(u32*)ivfcPointer; + ivfcPointer += 4; + // RomFS IVFC uses 3 levels + ivfc.levels.resize(3); + } else { + ivfc.masterHashSize = *(u64*)ivfcPointer; + ivfcPointer += 8; + // DISA/DIFF IVFC uses 4 levels + ivfc.levels.resize(4); + } - for (size_t i = 0; i < ivfc.levels.size(); i++) { - IVFCLevel level; - - level.logicalOffset = *(u64*)ivfcPointer; - ivfcPointer += 8; + for (size_t i = 0; i < ivfc.levels.size(); i++) { + IVFCLevel level; - level.size = *(u64*)ivfcPointer; - ivfcPointer += 8; + level.logicalOffset = *(u64*)ivfcPointer; + ivfcPointer += 8; - // This field is in log2 - level.blockSize = 1 << *(u32*)ivfcPointer; - ivfcPointer += 4; + level.size = *(u64*)ivfcPointer; + ivfcPointer += 8; - // Skip 4 reserved bytes - ivfcPointer += 4; + // This field is in log2 + level.blockSize = 1 << *(u32*)ivfcPointer; + ivfcPointer += 4; - ivfc.levels[i] = level; - } + // Skip 4 reserved bytes + ivfcPointer += 4; - u64 ivfcDescriptorSize = *(u64*)ivfcPointer; - ivfcPointer += 8; + ivfc.levels[i] = level; + } - uintptr_t ivfcActualSize = ivfcPointer - ivfcStart; + u64 ivfcDescriptorSize = *(u64*)ivfcPointer; + ivfcPointer += 8; - // According to 3DBrew, this is usually the case but not guaranteed - if (ivfcActualSize != ivfcDescriptorSize) { - printf("IVFC descriptor size mismatch: %lx != %lx\n", ivfcActualSize, ivfcDescriptorSize); - } + uintptr_t ivfcActualSize = ivfcPointer - ivfcStart; - 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; - } + // According to 3DBrew, this is usually the case but not guaranteed + if (ivfcActualSize != ivfcDescriptorSize) { + printf("IVFC descriptor size mismatch: %lx != %lx\n", ivfcActualSize, ivfcDescriptorSize); + } - 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 \ No newline at end of file + return ivfcActualSize; + } + +} // namespace IVFC \ No newline at end of file diff --git a/src/core/fs/romfs.cpp b/src/core/fs/romfs.cpp index 281c8b4a..9bd14682 100644 --- a/src/core/fs/romfs.cpp +++ b/src/core/fs/romfs.cpp @@ -1,136 +1,143 @@ #include "fs/romfs.hpp" + +#include +#include +#include + #include "fs/ivfc.hpp" #include "helpers.hpp" -#include -#include -#include -#include namespace RomFS { - constexpr u32 metadataInvalidEntry = 0xFFFFFFFF; + constexpr u32 metadataInvalidEntry = 0xFFFFFFFF; - struct Level3Header { - uint32_t headerSize; - uint32_t directoryHashTableOffset; - uint32_t directoryHashTableSize; - uint32_t directoryMetadataOffset; - uint32_t directoryMetadataSize; - uint32_t fileHashTableOffset; - uint32_t fileHashTableSize; - uint32_t fileMetadataOffset; - uint32_t fileMetadataSize; - uint32_t fileDataOffset; - }; + struct Level3Header { + u32 headerSize; + u32 directoryHashTableOffset; + u32 directoryHashTableSize; + u32 directoryMetadataOffset; + u32 directoryMetadataSize; + u32 fileHashTableOffset; + u32 fileHashTableSize; + u32 fileMetadataOffset; + u32 fileMetadataSize; + u32 fileDataOffset; + }; - inline uintptr_t align(uintptr_t value, uintptr_t alignment) { - if (value % alignment == 0) - return value; + inline uintptr_t align(uintptr_t value, uintptr_t alignment) { + if (value % alignment == 0) return value; - return value + (alignment - (value % alignment)); - } + return value + (alignment - (value % alignment)); + } - inline void printNode(const RomFSNode& node, int indentation, std::string path) { - for (int i = 0; i < indentation; i++) { - printf(" "); - } - 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()) + "/"; - indentation++; - for (auto& directory : node.directories) { - printNode(*directory, indentation, path); - } - indentation--; - } + inline void printNode(const RomFSNode& node, int indentation, std::string path) { + for (int i = 0; i < indentation; i++) { + printf(" "); + } + 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()) + "/"; + indentation++; + for (auto& directory : node.directories) { + printNode(*directory, indentation, path); + } + indentation--; + } - std::vector> parseDirectory(const uintptr_t metadataBase, const uintptr_t metadataOffset) { - std::vector> directories {}; + std::unique_ptr parseRootDirectory(uintptr_t metadataBase) { + std::unique_ptr rootDirectory = std::make_unique(); + rootDirectory->isDirectory = true; + rootDirectory->name = u"romfs:"; + rootDirectory->offset = 0; - // Get offset of first child directory - u32* metadataPtr = (u32*)(metadataBase + metadataOffset); - metadataPtr += 2; - u32 currentDirectoryOffset = *metadataPtr; + std::queue directoryOffsets; + directoryOffsets.push(rootDirectory.get()); - // Loop over all the sibling directories of the first child to get all the children directories - while (currentDirectoryOffset != metadataInvalidEntry) { - metadataPtr = (u32*)(metadataBase + currentDirectoryOffset); - 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; + while (!directoryOffsets.empty()) { + RomFSNode* currentNode = directoryOffsets.front(); + directoryOffsets.pop(); - // Arbitrary limit - if (nameLength > 128) { - printf("Invalid directory name length: %08X\n", nameLength); - return {}; - } + u32* metadataPtr = (u32*)(metadataBase + currentNode->offset); + metadataPtr += 2; - char16_t* namePtr = (char16_t*)metadataPtr; - std::u16string name(namePtr, nameLength); + // Offset of first child directory + u32 currentDirectoryOffset = *metadataPtr; - std::unique_ptr directory = std::make_unique(); - directory->isDirectory = true; - directory->name = name; - directory->offset = currentDirectoryOffset; - directories.push_back(std::move(directory)); + // Loop over all the sibling directories of the first child to get all the children directories + // of the current directory + while (currentDirectoryOffset != metadataInvalidEntry) { + metadataPtr = (u32*)(metadataBase + currentDirectoryOffset); + 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 - for (auto& directory : directories) { - directory->directories = parseDirectory(metadataBase, directory->offset); - } + char16_t* namePtr = (char16_t*)metadataPtr; + std::u16string name(namePtr, nameLength); - return directories; - } + std::unique_ptr directory = std::make_unique(); + directory->isDirectory = true; + directory->name = name; + directory->offset = currentDirectoryOffset; + currentNode->directories.push_back(std::move(directory)); - std::unique_ptr parseRomFSTree(uintptr_t romFS, u64 romFSSize) { - IVFC::IVFC ivfc; - size_t ivfcSize = IVFC::parseIVFC((uintptr_t)romFS, ivfc); + currentDirectoryOffset = siblingDirectoryOffset; + } - if (ivfcSize == 0) { - printf("Failed to parse IVFC\n"); - return {}; - } + for (auto& directory : currentNode->directories) { + directoryOffsets.push(directory.get()); + } + } - uintptr_t masterHashOffset = RomFS::align(ivfcSize, 0x10); - // 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; + return rootDirectory; + } - Level3Header header; - 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; + std::unique_ptr parseRomFSTree(uintptr_t romFS, u64 romFSSize) { + IVFC::IVFC ivfc; + size_t ivfcSize = IVFC::parseIVFC((uintptr_t)romFS, ivfc); - if (header.headerSize != 0x28) { - printf("Invalid level 3 header size: %08X\n", header.headerSize); - return {}; - } + if (ivfcSize == 0) { + printf("Failed to parse IVFC\n"); + return {}; + } - std::unique_ptr root = std::make_unique(); - root->isDirectory = true; - root->name = u""; - root->offset = 0; - root->directories = parseDirectory(level3Base + header.directoryMetadataOffset, 0); + uintptr_t masterHashOffset = RomFS::align(ivfcSize, 0x10); + // 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 level3Base = (uintptr_t)romFS + level3Offset; + u32* level3Ptr = (u32*)level3Base; - // If you want to print the tree, uncomment this - // printNode(*root, 0, ""); + Level3Header header; + 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 \ No newline at end of file + std::unique_ptr root = parseRootDirectory(level3Base + header.directoryMetadataOffset); + + // If you want to print the tree, uncomment this + // printNode(*root, 0, ""); + + return root; + } + +} // namespace RomFS \ No newline at end of file