Major refactor, better error handling and directory/image merge

This commit is contained in:
Jack Andersen 2017-02-04 20:19:34 -10:00
parent b3a76428da
commit dc474ad156
13 changed files with 1502 additions and 276 deletions

View File

@ -8,7 +8,9 @@ static void printHelp()
fprintf(stderr, "Usage:\n"
" nodtool extract [-f] <image-in> [<dir-out>]\n"
" nodtool makegcn <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> [<image-out>]\n"
" nodtool makewii(sl|dl) <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> <parthead-in> [<image-out>]\n");
" nodtool makewii <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> <parthead-in> [<image-out>]\n"
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n");
}
#if NOD_UCS2
@ -25,8 +27,9 @@ int main(int argc, char* argv[])
{
if (argc < 3 ||
(!strcasecmp(argv[1], _S("makegcn")) && argc < 7) ||
(!strcasecmp(argv[1], _S("makewiisl")) && argc < 8) ||
(!strcasecmp(argv[1], _S("makewiidl")) && argc < 8))
(!strcasecmp(argv[1], _S("makewii")) && argc < 8) ||
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) ||
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4))
{
printHelp();
return 1;
@ -55,6 +58,21 @@ int main(int argc, char* argv[])
outDir = argv[a];
}
size_t lastIdx = -1;
auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
{
if (idx != lastIdx)
{
lastIdx = idx;
printf("\n");
}
if (bytes != -1)
nod::Printf(_S("\r%s %" PRISize " B"), name.c_str(), bytes);
else
nod::Printf(_S("\r%s"), name.c_str());
fflush(stdout);
};
if (!strcasecmp(argv[1], _S("extract")))
{
bool isWii;
@ -79,39 +97,43 @@ int main(int argc, char* argv[])
{
#if NOD_UCS2
if (wcslen(argv[2]) < 6)
nod::LogModule.report(logvisor::Fatal, _S("game-id is not at least 6 characters"));
{
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
return 1;
}
#else
if (strlen(argv[2]) < 6)
nod::LogModule.report(logvisor::Fatal, _S("game-id is not at least 6 characters"));
{
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
return 1;
}
#endif
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, _S("unable to stat %s as directory"), argv[4]);
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
return 1;
}
if (nod::Stat(argv[5], &theStat) || !S_ISREG(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, _S("unable to stat %s as file"), argv[5]);
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[5]);
return 1;
}
if (nod::Stat(argv[6], &theStat) || !S_ISREG(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, "unable to stat %s as file", argv[6]);
{
nod::LogModule.report(logvisor::Error, "unable to stat %s as file", argv[6]);
return 1;
}
nod::SystemString gameIdSys(argv[2]);
nod::SystemUTF8View gameId(gameIdSys);
nod::SystemString gameTitleSys(argv[3]);
nod::SystemUTF8View gameTitle(gameTitleSys);
size_t lastIdx = -1;
auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
{
if (idx != lastIdx)
{
lastIdx = idx;
printf("\n");
}
if (bytes != -1)
nod::Printf(_S("\r%s %" PRISize " B"), name.c_str(), bytes);
else
nod::Printf(_S("\r%s"), name.c_str());
fflush(stdout);
};
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[4], argv[5]) == -1)
return 1;
bool ret;
@ -132,47 +154,54 @@ int main(int argc, char* argv[])
if (!ret)
return 1;
}
else if (!strcasecmp(argv[1], _S("makewiisl")) || !strcasecmp(argv[1], _S("makewiidl")))
else if (!strcasecmp(argv[1], _S("makewii")))
{
#if NOD_UCS2
if (wcslen(argv[2]) < 6)
nod::LogModule.report(logvisor::Fatal, _S("game-id is not at least 6 characters"));
{
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
return 1;
}
#else
if (strlen(argv[2]) < 6)
nod::LogModule.report(logvisor::Fatal, _S("game-id is not at least 6 characters"));
{
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
return 1;
}
#endif
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, _S("unable to stat %s as directory"), argv[4]);
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
return 1;
}
if (nod::Stat(argv[5], &theStat) || !S_ISREG(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, _S("unable to stat %s as file"), argv[5]);
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[5]);
return 1;
}
if (nod::Stat(argv[6], &theStat) || !S_ISREG(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, _S("unable to stat %s as file"), argv[6]);
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[6]);
return 1;
}
if (nod::Stat(argv[7], &theStat) || !S_ISREG(theStat.st_mode))
nod::LogModule.report(logvisor::Fatal, _S("unable to stat %s as file"), argv[7]);
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[7]);
return 1;
}
nod::SystemString gameIdSys(argv[2]);
nod::SystemUTF8View gameId(gameIdSys);
nod::SystemString gameTitleSys(argv[3]);
nod::SystemUTF8View gameTitle(gameTitleSys);
size_t lastIdx = -1;
auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
{
if (idx != lastIdx)
{
lastIdx = idx;
printf("\n");
}
if (bytes != -1)
nod::Printf(_S("\r%s %" PRISize " B"), name.c_str(), bytes);
else
nod::Printf(_S("\r%s"), name.c_str());
fflush(stdout);
};
bool dual = (argv[1][7] == _S('d') || argv[1][7] == _S('D'));
bool dual = false;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[4], argv[5], dual) == -1)
return 1;
bool ret;
if (argc < 9)
@ -192,12 +221,114 @@ int main(int argc, char* argv[])
if (!ret)
return 1;
}
else if (!strcasecmp(argv[1], _S("mergegcn")))
{
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1;
}
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[3]);
return 1;
}
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
if (!disc)
{
nod::LogModule.report(logvisor::Error, _S("unable to open image %s"), argv[3]);
return 1;
}
if (isWii)
{
nod::LogModule.report(logvisor::Error, _S("Wii images should be merged with 'mergewii'"));
return 1;
}
if (nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), argv[2]) == -1)
return 1;
bool ret;
if (argc < 5)
{
nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso"));
nod::DiscMergerGCN b(outPath.c_str(), static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
else
{
nod::DiscMergerGCN b(argv[4], static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
printf("\n");
if (!ret)
return 1;
}
else if (!strcasecmp(argv[1], _S("mergewii")))
{
/* Pre-validate paths */
nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1;
}
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode))
{
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[3]);
return 1;
}
bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
if (!disc)
{
nod::LogModule.report(logvisor::Error, _S("unable to open image %s"), argv[3]);
return 1;
}
if (!isWii)
{
nod::LogModule.report(logvisor::Error, _S("GameCube images should be merged with 'mergegcn'"));
return 1;
}
bool dual = false;
if (nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), argv[2], dual) == -1)
return 1;
bool ret;
if (argc < 5)
{
nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso"));
nod::DiscMergerWii b(outPath.c_str(), static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
else
{
nod::DiscMergerWii b(argv[4], static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
printf("\n");
if (!ret)
return 1;
}
else
{
printHelp();
return 1;
}
nod::LogModule.report(logvisor::Info, _S("Success!"));
return 0;
}

View File

@ -15,6 +15,8 @@
namespace nod
{
using FProgress = std::function<void(size_t, const SystemString&, size_t)>;
class FSTNode
{
uint32_t typeAndNameOffset;
@ -53,9 +55,14 @@ struct Header
uint32_t m_gcnMagic;
char m_gameTitle[64];
Header(IDiscIO& dio)
Header(IDiscIO& dio, bool& err)
{
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0);
if (!s)
{
err = true;
return;
}
s->read(this, sizeof(*this));
m_wiiMagic = SBig(m_wiiMagic);
m_gcnMagic = SBig(m_gcnMagic);
@ -212,6 +219,7 @@ public:
uint64_t m_dolOff;
uint64_t m_fstOff;
uint64_t m_fstSz;
uint64_t m_fstMemoryAddr;
uint64_t m_apploaderSz;
std::vector<Node> m_nodes;
void parseFST(IPartReadStream& s);
@ -260,6 +268,8 @@ public:
return buf;
}
inline uint64_t getFSTMemoryAddr() const {return m_fstMemoryAddr;}
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
{
@ -274,8 +284,8 @@ protected:
Header m_header;
std::vector<std::unique_ptr<IPartition>> m_partitions;
public:
DiscBase(std::unique_ptr<IDiscIO>&& dio)
: m_discIO(std::move(dio)), m_header(*m_discIO) {}
DiscBase(std::unique_ptr<IDiscIO>&& dio, bool& err)
: m_discIO(std::move(dio)), m_header(*m_discIO, err) {}
inline const Header& getHeader() const {return m_header;}
inline const IDiscIO& getDiscIO() const {return *m_discIO;}
@ -302,6 +312,7 @@ public:
class DiscBuilderBase
{
friend class DiscMergerWii;
public:
class PartitionBuilderBase
{
@ -320,9 +331,18 @@ public:
size_t m_buildNameOff = 0;
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
virtual uint32_t packOffset(uint64_t offset) const=0;
void recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode);
void recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode);
bool recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
std::function<void(void)> incParents);
bool recursiveMergeNodes(IPartWriteStream& ws, bool system,
const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn,
const SystemString& keyPath);
bool recursiveMergeFST(const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn, std::function<void(void)> incParents,
const SystemString& keyPath);
static bool RecursiveCalculateTotalSize(uint64_t& totalSz,
const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn);
void addBuildName(const SystemString& str)
{
SystemUTF8View utf8View(str);
@ -348,23 +368,32 @@ public:
bool buildFromDirectory(IPartWriteStream& ws,
const SystemChar* dirIn, const SystemChar* dolIn,
const SystemChar* apploaderIn);
static uint64_t CalculateTotalSizeBuild(const SystemChar* dolIn,
const SystemChar* dirIn);
bool mergeFromDirectory(IPartWriteStream& ws,
const DiscBase::IPartition* partIn,
const SystemChar* dirIn);
static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
const SystemChar* dirIn);
const char* getGameID() const {return m_gameID;}
const std::string& getGameTitle() const {return m_gameTitle;}
};
protected:
const SystemChar* m_outPath;
SystemString m_outPath;
std::unique_ptr<IFileIO> m_fileIO;
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions;
int64_t m_discCapacity;
public:
std::function<void(size_t idx, const SystemString&, size_t)> m_progressCB;
size_t m_progressIdx = 0;
virtual ~DiscBuilderBase() {}
virtual ~DiscBuilderBase() = default;
DiscBuilderBase(const SystemChar* outPath, int64_t discCapacity,
std::function<void(size_t idx, const SystemString&, size_t)> progressCB)
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)),
m_discCapacity(discCapacity), m_progressCB(progressCB) {}
DiscBuilderBase(DiscBuilderBase&&) = default;
DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
IFileIO& getFileIO() {return *m_fileIO;}
};

