From 12e535317a522d4a54a4e02b6cc96346c8df16af Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 14:28:43 +0800 Subject: [PATCH 01/11] [nx] Add other RomFs structures --- lib/libnx/include/nx/romfs.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/libnx/include/nx/romfs.h b/lib/libnx/include/nx/romfs.h index ab34c01..ed75974 100644 --- a/lib/libnx/include/nx/romfs.h +++ b/lib/libnx/include/nx/romfs.h @@ -15,7 +15,6 @@ namespace nx DIR_NODE_TABLE, FILE_HASHMAP_TABLE, FILE_NODE_TABLE, - SECTION_NUM }; } @@ -31,5 +30,26 @@ namespace nx } sections[romfs::SECTION_NUM]; le_uint64_t data_offset; }; + + struct sRomfsDirEntry + { + le_uint32_t sibling; + le_uint32_t child; + le_uint32_t file; + le_uint32_t hash; + le_uint32_t name_size; + le_uint16_t name[]; + }; + + struct sRomfsFileEntry + { + le_uint32_t parent; + le_uint32_t sibling; + le_uint64_t offset; + le_uint64_t size; + le_uint32_t hash; + le_uint32_t name_size; + le_uint16_t name[]; + }; #pragma pack(pop) } From dd1297fbed0f7189700b29c1184f72e6a2a4eccd Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 16:34:52 +0800 Subject: [PATCH 02/11] [nx] Add romfs header constant. --- lib/libnx/include/nx/romfs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/libnx/include/nx/romfs.h b/lib/libnx/include/nx/romfs.h index ed75974..c678886 100644 --- a/lib/libnx/include/nx/romfs.h +++ b/lib/libnx/include/nx/romfs.h @@ -17,6 +17,8 @@ namespace nx FILE_NODE_TABLE, SECTION_NUM }; + + static const uint64_t kRomfsHeaderAlign = 0x200; } #pragma pack(push,1) From b1642994732f384c3a67c0f455919a7f4ed21252 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 16:35:45 +0800 Subject: [PATCH 03/11] [nstool] Fix automatic ROMFS detection. --- programs/nstool/source/UserSettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 3b5c375..2d77146 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -577,7 +577,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) else if (_ASSERT_SIZE(sizeof(nx::sPfsHeader)) && std::string(_QUICK_CAST(nx::sPfsHeader, 0)->signature, 4) == nx::pfs::kHashedPfsSig) file_type = FILE_PARTITIONFS; // test romfs - else if (_ASSERT_SIZE(sizeof(nx::sRomfsHeader)) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == sizeof(nx::sRomfsHeader) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == _QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].offset.get()) + else if (_ASSERT_SIZE(sizeof(nx::sRomfsHeader)) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == sizeof(nx::sRomfsHeader) && _QUICK_CAST(nx::sRomfsHeader, 0)->sections[1].offset.get() == (_QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].offset.get() + _QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].size.get())) file_type = FILE_ROMFS; // test nca2 else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && std::string(nca_header->signature, 4) == nx::nca::kNca2Sig) From 7e81d04fac42928c2d27615e480e45622e83cdde Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 19:34:40 +0800 Subject: [PATCH 04/11] [nx] Fix mistake in sRomfsDirEntry and add romfs::kInvalidAddr --- lib/libnx/include/nx/romfs.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/libnx/include/nx/romfs.h b/lib/libnx/include/nx/romfs.h index c678886..21e2aeb 100644 --- a/lib/libnx/include/nx/romfs.h +++ b/lib/libnx/include/nx/romfs.h @@ -19,6 +19,7 @@ namespace nx }; static const uint64_t kRomfsHeaderAlign = 0x200; + static const uint32_t kInvalidAddr = 0xffffffff; } #pragma pack(push,1) @@ -35,12 +36,13 @@ namespace nx struct sRomfsDirEntry { + le_uint32_t parent; le_uint32_t sibling; le_uint32_t child; le_uint32_t file; le_uint32_t hash; le_uint32_t name_size; - le_uint16_t name[]; + char name[]; }; struct sRomfsFileEntry @@ -51,7 +53,7 @@ namespace nx le_uint64_t size; le_uint32_t hash; le_uint32_t name_size; - le_uint16_t name[]; + char name[]; }; #pragma pack(pop) } From 167a343937701d258e86e6844c85d10e11e5b5ad Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 19:36:21 +0800 Subject: [PATCH 05/11] [fnd] Fix/Enhance fnd::SimpleTextOutput::hxdStyleDump(). --- lib/libfnd/source/SimpleTextOutput.cpp | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/libfnd/source/SimpleTextOutput.cpp b/lib/libfnd/source/SimpleTextOutput.cpp index 8703386..6482454 100644 --- a/lib/libfnd/source/SimpleTextOutput.cpp +++ b/lib/libfnd/source/SimpleTextOutput.cpp @@ -6,6 +6,7 @@ void fnd::SimpleTextOutput::hxdStyleDump(const byte_t* data, size_t len, size_t // iterate over blocks for (size_t i = 0; i < (len / row_len); i++) { + printf("%08" PRIx64 " | ", i*row_len); // for block i print each byte for (size_t j = 0; j < row_len; j++) { @@ -22,6 +23,32 @@ void fnd::SimpleTextOutput::hxdStyleDump(const byte_t* data, size_t len, size_t } printf("\n"); } + if ((len % row_len) > 0) + { + size_t i = (len / row_len); + printf("%08" PRIx64 " | ", i * row_len); + // for block i print each byte + for (size_t j = 0; j < row_len; j++) + { + if (j < (len % row_len)) + printf("%02X", data[(i * row_len) + j]); + else + printf(" "); + if (((j+1) % byte_grouping_size) == 0) + { + putchar(' '); + } + } + printf(" "); + for (size_t j = 0; j < row_len; j++) + { + if (j < (len % row_len)) + printf("%c", isalnum(data[(i * row_len) + j]) ? data[(i * row_len) + j] : '.'); + else + printf(" "); + } + printf("\n"); + } } void fnd::SimpleTextOutput::hxdStyleDump(const byte_t* data, size_t len) From 3411118e26c5c28ece66be5517292f04e00ac619 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 19:36:58 +0800 Subject: [PATCH 06/11] [nstool] Add checks to ensure a reader object is provided to the Process classes. --- programs/nstool/source/NpdmProcess.cpp | 6 ++++++ programs/nstool/source/PfsProcess.cpp | 5 +++++ programs/nstool/source/XciProcess.cpp | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/programs/nstool/source/NpdmProcess.cpp b/programs/nstool/source/NpdmProcess.cpp index 5c1e846..a1a4e2f 100644 --- a/programs/nstool/source/NpdmProcess.cpp +++ b/programs/nstool/source/NpdmProcess.cpp @@ -627,6 +627,12 @@ NpdmProcess::NpdmProcess() : void NpdmProcess::process() { fnd::MemoryBlob scratch; + + if (mReader == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } + scratch.alloc(mReader->size()); mReader->read(scratch.getBytes(), 0, scratch.getSize()); diff --git a/programs/nstool/source/PfsProcess.cpp b/programs/nstool/source/PfsProcess.cpp index 365ef4f..10656d7 100644 --- a/programs/nstool/source/PfsProcess.cpp +++ b/programs/nstool/source/PfsProcess.cpp @@ -109,6 +109,11 @@ PfsProcess::PfsProcess() : void PfsProcess::process() { fnd::MemoryBlob scratch; + + if (mReader == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } // open minimum header to get full header size scratch.alloc(sizeof(nx::sPfsHeader)); diff --git a/programs/nstool/source/XciProcess.cpp b/programs/nstool/source/XciProcess.cpp index d66038b..3e0ae68 100644 --- a/programs/nstool/source/XciProcess.cpp +++ b/programs/nstool/source/XciProcess.cpp @@ -192,6 +192,11 @@ XciProcess::XciProcess() : void XciProcess::process() { fnd::MemoryBlob scratch; + + if (mReader == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } // read header page mReader->read((byte_t*)&mHdrPage, mOffset, sizeof(nx::sXciHeaderPage)); From 2bfe11bf1636156e1954aae67377bc010c561fae Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 19:37:22 +0800 Subject: [PATCH 07/11] [nstool] Add RomFs file previewing. --- programs/nstool/source/RomfsProcess.cpp | 249 ++++++++++++++++++++++++ programs/nstool/source/RomfsProcess.h | 145 ++++++++++++++ programs/nstool/source/main.cpp | 15 +- 3 files changed, 403 insertions(+), 6 deletions(-) create mode 100644 programs/nstool/source/RomfsProcess.cpp 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) { From d017a95a3a7deb0cf4b6839721813aa5e18478bb Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 20:27:18 +0800 Subject: [PATCH 08/11] [fnd|nstool] Replaced io::makePath() with io::appendToPath(). Enabled utf-8 support to io::makeDirectory. --- lib/libfnd/include/fnd/io.h | 8 ++++++- lib/libfnd/source/io.cpp | 29 +++++++++++++------------ programs/nstool/source/UserSettings.cpp | 9 +++----- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/libfnd/include/fnd/io.h b/lib/libfnd/include/fnd/io.h index df31808..58aa895 100644 --- a/lib/libfnd/include/fnd/io.h +++ b/lib/libfnd/include/fnd/io.h @@ -6,6 +6,12 @@ namespace fnd { namespace io { +#ifdef _WIN32 + const std::string kPathDivider = "\\"; +#else + const std::string kPathDivider = "/"; +#endif + size_t getFileSize(const std::string& path); void readFile(const std::string& path, MemoryBlob& blob); void readFile(const std::string& path, size_t offset, size_t len, MemoryBlob& blob); @@ -13,6 +19,6 @@ namespace fnd void writeFile(const std::string& path, const byte_t* data, size_t len); void makeDirectory(const std::string& path); void getEnvironVar(std::string& var, const std::string& key); - void makePath(std::string& out, const std::vector& elements); + void appendToPath(std::string& base, const std::string& add); } } diff --git a/lib/libfnd/source/io.cpp b/lib/libfnd/source/io.cpp index 34b9210..82323a3 100644 --- a/lib/libfnd/source/io.cpp +++ b/lib/libfnd/source/io.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #ifdef _WIN32 @@ -133,7 +134,8 @@ void io::writeFile(const std::string & path, const byte_t * data, size_t len) void io::makeDirectory(const std::string& path) { #ifdef _WIN32 - _mkdir(path.c_str()); + std::u16string wpath = fnd::StringConv::ConvertChar8ToChar16(path); + _wmkdir(wpath.c_str()); #else mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif @@ -163,20 +165,19 @@ void fnd::io::getEnvironVar(std::string & var, const std::string & key) #endif } -void fnd::io::makePath(std::string & out, const std::vector& elements) +void fnd::io::appendToPath(std::string& base, const std::string& add) { - out.clear(); - out = ""; - for (size_t i = 0; i < elements.size(); i++) + if (add.empty()) + return; + + if (base.empty()) { - if (i > 0) - { -#ifdef _WIN32 - out += "\\"; -#else - out += "/"; -#endif - } - out += elements[i]; + base = add; + } + else + { + if (base[base.length()-1] != io::kPathDivider[0]) + base += io::kPathDivider; + base += add; } } diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 2d77146..d95993d 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -264,14 +264,11 @@ void UserSettings::populateKeyset(sCmdArgs& args) const std::string kKeysetNameStr[2] = {"prod.keys", "dev.keys"}; const std::string kHomeSwitchDirStr = ".switch"; - - std::vector path_list; - path_list.push_back(home); - path_list.push_back(kHomeSwitchDirStr); - path_list.push_back(kKeysetNameStr[args.devkit_keys.isSet ? *args.devkit_keys : 0]); std::string keyset_path; - fnd::io::makePath(keyset_path, path_list); + fnd::io::appendToPath(keyset_path, home); + fnd::io::appendToPath(keyset_path, kHomeSwitchDirStr); + fnd::io::appendToPath(keyset_path, kKeysetNameStr[args.devkit_keys.isSet ? *args.devkit_keys : 0]); try { From 322e4d2b483e92946ee954f43a66a7d73cc52f54 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 20:27:47 +0800 Subject: [PATCH 09/11] [nstool] Added RomFs extraction support. --- programs/nstool/source/RomfsProcess.cpp | 56 ++++++++++++++++++++++++- programs/nstool/source/RomfsProcess.h | 3 ++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/programs/nstool/source/RomfsProcess.cpp b/programs/nstool/source/RomfsProcess.cpp index bcde3b3..0c3d8a1 100644 --- a/programs/nstool/source/RomfsProcess.cpp +++ b/programs/nstool/source/RomfsProcess.cpp @@ -54,9 +54,63 @@ void RomfsProcess::displayFs() displayDir(mRootDir, 1); } +void RomfsProcess::extractDir(const std::string& path, const sDirectory& dir) +{ + std::string dir_path; + std::string file_path; + + // make dir path + fnd::io::appendToPath(dir_path, path); + if (dir.name.empty() == false) + fnd::io::appendToPath(dir_path, dir.name); + + //printf("dirpath=[%s]\n", dir_path.c_str()); + + // make directory + fnd::io::makeDirectory(dir_path); + + + // allocate memory for file extraction + fnd::MemoryBlob scratch; + scratch.alloc(kFileExportBlockSize); + + // extract files + fnd::SimpleFile outFile; + for (size_t i = 0; i < dir.file_list.getSize(); i++) + { + file_path.clear(); + fnd::io::appendToPath(file_path, dir_path); + fnd::io::appendToPath(file_path, dir.file_list[i].name); + + if (mCliOutputType >= OUTPUT_VERBOSE) + printf("extract=[%s]\n", file_path.c_str()); + + + outFile.open(file_path, outFile.Create); + mReader->seek(mOffset + dir.file_list[i].offset); + for (size_t j = 0; j < (dir.file_list[i].size / kFileExportBlockSize); j++) + { + mReader->read(scratch.getBytes(), kFileExportBlockSize); + outFile.write(scratch.getBytes(), kFileExportBlockSize); + } + if (dir.file_list[i].size % kFileExportBlockSize) + { + mReader->read(scratch.getBytes(), dir.file_list[i].size % kFileExportBlockSize); + outFile.write(scratch.getBytes(), dir.file_list[i].size % kFileExportBlockSize); + } + outFile.close(); + } + + for (size_t i = 0; i < dir.dir_list.getSize(); i++) + { + extractDir(dir_path, dir.dir_list[i]); + } +} + + void RomfsProcess::extractFs() { - + extractDir(mExtractPath, mRootDir); } bool RomfsProcess::validateHeaderLayout(const nx::sRomfsHeader* hdr) const diff --git a/programs/nstool/source/RomfsProcess.h b/programs/nstool/source/RomfsProcess.h index ace7cdd..42cb54f 100644 --- a/programs/nstool/source/RomfsProcess.h +++ b/programs/nstool/source/RomfsProcess.h @@ -137,6 +137,9 @@ private: void displayHeader(); void displayFs(); + + void extractFile(const std::string& path, const sFile& file); + void extractDir(const std::string& path, const sDirectory& dir); void extractFs(); bool validateHeaderLayout(const nx::sRomfsHeader* hdr) const; From b3d5cd697659ff9430411721260084daa7c31147 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 22:22:19 +0800 Subject: [PATCH 10/11] [fnd] Fix typecast typo. --- lib/libfnd/source/io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libfnd/source/io.cpp b/lib/libfnd/source/io.cpp index 82323a3..9bb966b 100644 --- a/lib/libfnd/source/io.cpp +++ b/lib/libfnd/source/io.cpp @@ -135,7 +135,7 @@ void io::makeDirectory(const std::string& path) { #ifdef _WIN32 std::u16string wpath = fnd::StringConv::ConvertChar8ToChar16(path); - _wmkdir(wpath.c_str()); + _wmkdir((wchar_t*)wpath.c_str()); #else mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif From b6abf658de70f04f11f6c1c4ae092e6c48e09a37 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Apr 2018 22:23:35 +0800 Subject: [PATCH 11/11] [nstool] Update VS files. --- programs/nstool/nstool.vcxproj | 1 + programs/nstool/nstool.vcxproj.filters | 3 +++ 2 files changed, 4 insertions(+) diff --git a/programs/nstool/nstool.vcxproj b/programs/nstool/nstool.vcxproj index 29fdb49..5b877cc 100644 --- a/programs/nstool/nstool.vcxproj +++ b/programs/nstool/nstool.vcxproj @@ -175,6 +175,7 @@ + diff --git a/programs/nstool/nstool.vcxproj.filters b/programs/nstool/nstool.vcxproj.filters index c55aaf4..eebaaf8 100644 --- a/programs/nstool/nstool.vcxproj.filters +++ b/programs/nstool/nstool.vcxproj.filters @@ -56,6 +56,9 @@ Source Files + + Source Files +