nod/lib/DiscBase.cpp

255 lines
8.5 KiB
C++
Raw Normal View History

2015-07-02 18:33:55 +00:00
#include "NOD/DiscBase.hpp"
#include "NOD/IFileIO.hpp"
2016-01-21 06:30:37 +00:00
#include "NOD/DirectoryEnumerator.hpp"
2015-09-28 01:55:59 +00:00
#include "NOD/NOD.hpp"
2015-06-26 20:01:15 +00:00
2016-01-21 06:30:37 +00:00
#include <stdio.h>
2015-09-02 18:53:24 +00:00
#include <errno.h>
#ifndef _WIN32
#include <unistd.h>
#endif
2016-01-21 06:30:37 +00:00
#include <algorithm>
2015-06-26 20:01:15 +00:00
namespace NOD
{
2015-06-30 00:07:46 +00:00
void DiscBase::IPartition::parseFST(IPartReadStream& s)
2015-06-29 05:35:25 +00:00
{
2015-06-30 00:07:46 +00:00
std::unique_ptr<uint8_t[]> fst(new uint8_t[m_fstSz]);
s.seek(m_fstOff);
s.read(fst.get(), m_fstSz);
const FSTNode* nodes = (FSTNode*)fst.get();
/* Root node indicates the count of all contained nodes */
uint32_t nodeCount = nodes[0].getLength();
const char* names = (char*)fst.get() + 12 * nodeCount;
m_nodes.clear();
m_nodes.reserve(nodeCount);
/* Construct nodes */
for (uint32_t n=0 ; n<nodeCount ; ++n)
{
const FSTNode& node = nodes[n];
2015-06-30 06:46:19 +00:00
m_nodes.emplace_back(*this, node, n ? names + node.getNameOffset() : "");
2015-06-30 00:07:46 +00:00
}
/* Setup dir-child iterators */
for (std::vector<Node>::iterator it=m_nodes.begin();
it != m_nodes.end();
++it)
{
Node& node = *it;
2015-11-21 01:15:33 +00:00
if (node.m_kind == Node::Kind::Directory)
2015-06-30 00:07:46 +00:00
{
node.m_childrenBegin = it + 1;
node.m_childrenEnd = m_nodes.begin() + node.m_discLength;
}
}
2015-06-29 05:35:25 +00:00
}
2015-07-10 03:11:30 +00:00
void DiscBase::IPartition::parseDOL(IPartReadStream& s)
{
/* Read Dol header */
2015-09-27 17:10:40 +00:00
DOLHeader dolHeader;
s.read(&dolHeader, sizeof(DOLHeader));
2015-07-10 03:11:30 +00:00
/* Calculate Dol size */
2015-09-27 17:10:40 +00:00
uint32_t dolSize = SBig(dolHeader.textOff[0]);
2015-07-10 03:11:30 +00:00
for (uint32_t i = 0 ; i < 7 ; i++)
2015-09-27 17:10:40 +00:00
dolSize += SBig(dolHeader.textSizes[i]);
2015-07-10 03:11:30 +00:00
for (uint32_t i = 0 ; i < 11 ; i++)
2015-09-27 17:10:40 +00:00
dolSize += SBig(dolHeader.dataSizes[i]);
2015-07-10 03:11:30 +00:00
m_dolSz = dolSize;
}
2016-01-21 06:30:37 +00:00
bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath,
const ExtractionContext& ctx) const
2015-06-30 06:46:19 +00:00
{
2015-07-02 18:33:55 +00:00
SystemStringView nameView(getName());
SystemString path = basePath + _S("/") + nameView.sys_str();
2015-09-28 01:55:59 +00:00
2015-11-21 01:15:33 +00:00
if (m_kind == Kind::Directory)
2015-06-30 06:46:19 +00:00
{
2015-09-28 01:55:59 +00:00
if (ctx.verbose && ctx.progressCB && !getName().empty())
ctx.progressCB(getName());
2015-07-02 20:37:07 +00:00
if (Mkdir(path.c_str(), 0755) && errno != EEXIST)
{
2015-07-26 02:44:44 +00:00
LogModule.report(LogVisor::Error, _S("unable to mkdir '%s'"), path.c_str());
return false;
2015-07-02 20:37:07 +00:00
}
for (Node& subnode : *this)
2015-09-28 01:55:59 +00:00
if (!subnode.extractToDirectory(path, ctx))
2015-07-26 02:44:44 +00:00
return false;
2015-06-30 06:46:19 +00:00
}
2015-11-21 01:15:33 +00:00
else if (m_kind == Kind::File)
2015-06-30 06:46:19 +00:00
{
2015-07-02 20:37:07 +00:00
Sstat theStat;
2015-09-28 01:55:59 +00:00
if (ctx.verbose && ctx.progressCB)
ctx.progressCB(getName());
if (ctx.force || Stat(path.c_str(), &theStat))
2015-06-30 06:46:19 +00:00
{
std::unique_ptr<IPartReadStream> rs = beginReadStream();
2015-07-17 23:28:41 +00:00
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(path)->beginWriteStream();
2015-08-05 21:45:25 +00:00
ws->copyFromDisc(*rs, m_discLength);
2015-06-30 06:46:19 +00:00
}
}
2015-07-26 02:44:44 +00:00
return true;
2015-06-30 06:46:19 +00:00
}
2016-01-21 06:30:37 +00:00
bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
const ExtractionContext& ctx)
{
2015-07-02 20:37:07 +00:00
Sstat theStat;
if (Mkdir(path.c_str(), 0755) && errno != EEXIST)
{
2015-07-26 02:44:44 +00:00
LogModule.report(LogVisor::Error, _S("unable to mkdir '%s'"), path.c_str());
return false;
2015-07-02 20:37:07 +00:00
}
/* Extract Apploader */
2015-07-02 20:37:07 +00:00
SystemString apploaderPath = path + _S("/apploader.bin");
2015-09-28 01:55:59 +00:00
if (ctx.force || Stat(apploaderPath.c_str(), &theStat))
{
2015-09-28 01:55:59 +00:00
if (ctx.verbose && ctx.progressCB)
ctx.progressCB("apploader.bin");
2015-07-10 03:11:30 +00:00
std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
NewFileIO(apploaderPath)->beginWriteStream()->write(buf.get(), m_apploaderSz);
}
2015-07-03 03:49:31 +00:00
/* Extract Dol */
SystemString dolPath = path + _S("/main.dol");
2015-09-28 01:55:59 +00:00
if (ctx.force || Stat(dolPath.c_str(), &theStat))
2015-07-03 03:49:31 +00:00
{
2015-09-28 01:55:59 +00:00
if (ctx.verbose && ctx.progressCB)
ctx.progressCB("main.dol");
2015-07-10 03:11:30 +00:00
std::unique_ptr<uint8_t[]> buf = getDOLBuf();
NewFileIO(dolPath)->beginWriteStream()->write(buf.get(), m_dolSz);
2015-07-03 03:49:31 +00:00
}
/* Extract Filesystem */
2015-09-28 01:55:59 +00:00
return m_nodes[0].extractToDirectory(path, ctx);
}
2016-01-21 06:30:37 +00:00
static uint64_t GetInode(const SystemChar* path)
{
uint64_t inode;
#if _WIN32
OFSTRUCT ofs;
HFILE fp = OpenFile(path, &ofs, OF_READ);
if (fp == HFILE_ERROR)
LogModule.report(LogVisor::FatalError, "unable to open %s", path);
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(fp, &info))
LogModule.report(LogVisor::FatalError, "unable to GetFileInformationByHandle %s", path);
inode = info.nFileIndexHigh << 32;
inode |= info.nFileIndexLow;
CloseHandle(fp);
#else
struct stat st;
if (stat(path, &st))
LogModule.report(LogVisor::FatalError, "unable to stat %s", path);
inode = uint64_t(st.st_ino);
#endif
return inode;
}
void DiscBuilderBase::IPartitionBuilder::recursiveBuildNodes(const SystemChar* dirIn,
uint64_t dolInode,
std::function<void(void)> incParents)
{
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum)
{
if (e.m_isDir)
{
size_t dirNodeIdx = m_buildNodes.size();
m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1);
addBuildName(e.m_name);
incParents();
recursiveBuildNodes(e.m_path.c_str(), dolInode, [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();});
}
else
{
size_t fileSz = ROUND_UP_32(e.m_fileSz);
uint64_t fileOff = userAllocate(fileSz);
if (dolInode == GetInode(e.m_path.c_str()))
m_dolOffset = fileOff;
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(fileOff);
FILE* fp = Fopen(e.m_path.c_str(), _S("rb"), FileLockType::Read);
if (!fp)
LogModule.report(LogVisor::FatalError, "unable to open '%s' for reading", e.m_path.c_str());
char buf[8192];
size_t xferSz = 0;
++m_parent.m_progressIdx;
while (xferSz < e.m_fileSz)
{
size_t rdSz = fread(buf, 1, std::min(8192ul, e.m_fileSz - xferSz), fp);
if (!rdSz)
break;
ws->write(buf, rdSz);
xferSz += rdSz;
m_parent.m_progressCB(m_parent.m_progressIdx, e.m_name, xferSz);
}
fclose(fp);
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
ws->write("\xff", 1);
m_buildNodes.emplace_back(false, m_buildNameOff, fileOff, fileSz);
addBuildName(e.m_name);
incParents();
}
}
}
bool DiscBuilderBase::IPartitionBuilder::buildFromDirectory(const SystemChar* dirIn,
const SystemChar* dolIn,
const SystemChar* apploaderIn)
{
if (!dirIn || !dolIn || !apploaderIn)
LogModule.report(LogVisor::FatalError, "all arguments must be supplied to buildFromDirectory()");
/* Clear file */
m_parent.getFileIO().beginWriteStream();
m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1);
addBuildName(_S("<root>"));
recursiveBuildNodes(dirIn, GetInode(dolIn), [&](){m_buildNodes[0].incrementLength();});
if (!m_dolOffset)
{
Sstat dolStat;
if (Stat(dolIn, &dolStat))
LogModule.report(LogVisor::FatalError, "unable to stat %s", dolIn);
size_t fileSz = ROUND_UP_32(dolStat.st_size);
uint64_t fileOff = userAllocate(fileSz);
m_dolOffset = fileOff;
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(fileOff);
FILE* fp = Fopen(dolIn, _S("rb"), FileLockType::Read);
if (!fp)
LogModule.report(LogVisor::FatalError, "unable to open '%s' for reading", dolIn);
char buf[8192];
size_t xferSz = 0;
SystemString dolName(dolIn);
++m_parent.m_progressIdx;
while (xferSz < dolStat.st_size)
{
size_t rdSz = fread(buf, 1, std::min(8192ul, dolStat.st_size - xferSz), fp);
if (!rdSz)
break;
ws->write(buf, rdSz);
xferSz += rdSz;
m_parent.m_progressCB(m_parent.m_progressIdx, dolName, xferSz);
}
fclose(fp);
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
ws->write("\xff", 1);
}
return true;
}
2015-06-26 20:01:15 +00:00
}