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>
|
2016-01-24 05:06:54 +00:00
|
|
|
|
2015-06-30 19:38:51 +00:00
|
|
|
#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
|
|
|
}
|
2015-06-30 19:38:51 +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-06-30 19:38:51 +00:00
|
|
|
{
|
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
|
|
|
}
|
2015-06-30 19:38:51 +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-06-30 19:38:51 +00:00
|
|
|
{
|
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-06-30 19:38:51 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 03:49:31 +00:00
|
|
|
/* Extract Dol */
|
2016-01-22 23:45:58 +00:00
|
|
|
SystemString dolPath = path + _S("/boot.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)
|
2016-01-22 23:45:58 +00:00
|
|
|
ctx.progressCB("boot.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
|
|
|
}
|
|
|
|
|
2015-06-30 19:38:51 +00:00
|
|
|
/* Extract Filesystem */
|
2015-09-28 01:55:59 +00:00
|
|
|
return m_nodes[0].extractToDirectory(path, ctx);
|
2015-06-30 19:38:51 +00:00
|
|
|
}
|
|
|
|
|
2016-01-21 06:30:37 +00:00
|
|
|
static uint64_t GetInode(const SystemChar* path)
|
|
|
|
{
|
|
|
|
uint64_t inode;
|
|
|
|
#if _WIN32
|
2016-01-24 05:06:54 +00:00
|
|
|
HANDLE fp = CreateFileW(path,
|
|
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
|
nullptr,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
nullptr);
|
|
|
|
if (!fp)
|
2016-01-22 02:30:17 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("unable to open %s"), path);
|
2016-01-21 06:30:37 +00:00
|
|
|
BY_HANDLE_FILE_INFORMATION info;
|
|
|
|
if (!GetFileInformationByHandle(fp, &info))
|
2016-01-22 02:30:17 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("unable to GetFileInformationByHandle %s"), path);
|
2016-01-23 03:39:38 +00:00
|
|
|
inode = uint64_t(info.nFileIndexHigh) << 32;
|
|
|
|
inode |= uint64_t(info.nFileIndexLow);
|
2016-01-21 06:30:37 +00:00
|
|
|
CloseHandle(fp);
|
|
|
|
#else
|
|
|
|
struct stat st;
|
|
|
|
if (stat(path, &st))
|
2016-01-22 02:30:17 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("unable to stat %s"), path);
|
2016-01-21 06:30:37 +00:00
|
|
|
inode = uint64_t(st.st_ino);
|
|
|
|
#endif
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2016-01-23 03:39:38 +00:00
|
|
|
static bool IsSystemFile(const SystemString& name)
|
|
|
|
{
|
|
|
|
if (name.size() < 4)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".dol")))
|
|
|
|
return true;
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".rel")))
|
|
|
|
return true;
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".rso")))
|
|
|
|
return true;
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".sel")))
|
|
|
|
return true;
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".bnr")))
|
|
|
|
return true;
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".elf")))
|
|
|
|
return true;
|
|
|
|
if (!StrCaseCmp((&*name.cend()) - 4, _S(".wad")))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(bool system, const SystemChar* dirIn,
|
|
|
|
uint64_t dolInode)
|
2016-01-21 06:30:37 +00:00
|
|
|
{
|
|
|
|
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();
|
2016-01-23 03:39:38 +00:00
|
|
|
recursiveBuildNodes(system, e.m_path.c_str(), dolInode);
|
2016-01-21 06:30:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-23 03:39:38 +00:00
|
|
|
bool isSys = IsSystemFile(e.m_name);
|
|
|
|
if (system ^ isSys)
|
|
|
|
continue;
|
|
|
|
|
2016-01-22 23:45:58 +00:00
|
|
|
if (dolInode == GetInode(e.m_path.c_str()))
|
|
|
|
continue;
|
|
|
|
|
2016-01-21 06:30:37 +00:00
|
|
|
size_t fileSz = ROUND_UP_32(e.m_fileSz);
|
|
|
|
uint64_t fileOff = userAllocate(fileSz);
|
2016-01-23 03:39:38 +00:00
|
|
|
m_fileOffsetsSizes[e.m_path] = std::make_pair(fileOff, fileSz);
|
2016-01-21 06:30:37 +00:00
|
|
|
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)
|
2016-01-22 02:30:17 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("unable to open '%s' for reading"), e.m_path.c_str());
|
2016-01-22 23:45:58 +00:00
|
|
|
char buf[0x8000];
|
2016-01-21 06:30:37 +00:00
|
|
|
size_t xferSz = 0;
|
|
|
|
++m_parent.m_progressIdx;
|
|
|
|
while (xferSz < e.m_fileSz)
|
|
|
|
{
|
2016-01-24 05:06:54 +00:00
|
|
|
size_t rdSz = fread(buf, 1, NOD::min(size_t(0x8000ul), e.m_fileSz - xferSz), fp);
|
2016-01-21 06:30:37 +00:00
|
|
|
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);
|
2016-01-23 03:39:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(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, dirNodeIdx+1);
|
|
|
|
addBuildName(e.m_name);
|
|
|
|
incParents();
|
|
|
|
recursiveBuildFST(e.m_path.c_str(), dolInode, [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();});
|
|
|
|
}
|
2016-01-23 03:42:50 +00:00
|
|
|
else
|
2016-01-23 03:39:38 +00:00
|
|
|
{
|
|
|
|
if (dolInode == GetInode(e.m_path.c_str()))
|
|
|
|
{
|
|
|
|
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(m_dolOffset), m_dolSize);
|
|
|
|
addBuildName(e.m_name);
|
|
|
|
incParents();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<uint64_t,uint64_t> fileOffSz = m_fileOffsetsSizes.at(e.m_path);
|
|
|
|
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
|
2016-01-21 06:30:37 +00:00
|
|
|
addBuildName(e.m_name);
|
|
|
|
incParents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-23 03:39:38 +00:00
|
|
|
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(const SystemChar* dirIn,
|
2016-01-21 06:30:37 +00:00
|
|
|
const SystemChar* dolIn,
|
|
|
|
const SystemChar* apploaderIn)
|
|
|
|
{
|
|
|
|
if (!dirIn || !dolIn || !apploaderIn)
|
2016-01-24 05:06:54 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("all arguments must be supplied to buildFromDirectory()"));
|
2016-01-21 06:30:37 +00:00
|
|
|
|
|
|
|
/* Clear file */
|
|
|
|
m_parent.getFileIO().beginWriteStream();
|
2016-01-21 20:46:07 +00:00
|
|
|
++m_parent.m_progressIdx;
|
2016-01-24 05:06:54 +00:00
|
|
|
m_parent.m_progressCB(m_parent.m_progressIdx, _S("Preparing output image"), -1);
|
2016-01-21 06:30:37 +00:00
|
|
|
|
2016-01-22 23:45:58 +00:00
|
|
|
/* Add root node */
|
2016-01-21 06:30:37 +00:00
|
|
|
m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1);
|
|
|
|
addBuildName(_S("<root>"));
|
|
|
|
|
2016-01-22 23:45:58 +00:00
|
|
|
/* Write DOL first (ensures that it's within a 32-bit offset for Wii apploaders) */
|
2016-01-21 06:30:37 +00:00
|
|
|
{
|
|
|
|
Sstat dolStat;
|
|
|
|
if (Stat(dolIn, &dolStat))
|
2016-01-22 02:30:17 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("unable to stat %s"), dolIn);
|
2016-01-21 06:30:37 +00:00
|
|
|
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
|
|
|
uint64_t fileOff = userAllocate(fileSz);
|
|
|
|
m_dolOffset = fileOff;
|
2016-01-22 23:45:58 +00:00
|
|
|
m_dolSize = fileSz;
|
2016-01-21 06:30:37 +00:00
|
|
|
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(fileOff);
|
|
|
|
FILE* fp = Fopen(dolIn, _S("rb"), FileLockType::Read);
|
|
|
|
if (!fp)
|
2016-01-22 02:30:17 +00:00
|
|
|
LogModule.report(LogVisor::FatalError, _S("unable to open '%s' for reading"), dolIn);
|
2016-01-21 06:30:37 +00:00
|
|
|
char buf[8192];
|
|
|
|
size_t xferSz = 0;
|
|
|
|
SystemString dolName(dolIn);
|
|
|
|
++m_parent.m_progressIdx;
|
|
|
|
while (xferSz < dolStat.st_size)
|
|
|
|
{
|
2016-01-23 23:36:58 +00:00
|
|
|
size_t rdSz = fread(buf, 1, std::min(size_t(8192), size_t(dolStat.st_size - xferSz)), fp);
|
2016-01-21 06:30:37 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-01-22 23:45:58 +00:00
|
|
|
/* Gather files in root directory */
|
2016-01-23 03:39:38 +00:00
|
|
|
uint64_t dolInode = GetInode(dolIn);
|
|
|
|
recursiveBuildNodes(true, dirIn, dolInode);
|
|
|
|
recursiveBuildNodes(false, dirIn, dolInode);
|
|
|
|
recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();});
|
2016-01-22 23:45:58 +00:00
|
|
|
|
2016-01-21 06:30:37 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-26 20:01:15 +00:00
|
|
|
}
|