Major refactor, better error handling and directory/image merge

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

View File

@ -8,7 +8,9 @@ static void printHelp()
fprintf(stderr, "Usage:\n" 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;
} }

View File

@ -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;}
}; };

View File

@ -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);
}; };
} }

View File

@ -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);
}; };
} }

View File

@ -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
} }

View File

@ -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;
}
} }

View File

@ -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;
}
} }

View File

@ -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;
} }
}; };

View File

@ -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

View File

@ -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;
}
} }

View File

@ -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;
} }
}; };

View File

@ -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;
} }
}; };

View File

@ -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)