View File

@ -5,20 +5,35 @@
namespace nod
{
class DiscBuilderGCN;
class DiscGCN : public DiscBase
{
friend class DiscMergerGCN;
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
public:
DiscGCN(std::unique_ptr<IDiscIO>&& dio);
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
};
class DiscBuilderGCN : public DiscBuilderBase
{
friend class DiscMergerGCN;
public:
DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
uint32_t fstMemoryAddr, std::function<void(size_t, const SystemString&, size_t)> progressCB);
uint32_t fstMemoryAddr, FProgress progressCB);
bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
const SystemChar* apploaderIn);
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn);
};
class DiscMergerGCN
{
DiscGCN& m_sourceDisc;
DiscBuilderGCN m_builder;
public:
DiscMergerGCN(const SystemChar* outPath, DiscGCN& sourceDisc, FProgress progressCB);
bool mergeFromDirectory(const SystemChar* dirIn);
static uint64_t CalculateTotalSizeRequired(DiscGCN& sourceDisc, const SystemChar* dirIn);
};
}

View File

@ -5,24 +5,40 @@
namespace nod
{
class DiscBuilderWii;
class DiscWii : public DiscBase
{
public:
DiscWii(std::unique_ptr<IDiscIO>&& dio);
void writeOutDataPartitionHeader(const SystemChar* pathOut) const;
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
bool writeOutDataPartitionHeader(const SystemChar* pathOut) const;
};
class DiscBuilderWii : public DiscBuilderBase
{
bool m_dualLayer;
public:
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, bool dualLayer,
std::function<void(size_t, const SystemString&, size_t)> progressCB);
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
bool dualLayer, FProgress progressCB);
bool buildFromDirectory(const SystemChar* dirIn,
const SystemChar* dolIn,
const SystemChar* apploaderIn,
const SystemChar* partHeadIn);
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
bool& dualLayer);
};
class DiscMergerWii
{
DiscWii& m_sourceDisc;
DiscBuilderWii m_builder;
public:
DiscMergerWii(const SystemChar* outPath, DiscWii& sourceDisc,
bool dualLayer, FProgress progressCB);
bool mergeFromDirectory(const SystemChar* dirIn);
static uint64_t CalculateTotalSizeRequired(DiscWii& sourceDisc, const SystemChar* dirIn,
bool& dualLayer);
};
}

View File

@ -309,16 +309,25 @@ static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz)
wchar_t* end;
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
if (!ret || ret > 1024)
LogModule.report(logvisor::Fatal, _S("GetFullPathNameW %s"), path);
{
LogModule.report(logvisor::Error, _S("GetFullPathNameW %s"), path);
return false;
}
if (end)
end[0] = L'\0';
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr))
LogModule.report(logvisor::Fatal, _S("GetDiskFreeSpaceExW %s: %d"), path, GetLastError());
{
LogModule.report(logvisor::Error, _S("GetDiskFreeSpaceExW %s: %d"), path, GetLastError());
return false;
}
return reqSz < freeBytes.QuadPart;
#else
struct statvfs svfs;
if (statvfs(path, &svfs))
LogModule.report(logvisor::Fatal, "statvfs %s: %s", path, strerror(errno));
{
LogModule.report(logvisor::Error, "statvfs %s: %s", path, strerror(errno));
return false;
}
return reqSz < svfs.f_frsize * svfs.f_bavail;
#endif
}

View File

