2018-08-13 17:14:21 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
2018-04-25 11:37:22 +00:00
|
|
|
#include <fnd/SimpleTextOutput.h>
|
|
|
|
#include <fnd/SimpleFile.h>
|
|
|
|
#include <fnd/io.h>
|
2018-05-12 15:02:53 +00:00
|
|
|
#include "RomfsProcess.h"
|
2018-04-25 11:37:22 +00:00
|
|
|
|
2018-05-26 13:13:21 +00:00
|
|
|
RomfsProcess::RomfsProcess() :
|
2018-09-23 03:29:22 +00:00
|
|
|
mFile(),
|
2018-06-18 15:30:19 +00:00
|
|
|
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
2018-05-26 13:13:21 +00:00
|
|
|
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()
|
|
|
|
{
|
|
|
|
resolveRomfs();
|
2018-08-13 17:14:21 +00:00
|
|
|
|
2018-06-18 15:30:19 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
|
|
|
|
{
|
2018-05-26 13:13:21 +00:00
|
|
|
displayHeader();
|
2018-06-18 15:30:19 +00:00
|
|
|
if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
|
|
|
|
displayFs();
|
|
|
|
}
|
2018-08-13 17:14:21 +00:00
|
|
|
|
2018-05-26 13:13:21 +00:00
|
|
|
if (mExtract)
|
2018-06-24 15:01:16 +00:00
|
|
|
extractFs();
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
|
2018-09-23 03:29:22 +00:00
|
|
|
void RomfsProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-06-03 08:48:12 +00:00
|
|
|
mFile = file;
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 15:30:19 +00:00
|
|
|
void RomfsProcess::setCliOutputMode(CliOutputMode type)
|
2018-05-26 13:13:21 +00:00
|
|
|
{
|
2018-06-18 15:30:19 +00:00
|
|
|
mCliOutputMode = type;
|
2018-05-26 13:13:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-04-25 11:37:22 +00:00
|
|
|
void RomfsProcess::printTab(size_t tab) const
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < tab; i++)
|
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << " ";
|
2018-04-25 11:37:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RomfsProcess::displayFile(const sFile& file, size_t tab) const
|
|
|
|
{
|
|
|
|
printTab(tab);
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << file.name;
|
2018-06-18 15:30:19 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")";
|
2018-04-25 11:37:22 +00:00
|
|
|
}
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << std::endl;
|
2018-04-25 11:37:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const
|
|
|
|
{
|
|
|
|
if (dir.name.empty() == false)
|
|
|
|
{
|
|
|
|
printTab(tab);
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << dir.name << std::endl;
|
2018-04-25 11:37:22 +00:00
|
|
|
}
|
|
|
|
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < dir.dir_list.size(); i++)
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
|
|
|
displayDir(dir.dir_list[i], tab+1);
|
|
|
|
}
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < dir.file_list.size(); i++)
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
|
|
|
displayFile(dir.file_list[i], tab+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RomfsProcess::displayHeader()
|
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << "[RomFS]" << std::endl;
|
|
|
|
std::cout << " DirNum: " << std::dec << mDirNum << std::endl;
|
|
|
|
std::cout << " FileNum: " << std::dec << mFileNum << std::endl;
|
|
|
|
if (mMountName.empty() == false)
|
|
|
|
{
|
|
|
|
std::cout << " MountPoint: " << mMountName;
|
|
|
|
if (mMountName.at(mMountName.length()-1) != '/')
|
|
|
|
std::cout << "/";
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
2018-04-25 11:37:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RomfsProcess::displayFs()
|
|
|
|
{
|
|
|
|
displayDir(mRootDir, 1);
|
|
|
|
}
|
|
|
|
|
2018-04-25 12:27:47 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
// make directory
|
|
|
|
fnd::io::makeDirectory(dir_path);
|
|
|
|
|
|
|
|
// extract files
|
|
|
|
fnd::SimpleFile outFile;
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < dir.file_list.size(); i++)
|
2018-04-25 12:27:47 +00:00
|
|
|
{
|
|
|
|
file_path.clear();
|
|
|
|
fnd::io::appendToPath(file_path, dir_path);
|
|
|
|
fnd::io::appendToPath(file_path, dir.file_list[i].name);
|
|
|
|
|
2018-06-18 15:30:19 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
|
2018-08-13 17:14:21 +00:00
|
|
|
std::cout << "extract=[" << file_path << "]" << std::endl;
|
2018-04-25 12:27:47 +00:00
|
|
|
|
|
|
|
outFile.open(file_path, outFile.Create);
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->seek(dir.file_list[i].offset);
|
2018-05-27 10:17:34 +00:00
|
|
|
for (size_t j = 0; j < ((dir.file_list[i].size / kCacheSize) + ((dir.file_list[i].size % kCacheSize) != 0)); j++)
|
2018-04-25 12:27:47 +00:00
|
|
|
{
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize));
|
2018-06-24 15:01:16 +00:00
|
|
|
outFile.write(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize));
|
2018-05-27 10:17:34 +00:00
|
|
|
}
|
2018-04-25 12:27:47 +00:00
|
|
|
outFile.close();
|
|
|
|
}
|
|
|
|
|
2018-06-24 15:01:16 +00:00
|
|
|
for (size_t i = 0; i < dir.dir_list.size(); i++)
|
2018-04-25 12:27:47 +00:00
|
|
|
{
|
|
|
|
extractDir(dir_path, dir.dir_list[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-25 11:37:22 +00:00
|
|
|
void RomfsProcess::extractFs()
|
|
|
|
{
|
2018-05-26 13:13:21 +00:00
|
|
|
// allocate only when extractDir is invoked
|
2018-05-27 10:17:34 +00:00
|
|
|
mCache.alloc(kCacheSize);
|
2018-04-25 12:27:47 +00:00
|
|
|
extractDir(mExtractPath, mRootDir);
|
2018-04-25 11:37:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
bool RomfsProcess::validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
|
|
|
bool validLayout = true;
|
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
if (hdr->header_size.get() != sizeof(nn::hac::sRomfsHeader))
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
|
|
|
validLayout = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t pos = hdr->sections[0].offset.get();
|
2018-08-07 07:17:51 +00:00
|
|
|
for (size_t i = 0; i < nn::hac::romfs::SECTION_NUM; i++)
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2018-08-07 07:17:51 +00:00
|
|
|
nn::hac::sRomfsDirEntry* d_node = get_dir_node(dir_offset);
|
2018-04-25 11:37:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
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);
|
|
|
|
*/
|
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
for (uint32_t file_addr = d_node->file.get(); file_addr != nn::hac::romfs::kInvalidAddr; )
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
2018-08-07 07:17:51 +00:00
|
|
|
nn::hac::sRomfsFileEntry* f_node = get_file_node(file_addr);
|
2018-04-25 11:37:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
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);
|
|
|
|
*/
|
|
|
|
|
2018-05-26 03:26:56 +00:00
|
|
|
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()});
|
2018-04-25 11:37:22 +00:00
|
|
|
|
|
|
|
file_addr = f_node->sibling.get();
|
|
|
|
mFileNum++;
|
|
|
|
}
|
|
|
|
|
2018-08-07 07:17:51 +00:00
|
|
|
for (uint32_t child_addr = d_node->child.get(); child_addr != nn::hac::romfs::kInvalidAddr; )
|
2018-04-25 11:37:22 +00:00
|
|
|
{
|
2018-08-07 07:17:51 +00:00
|
|
|
nn::hac::sRomfsDirEntry* c_node = get_dir_node(child_addr);
|
2018-04-25 11:37:22 +00:00
|
|
|
|
2018-05-26 03:26:56 +00:00
|
|
|
dir.dir_list.addElement({std::string(c_node->name(), c_node->name_size.get())});
|
2018-04-25 11:37:22 +00:00
|
|
|
importDirectory(child_addr, dir.dir_list.atBack());
|
|
|
|
|
|
|
|
child_addr = c_node->sibling.get();
|
|
|
|
mDirNum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RomfsProcess::resolveRomfs()
|
|
|
|
{
|
2018-09-23 03:29:22 +00:00
|
|
|
if (*mFile == nullptr)
|
2018-08-13 17:14:21 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
|
|
|
}
|
|
|
|
|
2018-04-25 11:37:22 +00:00
|
|
|
// read header
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read((byte_t*)&mHdr, 0, sizeof(nn::hac::sRomfsHeader));
|
2018-04-25 11:37:22 +00:00
|
|
|
|
|
|
|
// logic check on the header layout
|
|
|
|
if (validateHeaderLayout(&mHdr) == false)
|
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "Invalid ROMFS Header");
|
|
|
|
}
|
|
|
|
|
|
|
|
// read directory nodes
|
2018-08-07 07:17:51 +00:00
|
|
|
mDirNodes.alloc(mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].size.get());
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(mDirNodes.data(), mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].offset.get(), mDirNodes.size());
|
2018-04-25 11:37:22 +00:00
|
|
|
//printf("[RAW DIR NODES]\n");
|
2018-06-24 15:01:16 +00:00
|
|
|
//fnd::SimpleTextOutput::hxdStyleDump(mDirNodes.data(), mDirNodes.size());
|
2018-04-25 11:37:22 +00:00
|
|
|
|
|
|
|
// read file nodes
|
2018-08-07 07:17:51 +00:00
|
|
|
mFileNodes.alloc(mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get());
|
2018-09-23 03:29:22 +00:00
|
|
|
(*mFile)->read(mFileNodes.data(), mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get(), mFileNodes.size());
|
2018-04-25 11:37:22 +00:00
|
|
|
//printf("[RAW FILE NODES]\n");
|
2018-06-24 15:01:16 +00:00
|
|
|
//fnd::SimpleTextOutput::hxdStyleDump(mFileNodes.data(), mFileNodes.size());
|
2018-04-25 11:37:22 +00:00
|
|
|
|
|
|
|
// A logic check on the root directory node
|
|
|
|
if ( get_dir_node(0)->parent.get() != 0 \
|
2018-08-07 07:17:51 +00:00
|
|
|
|| get_dir_node(0)->sibling.get() != nn::hac::romfs::kInvalidAddr \
|
|
|
|
|| get_dir_node(0)->hash.get() != nn::hac::romfs::kInvalidAddr \
|
2018-04-25 11:37:22 +00:00
|
|
|
|| 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);
|
|
|
|
}
|