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