@ -118,6 +118,8 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
{
std::unique_ptr<IPartReadStream> rs = beginReadStream();
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(path)->beginWriteStream();
if (!rs || !ws)
return false;
ws->copyFromDisc(*rs, m_discLength);
}
}
@ -141,7 +143,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
if (ctx.verbose && ctx.progressCB)
ctx.progressCB("apploader.bin");
std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
NewFileIO(apploaderPath)->beginWriteStream()->write(buf.get(), m_apploaderSz);
auto ws = NewFileIO(apploaderPath)->beginWriteStream();
if (!ws)
return false;
ws->write(buf.get(), m_apploaderSz);
}
/* Extract Dol */
@ -151,7 +156,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
if (ctx.verbose && ctx.progressCB)
ctx.progressCB("boot.dol");
std::unique_ptr<uint8_t[]> buf = getDOLBuf();
NewFileIO(dolPath)->beginWriteStream()->write(buf.get(), m_dolSz);
auto ws = NewFileIO(dolPath)->beginWriteStream();
if (!ws)
return false;
ws->write(buf.get(), m_dolSz);
}
/* Extract Filesystem */
@ -177,17 +185,26 @@ static uint64_t GetInode(const SystemChar* path)
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (!fp)
LogModule.report(logvisor::Fatal, _S("unable to open %s"), path);
{
LogModule.report(logvisor::Error, _S("unable to open %s"), path);
return 0;
}
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(fp, &info))
LogModule.report(logvisor::Fatal, _S("unable to GetFileInformationByHandle %s"), path);
{
LogModule.report(logvisor::Error, _S("unable to GetFileInformationByHandle %s"), path);
return 0;
}
inode = uint64_t(info.nFileIndexHigh) << 32;
inode |= uint64_t(info.nFileIndexLow);
CloseHandle(fp);
#else
struct stat st;
if (stat(path, &st))
LogModule.report(logvisor::Fatal, _S("unable to stat %s"), path);
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), path);
return 0;
}
inode = uint64_t(st.st_ino);
#endif
return inode;
@ -222,11 +239,9 @@ static bool IsSystemFile(const SystemString& name, bool& isDol)
/** Patches out pesky #001 integrity check performed by game's OSInit.
* This is required for multi-DOL games, but doesn't harm functionality otherwise */
static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t sz, bool& patched)
static void PatchDOL(std::unique_ptr<uint8_t[]>& buf, size_t sz, bool& patched)
{
patched = false;
std::unique_ptr<uint8_t[]> buf(new uint8_t[sz]);
sz = in.read(buf.get(), sz);
uint8_t* found = static_cast<uint8_t*>(memmem(buf.get(), sz,
"\x3C\x03\xF8\x00\x28\x00\x00\x00\x40\x82\x00\x0C"
"\x38\x60\x00\x01\x48\x00\x02\x44\x38\x61\x00\x18\x48", 25));
@ -235,10 +250,17 @@ static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t s
found[11] = '\x04';
patched = true;
}
}
static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t sz, bool& patched)
{
std::unique_ptr<uint8_t[]> buf(new uint8_t[sz]);
sz = in.read(buf.get(), sz);
PatchDOL(buf, sz, patched);
return out.write(buf.get(), sz);
}
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
bool system,
const SystemChar* dirIn,
uint64_t dolInode)
@ -248,7 +270,8 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
{
if (e.m_isDir)
{
recursiveBuildNodes(ws, system, e.m_path.c_str(), dolInode);
if (!recursiveBuildNodes(ws, system, e.m_path.c_str(), dolInode))
return false;
}
else
{
@ -262,8 +285,12 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
size_t fileSz = ROUND_UP_32(e.m_fileSz);
uint64_t fileOff = userAllocate(fileSz, ws);
if (fileOff == -1)
return false;
m_fileOffsetsSizes[e.m_path] = std::make_pair(fileOff, fileSz);
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(e.m_path)->beginReadStream();
if (!rs)
return false;
size_t xferSz = 0;
if (isDol)
{
@ -289,9 +316,11 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
ws.write("\xff", 1);
}
}
return true;
}
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
std::function<void(void)> incParents)
{
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
@ -303,7 +332,8 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar*
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();});
if (!recursiveBuildFST(e.m_path.c_str(), dolInode, [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}))
return false;
}
else
{
@ -321,6 +351,328 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar*
incParents();
}
}
return true;
}
bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream& ws,
bool system,
const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn,
const SystemString& keyPath)
{
/* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Partition::Node*> fileNodes;
std::unordered_map<std::string, const Partition::Node*> dirNodes;
if (nodeIn)
{
fileNodes.reserve(nodeIn->size());
dirNodes.reserve(nodeIn->size());
for (const Partition::Node& ch : *nodeIn)
{
if (ch.getKind() == Partition::Node::Kind::File)
fileNodes.insert(std::make_pair(ch.getName(), &ch));
else if (ch.getKind() == Partition::Node::Kind::Directory)
dirNodes.insert(std::make_pair(ch.getName(), &ch));
}
}
/* Merge this directory's files */
if (dirIn)
{
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum)
{
SystemUTF8View nameView(e.m_name);
SystemString chKeyPath = keyPath + _S('/') + e.m_name;
if (e.m_isDir)
{
auto search = dirNodes.find(nameView.utf8_str());
if (search != dirNodes.cend())
{
if (!recursiveMergeNodes(ws, system, search->second, e.m_path.c_str(), chKeyPath))
return false;
dirNodes.erase(search);
}
else
{
if (!recursiveMergeNodes(ws, system, nullptr, e.m_path.c_str(), chKeyPath))
return false;
}
}
else
{
bool isDol;
bool isSys = IsSystemFile(e.m_name, isDol);
if (system ^ isSys)
continue;
fileNodes.erase(nameView.utf8_str());
size_t fileSz = ROUND_UP_32(e.m_fileSz);
uint64_t fileOff = userAllocate(fileSz, ws);
if (fileOff == -1)
return false;
m_fileOffsetsSizes[chKeyPath] = std::make_pair(fileOff, fileSz);
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(e.m_path)->beginReadStream();
if (!rs)
return false;
size_t xferSz = 0;
if (isDol)
{
bool patched;
xferSz = PatchDOL(*rs, ws, e.m_fileSz, patched);
m_parent.m_progressCB(++m_parent.m_progressIdx, e.m_name +
(patched ? _S(" [PATCHED]") : _S("")), xferSz);
}
else
{
char buf[0x8000];
++m_parent.m_progressIdx;
while (xferSz < e.m_fileSz)
{
size_t rdSz = rs->read(buf, nod::min(size_t(0x8000ul), e.m_fileSz - xferSz));
if (!rdSz)
break;
ws.write(buf, rdSz);
xferSz += rdSz;
m_parent.m_progressCB(m_parent.m_progressIdx, e.m_name, xferSz);
}
}
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
ws.write("\xff", 1);
}
}
}
/* Write-through remaining dir nodes */
for (const auto& p : dirNodes)
{
SystemStringView sysName(p.second->getName());
SystemString chKeyPath = keyPath + _S('/') + sysName.sys_str();
if (!recursiveMergeNodes(ws, system, p.second, nullptr, chKeyPath))
return false;
}
/* Write-through remaining file nodes */
for (const auto& p : fileNodes)
{
const Partition::Node& ch = *p.second;
SystemStringView sysName(ch.getName());
SystemString chKeyPath = keyPath + _S('/') + sysName.sys_str();
bool isDol;
bool isSys = IsSystemFile(sysName.sys_str(), isDol);
if (system ^ isSys)
continue;
size_t fileSz = ROUND_UP_32(ch.size());
uint64_t fileOff = userAllocate(fileSz, ws);
if (fileOff == -1)
return false;
m_fileOffsetsSizes[chKeyPath] = std::make_pair(fileOff, fileSz);
std::unique_ptr<IPartReadStream> rs = ch.beginReadStream();
if (!rs)
return false;
size_t xferSz = 0;
if (isDol)
{
xferSz = ch.size();
std::unique_ptr<uint8_t[]> dolBuf = ch.getBuf();
bool patched;
PatchDOL(dolBuf, xferSz, patched);
ws.write(dolBuf.get(), xferSz);
m_parent.m_progressCB(++m_parent.m_progressIdx, sysName.sys_str() +
(patched ? _S(" [PATCHED]") : _S("")), xferSz);
}
else
{
char buf[0x8000];
++m_parent.m_progressIdx;
while (xferSz < ch.size())
{
size_t rdSz = rs->read(buf, nod::min(size_t(0x8000), size_t(ch.size() - xferSz)));
if (!rdSz)
break;
ws.write(buf, rdSz);
xferSz += rdSz;
m_parent.m_progressCB(m_parent.m_progressIdx, sysName.sys_str(), xferSz);
}
}
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
ws.write("\xff", 1);
}
return true;
}
bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Partition::Node* nodeIn,
const SystemChar* dirIn,
std::function<void(void)> incParents,
const SystemString& keyPath)
{
/* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Partition::Node*> fileNodes;
std::unordered_map<std::string, const Partition::Node*> dirNodes;
if (nodeIn)
{
fileNodes.reserve(nodeIn->size());
dirNodes.reserve(nodeIn->size());
for (const Partition::Node& ch : *nodeIn)
{
if (ch.getKind() == Partition::Node::Kind::File)
fileNodes.insert(std::make_pair(ch.getName(), &ch));
else if (ch.getKind() == Partition::Node::Kind::Directory)
dirNodes.insert(std::make_pair(ch.getName(), &ch));
}
}
/* Merge this directory's files */
if (dirIn)
{
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum)
{
SystemUTF8View nameView(e.m_name);
SystemString chKeyPath = keyPath + _S('/') + e.m_name;
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();
auto search = dirNodes.find(nameView.utf8_str());
if (search != dirNodes.cend())
{
if (!recursiveMergeFST(search->second, e.m_path.c_str(),
[&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();},
chKeyPath))
return false;
dirNodes.erase(search);
}
else
{
if (!recursiveMergeFST(nullptr, e.m_path.c_str(),
[&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();},
chKeyPath))
return false;
}
}
else
{
fileNodes.erase(nameView.utf8_str());
std::pair<uint64_t,uint64_t> fileOffSz = m_fileOffsetsSizes.at(chKeyPath);
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
addBuildName(e.m_name);
incParents();
}
}
}
/* Write-through remaining dir nodes */
for (const auto& p : dirNodes)
{
SystemStringView sysName(p.second->getName());
SystemString chKeyPath = keyPath + _S('/') + sysName.sys_str();
size_t dirNodeIdx = m_buildNodes.size();
m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1);
addBuildName(sysName.sys_str());
incParents();
if (!recursiveMergeFST(p.second, nullptr,
[&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();},
chKeyPath))
return false;
}
/* Write-through remaining file nodes */
for (const auto& p : fileNodes)
{
const Partition::Node& ch = *p.second;
SystemStringView sysName(ch.getName());
SystemString chKeyPath = keyPath + _S('/') + sysName.sys_str();
std::pair<uint64_t,uint64_t> fileOffSz = m_fileOffsetsSizes.at(chKeyPath);
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
addBuildName(sysName.sys_str());
incParents();
}
return true;
}
bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(
uint64_t& totalSz,
const DiscBase::IPartition::Node* nodeIn,
const SystemChar* dirIn)
{
/* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Partition::Node*> fileNodes;
std::unordered_map<std::string, const Partition::Node*> dirNodes;
if (nodeIn)
{
fileNodes.reserve(nodeIn->size());
dirNodes.reserve(nodeIn->size());
for (const Partition::Node& ch : *nodeIn)
{
if (ch.getKind() == Partition::Node::Kind::File)
fileNodes.insert(std::make_pair(ch.getName(), &ch));
else if (ch.getKind() == Partition::Node::Kind::Directory)
dirNodes.insert(std::make_pair(ch.getName(), &ch));
}
}
/* Merge this directory's files */
if (dirIn)
{
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum)
{
SystemUTF8View nameView(e.m_name);
if (e.m_isDir)
{
auto search = dirNodes.find(nameView.utf8_str());
if (search != dirNodes.cend())
{
if (!RecursiveCalculateTotalSize(totalSz, search->second, e.m_path.c_str()))
return false;
dirNodes.erase(search);
}
else
{
if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str()))
return false;
}
}
else
{
fileNodes.erase(nameView.utf8_str());
totalSz += ROUND_UP_32(e.m_fileSz);
}
}
}
/* Write-through remaining dir nodes */
for (const auto& p : dirNodes)
{
if (!RecursiveCalculateTotalSize(totalSz, p.second, nullptr))
return false;
}
/* Write-through remaining file nodes */
for (const auto& p : fileNodes)
{
const Partition::Node& ch = *p.second;
totalSz += ROUND_UP_32(ch.size());
}
return true;
}
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws,
@ -329,7 +681,10 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
const SystemChar* apploaderIn)
{
if (!dirIn || !dolIn || !apploaderIn)
LogModule.report(logvisor::Fatal, _S("all arguments must be supplied to buildFromDirectory()"));
{
LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()"));
return false;
}
/* Clear file */
++m_parent.m_progressIdx;
@ -343,26 +698,110 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
{
Sstat dolStat;
if (Stat(dolIn, &dolStat))
LogModule.report(logvisor::Fatal, _S("unable to stat %s"), dolIn);
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn);
return false;
}
size_t fileSz = ROUND_UP_32(dolStat.st_size);
uint64_t fileOff = userAllocate(fileSz, ws);
if (fileOff == -1)
return false;
m_dolOffset = fileOff;
m_dolSize = fileSz;
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn)->beginReadStream();
if (!rs)
return false;
bool patched;
size_t xferSz = PatchDOL(*rs, ws, dolStat.st_size, patched);
m_parent.m_progressCB(++m_parent.m_progressIdx, SystemString(dolIn) + (patched ? _S(" [PATCHED]") : _S("")), xferSz);
m_parent.m_progressCB(++m_parent.m_progressIdx, SystemString(dolIn) +
(patched ? _S(" [PATCHED]") : _S("")), xferSz);
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
ws.write("\xff", 1);
}
/* Gather files in root directory */
uint64_t dolInode = GetInode(dolIn);
recursiveBuildNodes(ws, true, dirIn, dolInode);
recursiveBuildNodes(ws, false, dirIn, dolInode);
recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();});
if (!recursiveBuildNodes(ws, true, dirIn, dolInode))
return false;
if (!recursiveBuildNodes(ws, false, dirIn, dolInode))
return false;
if (!recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();}))
return false;
return true;
}
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dolIn,
const SystemChar* dirIn)
{
Sstat dolStat;
if (Stat(dolIn, &dolStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn);
return -1;
}
uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
if (!RecursiveCalculateTotalSize(totalSz, nullptr, dirIn))
return -1;
return totalSz;
}
bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& ws,
const nod::Partition* partIn,
const SystemChar* dirIn)
{
if (!dirIn)
{
LogModule.report(logvisor::Error, _S("all arguments must be supplied to mergeFromDirectory()"));
return false;
}
/* Clear file */
++m_parent.m_progressIdx;
m_parent.m_progressCB(m_parent.m_progressIdx, _S("Preparing output image"), -1);
/* Add root node */
m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1);
addBuildName(_S("<root>"));
/* Write Boot DOL first (first thing seeked to after Apploader) */
{
size_t xferSz = partIn->getDOLSize();
size_t fileSz = ROUND_UP_32(xferSz);
uint64_t fileOff = userAllocate(fileSz, ws);
if (fileOff == -1)
return false;
m_dolOffset = fileOff;
m_dolSize = fileSz;
std::unique_ptr<uint8_t[]> dolBuf = partIn->getDOLBuf();
bool patched;
PatchDOL(dolBuf, xferSz, patched);
ws.write(dolBuf.get(), xferSz);
m_parent.m_progressCB(++m_parent.m_progressIdx, SystemString(_S("<boot-dol>")) +
(patched ? _S(" [PATCHED]") : _S("")), xferSz);
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
ws.write("\xff", 1);
}
/* Gather files in root directory */
SystemString keyPath;
if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), dirIn, keyPath))
return false;
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), dirIn, keyPath))
return false;
if (!recursiveMergeFST(&partIn->getFSTRoot(), dirIn, [&](){m_buildNodes[0].incrementLength();}, keyPath))
return false;
return true;
}
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
const SystemChar* dirIn)
{
uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize());
if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), dirIn))
return -1;
return totalSz;
}
}

