diff --git a/programs/nstool/source/RomfsProcess.cpp b/programs/nstool/source/RomfsProcess.cpp new file mode 100644 index 0000000..bcde3b3 --- /dev/null +++ b/programs/nstool/source/RomfsProcess.cpp @@ -0,0 +1,249 @@ +#include "RomfsProcess.h" +#include +#include +#include + +void RomfsProcess::printTab(size_t tab) const +{ + for (size_t i = 0; i < tab; i++) + { + printf(" "); + } +} + +void RomfsProcess::displayFile(const sFile& file, size_t tab) const +{ + printTab(tab); + printf("%s", file.name.c_str()); + if (mCliOutputType >= OUTPUT_VERBOSE) + { + printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")", file.offset, file.size); + } + putchar('\n'); +} + +void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const +{ + if (dir.name.empty() == false) + { + printTab(tab); + printf("%s\n", dir.name.c_str()); + } + + for (size_t i = 0; i < dir.dir_list.getSize(); i++) + { + displayDir(dir.dir_list[i], tab+1); + } + for (size_t i = 0; i < dir.file_list.getSize(); i++) + { + displayFile(dir.file_list[i], tab+1); + } +} + +void RomfsProcess::displayHeader() +{ + printf("[RomFS]\n"); + printf(" DirNum: %u\n", mDirNum); + printf(" FileNum: %u\n", mFileNum); + if (mMountName.empty() == false) + printf(" MountPoint: %s%s\n", mMountName.c_str(), mMountName.at(mMountName.length()-1) != '/' ? "/" : ""); +} + +void RomfsProcess::displayFs() +{ + displayDir(mRootDir, 1); +} + +void RomfsProcess::extractFs() +{ + +} + +bool RomfsProcess::validateHeaderLayout(const nx::sRomfsHeader* hdr) const +{ + bool validLayout = true; + + if (hdr->header_size.get() != sizeof(nx::sRomfsHeader)) + { + validLayout = false; + } + + uint64_t pos = hdr->sections[0].offset.get(); + for (size_t i = 0; i < nx::romfs::SECTION_NUM; i++) + { + if (hdr->sections[i].offset.get() != pos) + { + validLayout = false; + } + pos += hdr->sections[i].size.get(); + } + + return validLayout; +} + +void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir) +{ + nx::sRomfsDirEntry* d_node = get_dir_node(dir_offset); + + /* + printf("[DIR-NODE]\n"); + printf(" parent=%08x\n", d_node->parent.get()); + printf(" sibling=%08x\n", d_node->sibling.get()); + printf(" child=%08x\n", d_node->child.get()); + printf(" file=%08x\n", d_node->file.get()); + printf(" hash=%08x\n", d_node->hash.get()); + printf(" name_size=%08x\n", d_node->name_size.get()); + printf(" name=%s\n", d_node->name); + */ + + for (uint32_t file_addr = d_node->file.get(); file_addr != nx::romfs::kInvalidAddr; ) + { + nx::sRomfsFileEntry* f_node = get_file_node(file_addr); + + /* + printf("[FILE-NODE]\n"); + printf(" parent=%08x\n", f_node->parent.get()); + printf(" sibling=%08x\n", f_node->sibling.get()); + printf(" offset=%08" PRIx64 "\n", f_node->offset.get()); + printf(" size=%08" PRIx64 "\n", f_node->size.get()); + printf(" hash=%08x\n", f_node->hash.get()); + printf(" name_size=%08x\n", f_node->name_size.get()); + printf(" name=%s\n", f_node->name); + */ + + dir.file_list.addElement({std::string(f_node->name, f_node->name_size.get()), mHdr.data_offset.get() + f_node->offset.get(), f_node->size.get()}); + + file_addr = f_node->sibling.get(); + mFileNum++; + } + + for (uint32_t child_addr = d_node->child.get(); child_addr != nx::romfs::kInvalidAddr; ) + { + nx::sRomfsDirEntry* c_node = get_dir_node(child_addr); + + dir.dir_list.addElement({std::string(c_node->name, c_node->name_size.get())}); + importDirectory(child_addr, dir.dir_list.atBack()); + + child_addr = c_node->sibling.get(); + mDirNum++; + } +} + +void RomfsProcess::resolveRomfs() +{ + // read header + mReader->read((byte_t*)&mHdr, mOffset, sizeof(nx::sRomfsHeader)); + + // logic check on the header layout + if (validateHeaderLayout(&mHdr) == false) + { + throw fnd::Exception(kModuleName, "Invalid ROMFS Header"); + } + + // read directory nodes + mDirNodes.alloc(mHdr.sections[nx::romfs::DIR_NODE_TABLE].size.get()); + mReader->read(mDirNodes.getBytes(), mOffset + mHdr.sections[nx::romfs::DIR_NODE_TABLE].offset.get(), mDirNodes.getSize()); + //printf("[RAW DIR NODES]\n"); + //fnd::SimpleTextOutput::hxdStyleDump(mDirNodes.getBytes(), mDirNodes.getSize()); + + // read file nodes + mFileNodes.alloc(mHdr.sections[nx::romfs::FILE_NODE_TABLE].size.get()); + mReader->read(mFileNodes.getBytes(), mOffset + mHdr.sections[nx::romfs::FILE_NODE_TABLE].offset.get(), mFileNodes.getSize()); + //printf("[RAW FILE NODES]\n"); + //fnd::SimpleTextOutput::hxdStyleDump(mFileNodes.getBytes(), mFileNodes.getSize()); + + // A logic check on the root directory node + if ( get_dir_node(0)->parent.get() != 0 \ + || get_dir_node(0)->sibling.get() != nx::romfs::kInvalidAddr \ + || get_dir_node(0)->hash.get() != nx::romfs::kInvalidAddr \ + || get_dir_node(0)->name_size.get() != 0) + { + throw fnd::Exception(kModuleName, "Invalid root directory node"); + } + + // import directory into internal structure + mDirNum = 0; + mFileNum = 0; + importDirectory(0, mRootDir); +} + +RomfsProcess::RomfsProcess() : + mReader(nullptr), + mOffset(0), + mKeyset(nullptr), + mCliOutputType(OUTPUT_NORMAL), + mVerify(false), + mExtractPath(), + mExtract(false), + mMountName(), + mListFs(false), + mDirNum(0), + mFileNum(0) +{ + mRootDir.name.clear(); + mRootDir.dir_list.clear(); + mRootDir.file_list.clear(); +} + +void RomfsProcess::process() +{ + if (mReader == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } + + resolveRomfs(); + + if (mCliOutputType >= OUTPUT_NORMAL) + displayHeader(); + if (mListFs || mCliOutputType >= OUTPUT_VERBOSE) + displayFs(); + if (mExtract) + extractFs(); +} + +void RomfsProcess::setInputFile(fnd::IFile& reader) +{ + mReader = &reader; +} + +void RomfsProcess::setInputFileOffset(size_t offset) +{ + mOffset = offset; +} + +void RomfsProcess::setKeyset(const sKeyset* keyset) +{ + mKeyset = keyset; +} + +void RomfsProcess::setCliOutputMode(CliOutputType type) +{ + mCliOutputType = type; +} + +void RomfsProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void RomfsProcess::setMountPointName(const std::string& mount_name) +{ + mMountName = mount_name; +} + +void RomfsProcess::setExtractPath(const std::string& path) +{ + mExtract = true; + mExtractPath = path; +} + +void RomfsProcess::setListFs(bool list_fs) +{ + mListFs = list_fs; +} + +const RomfsProcess::sDirectory& RomfsProcess::getRootDir() const +{ + return mRootDir; +} \ No newline at end of file diff --git a/programs/nstool/source/RomfsProcess.h b/programs/nstool/source/RomfsProcess.h index e69de29..ace7cdd 100644 --- a/programs/nstool/source/RomfsProcess.h +++ b/programs/nstool/source/RomfsProcess.h @@ -0,0 +1,145 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "nstool.h" + +class RomfsProcess +{ +public: + struct sDirectory; + struct sFile; + + struct sDirectory + { + std::string name; + fnd::List dir_list; + fnd::List file_list; + + sDirectory& operator=(const sDirectory& other) + { + name = other.name; + dir_list = other.dir_list; + file_list = other.file_list; + return *this; + } + + bool operator==(const sDirectory& other) const + { + return (name == other.name) \ + && (dir_list == other.dir_list) \ + && (file_list == other.file_list); + } + + bool operator!=(const sDirectory& other) const + { + return !operator==(other); + } + + bool operator==(const std::string& other) const + { + return (name == other); + } + + bool operator!=(const std::string& other) const + { + return !operator==(other); + } + }; + + struct sFile + { + std::string name; + uint64_t offset; + uint64_t size; + + sFile& operator=(const sFile& other) + { + name = other.name; + offset = other.offset; + size = other.size; + return *this; + } + + bool operator==(const sFile& other) const + { + return (name == other.name) \ + && (offset == other.offset) \ + && (size == other.size); + } + + bool operator!=(const sFile& other) const + { + return !operator==(other); + } + + bool operator==(const std::string& other) const + { + return (name == other); + } + + bool operator!=(const std::string& other) const + { + return !operator==(other); + } + }; + + RomfsProcess(); + + void process(); + + // generic + void setInputFile(fnd::IFile& reader); + void setInputFileOffset(size_t offset); + void setKeyset(const sKeyset* keyset); + void setCliOutputMode(CliOutputType type); + void setVerifyMode(bool verify); + + // romfs specific + void setMountPointName(const std::string& mount_name); + void setExtractPath(const std::string& path); + void setListFs(bool list_fs); + + const sDirectory& getRootDir() const; +private: + const std::string kModuleName = "RomfsProcess"; + static const size_t kFileExportBlockSize = 0x1000000; + + fnd::IFile* mReader; + size_t mOffset; + const sKeyset* mKeyset; + CliOutputType mCliOutputType; + bool mVerify; + + std::string mExtractPath; + bool mExtract; + std::string mMountName; + bool mListFs; + + size_t mDirNum; + size_t mFileNum; + nx::sRomfsHeader mHdr; + fnd::MemoryBlob mDirNodes; + fnd::MemoryBlob mFileNodes; + sDirectory mRootDir; + + inline nx::sRomfsDirEntry* get_dir_node(uint32_t offset) { return (nx::sRomfsDirEntry*)(mDirNodes.getBytes() + offset); } + inline nx::sRomfsFileEntry* get_file_node(uint32_t offset) { return (nx::sRomfsFileEntry*)(mFileNodes.getBytes() + offset); } + + + void printTab(size_t tab) const; + void displayFile(const sFile& file, size_t tab) const; + void displayDir(const sDirectory& dir, size_t tab) const; + + void displayHeader(); + void displayFs(); + void extractFs(); + + bool validateHeaderLayout(const nx::sRomfsHeader* hdr) const; + void importDirectory(uint32_t dir_offset, sDirectory& dir); + void resolveRomfs(); +}; \ No newline at end of file diff --git a/programs/nstool/source/main.cpp b/programs/nstool/source/main.cpp index 4ea7765..ca22050 100644 --- a/programs/nstool/source/main.cpp +++ b/programs/nstool/source/main.cpp @@ -3,7 +3,7 @@ #include "UserSettings.h" #include "XciProcess.h" #include "PfsProcess.h" -//#include "RomfsProcess.h" +#include "RomfsProcess.h" //#include "NcaProcess.h" #include "NpdmProcess.h" @@ -54,17 +54,20 @@ int main(int argc, char** argv) } else if (user_set.getFileType() == FILE_ROMFS) { - /* + RomfsProcess romfs; - romfs.setRomfsPath(user_set.getInputPath()); - romfs.setExtractPath(user_set.getFsPath()); - romfs.setKeyset(user_set.getKeyset()); + romfs.setInputFile(inputFile); + romfs.setKeyset(&user_set.getKeyset()); romfs.setCliOutputMode(user_set.getCliOutputType()); romfs.setVerifyMode(user_set.isVerifyFile()); + if (user_set.getFsPath().isSet) + romfs.setExtractPath(user_set.getFsPath().var); + romfs.setListFs(user_set.isListFs()); + romfs.process(); - */ + } else if (user_set.getFileType() == FILE_NCA) {