mirror of https://github.com/AxioDL/nod.git
Major refactor, better error handling and directory/image merge
This commit is contained in:
parent
b3a76428da
commit
dc474ad156
219
driver/main.cpp
219
driver/main.cpp
|
@ -8,7 +8,9 @@ static void printHelp()
|
||||||
fprintf(stderr, "Usage:\n"
|
fprintf(stderr, "Usage:\n"
|
||||||
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
||||||
" nodtool makegcn <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> [<image-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
|
#if NOD_UCS2
|
||||||
|
@ -25,8 +27,9 @@ int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
if (argc < 3 ||
|
if (argc < 3 ||
|
||||||
(!strcasecmp(argv[1], _S("makegcn")) && argc < 7) ||
|
(!strcasecmp(argv[1], _S("makegcn")) && argc < 7) ||
|
||||||
(!strcasecmp(argv[1], _S("makewiisl")) && argc < 8) ||
|
(!strcasecmp(argv[1], _S("makewii")) && argc < 8) ||
|
||||||
(!strcasecmp(argv[1], _S("makewiidl")) && argc < 8))
|
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) ||
|
||||||
|
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4))
|
||||||
{
|
{
|
||||||
printHelp();
|
printHelp();
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -55,6 +58,21 @@ int main(int argc, char* argv[])
|
||||||
outDir = argv[a];
|
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")))
|
if (!strcasecmp(argv[1], _S("extract")))
|
||||||
{
|
{
|
||||||
bool isWii;
|
bool isWii;
|
||||||
|
@ -79,39 +97,43 @@ int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
#if NOD_UCS2
|
#if NOD_UCS2
|
||||||
if (wcslen(argv[2]) < 6)
|
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
|
#else
|
||||||
if (strlen(argv[2]) < 6)
|
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
|
#endif
|
||||||
|
|
||||||
/* Pre-validate paths */
|
/* Pre-validate paths */
|
||||||
nod::Sstat theStat;
|
nod::Sstat theStat;
|
||||||
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode))
|
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))
|
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))
|
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::SystemString gameIdSys(argv[2]);
|
||||||
nod::SystemUTF8View gameId(gameIdSys);
|
nod::SystemUTF8View gameId(gameIdSys);
|
||||||
nod::SystemString gameTitleSys(argv[3]);
|
nod::SystemString gameTitleSys(argv[3]);
|
||||||
nod::SystemUTF8View gameTitle(gameTitleSys);
|
nod::SystemUTF8View gameTitle(gameTitleSys);
|
||||||
size_t lastIdx = -1;
|
|
||||||
auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
|
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[4], argv[5]) == -1)
|
||||||
{
|
return 1;
|
||||||
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 ret;
|
bool ret;
|
||||||
|
|
||||||
|
@ -132,47 +154,54 @@ int main(int argc, char* argv[])
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return 1;
|
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 NOD_UCS2
|
||||||
if (wcslen(argv[2]) < 6)
|
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
|
#else
|
||||||
if (strlen(argv[2]) < 6)
|
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
|
#endif
|
||||||
|
|
||||||
/* Pre-validate paths */
|
/* Pre-validate paths */
|
||||||
nod::Sstat theStat;
|
nod::Sstat theStat;
|
||||||
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode))
|
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))
|
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))
|
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))
|
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::SystemString gameIdSys(argv[2]);
|
||||||
nod::SystemUTF8View gameId(gameIdSys);
|
nod::SystemUTF8View gameId(gameIdSys);
|
||||||
nod::SystemString gameTitleSys(argv[3]);
|
nod::SystemString gameTitleSys(argv[3]);
|
||||||
nod::SystemUTF8View gameTitle(gameTitleSys);
|
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;
|
bool ret;
|
||||||
|
|
||||||
if (argc < 9)
|
if (argc < 9)
|
||||||
|
@ -192,12 +221,114 @@ int main(int argc, char* argv[])
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return 1;
|
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
|
else
|
||||||
{
|
{
|
||||||
printHelp();
|
printHelp();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nod::LogModule.report(logvisor::Info, _S("Success!"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
namespace nod
|
namespace nod
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using FProgress = std::function<void(size_t, const SystemString&, size_t)>;
|
||||||
|
|
||||||
class FSTNode
|
class FSTNode
|
||||||
{
|
{
|
||||||
uint32_t typeAndNameOffset;
|
uint32_t typeAndNameOffset;
|
||||||
|
@ -53,9 +55,14 @@ struct Header
|
||||||
uint32_t m_gcnMagic;
|
uint32_t m_gcnMagic;
|
||||||
char m_gameTitle[64];
|
char m_gameTitle[64];
|
||||||
|
|
||||||
Header(IDiscIO& dio)
|
Header(IDiscIO& dio, bool& err)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0);
|
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0);
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
s->read(this, sizeof(*this));
|
s->read(this, sizeof(*this));
|
||||||
m_wiiMagic = SBig(m_wiiMagic);
|
m_wiiMagic = SBig(m_wiiMagic);
|
||||||
m_gcnMagic = SBig(m_gcnMagic);
|
m_gcnMagic = SBig(m_gcnMagic);
|
||||||
|
@ -212,6 +219,7 @@ public:
|
||||||
uint64_t m_dolOff;
|
uint64_t m_dolOff;
|
||||||
uint64_t m_fstOff;
|
uint64_t m_fstOff;
|
||||||
uint64_t m_fstSz;
|
uint64_t m_fstSz;
|
||||||
|
uint64_t m_fstMemoryAddr;
|
||||||
uint64_t m_apploaderSz;
|
uint64_t m_apploaderSz;
|
||||||
std::vector<Node> m_nodes;
|
std::vector<Node> m_nodes;
|
||||||
void parseFST(IPartReadStream& s);
|
void parseFST(IPartReadStream& s);
|
||||||
|
@ -260,6 +268,8 @@ public:
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint64_t getFSTMemoryAddr() const {return m_fstMemoryAddr;}
|
||||||
|
|
||||||
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
|
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
|
||||||
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
|
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
|
||||||
{
|
{
|
||||||
|
@ -274,8 +284,8 @@ protected:
|
||||||
Header m_header;
|
Header m_header;
|
||||||
std::vector<std::unique_ptr<IPartition>> m_partitions;
|
std::vector<std::unique_ptr<IPartition>> m_partitions;
|
||||||
public:
|
public:
|
||||||
DiscBase(std::unique_ptr<IDiscIO>&& dio)
|
DiscBase(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
||||||
: m_discIO(std::move(dio)), m_header(*m_discIO) {}
|
: m_discIO(std::move(dio)), m_header(*m_discIO, err) {}
|
||||||
|
|
||||||
inline const Header& getHeader() const {return m_header;}
|
inline const Header& getHeader() const {return m_header;}
|
||||||
inline const IDiscIO& getDiscIO() const {return *m_discIO;}
|
inline const IDiscIO& getDiscIO() const {return *m_discIO;}
|
||||||
|
@ -302,6 +312,7 @@ public:
|
||||||
|
|
||||||
class DiscBuilderBase
|
class DiscBuilderBase
|
||||||
{
|
{
|
||||||
|
friend class DiscMergerWii;
|
||||||
public:
|
public:
|
||||||
class PartitionBuilderBase
|
class PartitionBuilderBase
|
||||||
{
|
{
|
||||||
|
@ -320,9 +331,18 @@ public:
|
||||||
size_t m_buildNameOff = 0;
|
size_t m_buildNameOff = 0;
|
||||||
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
|
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
|
||||||
virtual uint32_t packOffset(uint64_t offset) const=0;
|
virtual uint32_t packOffset(uint64_t offset) const=0;
|
||||||
void recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode);
|
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode);
|
||||||
void recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
|
bool recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
|
||||||
std::function<void(void)> incParents);
|
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)
|
void addBuildName(const SystemString& str)
|
||||||
{
|
{
|
||||||
SystemUTF8View utf8View(str);
|
SystemUTF8View utf8View(str);
|
||||||
|
@ -348,23 +368,32 @@ public:
|
||||||
bool buildFromDirectory(IPartWriteStream& ws,
|
bool buildFromDirectory(IPartWriteStream& ws,
|
||||||
const SystemChar* dirIn, const SystemChar* dolIn,
|
const SystemChar* dirIn, const SystemChar* dolIn,
|
||||||
const SystemChar* apploaderIn);
|
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 char* getGameID() const {return m_gameID;}
|
||||||
const std::string& getGameTitle() const {return m_gameTitle;}
|
const std::string& getGameTitle() const {return m_gameTitle;}
|
||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
const SystemChar* m_outPath;
|
SystemString m_outPath;
|
||||||
std::unique_ptr<IFileIO> m_fileIO;
|
std::unique_ptr<IFileIO> m_fileIO;
|
||||||
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions;
|
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions;
|
||||||
int64_t m_discCapacity;
|
int64_t m_discCapacity;
|
||||||
public:
|
public:
|
||||||
std::function<void(size_t idx, const SystemString&, size_t)> m_progressCB;
|
std::function<void(size_t idx, const SystemString&, size_t)> m_progressCB;
|
||||||
size_t m_progressIdx = 0;
|
size_t m_progressIdx = 0;
|
||||||
virtual ~DiscBuilderBase() {}
|
virtual ~DiscBuilderBase() = default;
|
||||||
DiscBuilderBase(const SystemChar* outPath, int64_t discCapacity,
|
DiscBuilderBase(const SystemChar* outPath, int64_t discCapacity,
|
||||||
std::function<void(size_t idx, const SystemString&, size_t)> progressCB)
|
std::function<void(size_t idx, const SystemString&, size_t)> progressCB)
|
||||||
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)),
|
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)),
|
||||||
m_discCapacity(discCapacity), m_progressCB(progressCB) {}
|
m_discCapacity(discCapacity), m_progressCB(progressCB) {}
|
||||||
|
DiscBuilderBase(DiscBuilderBase&&) = default;
|
||||||
|
DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
|
||||||
|
|
||||||
IFileIO& getFileIO() {return *m_fileIO;}
|
IFileIO& getFileIO() {return *m_fileIO;}
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,20 +5,35 @@
|
||||||
|
|
||||||
namespace nod
|
namespace nod
|
||||||
{
|
{
|
||||||
|
class DiscBuilderGCN;
|
||||||
|
|
||||||
class DiscGCN : public DiscBase
|
class DiscGCN : public DiscBase
|
||||||
{
|
{
|
||||||
|
friend class DiscMergerGCN;
|
||||||
|
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
|
||||||
public:
|
public:
|
||||||
DiscGCN(std::unique_ptr<IDiscIO>&& dio);
|
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscBuilderGCN : public DiscBuilderBase
|
class DiscBuilderGCN : public DiscBuilderBase
|
||||||
{
|
{
|
||||||
|
friend class DiscMergerGCN;
|
||||||
public:
|
public:
|
||||||
DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
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,
|
bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||||
const SystemChar* apploaderIn);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,40 @@
|
||||||
|
|
||||||
namespace nod
|
namespace nod
|
||||||
{
|
{
|
||||||
|
class DiscBuilderWii;
|
||||||
|
|
||||||
class DiscWii : public DiscBase
|
class DiscWii : public DiscBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DiscWii(std::unique_ptr<IDiscIO>&& dio);
|
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||||
void writeOutDataPartitionHeader(const SystemChar* pathOut) const;
|
DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
|
||||||
|
bool writeOutDataPartitionHeader(const SystemChar* pathOut) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscBuilderWii : public DiscBuilderBase
|
class DiscBuilderWii : public DiscBuilderBase
|
||||||
{
|
{
|
||||||
bool m_dualLayer;
|
bool m_dualLayer;
|
||||||
public:
|
public:
|
||||||
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, bool dualLayer,
|
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
||||||
std::function<void(size_t, const SystemString&, size_t)> progressCB);
|
bool dualLayer, FProgress progressCB);
|
||||||
bool buildFromDirectory(const SystemChar* dirIn,
|
bool buildFromDirectory(const SystemChar* dirIn,
|
||||||
const SystemChar* dolIn,
|
const SystemChar* dolIn,
|
||||||
const SystemChar* apploaderIn,
|
const SystemChar* apploaderIn,
|
||||||
const SystemChar* partHeadIn);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,16 +309,25 @@ static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz)
|
||||||
wchar_t* end;
|
wchar_t* end;
|
||||||
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
|
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
|
||||||
if (!ret || ret > 1024)
|
if (!ret || ret > 1024)
|
||||||
LogModule.report(logvisor::Fatal, _S("GetFullPathNameW %s"), path);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("GetFullPathNameW %s"), path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (end)
|
if (end)
|
||||||
end[0] = L'\0';
|
end[0] = L'\0';
|
||||||
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr))
|
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;
|
return reqSz < freeBytes.QuadPart;
|
||||||
#else
|
#else
|
||||||
struct statvfs svfs;
|
struct statvfs svfs;
|
||||||
if (statvfs(path, &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;
|
return reqSz < svfs.f_frsize * svfs.f_bavail;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
475
lib/DiscBase.cpp
475
lib/DiscBase.cpp
|
@ -118,6 +118,8 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
|
||||||
{
|
{
|
||||||
std::unique_ptr<IPartReadStream> rs = beginReadStream();
|
std::unique_ptr<IPartReadStream> rs = beginReadStream();
|
||||||
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(path)->beginWriteStream();
|
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(path)->beginWriteStream();
|
||||||
|
if (!rs || !ws)
|
||||||
|
return false;
|
||||||
ws->copyFromDisc(*rs, m_discLength);
|
ws->copyFromDisc(*rs, m_discLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +143,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.verbose && ctx.progressCB)
|
||||||
ctx.progressCB("apploader.bin");
|
ctx.progressCB("apploader.bin");
|
||||||
std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
|
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 */
|
/* Extract Dol */
|
||||||
|
@ -151,7 +156,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.verbose && ctx.progressCB)
|
||||||
ctx.progressCB("boot.dol");
|
ctx.progressCB("boot.dol");
|
||||||
std::unique_ptr<uint8_t[]> buf = getDOLBuf();
|
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 */
|
/* Extract Filesystem */
|
||||||
|
@ -177,17 +185,26 @@ static uint64_t GetInode(const SystemChar* path)
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
nullptr);
|
nullptr);
|
||||||
if (!fp)
|
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;
|
BY_HANDLE_FILE_INFORMATION info;
|
||||||
if (!GetFileInformationByHandle(fp, &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.nFileIndexHigh) << 32;
|
||||||
inode |= uint64_t(info.nFileIndexLow);
|
inode |= uint64_t(info.nFileIndexLow);
|
||||||
CloseHandle(fp);
|
CloseHandle(fp);
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path, &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);
|
inode = uint64_t(st.st_ino);
|
||||||
#endif
|
#endif
|
||||||
return inode;
|
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.
|
/** Patches out pesky #001 integrity check performed by game's OSInit.
|
||||||
* This is required for multi-DOL games, but doesn't harm functionality otherwise */
|
* 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;
|
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,
|
uint8_t* found = static_cast<uint8_t*>(memmem(buf.get(), sz,
|
||||||
"\x3C\x03\xF8\x00\x28\x00\x00\x00\x40\x82\x00\x0C"
|
"\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));
|
"\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';
|
found[11] = '\x04';
|
||||||
patched = true;
|
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);
|
return out.write(buf.get(), sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
|
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
|
||||||
bool system,
|
bool system,
|
||||||
const SystemChar* dirIn,
|
const SystemChar* dirIn,
|
||||||
uint64_t dolInode)
|
uint64_t dolInode)
|
||||||
|
@ -248,7 +270,8 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
||||||
{
|
{
|
||||||
if (e.m_isDir)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -262,8 +285,12 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
||||||
|
|
||||||
size_t fileSz = ROUND_UP_32(e.m_fileSz);
|
size_t fileSz = ROUND_UP_32(e.m_fileSz);
|
||||||
uint64_t fileOff = userAllocate(fileSz, ws);
|
uint64_t fileOff = userAllocate(fileSz, ws);
|
||||||
|
if (fileOff == -1)
|
||||||
|
return false;
|
||||||
m_fileOffsetsSizes[e.m_path] = std::make_pair(fileOff, fileSz);
|
m_fileOffsetsSizes[e.m_path] = std::make_pair(fileOff, fileSz);
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(e.m_path)->beginReadStream();
|
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(e.m_path)->beginReadStream();
|
||||||
|
if (!rs)
|
||||||
|
return false;
|
||||||
size_t xferSz = 0;
|
size_t xferSz = 0;
|
||||||
if (isDol)
|
if (isDol)
|
||||||
{
|
{
|
||||||
|
@ -289,9 +316,11 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
||||||
ws.write("\xff", 1);
|
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)
|
std::function<void(void)> incParents)
|
||||||
{
|
{
|
||||||
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
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);
|
m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1);
|
||||||
addBuildName(e.m_name);
|
addBuildName(e.m_name);
|
||||||
incParents();
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -321,6 +351,328 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar*
|
||||||
incParents();
|
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,
|
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws,
|
||||||
|
@ -329,7 +681,10 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
||||||
const SystemChar* apploaderIn)
|
const SystemChar* apploaderIn)
|
||||||
{
|
{
|
||||||
if (!dirIn || !dolIn || !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 */
|
/* Clear file */
|
||||||
++m_parent.m_progressIdx;
|
++m_parent.m_progressIdx;
|
||||||
|
@ -343,26 +698,110 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
||||||
{
|
{
|
||||||
Sstat dolStat;
|
Sstat dolStat;
|
||||||
if (Stat(dolIn, &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);
|
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
||||||
uint64_t fileOff = userAllocate(fileSz, ws);
|
uint64_t fileOff = userAllocate(fileSz, ws);
|
||||||
|
if (fileOff == -1)
|
||||||
|
return false;
|
||||||
m_dolOffset = fileOff;
|
m_dolOffset = fileOff;
|
||||||
m_dolSize = fileSz;
|
m_dolSize = fileSz;
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn)->beginReadStream();
|
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn)->beginReadStream();
|
||||||
|
if (!rs)
|
||||||
|
return false;
|
||||||
bool patched;
|
bool patched;
|
||||||
size_t xferSz = PatchDOL(*rs, ws, dolStat.st_size, 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)
|
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
|
||||||
ws.write("\xff", 1);
|
ws.write("\xff", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gather files in root directory */
|
/* Gather files in root directory */
|
||||||
uint64_t dolInode = GetInode(dolIn);
|
uint64_t dolInode = GetInode(dolIn);
|
||||||
recursiveBuildNodes(ws, true, dirIn, dolInode);
|
if (!recursiveBuildNodes(ws, true, dirIn, dolInode))
|
||||||
recursiveBuildNodes(ws, false, dirIn, dolInode);
|
return false;
|
||||||
recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();});
|
if (!recursiveBuildNodes(ws, false, dirIn, dolInode))
|
||||||
|
return false;
|
||||||
|
if (!recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();}))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
218
lib/DiscGCN.cpp
218
lib/DiscGCN.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "nod/DiscGCN.hpp"
|
#include "nod/DiscGCN.hpp"
|
||||||
|
#include <inttypes.h>
|
||||||
#define BUFFER_SZ 0x8000
|
#define BUFFER_SZ 0x8000
|
||||||
|
|
||||||
namespace nod
|
namespace nod
|
||||||
|
@ -7,16 +8,22 @@ namespace nod
|
||||||
class PartitionGCN : public DiscBase::IPartition
|
class PartitionGCN : public DiscBase::IPartition
|
||||||
{
|
{
|
||||||
public:
|
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)
|
: IPartition(parent, kind, offset)
|
||||||
{
|
{
|
||||||
/* GCN-specific header reads */
|
/* GCN-specific header reads */
|
||||||
std::unique_ptr<IPartReadStream> s = beginReadStream(0x420);
|
std::unique_ptr<IPartReadStream> s = beginReadStream(0x420);
|
||||||
uint32_t vals[3];
|
if (!s)
|
||||||
s->read(vals, 12);
|
{
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t vals[5];
|
||||||
|
s->read(vals, 5 * 4);
|
||||||
m_dolOff = SBig(vals[0]);
|
m_dolOff = SBig(vals[0]);
|
||||||
m_fstOff = SBig(vals[1]);
|
m_fstOff = SBig(vals[1]);
|
||||||
m_fstSz = SBig(vals[2]);
|
m_fstSz = SBig(vals[2]);
|
||||||
|
m_fstMemoryAddr = SBig(vals[4]);
|
||||||
s->seek(0x2440 + 0x14);
|
s->seek(0x2440 + 0x14);
|
||||||
s->read(vals, 8);
|
s->read(vals, 8);
|
||||||
m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]);
|
m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]);
|
||||||
|
@ -39,11 +46,16 @@ public:
|
||||||
uint8_t m_buf[BUFFER_SZ];
|
uint8_t m_buf[BUFFER_SZ];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PartReadStream(const PartitionGCN& parent, uint64_t offset)
|
PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err)
|
||||||
: m_parent(parent), m_offset(offset)
|
: m_parent(parent), m_offset(offset)
|
||||||
{
|
{
|
||||||
size_t block = m_offset / BUFFER_SZ;
|
size_t block = m_offset / BUFFER_SZ;
|
||||||
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * 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_dio->read(m_buf, BUFFER_SZ);
|
||||||
m_curBlock = block;
|
m_curBlock = block;
|
||||||
}
|
}
|
||||||
|
@ -98,15 +110,29 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
|
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)
|
DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
||||||
: DiscBase(std::move(dio))
|
: DiscBase(std::move(dio), err)
|
||||||
{
|
{
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
|
||||||
/* One lone partition for GCN */
|
/* 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
|
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase
|
||||||
|
@ -122,10 +148,12 @@ public:
|
||||||
std::unique_ptr<IFileIO::IWriteStream> m_fio;
|
std::unique_ptr<IFileIO::IWriteStream> m_fio;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset)
|
PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err)
|
||||||
: m_parent(parent), m_offset(offset)
|
: m_parent(parent), m_offset(offset)
|
||||||
{
|
{
|
||||||
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset);
|
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset);
|
||||||
|
if (!m_fio)
|
||||||
|
err = true;
|
||||||
}
|
}
|
||||||
void close() {m_fio.reset();}
|
void close() {m_fio.reset();}
|
||||||
uint64_t position() const {return m_offset;}
|
uint64_t position() const {return m_offset;}
|
||||||
|
@ -152,7 +180,7 @@ public:
|
||||||
m_curUser &= 0xfffffffffffffff0;
|
m_curUser &= 0xfffffffffffffff0;
|
||||||
if (m_curUser < 0x30000)
|
if (m_curUser < 0x30000)
|
||||||
{
|
{
|
||||||
LogModule.report(logvisor::Fatal, "user area low mark reached");
|
LogModule.report(logvisor::Error, "user area low mark reached");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
static_cast<PartWriteStream&>(ws).seek(m_curUser);
|
static_cast<PartWriteStream&>(ws).seek(m_curUser);
|
||||||
|
@ -166,38 +194,25 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)
|
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);
|
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
|
||||||
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn);
|
if (!ws)
|
||||||
if (!result)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ws = beginWriteStream(0);
|
|
||||||
Header header(m_gameID, m_gameTitle.c_str(), false);
|
Header header(m_gameID, m_gameTitle.c_str(), false);
|
||||||
header.write(*ws);
|
header.write(*ws);
|
||||||
|
|
||||||
ws = beginWriteStream(0x2440);
|
ws = beginWriteStream(0x2440);
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
|
|
||||||
char buf[8192];
|
|
||||||
size_t xferSz = 0;
|
size_t xferSz = 0;
|
||||||
SystemString apploaderName(apploaderIn);
|
if (!func(*ws, xferSz))
|
||||||
++m_parent.m_progressIdx;
|
return false;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t fstOff = ROUND_UP_32(xferSz);
|
size_t fstOff = ROUND_UP_32(xferSz);
|
||||||
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
|
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
|
||||||
|
@ -211,10 +226,15 @@ public:
|
||||||
fstSz = ROUND_UP_32(fstSz);
|
fstSz = ROUND_UP_32(fstSz);
|
||||||
|
|
||||||
if (fstOff + fstSz >= m_curUser)
|
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)");
|
"FST flows into user area (one or the other is too big)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ws = beginWriteStream(0x420);
|
ws = beginWriteStream(0x420);
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
uint32_t vals[7];
|
uint32_t vals[7];
|
||||||
vals[0] = SBig(uint32_t(m_dolOffset));
|
vals[0] = SBig(uint32_t(m_dolOffset));
|
||||||
vals[1] = SBig(uint32_t(fstOff));
|
vals[1] = SBig(uint32_t(fstOff));
|
||||||
|
@ -227,28 +247,109 @@ public:
|
||||||
|
|
||||||
return true;
|
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,
|
bool DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||||
const SystemChar* apploaderIn)
|
const SystemChar* apploaderIn)
|
||||||
{
|
{
|
||||||
m_fileIO->beginWriteStream();
|
if (!m_fileIO->beginWriteStream())
|
||||||
|
return false;
|
||||||
if (!CheckFreeSpace(m_outPath, 0x57058000))
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
++m_progressIdx;
|
++m_progressIdx;
|
||||||
m_progressCB(m_progressIdx, _S("Preallocating image"), -1);
|
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]);
|
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
|
||||||
return pb.buildFromDirectory(dirIn, dolIn, apploaderIn);
|
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,
|
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)
|
: DiscBuilderBase(outPath, 0x57058000, progressCB)
|
||||||
{
|
{
|
||||||
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this, PartitionBuilderBase::Kind::Data,
|
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);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ public:
|
||||||
{
|
{
|
||||||
friend class DiscIOISO;
|
friend class DiscIOISO;
|
||||||
std::unique_ptr<IFileIO::IReadStream> fp;
|
std::unique_ptr<IFileIO::IReadStream> fp;
|
||||||
ReadStream(std::unique_ptr<IFileIO::IReadStream>&& fpin)
|
ReadStream(std::unique_ptr<IFileIO::IReadStream>&& fpin, bool& err)
|
||||||
: fp(std::move(fpin)) {}
|
: fp(std::move(fpin)) { if (!fp) err = true; }
|
||||||
public:
|
public:
|
||||||
uint64_t read(void* buf, uint64_t length)
|
uint64_t read(void* buf, uint64_t length)
|
||||||
{return fp->read(buf, length);}
|
{return fp->read(buf, length);}
|
||||||
|
@ -30,15 +30,19 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
|
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
|
class WriteStream : public IWriteStream
|
||||||
{
|
{
|
||||||
friend class DiscIOISO;
|
friend class DiscIOISO;
|
||||||
std::unique_ptr<IFileIO::IWriteStream> fp;
|
std::unique_ptr<IFileIO::IWriteStream> fp;
|
||||||
WriteStream(std::unique_ptr<IFileIO::IWriteStream>&& fpin)
|
WriteStream(std::unique_ptr<IFileIO::IWriteStream>&& fpin, bool& err)
|
||||||
: fp(std::move(fpin)) {}
|
: fp(std::move(fpin)) { if (!fp) err = true; }
|
||||||
public:
|
public:
|
||||||
uint64_t write(const void* buf, uint64_t length)
|
uint64_t write(const void* buf, uint64_t length)
|
||||||
{
|
{
|
||||||
|
@ -48,7 +52,11 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ class DiscIOWBFS : public IDiscIO
|
||||||
rs.seek(off, SEEK_SET);
|
rs.seek(off, SEEK_SET);
|
||||||
if (rs.read(buf, count*512ULL) != count*512ULL)
|
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 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -93,19 +93,25 @@ public:
|
||||||
/* Temporary file handle to read LBA table */
|
/* Temporary file handle to read LBA table */
|
||||||
std::unique_ptr<IFileIO> fio = NewFileIO(filepath);
|
std::unique_ptr<IFileIO> fio = NewFileIO(filepath);
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
||||||
|
if (!rs)
|
||||||
|
return;
|
||||||
|
|
||||||
WBFS* p = &wbfs;
|
WBFS* p = &wbfs;
|
||||||
WBFSHead tmpHead;
|
WBFSHead tmpHead;
|
||||||
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead))
|
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) {
|
||||||
LogModule.report(logvisor::Fatal, "unable to read WBFS head");
|
LogModule.report(logvisor::Error, "unable to read WBFS head");
|
||||||
|
return;
|
||||||
|
}
|
||||||
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
|
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
|
||||||
unsigned num_hd_sector = SBig(tmpHead.n_hd_sec);
|
unsigned num_hd_sector = SBig(tmpHead.n_hd_sec);
|
||||||
|
|
||||||
wbfsHead.reset(new uint8_t[hd_sector_size]);
|
wbfsHead.reset(new uint8_t[hd_sector_size]);
|
||||||
WBFSHead* head = (WBFSHead*)wbfsHead.get();
|
WBFSHead* head = (WBFSHead*)wbfsHead.get();
|
||||||
rs->seek(0, SEEK_SET);
|
rs->seek(0, SEEK_SET);
|
||||||
if (rs->read(head, hd_sector_size) != hd_sector_size)
|
if (rs->read(head, hd_sector_size) != hd_sector_size) {
|
||||||
LogModule.report(logvisor::Fatal, "unable to read WBFS head");
|
LogModule.report(logvisor::Error, "unable to read WBFS head");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//constants, but put here for consistancy
|
//constants, but put here for consistancy
|
||||||
p->wii_sec_sz = 0x8000;
|
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 = (num_hd_sector/0x8000)*hd_sector_size;
|
||||||
p->n_wii_sec_per_disc = 143432*2;//support for double layers discs..
|
p->n_wii_sec_per_disc = 143432*2;//support for double layers discs..
|
||||||
p->part_lba = 0;
|
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)) {
|
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)) {
|
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 = 1<<head->hd_sec_sz_s;
|
||||||
p->hd_sec_sz_s = 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])
|
if (head->disc_table[0])
|
||||||
{
|
{
|
||||||
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
|
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
|
||||||
if (!wbfsDiscInfo)
|
if (!wbfsDiscInfo) {
|
||||||
LogModule.report(logvisor::Fatal, "allocating memory");
|
LogModule.report(logvisor::Error, "allocating memory");
|
||||||
_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get());
|
return;
|
||||||
|
}
|
||||||
|
if (_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get()))
|
||||||
|
return;
|
||||||
p->n_disc_open++;
|
p->n_disc_open++;
|
||||||
//for(i=0;i<p->n_wbfs_sec_per_disc;i++)
|
//for(i=0;i<p->n_wbfs_sec_per_disc;i++)
|
||||||
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
|
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
|
||||||
|
@ -162,11 +174,11 @@ public:
|
||||||
uint64_t m_offset;
|
uint64_t m_offset;
|
||||||
std::unique_ptr<uint8_t[]> m_tmpBuffer;
|
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),
|
: m_parent(parent),
|
||||||
fp(std::move(fpin)),
|
fp(std::move(fpin)),
|
||||||
m_offset(offset),
|
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)
|
int wbfsReadSector(uint32_t lba, uint32_t count, void* buf)
|
||||||
{return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);}
|
{return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);}
|
||||||
|
@ -271,7 +283,11 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
|
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
|
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const
|
||||||
|
|
552
lib/DiscWii.cpp
552
lib/DiscWii.cpp
|
@ -1,5 +1,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <inttypes.h>
|
||||||
#include "nod/DiscWii.hpp"
|
#include "nod/DiscWii.hpp"
|
||||||
#include "nod/aes.hpp"
|
#include "nod/aes.hpp"
|
||||||
#include "nod/sha1.h"
|
#include "nod/sha1.h"
|
||||||
|
@ -191,10 +193,16 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
uint8_t m_decKey[16];
|
uint8_t m_decKey[16];
|
||||||
|
|
||||||
public:
|
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)
|
: IPartition(parent, kind, offset)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IDiscIO::IReadStream> s = parent.getDiscIO().beginReadStream(offset);
|
std::unique_ptr<IDiscIO::IReadStream> s = parent.getDiscIO().beginReadStream(offset);
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_ticket.read(*s);
|
m_ticket.read(*s);
|
||||||
|
|
||||||
uint32_t tmdSize;
|
uint32_t tmdSize;
|
||||||
|
@ -240,6 +248,12 @@ public:
|
||||||
|
|
||||||
/* Wii-specific header reads (now using title key to decrypt) */
|
/* Wii-specific header reads (now using title key to decrypt) */
|
||||||
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420);
|
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420);
|
||||||
|
if (!ds)
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t vals[3];
|
uint32_t vals[3];
|
||||||
ds->read(vals, 12);
|
ds->read(vals, 12);
|
||||||
m_dolOff = SBig(vals[0]) << 2;
|
m_dolOff = SBig(vals[0]) << 2;
|
||||||
|
@ -275,12 +289,17 @@ public:
|
||||||
m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00);
|
m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00);
|
||||||
}
|
}
|
||||||
public:
|
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(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset)
|
||||||
{
|
{
|
||||||
m_aes->setKey(parent.m_decKey);
|
m_aes->setKey(parent.m_decKey);
|
||||||
size_t block = m_offset / 0x7c00;
|
size_t block = m_offset / 0x7c00;
|
||||||
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
|
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
|
||||||
|
if (!m_dio)
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
decryptBlock();
|
decryptBlock();
|
||||||
m_curBlock = block;
|
m_curBlock = block;
|
||||||
}
|
}
|
||||||
|
@ -335,20 +354,59 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
|
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;}
|
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();
|
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(pathOut)->beginWriteStream();
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
uint64_t h3Off;
|
uint64_t h3Off;
|
||||||
{
|
{
|
||||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
|
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
|
||||||
|
if (!rs)
|
||||||
|
return false;
|
||||||
|
|
||||||
uint32_t h3;
|
uint32_t h3;
|
||||||
if (rs->read(&h3, 4) != 4)
|
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);
|
h3 = SBig(h3);
|
||||||
h3Off = uint64_t(h3) << 2;
|
h3Off = uint64_t(h3) << 2;
|
||||||
}
|
}
|
||||||
|
@ -356,6 +414,9 @@ public:
|
||||||
char buf[8192];
|
char buf[8192];
|
||||||
size_t rem = h3Off;
|
size_t rem = h3Off;
|
||||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
|
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
|
||||||
|
if (!rs)
|
||||||
|
return false;
|
||||||
|
|
||||||
while (rem)
|
while (rem)
|
||||||
{
|
{
|
||||||
size_t rdSz = nod::min(rem, size_t(8192ul));
|
size_t rdSz = nod::min(rem, size_t(8192ul));
|
||||||
|
@ -363,12 +424,17 @@ public:
|
||||||
ws->write(buf, rdSz);
|
ws->write(buf, rdSz);
|
||||||
rem -= rdSz;
|
rem -= rdSz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
|
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
||||||
: DiscBase(std::move(dio))
|
: DiscBase(std::move(dio), err)
|
||||||
{
|
{
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Read partition info */
|
/* Read partition info */
|
||||||
struct PartInfo
|
struct PartInfo
|
||||||
{
|
{
|
||||||
|
@ -379,9 +445,15 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
|
||||||
uint32_t partDataOff;
|
uint32_t partDataOff;
|
||||||
IPartition::Kind partType;
|
IPartition::Kind partType;
|
||||||
} parts[4];
|
} parts[4];
|
||||||
PartInfo(IDiscIO& dio)
|
PartInfo(IDiscIO& dio, bool& err)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000);
|
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000);
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
s->read(this, 32);
|
s->read(this, 32);
|
||||||
partCount = SBig(partCount);
|
partCount = SBig(partCount);
|
||||||
partInfoOff = SBig(partInfoOff);
|
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)));
|
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 */
|
/* Iterate for data partition */
|
||||||
m_partitions.reserve(partInfo.partCount);
|
m_partitions.reserve(partInfo.partCount);
|
||||||
|
@ -410,22 +484,32 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
|
||||||
kind = part.partType;
|
kind = part.partType;
|
||||||
break;
|
break;
|
||||||
default:
|
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)
|
for (const std::unique_ptr<IPartition>& part : m_partitions)
|
||||||
{
|
{
|
||||||
if (part->getKind() == IPartition::Kind::Data)
|
if (part->getKind() == IPartition::Kind::Data)
|
||||||
{
|
{
|
||||||
static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
|
return static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint8_t ZEROIV[16] = {0};
|
static const uint8_t ZEROIV[16] = {0};
|
||||||
|
@ -433,6 +517,8 @@ static const uint8_t ZEROIV[16] = {0};
|
||||||
class PartitionBuilderWii : public DiscBuilderBase::PartitionBuilderBase
|
class PartitionBuilderWii : public DiscBuilderBase::PartitionBuilderBase
|
||||||
{
|
{
|
||||||
friend class DiscBuilderWii;
|
friend class DiscBuilderWii;
|
||||||
|
friend class DiscMergerWii;
|
||||||
|
|
||||||
uint64_t m_baseOffset;
|
uint64_t m_baseOffset;
|
||||||
uint64_t m_userOffset = 0;
|
uint64_t m_userOffset = 0;
|
||||||
uint64_t m_curUser = 0x1F0000;
|
uint64_t m_curUser = 0x1F0000;
|
||||||
|
@ -512,17 +598,26 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_fio->write(m_buf, 0x200000) != 0x200000)
|
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:
|
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)
|
: m_parent(parent), m_baseOffset(baseOffset), m_offset(offset)
|
||||||
{
|
{
|
||||||
if (offset % 0x1F0000)
|
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;
|
size_t group = m_offset / 0x1F0000;
|
||||||
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(m_baseOffset + group * 0x200000);
|
m_fio = m_parent.m_parent.getFileIO().beginWriteStream(m_baseOffset + group * 0x200000);
|
||||||
|
if (!m_fio)
|
||||||
|
err = true;
|
||||||
m_curGroup = group;
|
m_curGroup = group;
|
||||||
}
|
}
|
||||||
~PartWriteStream() {close();}
|
~PartWriteStream() {close();}
|
||||||
|
@ -597,14 +692,14 @@ public:
|
||||||
reqSz = ROUND_UP_32(reqSz);
|
reqSz = ROUND_UP_32(reqSz);
|
||||||
if (m_curUser + reqSz >= 0x1FB450000)
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
uint64_t ret = m_curUser;
|
uint64_t ret = m_curUser;
|
||||||
PartWriteStream& cws = static_cast<PartWriteStream&>(ws);
|
PartWriteStream& cws = static_cast<PartWriteStream&>(ws);
|
||||||
if (cws.m_offset > ret)
|
if (cws.m_offset > ret)
|
||||||
{
|
{
|
||||||
LogModule.report(logvisor::Fatal, "partition overwrite error");
|
LogModule.report(logvisor::Error, "partition overwrite error");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (cws.m_offset < ret)
|
while (cws.m_offset < ret)
|
||||||
|
@ -620,86 +715,109 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)
|
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,
|
uint64_t _build(const std::function<bool(IPartWriteStream&)>& contentFunc,
|
||||||
const SystemChar* dolIn,
|
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc,
|
||||||
const SystemChar* apploaderIn,
|
const uint8_t* phBuf, size_t phSz, size_t apploaderSz)
|
||||||
const SystemChar* partHeadIn)
|
|
||||||
{
|
{
|
||||||
/* Read head and validate key members */
|
/* Read head and validate key members */
|
||||||
std::unique_ptr<IFileIO> ph = NewFileIO(partHeadIn);
|
|
||||||
|
|
||||||
uint8_t tkey[16];
|
uint8_t tkey[16];
|
||||||
{
|
{
|
||||||
if (ph->beginReadStream(0x1BF)->read(tkey, 16) != 16)
|
if (0x1BF + 16 > phSz)
|
||||||
LogModule.report(logvisor::Fatal, _S("unable to read title key from %s"), partHeadIn);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("unable to read title key"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memmove(tkey, phBuf + 0x1BF, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t tkeyiv[16] = {};
|
uint8_t tkeyiv[16] = {};
|
||||||
{
|
{
|
||||||
if (ph->beginReadStream(0x1DC)->read(tkeyiv, 8) != 8)
|
if (0x1DC + 8 > phSz)
|
||||||
LogModule.report(logvisor::Fatal, _S("unable to read title key IV from %s"), partHeadIn);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("unable to read title key IV"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memmove(tkeyiv, phBuf + 0x1DC, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ccIdx;
|
uint8_t ccIdx;
|
||||||
{
|
{
|
||||||
if (ph->beginReadStream(0x1F1)->read(&ccIdx, 1) != 1)
|
if (0x1F1 + 1 > phSz)
|
||||||
LogModule.report(logvisor::Fatal, _S("unable to read common key index from %s"), partHeadIn);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("unable to read common key index"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memmove(&ccIdx, phBuf + 0x1F1, 1);
|
||||||
if (ccIdx > 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;
|
uint32_t tmdSz;
|
||||||
{
|
{
|
||||||
if (ph->beginReadStream(0x2A4)->read(&tmdSz, 4) != 4)
|
if (0x2A4 + 4 > phSz)
|
||||||
LogModule.report(logvisor::Fatal, _S("unable to read TMD size from %s"), partHeadIn);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("unable to read TMD size"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memmove(&tmdSz, phBuf + 0x2A4, 4);
|
||||||
tmdSz = SBig(tmdSz);
|
tmdSz = SBig(tmdSz);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t h3Off;
|
uint64_t h3Off;
|
||||||
{
|
{
|
||||||
uint32_t h3Ptr;
|
uint32_t h3Ptr;
|
||||||
if (ph->beginReadStream(0x2B4)->read(&h3Ptr, 4) != 4)
|
if (0x2B4 + 4 > phSz)
|
||||||
LogModule.report(logvisor::Fatal, _S("unable to read H3 pointer from %s"), partHeadIn);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("unable to read H3 pointer"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memmove(&h3Ptr, phBuf + 0x2B4, 4);
|
||||||
h3Off = uint64_t(SBig(h3Ptr)) << 2;
|
h3Off = uint64_t(SBig(h3Ptr)) << 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t dataOff;
|
uint64_t dataOff;
|
||||||
{
|
{
|
||||||
uint32_t dataPtr;
|
uint32_t dataPtr;
|
||||||
if (ph->beginReadStream(0x2B8)->read(&dataPtr, 4) != 4)
|
if (0x2B8 + 4 > phSz)
|
||||||
LogModule.report(logvisor::Fatal, _S("unable to read data pointer from %s"), partHeadIn);
|
{
|
||||||
|
LogModule.report(logvisor::Error, _S("unable to read data pointer"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memmove(&dataPtr, phBuf + 0x2B8, 4);
|
||||||
dataOff = uint64_t(SBig(dataPtr)) << 2;
|
dataOff = uint64_t(SBig(dataPtr)) << 2;
|
||||||
}
|
}
|
||||||
m_userOffset = dataOff;
|
m_userOffset = dataOff;
|
||||||
|
|
||||||
std::unique_ptr<uint8_t[]> tmdData(new uint8_t[tmdSz]);
|
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 */
|
/* Copy partition head up to H3 table */
|
||||||
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
|
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
|
||||||
{
|
if (!ws)
|
||||||
uint64_t remCopy = h3Off;
|
return -1;
|
||||||
|
size_t copySz = std::min(phSz, size_t(h3Off));
|
||||||
uint8_t copyBuf[8192];
|
ws->write(phBuf, copySz);
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = ph->beginReadStream();
|
size_t remCopy = (h3Off > phSz) ? (h3Off - copySz) : 0;
|
||||||
while (remCopy)
|
for (size_t i=0 ; i<remCopy ; ++i)
|
||||||
{
|
ws->write("", 1);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare crypto pass */
|
/* Prepare crypto pass */
|
||||||
m_aes->setKey(COMMON_KEYS[ccIdx]);
|
m_aes->setKey(COMMON_KEYS[ccIdx]);
|
||||||
|
@ -709,9 +827,10 @@ public:
|
||||||
{
|
{
|
||||||
/* Assemble partition data */
|
/* Assemble partition data */
|
||||||
std::unique_ptr<IPartWriteStream> cws = beginWriteStream(0x1F0000);
|
std::unique_ptr<IPartWriteStream> cws = beginWriteStream(0x1F0000);
|
||||||
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*cws, dirIn, dolIn, apploaderIn);
|
if (!cws)
|
||||||
if (!result)
|
return -1;
|
||||||
return 0;
|
if (!contentFunc(*cws))
|
||||||
|
return -1;
|
||||||
|
|
||||||
/* Pad out user area to nearest cleartext sector */
|
/* Pad out user area to nearest cleartext sector */
|
||||||
m_curUser = cws->position();
|
m_curUser = cws->position();
|
||||||
|
@ -726,23 +845,23 @@ public:
|
||||||
|
|
||||||
/* Begin crypto write and add content header */
|
/* Begin crypto write and add content header */
|
||||||
cws = beginWriteStream(0);
|
cws = beginWriteStream(0);
|
||||||
|
if (!cws)
|
||||||
|
return -1;
|
||||||
Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0);
|
Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0);
|
||||||
header.write(*cws);
|
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 */
|
/* 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();
|
size_t fstSz = sizeof(FSTNode) * m_buildNodes.size();
|
||||||
fstSz += m_buildNameOff;
|
fstSz += m_buildNameOff;
|
||||||
fstSz = ROUND_UP_32(fstSz);
|
fstSz = ROUND_UP_32(fstSz);
|
||||||
|
|
||||||
if (fstOff + fstSz >= 0x1F0000)
|
if (fstOff + fstSz >= 0x1F0000)
|
||||||
LogModule.report(logvisor::Fatal,
|
{
|
||||||
|
LogModule.report(logvisor::Error,
|
||||||
"FST flows into user area (one or the other is too big)");
|
"FST flows into user area (one or the other is too big)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
cws->write(nullptr, 0x420 - sizeof(Header));
|
cws->write(nullptr, 0x420 - sizeof(Header));
|
||||||
uint32_t vals[4];
|
uint32_t vals[4];
|
||||||
|
@ -752,29 +871,16 @@ public:
|
||||||
vals[3] = SBig(uint32_t(fstSz));
|
vals[3] = SBig(uint32_t(fstSz));
|
||||||
cws->write(vals, 16);
|
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;
|
size_t xferSz = 0;
|
||||||
SystemString apploaderName(apploaderIn);
|
if (!apploaderFunc(*cws, xferSz))
|
||||||
++m_parent.m_progressIdx;
|
return -1;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t fstOffRel = fstOff - 0x2440;
|
size_t fstOffRel = fstOff - 0x2440;
|
||||||
if (xferSz > fstOffRel)
|
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)
|
for (size_t i=0 ; i<fstOffRel-xferSz ; ++i)
|
||||||
cws->write("\xff", 1);
|
cws->write("\xff", 1);
|
||||||
|
|
||||||
|
@ -789,22 +895,26 @@ public:
|
||||||
uint64_t cryptContentSize = (groupCount * 0x200000) >> uint64_t(2);
|
uint64_t cryptContentSize = (groupCount * 0x200000) >> uint64_t(2);
|
||||||
uint32_t cryptContentSizeBig = SBig(uint32_t(cryptContentSize));
|
uint32_t cryptContentSizeBig = SBig(uint32_t(cryptContentSize));
|
||||||
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2BC);
|
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2BC);
|
||||||
|
if (!ws)
|
||||||
|
return -1;
|
||||||
ws->write(&cryptContentSizeBig, 0x4);
|
ws->write(&cryptContentSizeBig, 0x4);
|
||||||
|
|
||||||
/* Write new H3 */
|
/* Write new H3 */
|
||||||
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + h3Off);
|
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + h3Off);
|
||||||
|
if (!ws)
|
||||||
|
return -1;
|
||||||
ws->write(m_h3, 0x18000);
|
ws->write(m_h3, 0x18000);
|
||||||
|
|
||||||
/* Compute content hash and replace in TMD */
|
/* Compute content hash and replace in TMD */
|
||||||
sha1nfo sha;
|
sha1nfo sha;
|
||||||
sha1_init(&sha);
|
sha1_init(&sha);
|
||||||
sha1_write(&sha, (char*)m_h3, 0x18000);
|
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 */
|
/* Same for content size */
|
||||||
uint64_t contentSize = groupCount * 0x1F0000;
|
uint64_t contentSize = groupCount * 0x1F0000;
|
||||||
uint64_t contentSizeBig = SBig(contentSize);
|
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 */
|
/* Zero-out TMD signature to simplify brute-force */
|
||||||
memset(tmdData.get() + 0x4, 0, 0x100);
|
memset(tmdData.get() + 0x4, 0, 0x100);
|
||||||
|
@ -841,10 +951,98 @@ public:
|
||||||
m_parent.m_progressCB(m_parent.m_progressIdx, bfName, attempts);
|
m_parent.m_progressCB(m_parent.m_progressIdx, bfName, attempts);
|
||||||
|
|
||||||
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2C0);
|
ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2C0);
|
||||||
|
if (!ws)
|
||||||
|
return -1;
|
||||||
ws->write(tmdData.get(), tmdSz);
|
ws->write(tmdData.get(), tmdSz);
|
||||||
|
|
||||||
return m_baseOffset + dataOff + groupCount * 0x200000;
|
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,
|
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]);
|
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]);
|
||||||
uint64_t filledSz = pb.m_baseOffset;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
++m_progressIdx;
|
++m_progressIdx;
|
||||||
m_progressCB(m_progressIdx, _S("Preallocating image"), -1);
|
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 */
|
/* Assemble image */
|
||||||
filledSz = pb.buildFromDirectory(dirIn, dolIn, apploaderIn, partHeadIn);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,21 +1079,29 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
|
||||||
m_progressCB(m_progressIdx, _S("Finishing Disc"), -1);
|
m_progressCB(m_progressIdx, _S("Finishing Disc"), -1);
|
||||||
|
|
||||||
/* Populate disc header */
|
/* 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 header(pb.getGameID(), pb.getGameTitle().c_str(), true, 0, 0, 0);
|
||||||
header.write(*ws);
|
header.write(*ws);
|
||||||
|
|
||||||
/* Populate partition info */
|
/* Populate partition info */
|
||||||
ws = m_fileIO->beginWriteStream(0x40000);
|
ws = m_fileIO->beginWriteStream(0x40000);
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))};
|
uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))};
|
||||||
ws->write(vals, 8);
|
ws->write(vals, 8);
|
||||||
|
|
||||||
ws = m_fileIO->beginWriteStream(0x40020);
|
ws = m_fileIO->beginWriteStream(0x40020);
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2)));
|
vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2)));
|
||||||
ws->write(vals, 4);
|
ws->write(vals, 4);
|
||||||
|
|
||||||
/* Populate region info */
|
/* Populate region info */
|
||||||
ws = m_fileIO->beginWriteStream(0x4E000);
|
ws = m_fileIO->beginWriteStream(0x4E000);
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
const char* gameID = pb.getGameID();
|
const char* gameID = pb.getGameID();
|
||||||
if (gameID[3] == 'P')
|
if (gameID[3] == 'P')
|
||||||
vals[0] = SBig(uint32_t(2));
|
vals[0] = SBig(uint32_t(2));
|
||||||
|
@ -901,11 +1113,15 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
|
||||||
|
|
||||||
/* Make disc unrated */
|
/* Make disc unrated */
|
||||||
ws = m_fileIO->beginWriteStream(0x4E010);
|
ws = m_fileIO->beginWriteStream(0x4E010);
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
for (int i=0 ; i<16 ; ++i)
|
for (int i=0 ; i<16 ; ++i)
|
||||||
ws->write("\x80", 1);
|
ws->write("\x80", 1);
|
||||||
|
|
||||||
/* Fill image to end */
|
/* Fill image to end */
|
||||||
ws = m_fileIO->beginWriteStream(filledSz);
|
ws = m_fileIO->beginWriteStream(filledSz);
|
||||||
|
if (!ws)
|
||||||
|
return false;
|
||||||
uint8_t fillBuf[512];
|
uint8_t fillBuf[512];
|
||||||
memset(fillBuf, 0xff, 512);
|
memset(fillBuf, 0xff, 512);
|
||||||
for (size_t i=m_discCapacity-filledSz ; i>0 ;)
|
for (size_t i=m_discCapacity-filledSz ; i>0 ;)
|
||||||
|
@ -923,8 +1139,27 @@ bool DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemCha
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, bool dualLayer,
|
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||||
std::function<void(size_t, const SystemString&, size_t)> progressCB)
|
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)
|
: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer)
|
||||||
{
|
{
|
||||||
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data,
|
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);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,17 @@ public:
|
||||||
{
|
{
|
||||||
FILE* fp;
|
FILE* fp;
|
||||||
int64_t m_maxWriteSize;
|
int64_t m_maxWriteSize;
|
||||||
WriteStream(const SystemString& path, int64_t maxWriteSize)
|
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err)
|
||||||
: m_maxWriteSize(maxWriteSize)
|
: m_maxWriteSize(maxWriteSize)
|
||||||
{
|
{
|
||||||
fp = fopen(path.c_str(), "wb");
|
fp = fopen(path.c_str(), "wb");
|
||||||
if (!fp)
|
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)
|
: m_maxWriteSize(maxWriteSize)
|
||||||
{
|
{
|
||||||
fp = fopen(path.c_str(), "ab");
|
fp = fopen(path.c_str(), "ab");
|
||||||
|
@ -61,7 +64,8 @@ public:
|
||||||
FSeek(fp, offset, SEEK_SET);
|
FSeek(fp, offset, SEEK_SET);
|
||||||
return;
|
return;
|
||||||
FailLoc:
|
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()
|
~WriteStream()
|
||||||
{
|
{
|
||||||
|
@ -72,7 +76,10 @@ public:
|
||||||
if (m_maxWriteSize >= 0)
|
if (m_maxWriteSize >= 0)
|
||||||
{
|
{
|
||||||
if (FTell(fp) + length > m_maxWriteSize)
|
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);
|
return fwrite(buf, 1, length, fp);
|
||||||
}
|
}
|
||||||
|
@ -86,12 +93,12 @@ public:
|
||||||
uint64_t readSz = discio.read(buf, thisSz);
|
uint64_t readSz = discio.read(buf, thisSz);
|
||||||
if (thisSz != readSz)
|
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;
|
return read;
|
||||||
}
|
}
|
||||||
if (write(buf, readSz) != readSz)
|
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;
|
return read;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
|
@ -102,25 +109,38 @@ public:
|
||||||
};
|
};
|
||||||
std::unique_ptr<IWriteStream> beginWriteStream() const
|
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
|
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
|
struct ReadStream : public IFileIO::IReadStream
|
||||||
{
|
{
|
||||||
FILE* fp;
|
FILE* fp;
|
||||||
ReadStream(const SystemString& path)
|
ReadStream(const SystemString& path, bool& err)
|
||||||
{
|
{
|
||||||
fp = fopen(path.c_str(), "rb");
|
fp = fopen(path.c_str(), "rb");
|
||||||
if (!fp)
|
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(const SystemString& path, uint64_t offset, bool& err)
|
||||||
: ReadStream(path)
|
: ReadStream(path, err)
|
||||||
{
|
{
|
||||||
|
if (err)
|
||||||
|
return;
|
||||||
FSeek(fp, offset, SEEK_SET);
|
FSeek(fp, offset, SEEK_SET);
|
||||||
}
|
}
|
||||||
~ReadStream()
|
~ReadStream()
|
||||||
|
@ -148,12 +168,12 @@ public:
|
||||||
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
||||||
if (read(buf, thisSz) != thisSz)
|
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;
|
return written;
|
||||||
}
|
}
|
||||||
if (discio.write(buf, thisSz) != thisSz)
|
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;
|
return written;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
|
@ -164,11 +184,19 @@ public:
|
||||||
};
|
};
|
||||||
std::unique_ptr<IReadStream> beginReadStream() const
|
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
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,21 +47,28 @@ public:
|
||||||
{
|
{
|
||||||
HANDLE fp;
|
HANDLE fp;
|
||||||
int64_t m_maxWriteSize;
|
int64_t m_maxWriteSize;
|
||||||
WriteStream(const SystemString& path, int64_t maxWriteSize)
|
WriteStream(const SystemString& path, int64_t maxWriteSize, bool& err)
|
||||||
: m_maxWriteSize(maxWriteSize)
|
: m_maxWriteSize(maxWriteSize)
|
||||||
{
|
{
|
||||||
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
|
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
|
||||||
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (fp == INVALID_HANDLE_VALUE)
|
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)
|
: m_maxWriteSize(maxWriteSize)
|
||||||
{
|
{
|
||||||
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
|
fp = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE,
|
||||||
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (fp == INVALID_HANDLE_VALUE)
|
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;
|
LARGE_INTEGER lioffset;
|
||||||
lioffset.QuadPart = offset;
|
lioffset.QuadPart = offset;
|
||||||
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
|
SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN);
|
||||||
|
@ -78,7 +85,10 @@ public:
|
||||||
LARGE_INTEGER res;
|
LARGE_INTEGER res;
|
||||||
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
|
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
|
||||||
if (res.QuadPart + int64_t(length) > m_maxWriteSize)
|
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;
|
DWORD ret = 0;
|
||||||
|
@ -95,12 +105,12 @@ public:
|
||||||
uint64_t readSz = discio.read(buf, thisSz);
|
uint64_t readSz = discio.read(buf, thisSz);
|
||||||
if (thisSz != readSz)
|
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;
|
return read;
|
||||||
}
|
}
|
||||||
if (write(buf, readSz) != readSz)
|
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;
|
return read;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
|
@ -111,24 +121,35 @@ public:
|
||||||
};
|
};
|
||||||
std::unique_ptr<IWriteStream> beginWriteStream() const
|
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
|
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
|
struct ReadStream : public IFileIO::IReadStream
|
||||||
{
|
{
|
||||||
HANDLE fp;
|
HANDLE fp;
|
||||||
ReadStream(const SystemString& path)
|
ReadStream(const SystemString& path, bool& err)
|
||||||
{
|
{
|
||||||
fp = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ,
|
fp = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ,
|
||||||
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (fp == INVALID_HANDLE_VALUE)
|
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)
|
: ReadStream(path)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER lioffset;
|
LARGE_INTEGER lioffset;
|
||||||
|
@ -167,12 +188,12 @@ public:
|
||||||
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
||||||
if (read(buf, thisSz) != thisSz)
|
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;
|
return written;
|
||||||
}
|
}
|
||||||
if (discio.write(buf, thisSz) != thisSz)
|
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;
|
return written;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
|
@ -183,11 +204,19 @@ public:
|
||||||
};
|
};
|
||||||
std::unique_ptr<IReadStream> beginReadStream() const
|
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
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
26
lib/nod.cpp
26
lib/nod.cpp
|
@ -17,15 +17,20 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
|
||||||
if (!fio->exists())
|
if (!fio->exists())
|
||||||
{
|
{
|
||||||
LogModule.report(logvisor::Error, _S("Unable to open '%s'"), path);
|
LogModule.report(logvisor::Error, _S("Unable to open '%s'"), path);
|
||||||
return std::unique_ptr<DiscBase>();
|
return {};
|
||||||
}
|
}
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
||||||
|
if (!rs)
|
||||||
|
return {};
|
||||||
|
|
||||||
isWii = false;
|
isWii = false;
|
||||||
std::unique_ptr<IDiscIO> discIO;
|
std::unique_ptr<IDiscIO> discIO;
|
||||||
uint32_t magic = 0;
|
uint32_t magic = 0;
|
||||||
if (rs->read(&magic, 4) != 4)
|
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'))
|
if (magic == nod::SBig((uint32_t)'WBFS'))
|
||||||
{
|
{
|
||||||
|
@ -54,14 +59,23 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
|
||||||
if (!discIO)
|
if (!discIO)
|
||||||
{
|
{
|
||||||
LogModule.report(logvisor::Error, _S("'%s' is not a valid image"), path);
|
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)
|
if (isWii)
|
||||||
return std::unique_ptr<DiscBase>(new DiscWii(std::move(discIO)));
|
{
|
||||||
|
ret = std::unique_ptr<DiscBase>(new DiscWii(std::move(discIO), Err));
|
||||||
return std::unique_ptr<DiscBase>(new DiscGCN(std::move(discIO)));
|
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)
|
std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path)
|
||||||
|
|
Loading…
Reference in New Issue