View File

@ -1,4 +1,5 @@
#include "nod/DiscGCN.hpp"
#include <inttypes.h>
#define BUFFER_SZ 0x8000
namespace nod
@ -7,16 +8,22 @@ namespace nod
class PartitionGCN : public DiscBase::IPartition
{
public:
PartitionGCN(const DiscGCN& parent, Kind kind, uint64_t offset)
PartitionGCN(const DiscGCN& parent, Kind kind, uint64_t offset, bool& err)
: IPartition(parent, kind, offset)
{
/* GCN-specific header reads */
std::unique_ptr<IPartReadStream> s = beginReadStream(0x420);
uint32_t vals[3];
s->read(vals, 12);
if (!s)
{
err = true;
return;
}
uint32_t vals[5];
s->read(vals, 5 * 4);
m_dolOff = SBig(vals[0]);
m_fstOff = SBig(vals[1]);
m_fstSz = SBig(vals[2]);
m_fstMemoryAddr = SBig(vals[4]);
s->seek(0x2440 + 0x14);
s->read(vals, 8);
m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]);
@ -39,11 +46,16 @@ public:
uint8_t m_buf[BUFFER_SZ];
public:
PartReadStream(const PartitionGCN& parent, uint64_t offset)
PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err)
: m_parent(parent), m_offset(offset)
{
size_t block = m_offset / BUFFER_SZ;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ);
if (!m_dio)
{
err = true;
return;
}
m_dio->read(m_buf, BUFFER_SZ);
m_curBlock = block;
}
@ -98,15 +110,29 @@ public:
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, offset));
bool Err = false;
auto ret = std::unique_ptr<IPartReadStream>(new PartReadStream(*this, offset, Err));
if (Err)
return {};
return ret;
}
};
DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio)
: DiscBase(std::move(dio))
DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err)
: DiscBase(std::move(dio), err)
{
if (err)
return;
/* One lone partition for GCN */
m_partitions.emplace_back(new PartitionGCN(*this, IPartition::Kind::Data, 0));
m_partitions.emplace_back(new PartitionGCN(*this, IPartition::Kind::Data, 0, err));
}
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB)
{
IPartition* dataPart = getDataPartition();
return DiscBuilderGCN(outPath, m_header.m_gameID, m_header.m_gameTitle,
dataPart->getFSTMemoryAddr(), progressCB);
}
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase
@ -122,10 +148,12 @@ public:
std::unique_ptr<IFileIO::IWriteStream> m_fio;
public:
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset)
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err)
: m_parent(parent), m_offset(offset)
{
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset);
if (!m_fio)
err = true;
}
void close() {m_fio.reset();}
uint64_t position() const {return m_offset;}
@ -152,7 +180,7 @@ public:
m_curUser &= 0xfffffffffffffff0;
if (m_curUser < 0x30000)
{
LogModule.report(logvisor::Fatal, "user area low mark reached");
LogModule.report(logvisor::Error, "user area low mark reached");
return -1;
}
static_cast<PartWriteStream&>(ws).seek(m_curUser);
@ -166,38 +194,25 @@ public:
std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)
{
return std::make_unique<PartWriteStream>(*this, offset);
bool Err = false;
std::unique_ptr<IPartWriteStream> ret = std::make_unique<PartWriteStream>(*this, offset, Err);
if (Err)
return {};
return ret;
}
bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, const SystemChar* apploaderIn)
bool _build(const std::function<bool(IPartWriteStream&, size_t&)>& func)
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn);
if (!result)
if (!ws)
return false;
ws = beginWriteStream(0);
Header header(m_gameID, m_gameTitle.c_str(), false);
header.write(*ws);
ws = beginWriteStream(0x2440);
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
char buf[8192];
size_t xferSz = 0;
SystemString apploaderName(apploaderIn);
++m_parent.m_progressIdx;
while (true)
{
size_t rdSz = rs->read(buf, 8192);
if (!rdSz)
break;
ws->write(buf, rdSz);
xferSz += rdSz;
if (0x2440 + xferSz >= m_curUser)
LogModule.report(logvisor::Fatal,
"apploader flows into user area (one or the other is too big)");
m_parent.m_progressCB(m_parent.m_progressIdx, apploaderName, xferSz);
}
if (!func(*ws, xferSz))
return false;
size_t fstOff = ROUND_UP_32(xferSz);
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
@ -211,10 +226,15 @@ public:
fstSz = ROUND_UP_32(fstSz);
if (fstOff + fstSz >= m_curUser)
LogModule.report(logvisor::Fatal,
{
LogModule.report(logvisor::Error,
"FST flows into user area (one or the other is too big)");
return false;
}
ws = beginWriteStream(0x420);
if (!ws)
return false;
uint32_t vals[7];
vals[0] = SBig(uint32_t(m_dolOffset));
vals[1] = SBig(uint32_t(fstOff));
@ -227,28 +247,109 @@ public:
return true;
}
bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, const SystemChar* apploaderIn)
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws)
return false;
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn);
if (!result)
return false;
return _build([this, apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
if (!rs)
return false;
char buf[8192];
SystemString apploaderName(apploaderIn);
++m_parent.m_progressIdx;
while (true)
{
size_t rdSz = rs->read(buf, 8192);
if (!rdSz)
break;
ws.write(buf, rdSz);
xferSz += rdSz;
if (0x2440 + xferSz >= m_curUser)
{
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false;
}
m_parent.m_progressCB(m_parent.m_progressIdx, apploaderName, xferSz);
}
return true;
});
}
bool mergeFromDirectory(const PartitionGCN* partIn, const SystemChar* dirIn)
{
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws)
return false;
bool result = DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(*ws, partIn, dirIn);
if (!result)
return false;
return _build([this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool
{
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize();
SystemString apploaderName(_S("<apploader>"));
++m_parent.m_progressIdx;
ws.write(apploaderBuf.get(), apploaderSz);
xferSz += apploaderSz;
if (0x2440 + xferSz >= m_curUser)
{
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false;
}
m_parent.m_progressCB(m_parent.m_progressIdx, apploaderName, xferSz);
return true;
});
}
};
bool DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
const SystemChar* apploaderIn)
{
m_fileIO->beginWriteStream();
if (!CheckFreeSpace(m_outPath, 0x57058000))
if (!m_fileIO->beginWriteStream())
return false;
if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000))
{
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_outPath);
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_outPath.c_str());
return false;
}
++m_progressIdx;
m_progressCB(m_progressIdx, _S("Preallocating image"), -1);
m_fileIO->beginWriteStream(0x57058000 - 1)->write("", 1);
auto ws = m_fileIO->beginWriteStream(0x57058000 - 1);
if (!ws)
return false;
ws->write("", 1);
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
return pb.buildFromDirectory(dirIn, dolIn, apploaderIn);
}
uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn)
{
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn);
if (sz == -1)
return -1;
sz += 0x30000;
if (sz > 0x57058000)
{
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000);
return -1;
}
return sz;
}
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
uint32_t fstMemoryAddr, std::function<void(size_t, const SystemString&, size_t)> progressCB)
uint32_t fstMemoryAddr, FProgress progressCB)
: DiscBuilderBase(outPath, 0x57058000, progressCB)
{
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this, PartitionBuilderBase::Kind::Data,
@ -256,4 +357,43 @@ DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, const char gameID[6],
m_partitions.emplace_back(partBuilder);
}
DiscMergerGCN::DiscMergerGCN(const SystemChar* outPath, DiscGCN& sourceDisc, FProgress progressCB)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB))
{}
bool DiscMergerGCN::mergeFromDirectory(const SystemChar* dirIn)
{
if (!m_builder.getFileIO().beginWriteStream())
return false;
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000))
{
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_builder.m_outPath.c_str());
return false;
}
++m_builder.m_progressIdx;
m_builder.m_progressCB(m_builder.m_progressIdx, _S("Preallocating image"), -1);
auto ws = m_builder.m_fileIO->beginWriteStream(0x57058000 - 1);
if (!ws)
return false;
ws->write("", 1);
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_builder.m_partitions[0]);
return pb.mergeFromDirectory(static_cast<PartitionGCN*>(m_sourceDisc.getDataPartition()), dirIn);
}
uint64_t DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, const SystemChar* dirIn)
{
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(
sourceDisc.getDataPartition(), dirIn);
if (sz == -1)
return -1;
sz += 0x30000;
if (sz > 0x57058000)
{
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000);
return -1;
}
return sz;
}
}

View File

@ -17,8 +17,8 @@ public:
{
friend class DiscIOISO;
std::unique_ptr<IFileIO::IReadStream> fp;
ReadStream(std::unique_ptr<IFileIO::IReadStream>&& fpin)
: fp(std::move(fpin)) {}
ReadStream(std::unique_ptr<IFileIO::IReadStream>&& fpin, bool& err)
: fp(std::move(fpin)) { if (!fp) err = true; }
public:
uint64_t read(void* buf, uint64_t length)
{return fp->read(buf, length);}
@ -30,15 +30,19 @@ public:
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IReadStream>(new ReadStream(m_fio->beginReadStream(offset)));
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_fio->beginReadStream(offset), Err));
if (Err)
return {};
return ret;
}
class WriteStream : public IWriteStream
{
friend class DiscIOISO;
std::unique_ptr<IFileIO::IWriteStream> fp;
WriteStream(std::unique_ptr<IFileIO::IWriteStream>&& fpin)
: fp(std::move(fpin)) {}
WriteStream(std::unique_ptr<IFileIO::IWriteStream>&& fpin, bool& err)
: fp(std::move(fpin)) { if (!fp) err = true; }
public:
uint64_t write(const void* buf, uint64_t length)
{
@ -48,7 +52,11 @@ public:
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const
{
return std::unique_ptr<IWriteStream>(new WriteStream(m_fio->beginWriteStream(offset)));
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_fio->beginWriteStream(offset), Err));
if (Err)
return {};
return ret;
}
};

View File

@ -80,7 +80,7 @@ class DiscIOWBFS : public IDiscIO
rs.seek(off, SEEK_SET);
if (rs.read(buf, count*512ULL) != count*512ULL)
{
LogModule.report(logvisor::Fatal, "error reading disc");
LogModule.report(logvisor::Error, "error reading disc");
return 1;
}
return 0;
@ -93,19 +93,25 @@ public:
/* Temporary file handle to read LBA table */
std::unique_ptr<IFileIO> fio = NewFileIO(filepath);
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
if (!rs)
return;
WBFS* p = &wbfs;
WBFSHead tmpHead;
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead))
LogModule.report(logvisor::Fatal, "unable to read WBFS head");
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) {
LogModule.report(logvisor::Error, "unable to read WBFS head");
return;
}
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
unsigned num_hd_sector = SBig(tmpHead.n_hd_sec);
wbfsHead.reset(new uint8_t[hd_sector_size]);
WBFSHead* head = (WBFSHead*)wbfsHead.get();
rs->seek(0, SEEK_SET);
if (rs->read(head, hd_sector_size) != hd_sector_size)
LogModule.report(logvisor::Fatal, "unable to read WBFS head");
if (rs->read(head, hd_sector_size) != hd_sector_size) {
LogModule.report(logvisor::Error, "unable to read WBFS head");
return;
}
//constants, but put here for consistancy
p->wii_sec_sz = 0x8000;
@ -113,12 +119,15 @@ public:
p->n_wii_sec = (num_hd_sector/0x8000)*hd_sector_size;
p->n_wii_sec_per_disc = 143432*2;//support for double layers discs..
p->part_lba = 0;
_wbfsReadSector(*rs, p->part_lba, 1, head);
if (_wbfsReadSector(*rs, p->part_lba, 1, head))
return;
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) {
LogModule.report(logvisor::Fatal, "hd sector size doesn't match");
LogModule.report(logvisor::Error, "hd sector size doesn't match");
return;
}
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
LogModule.report(logvisor::Fatal, "hd num sector doesn't match");
LogModule.report(logvisor::Error, "hd num sector doesn't match");
return;
}
p->hd_sec_sz = 1<<head->hd_sec_sz_s;
p->hd_sec_sz_s = head->hd_sec_sz_s;
@ -145,9 +154,12 @@ public:
if (head->disc_table[0])
{
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
if (!wbfsDiscInfo)
LogModule.report(logvisor::Fatal, "allocating memory");
_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get());
if (!wbfsDiscInfo) {
LogModule.report(logvisor::Error, "allocating memory");
return;
}
if (_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get()))
return;
p->n_disc_open++;
//for(i=0;i<p->n_wbfs_sec_per_disc;i++)
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
@ -162,11 +174,11 @@ public:
uint64_t m_offset;
std::unique_ptr<uint8_t[]> m_tmpBuffer;
ReadStream(const DiscIOWBFS& parent, std::unique_ptr<IFileIO::IReadStream>&& fpin, uint64_t offset)
ReadStream(const DiscIOWBFS& parent, std::unique_ptr<IFileIO::IReadStream>&& fpin, uint64_t offset, bool& err)
: m_parent(parent),
fp(std::move(fpin)),
m_offset(offset),
m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) {}
m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) { if (!fp) err = true; }
int wbfsReadSector(uint32_t lba, uint32_t count, void* buf)
{return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);}
@ -271,7 +283,11 @@ public:
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IReadStream>(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset));
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset, Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const

View File

@ -1,5 +1,7 @@
#include <stdio.h>
#include <string.h>
#include <cstdlib>
#include <inttypes.h>
#include "nod/DiscWii.hpp"
#include "nod/aes.hpp"
#include "nod/sha1.h"
@ -191,10 +193,16 @@ class PartitionWii : public DiscBase::IPartition
uint8_t m_decKey[16];
public:
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset)
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err)
: IPartition(parent, kind, offset)
{
std::unique_ptr<IDiscIO::IReadStream> s = parent.getDiscIO().beginReadStream(offset);
if (!s)
{
err = true;
return;
}
m_ticket.read(*s);
uint32_t tmdSize;
@ -240,6 +248,12 @@ public:
/* Wii-specific header reads (now using title key to decrypt) */
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420);
if (!ds)
{
err = true;
return;
}
uint32_t vals[3];
ds->read(vals, 12);
m_dolOff = SBig(vals[0]) << 2;
@ -275,12 +289,17 @@ public:
m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00);
}
public:
PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset)
PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset, bool& err)
: m_aes(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset)
{
m_aes->setKey(parent.m_decKey);
size_t block = m_offset / 0x7c00;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
if (!m_dio)
{
err = true;
return;
}
decryptBlock();
m_curBlock = block;
}
@ -335,20 +354,59 @@ public:
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, m_dataOff, offset));
bool Err = false;
auto ret = std::unique_ptr<IPartReadStream>(new PartReadStream(*this, m_dataOff, offset, Err));
if (Err)
return {};
return ret;
}
uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset << 2;}
void writeOutPartitionHeader(const SystemChar* pathOut) const
std::unique_ptr<uint8_t[]> readPartitionHeaderBuf(size_t& szOut) const
{
{
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
if (!rs)
return {};
uint32_t h3;
if (rs->read(&h3, 4) != 4)
{
LogModule.report(logvisor::Error, _S("unable to read H3 offset apploader"));
return {};
}
h3 = SBig(h3);
szOut = uint64_t(h3) << 2;
}
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
if (!rs)
return {};
std::unique_ptr<uint8_t[]> buf(new uint8_t[szOut]);
rs->read(buf.get(), szOut);
return buf;
}
bool writeOutPartitionHeader(const SystemChar* pathOut) const
{
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(pathOut)->beginWriteStream();
if (!ws)
return false;
uint64_t h3Off;
{
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
if (!rs)
return false;
uint32_t h3;
if (rs->read(&h3, 4) != 4)
LogModule.report(logvisor::Fatal, _S("unable to read H3 offset from %s"), pathOut);
{
LogModule.report(logvisor::Error, _S("unable to read H3 offset to %s"), pathOut);
return false;
}
h3 = SBig(h3);
h3Off = uint64_t(h3) << 2;
}
@ -356,6 +414,9 @@ public:
char buf[8192];
size_t rem = h3Off;
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
if (!rs)
return false;
while (rem)
{
size_t rdSz = nod::min(rem, size_t(8192ul));
@ -363,12 +424,17 @@ public:
ws->write(buf, rdSz);
rem -= rdSz;
}
return true;
}
};
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
: DiscBase(std::move(dio))
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
: DiscBase(std::move(dio), err)
{
if (err)
return;
/* Read partition info */
struct PartInfo
{
@ -379,9 +445,15 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
uint32_t partDataOff;
IPartition::Kind partType;
} parts[4];
PartInfo(IDiscIO& dio)
PartInfo(IDiscIO& dio, bool& err)
{
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000);
if (!s)
{
err = true;
return;
}
s->read(this, 32);
partCount = SBig(partCount);
partInfoOff = SBig(partInfoOff);
@ -394,7 +466,9 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
parts[p].partType = IPartition::Kind(SBig(uint32_t(parts[p].partType)));
}
}
} partInfo(*m_discIO);
} partInfo(*m_discIO, err);
if (err)
return;
/* Iterate for data partition */
m_partitions.reserve(partInfo.partCount);
@ -410,22 +484,32 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
kind = part.partType;
break;
default:
LogModule.report(logvisor::Fatal, "invalid partition type %d", part.partType);
LogModule.report(logvisor::Error, "invalid partition type %d", part.partType);
err = true;
return;
}
m_partitions.emplace_back(new PartitionWii(*this, kind, part.partDataOff << 2));
m_partitions.emplace_back(new PartitionWii(*this, kind, part.partDataOff << 2, err));
if (err)
return;
}
}
void DiscWii::writeOutDataPartitionHeader(const SystemChar* pathOut) const
DiscBuilderWii DiscWii::makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
{
return DiscBuilderWii(outPath, m_header.m_gameID, m_header.m_gameTitle,
dualLayer, progressCB);
}
bool DiscWii::writeOutDataPartitionHeader(const SystemChar* pathOut) const
{
for (const std::unique_ptr<IPartition>& part : m_partitions)
{
if (part->getKind() == IPartition::Kind::Data)
{
static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
break;
return static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
}
}
return false;
}
static const uint8_t ZEROIV[16] = {0};
@ -433,6 +517,8 @@ static const uint8_t ZEROIV[16] = {0};
class PartitionBuilderWii : public DiscBuilderBase::PartitionBuilderBase
{
friend class DiscBuilderWii;
friend class DiscMergerWii;
uint64_t m_baseOffset;
uint64_t m_userOffset = 0;
uint64_t m_curUser = 0x1F0000;
@ -512,17 +598,26 @@ public:
}
if (m_fio->write(m_buf, 0x200000) != 0x200000)
LogModule.report(logvisor::Fatal, "unable to write full disc group");
{
LogModule.report(logvisor::Error, "unable to write full disc group");
return;
}
}
public:
PartWriteStream(PartitionBuilderWii& parent, uint64_t baseOffset, uint64_t offset)
PartWriteStream(PartitionBuilderWii& parent, uint64_t baseOffset, uint64_t offset, bool& err)
: m_parent(parent), m_baseOffset(baseOffset), m_offset(offset)
{
if (offset % 0x1F0000)
LogModule.report(logvisor::Fatal, "partition write stream MUST begin on 0x1F0000-aligned boundary");
{
LogModule.report(logvisor::Error, "partition write stream MUST begin on 0x1F0000-aligned boundary");
err = true;
return;
}
size_t group = m_offset / 0x1F0000;
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(m_baseOffset + group * 0x200000);
if (!m_fio)
err = true;
m_curGroup = group;
}
~PartWriteStream() {close();}
@ -597,14 +692,14 @@ public:
reqSz = ROUND_UP_32(reqSz);
if (m_curUser + reqSz >= 0x1FB450000)
{
LogModule.report(logvisor::Fatal, "partition exceeds maximum single-partition capacity");
LogModule.report(logvisor::Error, "partition exceeds maximum single-partition capacity");
return -1;
}
uint64_t ret = m_curUser;
PartWriteStream& cws = static_cast<PartWriteStream&>(ws);
if (cws.m_offset > ret)
{
LogModule.report(logvisor::Fatal, "partition overwrite error");
LogModule.report(logvisor::Error, "partition overwrite error");
return -1;
}
while (cws.m_offset < ret)
@ -620,86 +715,109 @@ public:
std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)
{
return std::make_unique<PartWriteStream>(*this, m_baseOffset + m_userOffset, offset);
bool Err = false;
std::unique_ptr<IPartWriteStream> ret =
std::make_unique<PartWriteStream>(*this, m_baseOffset + m_userOffset, offset, Err);
if (Err)
return {};
return ret;
}
uint64_t buildFromDirectory(const SystemChar* dirIn,
const SystemChar* dolIn,
const SystemChar* apploaderIn,
const SystemChar* partHeadIn)
uint64_t _build(const std::function<bool(IPartWriteStream&)>& contentFunc,
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc,
const uint8_t* phBuf, size_t phSz, size_t apploaderSz)
{
/* Read head and validate key members */
std::unique_ptr<IFileIO> ph = NewFileIO(partHeadIn);
uint8_t tkey[16];
{
if (ph->beginReadStream(0x1BF)->read(tkey, 16) != 16)
LogModule.report(logvisor::Fatal, _S("unable to read title key from %s"), partHeadIn);
if (0x1BF + 16 > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read title key"));
return -1;
}
memmove(tkey, phBuf + 0x1BF, 16);
}
uint8_t tkeyiv[16] = {};
{
if (ph->beginReadStream(0x1DC)->read(tkeyiv, 8) != 8)
LogModule.report(logvisor::Fatal, _S("unable to read title key IV from %s"), partHeadIn);
if (0x1DC + 8 > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read title key IV"));
return -1;
}
memmove(tkeyiv, phBuf + 0x1DC, 8);
}
uint8_t ccIdx;
{
if (ph->beginReadStream(0x1F1)->read(&ccIdx, 1) != 1)
LogModule.report(logvisor::Fatal, _S("unable to read common key index from %s"), partHeadIn);
if (0x1F1 + 1 > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read common key index"));
return -1;
}
memmove(&ccIdx, phBuf + 0x1F1, 1);
if (ccIdx > 1)
LogModule.report(logvisor::Fatal, _S("common key index may only be 0 or 1"));
{
LogModule.report(logvisor::Error, _S("common key index may only be 0 or 1"));
return -1;
}
}
uint32_t tmdSz;
{
if (ph->beginReadStream(0x2A4)->read(&tmdSz, 4) != 4)
LogModule.report(logvisor::Fatal, _S("unable to read TMD size from %s"), partHeadIn);
if (0x2A4 + 4 > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read TMD size"));
return -1;
}
memmove(&tmdSz, phBuf + 0x2A4, 4);
tmdSz = SBig(tmdSz);
}
uint64_t h3Off;
{
uint32_t h3Ptr;
if (ph->beginReadStream(0x2B4)->read(&h3Ptr, 4) != 4)
LogModule.report(logvisor::Fatal, _S("unable to read H3 pointer from %s"), partHeadIn);
if (0x2B4 + 4 > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read H3 pointer"));
return -1;
}
memmove(&h3Ptr, phBuf + 0x2B4, 4);
h3Off = uint64_t(SBig(h3Ptr)) << 2;
}
uint64_t dataOff;
{
uint32_t dataPtr;
if (ph->beginReadStream(0x2B8)->read(&dataPtr, 4) != 4)
LogModule.report(logvisor::Fatal, _S("unable to read data pointer from %s"), partHeadIn);
if (0x2B8 + 4 > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read data pointer"));
return -1;
}
memmove(&dataPtr, phBuf + 0x2B8, 4);
dataOff = uint64_t(SBig(dataPtr)) << 2;
}
m_userOffset = dataOff;
std::unique_ptr<uint8_t[]> tmdData(new uint8_t[tmdSz]);
if (ph->beginReadStream(0x2C0)->read(tmdData.get(), tmdSz) != tmdSz)
LogModule.report(logvisor::Fatal, _S("unable to read TMD from %s"), partHeadIn);
{
if (0x2C0 + tmdSz > phSz)
{
LogModule.report(logvisor::Error, _S("unable to read TMD"));
return -1;
}
memmove(tmdData.get(), phBuf + 0x2C0, tmdSz);
}
/* Copy partition head up to H3 table */
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
{
uint64_t remCopy = h3Off;
uint8_t copyBuf[8192];
std::unique_ptr<IFileIO::IReadStream> rs = ph->beginReadStream();
while (remCopy)
{
size_t rdBytes = rs->read(copyBuf, std::min(size_t(8192), size_t(remCopy)));
if (rdBytes)
{
ws->write(copyBuf, rdBytes);
remCopy -= rdBytes;
continue;
}
for (size_t i=0 ; i<remCopy ; ++i)
ws->write("", 1);
break;
}
}
if (!ws)
return -1;
size_t copySz = std::min(phSz, size_t(h3Off));
ws->write(phBuf, copySz);
size_t remCopy = (h3Off > phSz) ? (h3Off - copySz) : 0;
for (size_t i=0 ; i<remCopy ; ++i)
ws->write("", 1);
/* Prepare crypto pass */
m_aes->setKey(COMMON_KEYS[ccIdx]);
@ -709,9 +827,10 @@ public:
{
/* Assemble partition data */
std::unique_ptr<IPartWriteStream> cws = beginWriteStream(0x1F0000);
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*cws, dirIn, dolIn, apploaderIn);
if (!result)
return 0;
if (!cws)
return -1;
if (!contentFunc(*cws))
return -1;
/* Pad out user area to nearest cleartext sector */
m_curUser = cws->position();
@ -726,23 +845,23 @@ public:
/* Begin crypto write and add content header */
cws = beginWriteStream(0);
if (!cws)
return -1;
Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0);
header.write(*cws);
/* Get Apploader Size */
Sstat theStat;
if (Stat(apploaderIn, &theStat))
LogModule.report(logvisor::Fatal, _S("unable to stat %s"), apploaderIn);
/* Compute boot table members and write */
size_t fstOff = 0x2440 + ROUND_UP_32(theStat.st_size);
size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz);
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
fstSz += m_buildNameOff;
fstSz = ROUND_UP_32(fstSz);
if (fstOff + fstSz >= 0x1F0000)
LogModule.report(logvisor::Fatal,
{
LogModule.report(logvisor::Error,
"FST flows into user area (one or the other is too big)");
return -1;
}
cws->write(nullptr, 0x420 - sizeof(Header));
uint32_t vals[4];
@ -752,29 +871,16 @@ public:
vals[3] = SBig(uint32_t(fstSz));
cws->write(vals, 16);
/* Write Apploader */
cws->write(nullptr, 0x2440 - 0x430);
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
char buf[8192];
size_t xferSz = 0;
SystemString apploaderName(apploaderIn);
++m_parent.m_progressIdx;
while (true)
{
size_t rdSz = rs->read(buf, 8192);
if (!rdSz)
break;
cws->write(buf, rdSz);
xferSz += rdSz;
if (0x2440 + xferSz >= 0x1F0000)
LogModule.report(logvisor::Fatal,
"apploader flows into user area (one or the other is too big)");
m_parent.m_progressCB(m_parent.m_progressIdx, apploaderName, xferSz);
}
if (!apploaderFunc(*cws, xferSz))
return -1;
size_t fstOffRel = fstOff - 0x2440;
if (xferSz > fstOffRel)
LogModule.report(logvisor::Fatal, "apploader unexpectedly flows into FST");
{
LogModule.report(logvisor::Error, "apploader unexpectedly flows into FST");
return -1;
}
for (size_t i=0 ; i<fstOffRel-xferSz ; ++i)
cws->write("\xff", 1);
@ -789,22 +895,26 @@ public:
uint64_t cryptContentSize = (groupCount * 0x200000) >> uint64_t(2);
uint32_t cryptContentSizeBig = SBig(uint32_t(cryptContentSize));
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2BC);
if (!ws)
return -1;
ws->write(&cryptContentSizeBig, 0x4);
/* Write new H3 */
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + h3Off);
if (!ws)
return -1;
ws->write(m_h3, 0x18000);
/* Compute content hash and replace in TMD */
sha1nfo sha;
sha1_init(&sha);
sha1_write(&sha, (char*)m_h3, 0x18000);
memcpy(tmdData.get() + 0x1F4, sha1_result(&sha), 20);
memmove(tmdData.get() + 0x1F4, sha1_result(&sha), 20);
/* Same for content size */
uint64_t contentSize = groupCount * 0x1F0000;
uint64_t contentSizeBig = SBig(contentSize);
memcpy(tmdData.get() + 0x1EC, &contentSizeBig, 8);
memmove(tmdData.get() + 0x1EC, &contentSizeBig, 8);
/* Zero-out TMD signature to simplify brute-force */
memset(tmdData.get() + 0x4, 0, 0x100);
@ -841,10 +951,98 @@ public:
m_parent.m_progressCB(m_parent.m_progressIdx, bfName, attempts);
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2C0);
if (!ws)
return -1;
ws->write(tmdData.get(), tmdSz);
return m_baseOffset + dataOff + groupCount * 0x200000;
}
uint64_t buildFromDirectory(const SystemChar* dirIn,
const SystemChar* dolIn,
const SystemChar* apploaderIn,
const SystemChar* partHeadIn)
{
std::unique_ptr<IFileIO> ph = NewFileIO(partHeadIn);
size_t phSz = ph->size();
std::unique_ptr<uint8_t[]> phBuf(new uint8_t[phSz]);
{
auto rs = ph->beginReadStream();
if (!rs)
return -1;
rs->read(phBuf.get(), phSz);
}
/* Get Apploader Size */
Sstat theStat;
if (Stat(apploaderIn, &theStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn);
return -1;
}
return _build(
[this, dirIn, dolIn, apploaderIn](IPartWriteStream& cws) -> bool
{
return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn, dolIn, apploaderIn);
},
[this, apploaderIn](IPartWriteStream& cws, size_t& xferSz) -> bool
{
cws.write(nullptr, 0x2440 - 0x430);
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
if (!rs)
return false;
char buf[8192];
SystemString apploaderName(apploaderIn);
++m_parent.m_progressIdx;
while (true)
{
size_t rdSz = rs->read(buf, 8192);
if (!rdSz)
break;
cws.write(buf, rdSz);
xferSz += rdSz;
if (0x2440 + xferSz >= 0x1F0000)
{
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false;
}
m_parent.m_progressCB(m_parent.m_progressIdx, apploaderName, xferSz);
}
return true;
}, phBuf.get(), phSz, theStat.st_size);
}
bool mergeFromDirectory(const PartitionWii* partIn, const SystemChar* dirIn)
{
size_t phSz;
std::unique_ptr<uint8_t[]> phBuf = partIn->readPartitionHeaderBuf(phSz);
return _build(
[this, partIn, dirIn](IPartWriteStream& cws) -> bool
{
return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn);
},
[this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool
{
cws.write(nullptr, 0x2440 - 0x430);
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize();
SystemString apploaderName(_S("<apploader>"));
++m_parent.m_progressIdx;
cws.write(apploaderBuf.get(), apploaderSz);
xferSz += apploaderSz;
if (0x2440 + xferSz >= 0x1F0000)
{
LogModule.report(logvisor::Error,
"apploader flows into user area (one or the other is too big)");
return false;
}
m_parent.m_progressCB(m_parent.m_progressIdx, apploaderName, xferSz);
return true;
}, phBuf.get(), phSz, partIn->getApploaderSize());
}
};
bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
@ -852,22 +1050,28 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
{
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]);
uint64_t filledSz = pb.m_baseOffset;
m_fileIO->beginWriteStream();
if (!m_fileIO->beginWriteStream())
return false;
if (!CheckFreeSpace(m_outPath, m_discCapacity))
if (!CheckFreeSpace(m_outPath.c_str(), m_discCapacity))
{
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_outPath);
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_outPath.c_str());
return false;
}
++m_progressIdx;
m_progressCB(m_progressIdx, _S("Preallocating image"), -1);
m_fileIO->beginWriteStream(m_discCapacity - 1)->write("", 1);
std::unique_ptr<IFileIO::IWriteStream> ws = m_fileIO->beginWriteStream(m_discCapacity - 1);
if (!ws)
return false;
ws->write("", 1);
/* Assemble image */
filledSz = pb.buildFromDirectory(dirIn, dolIn, apploaderIn, partHeadIn);
if (filledSz >= uint64_t(m_discCapacity))
if (filledSz == -1)
return false;
else if (filledSz >= uint64_t(m_discCapacity))
{
LogModule.report(logvisor::Fatal, "data partition exceeds disc capacity");
LogModule.report(logvisor::Error, "data partition exceeds disc capacity");
return false;
}
@ -875,21 +1079,29 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
m_progressCB(m_progressIdx, _S("Finishing Disc"), -1);
/* Populate disc header */
std::unique_ptr<IFileIO::IWriteStream> ws = m_fileIO->beginWriteStream(0);
ws = m_fileIO->beginWriteStream(0);
if (!ws)
return false;
Header header(pb.getGameID(), pb.getGameTitle().c_str(), true, 0, 0, 0);
header.write(*ws);
/* Populate partition info */
ws = m_fileIO->beginWriteStream(0x40000);
if (!ws)
return false;
uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))};
ws->write(vals, 8);
ws = m_fileIO->beginWriteStream(0x40020);
if (!ws)
return false;
vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2)));
ws->write(vals, 4);
/* Populate region info */
ws = m_fileIO->beginWriteStream(0x4E000);
if (!ws)
return false;
const char* gameID = pb.getGameID();
if (gameID[3] == 'P')
vals[0] = SBig(uint32_t(2));
@ -901,11 +1113,15 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
/* Make disc unrated */
ws = m_fileIO->beginWriteStream(0x4E010);
if (!ws)
return false;
for (int i=0 ; i<16 ; ++i)
ws->write("\x80", 1);
/* Fill image to end */
ws = m_fileIO->beginWriteStream(filledSz);
if (!ws)
return false;
uint8_t fillBuf[512];
memset(fillBuf, 0xff, 512);
for (size_t i=m_discCapacity-filledSz ; i>0 ;)
@ -923,8 +1139,27 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
return true;
}
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, bool dualLayer,
std::function<void(size_t, const SystemString&, size_t)> progressCB)
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
bool& dualLayer)
{
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn);
if (sz == -1)
return -1;
auto szDiv = std::lldiv(sz, 0x1F0000);
if (szDiv.rem) ++szDiv.quot;
sz = szDiv.quot * 0x200000;
sz += 0x200000;
dualLayer = (sz > 0x118240000);
if (sz > 0x1FB4E0000)
{
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x1FB4E0000);
return -1;
}
return sz;
}
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
bool dualLayer, FProgress progressCB)
: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer)
{
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data,
@ -932,4 +1167,121 @@ DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6],
m_partitions.emplace_back(partBuilder);
}
DiscMergerWii::DiscMergerWii(const SystemChar* outPath, DiscWii& sourceDisc,
bool dualLayer, FProgress progressCB)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, dualLayer, progressCB))
{}
bool DiscMergerWii::mergeFromDirectory(const SystemChar* dirIn)
{
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_builder.m_partitions[0]);
uint64_t filledSz = pb.m_baseOffset;
if (!m_builder.m_fileIO->beginWriteStream())
return false;
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), m_builder.m_discCapacity))
{
LogModule.report(logvisor::Error, _S("not enough free disk space for %s"), m_builder.m_outPath.c_str());
return false;
}
++m_builder.m_progressIdx;
m_builder.m_progressCB(m_builder.m_progressIdx, _S("Preallocating image"), -1);
std::unique_ptr<IFileIO::IWriteStream> ws = m_builder.m_fileIO->beginWriteStream(m_builder.m_discCapacity - 1);
if (!ws)
return false;
ws->write("", 1);
/* Assemble image */
filledSz = pb.mergeFromDirectory(static_cast<PartitionWii*>(m_sourceDisc.getDataPartition()), dirIn);
if (filledSz == -1)
return false;
else if (filledSz >= uint64_t(m_builder.m_discCapacity))
{
LogModule.report(logvisor::Error, "data partition exceeds disc capacity");
return false;
}
++m_builder.m_progressIdx;
m_builder.m_progressCB(m_builder.m_progressIdx, _S("Finishing Disc"), -1);
/* Populate disc header */
ws = m_builder.m_fileIO->beginWriteStream(0);
if (!ws)
return false;
m_sourceDisc.getHeader().write(*ws);
/* Populate partition info */
ws = m_builder.m_fileIO->beginWriteStream(0x40000);
if (!ws)
return false;
uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))};
ws->write(vals, 8);
ws = m_builder.m_fileIO->beginWriteStream(0x40020);
if (!ws)
return false;
vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2)));
ws->write(vals, 4);
/* Populate region info */
ws = m_builder.m_fileIO->beginWriteStream(0x4E000);
if (!ws)
return false;
const char* gameID = pb.getGameID();
if (gameID[3] == 'P')
vals[0] = SBig(uint32_t(2));
else if (gameID[3] == 'J')
vals[0] = SBig(uint32_t(0));
else
vals[0] = SBig(uint32_t(1));
ws->write(vals, 4);
/* Make disc unrated */
ws = m_builder.m_fileIO->beginWriteStream(0x4E010);
if (!ws)
return false;
for (int i=0 ; i<16 ; ++i)
ws->write("\x80", 1);
/* Fill image to end */
ws = m_builder.m_fileIO->beginWriteStream(filledSz);
if (!ws)
return false;
uint8_t fillBuf[512];
memset(fillBuf, 0xff, 512);
for (size_t i=m_builder.m_discCapacity-filledSz ; i>0 ;)
{
if (i >= 512)
{
ws->write(fillBuf, 512);
i -= 512;
continue;
}
ws->write(fillBuf, i);
break;
}
return true;
}
uint64_t DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc,
const SystemChar* dirIn, bool& dualLayer)
{
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(
sourceDisc.getDataPartition(), dirIn);
if (sz == -1)
return -1;
auto szDiv = std::lldiv(sz, 0x1F0000);
if (szDiv.rem) ++szDiv.quot;
sz = szDiv.quot * 0x200000;
sz += 0x200000;
dualLayer = (sz > 0x118240000);
if (sz > 0x1FB4E0000)
{
LogModule.report(logvisor::Error, _S("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x1FB4E0000);
return -1;
}
return sz;
}
}

View File

@ -41,14 +41,17 @@ public:
{
FILE* fp;
int64_t m_maxWriteSize;
WriteStream(const SystemString& path, int64_t maxWriteSize)
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = fopen(path.c_str(), "wb");
if (!fp)
LogModule.report(logvisor::Fatal, _S("unable to open '%s' for writing"), path.c_str());
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
}
}
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize)
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = fopen(path.c_str(), "ab");
@ -61,7 +64,8 @@ public:
FSeek(fp, offset, SEEK_SET);
return;
FailLoc:
LogModule.report(logvisor::Fatal, _S("unable to open '%s' for writing"), path.c_str());
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
}
~WriteStream()
{
@ -72,7 +76,10 @@ public:
if (m_maxWriteSize >= 0)
{
if (FTell(fp) + length > m_maxWriteSize)
LogModule.report(logvisor::Fatal, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
{
LogModule.report(logvisor::Error, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
return 0;
}
}
return fwrite(buf, 1, length, fp);
}
@ -86,12 +93,12 @@ public:
uint64_t readSz = discio.read(buf, thisSz);
if (thisSz != readSz)
{
LogModule.report(logvisor::Fatal, "unable to read enough from disc");
LogModule.report(logvisor::Error, "unable to read enough from disc");
return read;
}
if (write(buf, readSz) != readSz)
{
LogModule.report(logvisor::Fatal, "unable to write in file");
LogModule.report(logvisor::Error, "unable to write in file");
return read;
}
length -= thisSz;
@ -102,25 +109,38 @@ public:
};
std::unique_ptr<IWriteStream> beginWriteStream() const
{
return std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize));
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const
{
return std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize));
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, Err));
if (Err)
return {};
return ret;
}
struct ReadStream : public IFileIO::IReadStream
{
FILE* fp;
ReadStream(const SystemString& path)
ReadStream(const SystemString& path, bool& err)
{
fp = fopen(path.c_str(), "rb");
if (!fp)
LogModule.report(logvisor::Fatal, _S("unable to open '%s' for reading"), path.c_str());
{
err = true;
LogModule.report(logvisor::Error, _S("unable to open '%s' for reading"), path.c_str());
}
}
ReadStream(const SystemString& path, uint64_t offset)
: ReadStream(path)
ReadStream(const SystemString& path, uint64_t offset, bool& err)
: ReadStream(path, err)
{
if (err)
return;
FSeek(fp, offset, SEEK_SET);
}
~ReadStream()
@ -148,12 +168,12 @@ public:
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Fatal, "unable to read enough from file");
LogModule.report(logvisor::Error, "unable to read enough from file");
return written;
}
if (discio.write(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Fatal, "unable to write enough to disc");
LogModule.report(logvisor::Error, "unable to write enough to disc");
return written;
}
length -= thisSz;
@ -164,11 +184,19 @@ public:
};
std::unique_ptr<IReadStream> beginReadStream() const
{
return std::unique_ptr<IReadStream>(new ReadStream(m_path));
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IReadStream>(new ReadStream(m_path, offset));
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, Err));
if (Err)
return {};
return ret;
}
};

View File

@ -47,21 +47,28 @@ public:
{
HANDLE fp;
int64_t m_maxWriteSize;
WriteStream(const SystemString& path, int64_t maxWriteSize)
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE)
LogModule.report(logvisor::Fatal, _S("unable to open '%s' for writing"), path.c_str());
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
}
}
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize)
WriteStream(const SystemString& path, uint64_t offset, int64_t maxWriteSize, bool& err)
: m_maxWriteSize(maxWriteSize)
{
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE)
LogModule.report(logvisor::Fatal, _S("unable to open '%s' for writing"), path.c_str());
{
LogModule.report(logvisor::Error, _S("unable to open '%s' for writing"), path.c_str());
err = true;
return;
}
LARGE_INTEGER lioffset;
lioffset.QuadPart = offset;
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
@ -78,7 +85,10 @@ public:
LARGE_INTEGER res;
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
if (res.QuadPart + int64_t(length) > m_maxWriteSize)
LogModule.report(logvisor::Fatal, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
{
LogModule.report(logvisor::Error, _S("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize);
return 0;
}
}
DWORD ret = 0;
@ -95,12 +105,12 @@ public:
uint64_t readSz = discio.read(buf, thisSz);
if (thisSz != readSz)
{
LogModule.report(logvisor::Fatal, "unable to read enough from disc");
LogModule.report(logvisor::Error, "unable to read enough from disc");
return read;
}
if (write(buf, readSz) != readSz)
{
LogModule.report(logvisor::Fatal, "unable to write in file");
LogModule.report(logvisor::Error, "unable to write in file");
return read;
}
length -= thisSz;
@ -111,24 +121,35 @@ public:
};
std::unique_ptr<IWriteStream> beginWriteStream() const
{
return std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize));
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const
{
return std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize));
bool Err = false;
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, Err));
if (Err)
return {};
return ret;
}
struct ReadStream : public IFileIO::IReadStream
{
HANDLE fp;
ReadStream(const SystemString& path)
ReadStream(const SystemString& path, bool& err)
{
fp = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fp == INVALID_HANDLE_VALUE)
LogModule.report(logvisor::Fatal, _S("unable to open '%s' for reading"), path.c_str());
{
err = true;
LogModule.report(logvisor::Error, _S("unable to open '%s' for reading"), path.c_str());
}
}
ReadStream(const SystemString& path, uint64_t offset)
ReadStream(const SystemString& path, uint64_t offset, bool& err)
: ReadStream(path)
{
LARGE_INTEGER lioffset;
@ -167,12 +188,12 @@ public:
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
if (read(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Fatal, "unable to read enough from file");
LogModule.report(logvisor::Error, "unable to read enough from file");
return written;
}
if (discio.write(buf, thisSz) != thisSz)
{
LogModule.report(logvisor::Fatal, "unable to write enough to disc");
LogModule.report(logvisor::Error, "unable to write enough to disc");
return written;
}
length -= thisSz;
@ -183,11 +204,19 @@ public:
};
std::unique_ptr<IReadStream> beginReadStream() const
{
return std::unique_ptr<IReadStream>(new ReadStream(m_path));
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IReadStream>(new ReadStream(m_path, offset));
bool Err = false;
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, Err));
if (Err)
return {};
return ret;
}
};

View File

@ -17,15 +17,20 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
if (!fio->exists())
{
LogModule.report(logvisor::Error, _S("Unable to open '%s'"), path);
return std::unique_ptr<DiscBase>();
return {};
}
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
if (!rs)
return {};
isWii = false;
std::unique_ptr<IDiscIO> discIO;
uint32_t magic = 0;
if (rs->read(&magic, 4) != 4)
LogModule.report(logvisor::Fatal, _S("Unable to read magic from '%s'"), path);
{
LogModule.report(logvisor::Error, _S("Unable to read magic from '%s'"), path);
return {};
}
if (magic == nod::SBig((uint32_t)'WBFS'))
{
@ -54,14 +59,23 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
if (!discIO)
{
LogModule.report(logvisor::Error, _S("'%s' is not a valid image"), path);
return std::unique_ptr<DiscBase>();
return {};
}
bool Err = false;
std::unique_ptr<DiscBase> ret;
if (isWii)
return std::unique_ptr<DiscBase>(new DiscWii(std::move(discIO)));
return std::unique_ptr<DiscBase>(new DiscGCN(std::move(discIO)));
{
ret = std::unique_ptr<DiscBase>(new DiscWii(std::move(discIO), Err));
if (Err)
return {};
return ret;
}
ret = std::unique_ptr<DiscBase>(new DiscGCN(std::move(discIO), Err));
if (Err)
return {};
return ret;
}
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path)