Refactor of extracted directory structure and API simplification

This commit is contained in:
Jack Andersen 2017-07-01 13:36:16 -10:00
parent e49568ac83
commit 41148a1368
14 changed files with 814 additions and 577 deletions

View File

@ -51,12 +51,12 @@ auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
}; };
/* Making a GCN image */ /* Making a GCN image */
nod::DiscBuilderGCN b(isoOutPath, gameID, gameTitle, dolLoadAddress, progFunc); nod::DiscBuilderGCN b(isoOutPath, progFunc);
ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath); ret = b.buildFromDirectory(fsRootDirPath);
/* Making a Wii image */ /* Making a Wii image */
nod::DiscBuilderWii b(isoOutPath, gameID, gameTitle, dualLayer, progFunc); nod::DiscBuilderWii b(isoOutPath, dualLayer, progFunc);
ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath, partitionHeadPath); ret = b.buildFromDirectory(fsRootDirPath);
``` ```
Wii images are fakesigned using a commonly-applied [signing bug](http://wiibrew.org/wiki/Signing_bug). Wii images are fakesigned using a commonly-applied [signing bug](http://wiibrew.org/wiki/Signing_bug).
@ -75,7 +75,6 @@ An extract/repack works like so:
>$ cd <dir-out> >$ cd <dir-out>
# Then one of: # Then one of:
>$ nodtool makegcn <gameid> <game-title> fsroot boot.dol apploader.bin [<image-out>] >$ nodtool makegcn fsroot [<image-out>]
>$ nodtool makewiisl <gameid> <game-title> fsroot boot.dol apploader.bin partition_head.bin [<image-out>] >$ nodtool makewii fsroot [<image-out>]
>$ nodtool makewiidl <gameid> <game-title> fsroot boot.dol apploader.bin partition_head.bin [<image-out>]
``` ```

View File

@ -7,8 +7,8 @@ static void printHelp()
{ {
fprintf(stderr, "Usage:\n" fprintf(stderr, "Usage:\n"
" nodtool extract [-f] <image-in> [<dir-out>]\n" " nodtool extract [-f] <image-in> [<dir-out>]\n"
" nodtool makegcn <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> [<image-out>]\n" " nodtool makegcn <fsroot-in> [<image-out>]\n"
" nodtool makewii <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> <parthead-in> [<image-out>]\n" " nodtool makewii <fsroot-in> [<image-out>]\n"
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n" " nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n"); " nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n");
} }
@ -26,8 +26,8 @@ int main(int argc, char* argv[])
#endif #endif
{ {
if (argc < 3 || if (argc < 3 ||
(!strcasecmp(argv[1], _S("makegcn")) && argc < 7) || (!strcasecmp(argv[1], _S("makegcn")) && argc < 3) ||
(!strcasecmp(argv[1], _S("makewii")) && argc < 8) || (!strcasecmp(argv[1], _S("makewii")) && argc < 3) ||
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) || (!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) ||
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4)) (!strcasecmp(argv[1], _S("mergewii")) && argc < 4))
{ {
@ -39,9 +39,12 @@ int main(int argc, char* argv[])
logvisor::RegisterStandardExceptions(); logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger(); logvisor::RegisterConsoleLogger();
nod::ExtractionContext ctx = { true, true, [&](const std::string& str, float c){ bool verbose = false;
fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f); nod::ExtractionContext ctx = {true,
}}; [&](const std::string& str, float c) {
if (verbose)
fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f);
}};
const nod::SystemChar* inDir = nullptr; const nod::SystemChar* inDir = nullptr;
const nod::SystemChar* outDir = _S("."); const nod::SystemChar* outDir = _S(".");
@ -50,7 +53,7 @@ int main(int argc, char* argv[])
if (argv[a][0] == '-' && argv[a][1] == 'f') if (argv[a][0] == '-' && argv[a][1] == 'f')
ctx.force = true; ctx.force = true;
else if (argv[a][0] == '-' && argv[a][1] == 'v') else if (argv[a][0] == '-' && argv[a][1] == 'v')
ctx.verbose = true; verbose = true;
else if (!inDir) else if (!inDir)
inDir = argv[a]; inDir = argv[a];
@ -77,10 +80,6 @@ int main(int argc, char* argv[])
nod::Mkdir(outDir, 0755); nod::Mkdir(outDir, 0755);
if (isWii)
static_cast<nod::DiscWii&>(*disc).writeOutDataPartitionHeader(
(nod::SystemString(outDir) + _S("/partition_head.bin")).c_str());
nod::Partition* dataPart = disc->getDataPartition(); nod::Partition* dataPart = disc->getDataPartition();
if (!dataPart) if (!dataPart)
return 1; return 1;
@ -90,59 +89,30 @@ int main(int argc, char* argv[])
} }
else if (!strcasecmp(argv[1], _S("makegcn"))) else if (!strcasecmp(argv[1], _S("makegcn")))
{ {
#if NOD_UCS2 /* Pre-validate path */
if (wcslen(argv[2]) < 6)
{
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::Error, _S("game-id is not at least 6 characters"));
return 1;
}
#endif
/* Pre-validate paths */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode)) if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{ {
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]); nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
return 1;
}
if (nod::Stat(argv[5], &theStat) || !S_ISREG(theStat.st_mode))
{
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::Error, "unable to stat %s as file", argv[6]);
return 1; return 1;
} }
nod::SystemString gameIdSys(argv[2]); if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1)
nod::SystemUTF8View gameId(gameIdSys);
nod::SystemString gameTitleSys(argv[3]);
nod::SystemUTF8View gameTitle(gameTitleSys);
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[4], argv[5]) == -1)
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 8) if (argc < 4)
{ {
nod::SystemString outPath(argv[4]); nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso")); outPath.append(_S(".iso"));
nod::DiscBuilderGCN b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc); nod::DiscBuilderGCN b(outPath.c_str(), progFunc);
ret = b.buildFromDirectory(argv[4], argv[5], argv[6]); ret = b.buildFromDirectory(argv[2]);
} }
else else
{ {
nod::DiscBuilderGCN b(argv[7], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc); nod::DiscBuilderGCN b(argv[3], progFunc);
ret = b.buildFromDirectory(argv[4], argv[5], argv[6]); ret = b.buildFromDirectory(argv[2]);
} }
printf("\n"); printf("\n");
@ -151,65 +121,31 @@ int main(int argc, char* argv[])
} }
else if (!strcasecmp(argv[1], _S("makewii"))) else if (!strcasecmp(argv[1], _S("makewii")))
{ {
#if NOD_UCS2 /* Pre-validate path */
if (wcslen(argv[2]) < 6)
{
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::Error, _S("game-id is not at least 6 characters"));
return 1;
}
#endif
/* Pre-validate paths */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode)) if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
{ {
nod::LogModule.report(logvisor::Error, _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; return 1;
} }
if (nod::Stat(argv[5], &theStat) || !S_ISREG(theStat.st_mode))
{
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::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::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);
bool dual = false; bool dual = false;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[4], argv[5], dual) == -1) if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1)
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 9) if (argc < 4)
{ {
nod::SystemString outPath(argv[4]); nod::SystemString outPath(argv[2]);
outPath.append(_S(".iso")); outPath.append(_S(".iso"));
nod::DiscBuilderWii b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc); nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc);
ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]); ret = b.buildFromDirectory(argv[2]);
} }
else else
{ {
nod::DiscBuilderWii b(argv[8], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc); nod::DiscBuilderWii b(argv[3], dual, progFunc);
ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]); ret = b.buildFromDirectory(argv[2]);
} }
printf("\n"); printf("\n");

View File

@ -67,60 +67,126 @@ struct Header
uint32_t m_debugMonOff; uint32_t m_debugMonOff;
uint32_t m_debugLoadAddr; uint32_t m_debugLoadAddr;
char m_unk3[0x18]; char m_unk3[0x18];
uint32_t m_dolOff;
uint32_t m_fstOff;
uint32_t m_fstSz;
uint32_t m_fstMaxSz;
uint32_t m_fstMemoryAddress;
uint32_t m_userPosition;
uint32_t m_userSz;
uint8_t padding1[4];
Header() = default;
Header(IDiscIO& dio, bool& err) Header(IDiscIO& dio, bool& err)
{ {
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0); auto rs = dio.beginReadStream();
if (!s) if (!rs)
{ {
err = true; err = true;
return; return;
} }
s->read(this, sizeof(*this)); read(*rs);
}
void read(IReadStream& s)
{
memset(this, 0, sizeof(*this));
s.read(this, sizeof(*this));
m_wiiMagic = SBig(m_wiiMagic); m_wiiMagic = SBig(m_wiiMagic);
m_gcnMagic = SBig(m_gcnMagic); m_gcnMagic = SBig(m_gcnMagic);
m_debugMonOff = SBig(m_debugMonOff); m_debugMonOff = SBig(m_debugMonOff);
m_debugLoadAddr = SBig(m_debugLoadAddr); m_debugLoadAddr = SBig(m_debugLoadAddr);
m_dolOff = SBig(m_dolOff);
m_fstOff = SBig(m_fstOff);
m_fstSz = SBig(m_fstSz);
m_fstMaxSz = SBig(m_fstMaxSz);
m_fstMemoryAddress = SBig(m_fstMemoryAddress);
m_userPosition = SBig(m_userPosition);
m_userSz = SBig(m_userSz);
} }
Header(const char gameID[6], const char* gameTitle, bool wii, char discNum=0, char discVersion=0, void write(IWriteStream& ws) const
char audioStreaming=1, char streamBufSz=0)
{
memset(this, 0, sizeof(*this));
memcpy(m_gameID, gameID, 6);
strncpy(m_gameTitle, gameTitle, 64);
m_discNum = discNum;
m_discVersion = discVersion;
m_audioStreaming = audioStreaming;
m_streamBufSz = streamBufSz;
if (wii)
m_wiiMagic = 0x5D1C9EA3;
else
m_gcnMagic = 0xC2339F3D;
}
template <class WriteStream>
void write(WriteStream& ws) const
{ {
Header hs(*this); Header hs(*this);
hs.m_wiiMagic = SBig(hs.m_wiiMagic); hs.m_wiiMagic = SBig(hs.m_wiiMagic);
hs.m_gcnMagic = SBig(hs.m_gcnMagic); hs.m_gcnMagic = SBig(hs.m_gcnMagic);
hs.m_debugMonOff = SBig(hs.m_debugMonOff); hs.m_debugMonOff = SBig(hs.m_debugMonOff);
hs.m_debugLoadAddr = SBig(hs.m_debugLoadAddr); hs.m_debugLoadAddr = SBig(hs.m_debugLoadAddr);
hs.m_dolOff = SBig(hs.m_dolOff);
hs.m_fstOff = SBig(hs.m_fstOff);
hs.m_fstSz = SBig(hs.m_fstSz);
hs.m_fstMaxSz = SBig(hs.m_fstMaxSz);
hs.m_fstMemoryAddress = SBig(hs.m_fstMemoryAddress);
hs.m_userPosition = SBig(hs.m_userPosition);
hs.m_userSz = SBig(hs.m_userSz);
ws.write(&hs, sizeof(hs)); ws.write(&hs, sizeof(hs));
} }
}; };
/* Currently only kept for dolphin compatibility */
struct BI2Header
{
int32_t m_debugMonitorSize;
int32_t m_simMemSize;
uint32_t m_argOffset;
uint32_t m_debugFlag;
uint32_t m_trkAddress;
uint32_t m_trkSz;
uint32_t m_countryCode;
uint32_t m_unk1;
uint32_t m_unk2;
uint32_t m_unk3;
uint32_t m_dolLimit;
uint32_t m_unk4;
uint8_t padding2[0x1FD0];
void read(IReadStream& rs)
{
memset(this, 0, sizeof(*this));
rs.read(this, sizeof(*this));
m_debugMonitorSize = SBig(m_debugMonitorSize);
m_simMemSize = SBig(m_simMemSize);
m_argOffset = SBig(m_argOffset);
m_debugFlag = SBig(m_debugFlag);
m_trkAddress = SBig(m_trkAddress);
m_trkSz = SBig(m_trkSz);
m_countryCode = SBig(m_countryCode);
m_unk1 = SBig(m_unk1);
m_unk2 = SBig(m_unk2);
m_unk3 = SBig(m_unk3);
m_dolLimit = SBig(m_dolLimit);
m_unk4 = SBig(m_unk4);
}
void write(IWriteStream& ws) const
{
BI2Header h = *this;
h.m_debugMonitorSize = SBig(h.m_debugMonitorSize);
h.m_simMemSize = SBig(h.m_simMemSize);
h.m_argOffset = SBig(h.m_argOffset);
h.m_debugFlag = SBig(h.m_debugFlag);
h.m_trkAddress = SBig(h.m_trkAddress);
h.m_trkSz = SBig(h.m_trkSz);
h.m_countryCode = SBig(h.m_countryCode);
h.m_unk1 = SBig(h.m_unk1);
h.m_unk2 = SBig(h.m_unk2);
h.m_unk3 = SBig(h.m_unk3);
h.m_dolLimit = SBig(h.m_dolLimit);
h.m_unk4 = SBig(h.m_unk4);
ws.write(&h, sizeof(h));
}
};
struct ExtractionContext; struct ExtractionContext;
class DiscBase class DiscBase
{ {
public: public:
virtual ~DiscBase() {} virtual ~DiscBase() = default;
class IPartition class IPartition
{ {
public: public:
virtual ~IPartition() {} virtual ~IPartition() = default;
enum class Kind : uint32_t enum class Kind : uint32_t
{ {
Data, Data,
@ -140,32 +206,6 @@ public:
uint32_t entryPoint; uint32_t entryPoint;
}; };
/* Currently only kept for dolphin compatibility*/
struct BI2Header
{
uint32_t dolOff;
uint32_t fstOff;
uint32_t fstSz;
uint32_t fstMaxSz;
uint32_t fstMemoryAddress;
uint32_t userPosition;
uint32_t userSz;
uint8_t padding1[4];
int32_t debugMonitorSize;
int32_t simMemSize;
uint32_t argOffset;
uint32_t debugFlag;
uint32_t trkAddress;
uint32_t trkSz;
uint32_t countryCode;
uint32_t unk1;
uint32_t unk2;
uint32_t unk3;
uint32_t dolLimit;
uint32_t unk4;
uint8_t padding2[0x1fd0];
};
class Node class Node
{ {
public: public:
@ -259,11 +299,11 @@ public:
bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const; bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const;
}; };
protected: protected:
Header m_header;
BI2Header m_bi2Header; BI2Header m_bi2Header;
uint64_t m_dolOff; uint64_t m_dolOff;
uint64_t m_fstOff; uint64_t m_fstOff;
uint64_t m_fstSz; uint64_t m_fstSz;
uint64_t m_fstMemoryAddr;
uint64_t m_apploaderSz; uint64_t m_apploaderSz;
std::vector<Node> m_nodes; std::vector<Node> m_nodes;
void parseFST(IPartReadStream& s); void parseFST(IPartReadStream& s);
@ -325,8 +365,6 @@ public:
return buf; return buf;
} }
inline uint64_t getFSTMemoryAddr() const {return m_fstMemoryAddr;}
inline uint64_t getApploaderSize() const {return m_apploaderSz;} inline uint64_t getApploaderSize() const {return m_apploaderSz;}
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
{ {
@ -336,8 +374,9 @@ public:
} }
inline size_t getNodeCount() const { return m_nodes.size(); } inline size_t getNodeCount() const { return m_nodes.size(); }
inline const Header& getHeader() const { return m_parent.getHeader(); } inline const Header& getHeader() const { return m_header; }
inline const uint8_t* getBI2Buf() const { return reinterpret_cast<const uint8_t*>(&m_bi2Header); } inline const BI2Header& getBI2() const { return m_bi2Header; }
virtual bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { return true; }
}; };
protected: protected:
@ -364,6 +403,7 @@ public:
return part.get(); return part.get();
return nullptr; return nullptr;
} }
inline IPartition* getUpdatePartition() inline IPartition* getUpdatePartition()
{ {
for (const std::unique_ptr<IPartition>& part : m_partitions) for (const std::unique_ptr<IPartition>& part : m_partitions)
@ -371,12 +411,14 @@ public:
return part.get(); return part.get();
return nullptr; return nullptr;
} }
inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx) inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx)
{ {
for (std::unique_ptr<IPartition>& part : m_partitions) for (std::unique_ptr<IPartition>& part : m_partitions)
part->extractToDirectory(path, ctx); part->extractToDirectory(path, ctx);
} }
virtual bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const=0;
}; };
class DiscBuilderBase class DiscBuilderBase
@ -386,7 +428,7 @@ public:
class PartitionBuilderBase class PartitionBuilderBase
{ {
public: public:
virtual ~PartitionBuilderBase() {} virtual ~PartitionBuilderBase() = default;
enum class Kind : uint32_t enum class Kind : uint32_t
{ {
Data, Data,
@ -401,10 +443,10 @@ public:
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0; virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
virtual uint32_t packOffset(uint64_t offset) const=0; virtual uint32_t packOffset(uint64_t offset) const=0;
void recursiveBuildNodesPre(const SystemChar* dirIn, uint64_t dolInode); void recursiveBuildNodesPre(const SystemChar* dirIn);
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode); bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn);
bool recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode, bool recursiveBuildFST(const SystemChar* dirIn,
std::function<void(void)> incParents); std::function<void(void)> incParents);
void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn); void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn);
@ -428,32 +470,21 @@ public:
DiscBuilderBase& m_parent; DiscBuilderBase& m_parent;
Kind m_kind; Kind m_kind;
char m_gameID[6];
std::string m_gameTitle;
uint64_t m_dolOffset = 0; uint64_t m_dolOffset = 0;
uint64_t m_dolSize = 0; uint64_t m_dolSize = 0;
public: public:
PartitionBuilderBase(DiscBuilderBase& parent, Kind kind, PartitionBuilderBase(DiscBuilderBase& parent, Kind kind)
const char gameID[6], const char* gameTitle) : m_parent(parent), m_kind(kind)
: m_parent(parent), m_kind(kind), m_gameTitle(gameTitle) {}
{
memcpy(m_gameID, gameID, 6);
}
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0; virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0;
bool buildFromDirectory(IPartWriteStream& ws, bool buildFromDirectory(IPartWriteStream& ws,
const SystemChar* dirIn, const SystemChar* dolIn, const SystemChar* dirIn);
const SystemChar* apploaderIn); static uint64_t CalculateTotalSizeBuild(const SystemChar* dirIn);
static uint64_t CalculateTotalSizeBuild(const SystemChar* dolIn,
const SystemChar* dirIn);
bool mergeFromDirectory(IPartWriteStream& ws, bool mergeFromDirectory(IPartWriteStream& ws,
const DiscBase::IPartition* partIn, const DiscBase::IPartition* partIn,
const SystemChar* dirIn); const SystemChar* dirIn);
static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn, static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
const SystemChar* dirIn); const SystemChar* dirIn);
const char* getGameID() const {return m_gameID;}
const std::string& getGameTitle() const {return m_gameTitle;}
}; };
protected: protected:
SystemString m_outPath; SystemString m_outPath;
@ -480,13 +511,14 @@ public:
} }
virtual ~DiscBuilderBase() = default; virtual ~DiscBuilderBase() = default;
DiscBuilderBase(const SystemChar* outPath, int64_t discCapacity, FProgress progressCB) DiscBuilderBase(const SystemChar* outPath,
int64_t discCapacity, FProgress progressCB)
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)), : m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)),
m_discCapacity(discCapacity), m_progressCB(progressCB) {} m_discCapacity(discCapacity), m_progressCB(progressCB) {}
DiscBuilderBase(DiscBuilderBase&&) = default; DiscBuilderBase(DiscBuilderBase&&) = default;
DiscBuilderBase& operator=(DiscBuilderBase&&) = default; DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
IFileIO& getFileIO() {return *m_fileIO;} IFileIO& getFileIO() { return *m_fileIO; }
}; };
using Partition = DiscBase::IPartition; using Partition = DiscBase::IPartition;

View File

@ -13,17 +13,16 @@ class DiscGCN : public DiscBase
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB); DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
public: public:
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err); DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const;
}; };
class DiscBuilderGCN : public DiscBuilderBase class DiscBuilderGCN : public DiscBuilderBase
{ {
friend class DiscMergerGCN; friend class DiscMergerGCN;
public: public:
DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle, DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB);
uint32_t fstMemoryAddr, FProgress progressCB); EBuildResult buildFromDirectory(const SystemChar* dirIn);
EBuildResult buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn);
const SystemChar* apploaderIn);
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn);
}; };
class DiscMergerGCN class DiscMergerGCN

View File

@ -12,21 +12,16 @@ class DiscWii : public DiscBase
public: public:
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err); DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB); DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
bool writeOutDataPartitionHeader(const SystemChar* pathOut) const; bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const;
}; };
class DiscBuilderWii : public DiscBuilderBase class DiscBuilderWii : public DiscBuilderBase
{ {
bool m_dualLayer; bool m_dualLayer;
public: public:
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
bool dualLayer, FProgress progressCB); EBuildResult buildFromDirectory(const SystemChar* dirIn);
EBuildResult buildFromDirectory(const SystemChar* dirIn, static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer);
const SystemChar* dolIn,
const SystemChar* apploaderIn,
const SystemChar* partHeadIn);
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
bool& dualLayer);
}; };
class DiscMergerWii class DiscMergerWii

View File

@ -14,42 +14,38 @@
namespace nod namespace nod
{ {
struct IReadStream
{
virtual ~IReadStream() = default;
virtual uint64_t read(void* buf, uint64_t length)=0;
virtual void seek(int64_t offset, int whence=SEEK_SET)=0;
virtual uint64_t position() const=0;
};
struct IWriteStream
{
virtual ~IWriteStream() = default;
virtual uint64_t write(const void* buf, uint64_t length)=0;
};
class IDiscIO class IDiscIO
{ {
public: public:
virtual ~IDiscIO() {} virtual ~IDiscIO() = default;
struct IReadStream
{
virtual ~IReadStream() {}
virtual uint64_t read(void* buf, uint64_t length)=0;
virtual void seek(int64_t offset, int whence=SEEK_SET)=0;
virtual uint64_t position() const=0;
};
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset=0) const=0; virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset=0) const=0;
struct IWriteStream
{
virtual ~IWriteStream() {}
virtual uint64_t write(const void* buf, uint64_t length)=0;
};
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset=0) const=0; virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset=0) const=0;
}; };
struct IPartReadStream struct IPartReadStream : IReadStream
{ {
virtual ~IPartReadStream() {} virtual ~IPartReadStream() = default;
virtual void seek(int64_t offset, int whence=SEEK_SET)=0;
virtual uint64_t position() const=0;
virtual uint64_t read(void* buf, uint64_t length)=0;
}; };
struct IPartWriteStream struct IPartWriteStream : IWriteStream
{ {
virtual ~IPartWriteStream() {} virtual ~IPartWriteStream() = default;
virtual void close()=0; virtual void close()=0;
virtual uint64_t position() const=0; virtual uint64_t position() const=0;
virtual uint64_t write(const void* buf, uint64_t length)=0;
}; };
#if NOD_ATHENA #if NOD_ATHENA

View File

@ -13,15 +13,12 @@ namespace nod
class IFileIO class IFileIO
{ {
public: public:
virtual ~IFileIO() {} virtual ~IFileIO() = default;
virtual bool exists()=0; virtual bool exists()=0;
virtual uint64_t size()=0; virtual uint64_t size()=0;
struct IWriteStream struct IWriteStream : nod::IWriteStream
{ {
virtual ~IWriteStream() {}
virtual uint64_t write(const void* buf, uint64_t length)=0;
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length) uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length)
{ {
uint64_t read = 0; uint64_t read = 0;
@ -74,12 +71,8 @@ public:
virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0; virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0;
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const=0; virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const=0;
struct IReadStream struct IReadStream : nod::IReadStream
{ {
virtual ~IReadStream() {}
virtual void seek(int64_t offset, int whence)=0;
virtual int64_t position()=0;
virtual uint64_t read(void* buf, uint64_t length)=0;
virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length)=0; virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length)=0;
}; };
virtual std::unique_ptr<IReadStream> beginReadStream() const=0; virtual std::unique_ptr<IReadStream> beginReadStream() const=0;

View File

@ -11,7 +11,7 @@ namespace nod
class IAES class IAES
{ {
public: public:
virtual ~IAES() {} virtual ~IAES() = default;
virtual void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; virtual void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0;
virtual void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; virtual void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0;
virtual void setKey(const uint8_t* key)=0; virtual void setKey(const uint8_t* key)=0;

View File

@ -13,7 +13,6 @@ class DiscBase;
struct ExtractionContext final struct ExtractionContext final
{ {
bool verbose : 1;
bool force : 1; bool force : 1;
std::function<void(const std::string&, float)> progressCB; std::function<void(const std::string&, float)> progressCB;
}; };

View File

@ -98,7 +98,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
if (m_kind == Kind::Directory) if (m_kind == Kind::Directory)
{ {
++m_parent.m_curNodeIdx; ++m_parent.m_curNodeIdx;
if (ctx.verbose && ctx.progressCB && !getName().empty()) if (ctx.progressCB && !getName().empty())
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
if (Mkdir(path.c_str(), 0755) && errno != EEXIST) if (Mkdir(path.c_str(), 0755) && errno != EEXIST)
{ {
@ -112,7 +112,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
else if (m_kind == Kind::File) else if (m_kind == Kind::File)
{ {
Sstat theStat; Sstat theStat;
if (ctx.verbose && ctx.progressCB) if (ctx.progressCB)
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
if (ctx.force || Stat(path.c_str(), &theStat)) if (ctx.force || Stat(path.c_str(), &theStat))
@ -124,7 +124,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
ws->copyFromDisc(*rs, m_discLength, ws->copyFromDisc(*rs, m_discLength,
[&](float prog) [&](float prog)
{ {
if (ctx.verbose && ctx.progressCB) if (ctx.progressCB)
ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount())); ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount()));
}); });
} }
@ -144,17 +144,31 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
return false; return false;
} }
if (Mkdir((path + _S("/sys")).c_str(), 0755) && errno != EEXIST) if (Mkdir((path + _S("/DATA")).c_str(), 0755) && errno != EEXIST)
{ {
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/sys'"), path.c_str()); LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA'"), path.c_str());
return false; return false;
} }
if (Mkdir((path + _S("/DATA/sys")).c_str(), 0755) && errno != EEXIST)
{
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/sys'"), path.c_str());
return false;
}
/* Extract Disc Files */
if (!m_parent.extractDiscHeaderFiles(path, ctx))
return false;
/* Extract Crypto Files */
if (!extractCryptoFiles(path, ctx))
return false;
/* Extract Apploader */ /* Extract Apploader */
SystemString apploaderPath = path + _S("/sys/apploader.img"); SystemString apploaderPath = path + _S("/DATA/sys/apploader.img");
if (ctx.force || Stat(apploaderPath.c_str(), &theStat)) if (ctx.force || Stat(apploaderPath.c_str(), &theStat))
{ {
if (ctx.verbose && ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("apploader.bin", 0.f); ctx.progressCB("apploader.bin", 0.f);
std::unique_ptr<uint8_t[]> buf = getApploaderBuf(); std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
auto ws = NewFileIO(apploaderPath)->beginWriteStream(); auto ws = NewFileIO(apploaderPath)->beginWriteStream();
@ -164,10 +178,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
} }
/* Extract Dol */ /* Extract Dol */
SystemString dolPath = path + _S("/sys/main.dol"); SystemString dolPath = path + _S("/DATA/sys/main.dol");
if (ctx.force || Stat(dolPath.c_str(), &theStat)) if (ctx.force || Stat(dolPath.c_str(), &theStat))
{ {
if (ctx.verbose && ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("main.dol", 0.f); ctx.progressCB("main.dol", 0.f);
std::unique_ptr<uint8_t[]> buf = getDOLBuf(); std::unique_ptr<uint8_t[]> buf = getDOLBuf();
auto ws = NewFileIO(dolPath)->beginWriteStream(); auto ws = NewFileIO(dolPath)->beginWriteStream();
@ -177,32 +191,32 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
} }
/* Extract Boot info */ /* Extract Boot info */
SystemString bootPath = path + _S("/sys/boot.bin"); SystemString bootPath = path + _S("/DATA/sys/boot.bin");
if (ctx.force || Stat(bootPath.c_str(), &theStat)) if (ctx.force || Stat(bootPath.c_str(), &theStat))
{ {
if (ctx.verbose && ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("boot.bin", 0.f); ctx.progressCB("boot.bin", 0.f);
auto ws = NewFileIO(bootPath)->beginWriteStream(); auto ws = NewFileIO(bootPath)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
getHeader().write(*ws.get()); m_header.write(*ws.get());
} }
/* Extract BI2 info */ /* Extract BI2 info */
SystemString bi2Path = path + _S("/sys/bi2.bin"); SystemString bi2Path = path + _S("/DATA/sys/bi2.bin");
if (ctx.force || Stat(bi2Path.c_str(), &theStat)) if (ctx.force || Stat(bi2Path.c_str(), &theStat))
{ {
if (ctx.verbose && ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("bi2.bin", 0.f); ctx.progressCB("bi2.bin", 0.f);
const uint8_t* buf = getBI2Buf();
auto ws = NewFileIO(bi2Path)->beginWriteStream(); auto ws = NewFileIO(bi2Path)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
ws->write(buf, sizeof(BI2Header)); m_bi2Header.write(*ws);
} }
/* Extract Filesystem */ /* Extract Filesystem */
SystemString fsPath = path + _S("/files"); SystemString fsPath = path + _S("/DATA/files");
if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST) if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST)
{ {
LogModule.report(logvisor::Error, _S("unable to mkdir '%s'"), fsPath.c_str()); LogModule.report(logvisor::Error, _S("unable to mkdir '%s'"), fsPath.c_str());
@ -299,37 +313,28 @@ static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t s
return out.write(buf.get(), sz); return out.write(buf.get(), sz);
} }
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* dirIn, void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* filesIn)
uint64_t dolInode)
{ {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) for (const DirectoryEnumerator::Entry& e : dEnum)
{ {
if (e.m_isDir) if (e.m_isDir)
{ recursiveBuildNodesPre(e.m_path.c_str());
recursiveBuildNodesPre(e.m_path.c_str(), dolInode);
}
else else
{
if (dolInode == GetInode(e.m_path.c_str()))
continue;
++m_parent.m_progressTotal; ++m_parent.m_progressTotal;
}
} }
} }
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws, bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
bool system, bool system,
const SystemChar* dirIn, const SystemChar* filesIn)
uint64_t dolInode)
{ {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) for (const DirectoryEnumerator::Entry& e : dEnum)
{ {
if (e.m_isDir) if (e.m_isDir)
{ {
if (!recursiveBuildNodes(ws, system, e.m_path.c_str(), dolInode)) if (!recursiveBuildNodes(ws, system, e.m_path.c_str()))
return false; return false;
} }
else else
@ -339,9 +344,6 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
if (system ^ isSys) if (system ^ isSys)
continue; continue;
if (dolInode == GetInode(e.m_path.c_str()))
continue;
size_t fileSz = ROUND_UP_32(e.m_fileSz); size_t fileSz = ROUND_UP_32(e.m_fileSz);
uint64_t fileOff = userAllocate(fileSz, ws); uint64_t fileOff = userAllocate(fileSz, ws);
if (fileOff == -1) if (fileOff == -1)
@ -380,10 +382,10 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
return true; return true;
} }
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode, bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* filesIn,
std::function<void(void)> incParents) std::function<void(void)> incParents)
{ {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) for (const DirectoryEnumerator::Entry& e : dEnum)
{ {
if (e.m_isDir) if (e.m_isDir)
@ -392,19 +394,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar*
m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1); m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1);
addBuildName(e.m_name); addBuildName(e.m_name);
incParents(); incParents();
if (!recursiveBuildFST(e.m_path.c_str(), dolInode, [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();})) if (!recursiveBuildFST(e.m_path.c_str(), [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}))
return false; return false;
} }
else else
{ {
if (dolInode == GetInode(e.m_path.c_str()))
{
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(m_dolOffset), m_dolSize);
addBuildName(e.m_name);
incParents();
continue;
}
std::pair<uint64_t,uint64_t> fileOffSz = m_fileOffsetsSizes.at(e.m_path); std::pair<uint64_t,uint64_t> fileOffSz = m_fileOffsetsSizes.at(e.m_path);
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second); m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
addBuildName(e.m_name); addBuildName(e.m_name);
@ -796,20 +790,21 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(
} }
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws, bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws,
const SystemChar* dirIn, const SystemChar* dirIn)
const SystemChar* dolIn,
const SystemChar* apploaderIn)
{ {
if (!dirIn || !dolIn || !apploaderIn) if (!dirIn)
{ {
LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()")); LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()"));
return false; return false;
} }
SystemString dirStr(dirIn);
SystemString dolIn = dirStr + _S("/DATA/sys/main.dol");
SystemString filesIn = dirStr + _S("/DATA/files");
/* 1st pass - Tally up total progress steps */ /* 1st pass - Tally up total progress steps */
uint64_t dolInode = GetInode(dolIn);
m_parent.m_progressTotal += 2; /* Prep and DOL */ m_parent.m_progressTotal += 2; /* Prep and DOL */
recursiveBuildNodesPre(dirIn, dolInode); recursiveBuildNodesPre(filesIn.c_str());
/* Clear file */ /* Clear file */
m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1); m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1);
@ -822,9 +817,9 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
/* Write Boot DOL first (first thing seeked to after Apploader) */ /* Write Boot DOL first (first thing seeked to after Apploader) */
{ {
Sstat dolStat; Sstat dolStat;
if (Stat(dolIn, &dolStat)) if (Stat(dolIn.c_str(), &dolStat))
{ {
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn); LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn.c_str());
return false; return false;
} }
size_t fileSz = ROUND_UP_32(dolStat.st_size); size_t fileSz = ROUND_UP_32(dolStat.st_size);
@ -833,12 +828,12 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
return false; return false;
m_dolOffset = fileOff; m_dolOffset = fileOff;
m_dolSize = fileSz; m_dolSize = fileSz;
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn)->beginReadStream(); std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn.c_str())->beginReadStream();
if (!rs) if (!rs)
return false; return false;
bool patched; bool patched;
size_t xferSz = PatchDOL(*rs, ws, dolStat.st_size, patched); size_t xferSz = PatchDOL(*rs, ws, dolStat.st_size, patched);
m_parent.m_progressCB(m_parent.getProgressFactor(), SystemString(dolIn) + m_parent.m_progressCB(m_parent.getProgressFactor(), dolIn +
(patched ? _S(" [PATCHED]") : _S("")), xferSz); (patched ? _S(" [PATCHED]") : _S("")), xferSz);
++m_parent.m_progressIdx; ++m_parent.m_progressIdx;
for (size_t i=0 ; i<fileSz-xferSz ; ++i) for (size_t i=0 ; i<fileSz-xferSz ; ++i)
@ -846,27 +841,30 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
} }
/* Gather files in root directory */ /* Gather files in root directory */
if (!recursiveBuildNodes(ws, true, dirIn, dolInode)) if (!recursiveBuildNodes(ws, true, filesIn.c_str()))
return false; return false;
if (!recursiveBuildNodes(ws, false, dirIn, dolInode)) if (!recursiveBuildNodes(ws, false, filesIn.c_str()))
return false; return false;
if (!recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();})) if (!recursiveBuildFST(filesIn.c_str(), [&](){m_buildNodes[0].incrementLength();}))
return false; return false;
return true; return true;
} }
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dolIn, uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dirIn)
const SystemChar* dirIn)
{ {
SystemString dirStr(dirIn);
SystemString dolIn = dirStr + _S("/DATA/sys/main.dol");
SystemString filesIn = dirStr + _S("/DATA/files");
Sstat dolStat; Sstat dolStat;
if (Stat(dolIn, &dolStat)) if (Stat(dolIn.c_str(), &dolStat))
{ {
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn); LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn.c_str());
return -1; return -1;
} }
uint64_t totalSz = ROUND_UP_32(dolStat.st_size); uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
if (!RecursiveCalculateTotalSize(totalSz, nullptr, dirIn)) if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str()))
return -1; return -1;
return totalSz; return totalSz;
} }
@ -881,9 +879,12 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
return false; return false;
} }
SystemString dirStr(dirIn);
SystemString filesIn = dirStr + _S("/DATA/files");
/* 1st pass - Tally up total progress steps */ /* 1st pass - Tally up total progress steps */
m_parent.m_progressTotal += 2; /* Prep and DOL */ m_parent.m_progressTotal += 2; /* Prep and DOL */
recursiveMergeNodesPre(&partIn->getFSTRoot(), dirIn); recursiveMergeNodesPre(&partIn->getFSTRoot(), filesIn.c_str());
/* Clear file */ /* Clear file */
m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1); m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1);
@ -915,11 +916,11 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
/* Gather files in root directory */ /* Gather files in root directory */
SystemString keyPath; SystemString keyPath;
if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), dirIn, keyPath)) if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
return false; return false;
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), dirIn, keyPath)) if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
return false; return false;
if (!recursiveMergeFST(&partIn->getFSTRoot(), dirIn, [&](){m_buildNodes[0].incrementLength();}, keyPath)) if (!recursiveMergeFST(&partIn->getFSTRoot(), filesIn.c_str(), [&](){m_buildNodes[0].incrementLength();}, keyPath))
return false; return false;
return true; return true;
@ -928,8 +929,11 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn, uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
const SystemChar* dirIn) const SystemChar* dirIn)
{ {
SystemString dirStr(dirIn);
SystemString filesIn = dirStr + _S("/DATA/files");
uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize()); uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize());
if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), dirIn)) if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), filesIn.c_str()))
return -1; return -1;
return totalSz; return totalSz;
} }

View File

@ -1,4 +1,5 @@
#include "nod/DiscGCN.hpp" #include "nod/DiscGCN.hpp"
#include "nod/nod.hpp"
#include <inttypes.h> #include <inttypes.h>
#define BUFFER_SZ 0x8000 #define BUFFER_SZ 0x8000
@ -12,18 +13,17 @@ public:
: IPartition(parent, kind, offset) : IPartition(parent, kind, offset)
{ {
/* GCN-specific header reads */ /* GCN-specific header reads */
std::unique_ptr<IPartReadStream> s = beginReadStream(0x420); std::unique_ptr<IPartReadStream> s = beginReadStream(0x0);
if (!s) if (!s)
{ {
err = true; err = true;
return; return;
} }
m_header.read(*s);
s->read(&m_bi2Header, sizeof(BI2Header)); m_bi2Header.read(*s);
m_dolOff = SBig(m_bi2Header.dolOff); m_dolOff = m_header.m_dolOff;
m_fstOff = SBig(m_bi2Header.fstOff); m_fstOff = m_header.m_fstOff;
m_fstSz = SBig(m_bi2Header.fstSz); m_fstSz = m_header.m_fstSz;
m_fstMemoryAddr = SBig(m_bi2Header.fstMemoryAddress);
uint32_t vals[2]; uint32_t vals[2];
s->seek(0x2440 + 0x14); s->seek(0x2440 + 0x14);
s->read(vals, 8); s->read(vals, 8);
@ -40,7 +40,7 @@ public:
class PartReadStream : public IPartReadStream class PartReadStream : public IPartReadStream
{ {
const PartitionGCN& m_parent; const PartitionGCN& m_parent;
std::unique_ptr<IDiscIO::IReadStream> m_dio; std::unique_ptr<IReadStream> m_dio;
uint64_t m_offset; uint64_t m_offset;
size_t m_curBlock = SIZE_MAX; size_t m_curBlock = SIZE_MAX;
@ -97,7 +97,7 @@ public:
if (cacheSize + cacheOffset > BUFFER_SZ) if (cacheSize + cacheOffset > BUFFER_SZ)
cacheSize = BUFFER_SZ - cacheOffset; cacheSize = BUFFER_SZ - cacheOffset;
memcpy(dst, m_buf + cacheOffset, cacheSize); memmove(dst, m_buf + cacheOffset, cacheSize);
dst += cacheSize; dst += cacheSize;
rem -= cacheSize; rem -= cacheSize;
cacheOffset = 0; cacheOffset = 0;
@ -131,15 +131,42 @@ DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err)
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB) DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB)
{ {
IPartition* dataPart = getDataPartition(); return DiscBuilderGCN(outPath, progressCB);
return DiscBuilderGCN(outPath, m_header.m_gameID, m_header.m_gameTitle, }
dataPart->getFSTMemoryAddr(), progressCB);
bool DiscGCN::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const
{
if (Mkdir((path + _S("/DATA/disc")).c_str(), 0755) && errno != EEXIST)
{
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/disc'"), path.c_str());
return false;
}
Sstat theStat;
/* Extract Header */
SystemString headerPath = path + _S("/DATA/disc/header.bin");
if (ctx.force || Stat(headerPath.c_str(), &theStat))
{
if (ctx.progressCB)
ctx.progressCB("header.bin", 0.f);
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x0);
if (!rs)
return false;
Header header;
header.read(*rs);
auto ws = NewFileIO(headerPath)->beginWriteStream();
if (!ws)
return false;
header.write(*ws);
}
return true;
} }
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase
{ {
uint64_t m_curUser = 0x57058000; uint64_t m_curUser = 0x57058000;
uint32_t m_fstMemoryAddr;
public: public:
class PartWriteStream : public IPartWriteStream class PartWriteStream : public IPartWriteStream
@ -171,9 +198,8 @@ public:
} }
}; };
PartitionBuilderGCN(DiscBuilderBase& parent, Kind kind, PartitionBuilderGCN(DiscBuilderBase& parent)
const char gameID[6], const char* gameTitle, uint32_t fstMemoryAddr) : DiscBuilderBase::PartitionBuilderBase(parent, Kind::Data) {}
: DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle), m_fstMemoryAddr(fstMemoryAddr) {}
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)
{ {
@ -202,17 +228,16 @@ public:
return ret; return ret;
} }
bool _build(const std::function<bool(IPartWriteStream&, size_t&)>& func) bool _build(const std::function<bool(IPartWriteStream&, uint32_t, uint32_t,
uint32_t, uint32_t, uint32_t)>& headerFunc,
const std::function<bool(IPartWriteStream&)>& bi2Func,
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc)
{ {
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0); std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0x2440);
if (!ws) if (!ws)
return false; return false;
Header header(m_gameID, m_gameTitle.c_str(), false);
header.write(*ws);
ws = beginWriteStream(0x2440);
size_t xferSz = 0; size_t xferSz = 0;
if (!func(*ws, xferSz)) if (!apploaderFunc(*ws, xferSz))
return false; return false;
size_t fstOff = ROUND_UP_32(xferSz); size_t fstOff = ROUND_UP_32(xferSz);
@ -233,38 +258,89 @@ public:
return false; return false;
} }
ws = beginWriteStream(0x420); ws = beginWriteStream(0);
if (!ws) if (!ws)
return false; return false;
uint32_t vals[7]; if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser))
vals[0] = SBig(uint32_t(m_dolOffset)); return false;
vals[1] = SBig(uint32_t(fstOff)); if (!bi2Func(*ws))
vals[2] = SBig(uint32_t(fstSz)); return false;
vals[3] = SBig(uint32_t(fstSz));
vals[4] = SBig(uint32_t(m_fstMemoryAddr));
vals[5] = SBig(uint32_t(m_curUser));
vals[6] = SBig(uint32_t(0x57058000 - m_curUser));
ws->write(vals, sizeof(vals));
return true; return true;
} }
bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, const SystemChar* apploaderIn) bool buildFromDirectory(const SystemChar* dirIn)
{ {
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0); std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
if (!ws) if (!ws)
return false; return false;
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn); bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn);
if (!result) if (!result)
return false; return false;
return _build([this, apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool SystemString dirStr(dirIn);
/* Check Apploader */
SystemString apploaderIn = dirStr + _S("/DATA/sys/apploader.img");
Sstat apploaderStat;
if (Stat(apploaderIn.c_str(), &apploaderStat))
{ {
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream(); LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn.c_str());
return -1;
}
/* Check Boot */
SystemString bootIn = dirStr + _S("/DATA/sys/boot.bin");
Sstat bootStat;
if (Stat(bootIn.c_str(), &bootStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), bootIn.c_str());
return -1;
}
/* Check BI2 */
SystemString bi2In = dirStr + _S("/DATA/sys/bi2.bin");
Sstat bi2Stat;
if (Stat(bi2In.c_str(), &bi2Stat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), bi2In.c_str());
return -1;
}
return _build(
[this, &bootIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz,
uint32_t userOff, uint32_t userSz) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
if (!rs)
return false;
Header header;
header.read(*rs);
header.m_dolOff = dolOff;
header.m_fstOff = fstOff;
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.m_userPosition = userOff;
header.m_userSz = userSz;
header.write(ws);
return true;
},
[this, &bi2In](IPartWriteStream& ws) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
if (!rs)
return false;
BI2Header bi2;
bi2.read(*rs);
bi2.write(ws);
return true;
},
[this, &apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
if (!rs) if (!rs)
return false; return false;
char buf[8192]; char buf[8192];
SystemString apploaderName(apploaderIn);
while (true) while (true)
{ {
size_t rdSz = rs->read(buf, 8192); size_t rdSz = rs->read(buf, 8192);
@ -278,7 +354,7 @@ public:
"apploader flows into user area (one or the other is too big)"); "apploader flows into user area (one or the other is too big)");
return false; return false;
} }
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
} }
++m_parent.m_progressIdx; ++m_parent.m_progressIdx;
return true; return true;
@ -294,7 +370,26 @@ public:
if (!result) if (!result)
return false; return false;
return _build([this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool return _build(
[this, partIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz,
uint32_t userOff, uint32_t userSz) -> bool
{
Header header = partIn->getHeader();
header.m_dolOff = dolOff;
header.m_fstOff = fstOff;
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.m_userPosition = userOff;
header.m_userSz = userSz;
header.write(ws);
return true;
},
[this, partIn](IPartWriteStream& ws) -> bool
{
partIn->getBI2().write(ws);
return true;
},
[this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool
{ {
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf(); std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize(); size_t apploaderSz = partIn->getApploaderSize();
@ -314,8 +409,7 @@ public:
} }
}; };
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn)
const SystemChar* apploaderIn)
{ {
if (!m_fileIO->beginWriteStream()) if (!m_fileIO->beginWriteStream())
return EBuildResult::Failed; return EBuildResult::Failed;
@ -332,12 +426,12 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const S
ws->write("", 1); ws->write("", 1);
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]); PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
return pb.buildFromDirectory(dirIn, dolIn, apploaderIn) ? EBuildResult::Success : EBuildResult::Failed; return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed;
} }
uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn) uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn)
{ {
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn); uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn);
if (sz == -1) if (sz == -1)
return -1; return -1;
sz += 0x30000; sz += 0x30000;
@ -349,12 +443,10 @@ uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, con
return sz; return sz;
} }
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle, DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB)
uint32_t fstMemoryAddr, FProgress progressCB)
: DiscBuilderBase(outPath, 0x57058000, progressCB) : DiscBuilderBase(outPath, 0x57058000, progressCB)
{ {
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this, PartitionBuilderBase::Kind::Data, PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this);
gameID, gameTitle, fstMemoryAddr);
m_partitions.emplace_back(partBuilder); m_partitions.emplace_back(partBuilder);
} }

View File

@ -5,6 +5,7 @@
#include "nod/DiscWii.hpp" #include "nod/DiscWii.hpp"
#include "nod/aes.hpp" #include "nod/aes.hpp"
#include "nod/sha1.h" #include "nod/sha1.h"
#include "nod/nod.hpp"
namespace nod namespace nod
{ {
@ -66,7 +67,7 @@ class PartitionWii : public DiscBase::IPartition
uint32_t timeLimit; uint32_t timeLimit;
} timeLimits[8]; } timeLimits[8];
void read(IDiscIO::IReadStream& s) void read(IReadStream& s)
{ {
s.read(this, 676); s.read(this, 676);
sigType = SBig(sigType); sigType = SBig(sigType);
@ -79,6 +80,21 @@ class PartitionWii : public DiscBase::IPartition
timeLimits[t].timeLimit = SBig(timeLimits[t].timeLimit); timeLimits[t].timeLimit = SBig(timeLimits[t].timeLimit);
} }
} }
void write(IWriteStream& s) const
{
Ticket tik = *this;
tik.sigType = SBig(tik.sigType);
tik.ticketVersion = SBig(tik.ticketVersion);
tik.permittedTitlesMask = SBig(tik.permittedTitlesMask);
tik.permitMask = SBig(tik.permitMask);
for (size_t t=0 ; t<8 ; ++t)
{
tik.timeLimits[t].enableTimeLimit = SBig(tik.timeLimits[t].enableTimeLimit);
tik.timeLimits[t].timeLimit = SBig(tik.timeLimits[t].timeLimit);
}
s.write(&tik, 676);
}
} m_ticket; } m_ticket;
struct TMD struct TMD
@ -112,7 +128,7 @@ class PartitionWii : public DiscBase::IPartition
uint64_t size; uint64_t size;
char hash[20]; char hash[20];
void read(IDiscIO::IReadStream& s) void read(IReadStream& s)
{ {
s.read(this, 36); s.read(this, 36);
id = SBig(id); id = SBig(id);
@ -120,10 +136,20 @@ class PartitionWii : public DiscBase::IPartition
type = SBig(type); type = SBig(type);
size = SBig(size); size = SBig(size);
} }
void write(IWriteStream& s) const
{
Content c = *this;
c.id = SBig(c.id);
c.index = SBig(c.index);
c.type = SBig(c.type);
c.size = SBig(c.size);
s.write(&c, 36);
}
}; };
std::vector<Content> contents; std::vector<Content> contents;
void read(IDiscIO::IReadStream& s) void read(IReadStream& s)
{ {
s.read(this, 484); s.read(this, 484);
sigType = SigType(SBig(uint32_t(sigType))); sigType = SigType(SBig(uint32_t(sigType)));
@ -144,6 +170,24 @@ class PartitionWii : public DiscBase::IPartition
contents.back().read(s); contents.back().read(s);
} }
} }
void write(IWriteStream& s) const
{
TMD tmd = *this;
tmd.sigType = SigType(SBig(uint32_t(tmd.sigType)));
tmd.iosIdMajor = SBig(tmd.iosIdMajor);
tmd.iosIdMinor = SBig(tmd.iosIdMinor);
tmd.titleIdMajor = SBig(tmd.titleIdMajor);
tmd.titleType = SBig(tmd.titleType);
tmd.groupId = SBig(tmd.groupId);
tmd.accessFlags = SBig(tmd.accessFlags);
tmd.titleVersion = SBig(tmd.titleVersion);
tmd.numContents = SBig(tmd.numContents);
tmd.bootIdx = SBig(tmd.bootIdx);
s.write(&tmd, 484);
for (uint16_t c=0 ; c<numContents ; ++c)
tmd.contents.back().write(s);
}
} m_tmd; } m_tmd;
struct Certificate struct Certificate
@ -157,7 +201,7 @@ class PartitionWii : public DiscBase::IPartition
uint32_t modulus; uint32_t modulus;
uint32_t pubExp; uint32_t pubExp;
void read(IDiscIO::IReadStream& s) void read(IReadStream& s)
{ {
s.read(&sigType, 4); s.read(&sigType, 4);
sigType = SigType(SBig(uint32_t(sigType))); sigType = SigType(SBig(uint32_t(sigType)));
@ -184,11 +228,46 @@ class PartitionWii : public DiscBase::IPartition
s.seek(52, SEEK_CUR); s.seek(52, SEEK_CUR);
} }
void write(IWriteStream& s) const
{
Certificate c = *this;
c.sigType = SigType(SBig(uint32_t(c.sigType)));
s.write(&c.sigType, 4);
if (sigType == SigType::RSA_4096)
s.write(sig, 512);
else if (sigType == SigType::RSA_2048)
s.write(sig, 256);
else if (sigType == SigType::ELIPTICAL_CURVE)
s.write(sig, 64);
uint32_t zero = 0;
for (int i=0 ; i<15 ; ++i)
s.write(&zero, 4);
s.write(issuer, 64);
c.keyType = KeyType(SBig(uint32_t(c.keyType)));
s.write(&c.keyType, 4);
s.write(subject, 64);
if (keyType == KeyType::RSA_4096)
s.write(key, 512);
else if (keyType == KeyType::RSA_2048)
s.write(key, 256);
c.modulus = SBig(c.modulus);
c.pubExp = SBig(c.pubExp);
s.write(&c.modulus, 8);
for (int i=0 ; i<13 ; ++i)
s.write(&zero, 4);
}
}; };
Certificate m_caCert; Certificate m_caCert;
Certificate m_tmdCert; Certificate m_tmdCert;
Certificate m_ticketCert; Certificate m_ticketCert;
std::unique_ptr<uint8_t[]> m_h3Data;
uint64_t m_dataOff; uint64_t m_dataOff;
uint8_t m_decKey[16]; uint8_t m_decKey[16];
@ -196,7 +275,7 @@ public:
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err) PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err)
: IPartition(parent, kind, offset) : IPartition(parent, kind, offset)
{ {
std::unique_ptr<IDiscIO::IReadStream> s = parent.getDiscIO().beginReadStream(offset); std::unique_ptr<IReadStream> s = parent.getDiscIO().beginReadStream(offset);
if (!s) if (!s)
{ {
err = true; err = true;
@ -239,25 +318,29 @@ public:
m_tmdCert.read(*s); m_tmdCert.read(*s);
m_ticketCert.read(*s); m_ticketCert.read(*s);
s->seek(globalHashTableOff);
m_h3Data.reset(new uint8_t[0x18000]);
s->read(m_h3Data.get(), 0x18000);
/* Decrypt title key */ /* Decrypt title key */
std::unique_ptr<IAES> aes = NewAES(); std::unique_ptr<IAES> aes = NewAES();
uint8_t iv[16] = {}; uint8_t iv[16] = {};
memcpy(iv, m_ticket.titleId, 8); memmove(iv, m_ticket.titleId, 8);
aes->setKey(COMMON_KEYS[(int)m_ticket.commonKeyIdx]); aes->setKey(COMMON_KEYS[(int)m_ticket.commonKeyIdx]);
aes->decrypt(iv, m_ticket.encKey, m_decKey, 16); aes->decrypt(iv, m_ticket.encKey, m_decKey, 16);
/* Wii-specific header reads (now using title key to decrypt) */ /* Wii-specific header reads (now using title key to decrypt) */
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420); std::unique_ptr<IPartReadStream> ds = beginReadStream(0x0);
if (!ds) if (!ds)
{ {
err = true; err = true;
return; return;
} }
m_header.read(*ds);
s->read(&m_bi2Header, sizeof(BI2Header)); m_bi2Header.read(*ds);
m_dolOff = SBig(m_bi2Header.dolOff) << 2; m_dolOff = m_header.m_dolOff << 2;
m_fstOff = SBig(m_bi2Header.fstOff) << 2; m_fstOff = m_header.m_fstOff << 2;
m_fstSz = SBig(m_bi2Header.fstSz) << 2; m_fstSz = m_header.m_fstSz << 2;
ds->seek(0x2440 + 0x14); ds->seek(0x2440 + 0x14);
uint32_t vals[2]; uint32_t vals[2];
ds->read(vals, 8); ds->read(vals, 8);
@ -277,7 +360,7 @@ public:
const PartitionWii& m_parent; const PartitionWii& m_parent;
uint64_t m_baseOffset; uint64_t m_baseOffset;
uint64_t m_offset; uint64_t m_offset;
std::unique_ptr<IDiscIO::IReadStream> m_dio; std::unique_ptr<IReadStream> m_dio;
size_t m_curBlock = SIZE_MAX; size_t m_curBlock = SIZE_MAX;
uint8_t m_encBuf[0x8000]; uint8_t m_encBuf[0x8000];
@ -340,7 +423,7 @@ public:
if (cacheSize + cacheOffset > 0x7c00) if (cacheSize + cacheOffset > 0x7c00)
cacheSize = 0x7c00 - cacheOffset; cacheSize = 0x7c00 - cacheOffset;
memcpy(dst, m_decBuf + cacheOffset, cacheSize); memmove(dst, m_decBuf + cacheOffset, cacheSize);
dst += cacheSize; dst += cacheSize;
rem -= cacheSize; rem -= cacheSize;
cacheOffset = 0; cacheOffset = 0;
@ -366,7 +449,7 @@ public:
std::unique_ptr<uint8_t[]> readPartitionHeaderBuf(size_t& szOut) const std::unique_ptr<uint8_t[]> readPartitionHeaderBuf(size_t& szOut) const
{ {
{ {
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4); std::unique_ptr<IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
if (!rs) if (!rs)
return {}; return {};
@ -380,7 +463,7 @@ public:
szOut = uint64_t(h3) << 2; szOut = uint64_t(h3) << 2;
} }
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset); std::unique_ptr<IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
if (!rs) if (!rs)
return {}; return {};
@ -390,39 +473,58 @@ public:
return buf; return buf;
} }
bool writeOutPartitionHeader(const SystemChar* pathOut) const bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const
{ {
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(pathOut)->beginWriteStream(); Sstat theStat;
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; /* Extract Ticket */
if (rs->read(&h3, 4) != 4) SystemString ticketPath = path + _S("/ticket.bin");
{ if (ctx.force || Stat(ticketPath.c_str(), &theStat))
LogModule.report(logvisor::Error, _S("unable to read H3 offset to %s"), pathOut); {
if (ctx.progressCB)
ctx.progressCB("ticket.bin", 0.f);
auto ws = NewFileIO(ticketPath)->beginWriteStream();
if (!ws)
return false; return false;
} m_ticket.write(*ws);
h3 = SBig(h3);
h3Off = uint64_t(h3) << 2;
} }
char buf[8192]; /* Extract TMD */
size_t rem = h3Off; SystemString tmdPath = path + _S("/tmd.bin");
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset); if (ctx.force || Stat(tmdPath.c_str(), &theStat))
if (!rs)
return false;
while (rem)
{ {
size_t rdSz = nod::min(rem, size_t(8192ul)); if (ctx.progressCB)
rs->read(buf, rdSz); ctx.progressCB("tmd.bin", 0.f);
ws->write(buf, rdSz); auto ws = NewFileIO(tmdPath)->beginWriteStream();
rem -= rdSz; if (!ws)
return false;
m_tmd.write(*ws);
}
/* Extract Certs */
SystemString certPath = path + _S("/cert.bin");
if (ctx.force || Stat(certPath.c_str(), &theStat))
{
if (ctx.progressCB)
ctx.progressCB("cert.bin", 0.f);
auto ws = NewFileIO(certPath)->beginWriteStream();
if (!ws)
return false;
m_caCert.write(*ws);
m_tmdCert.write(*ws);
m_ticketCert.write(*ws);
}
/* Extract H3 */
SystemString h3Path = path + _S("/h3.bin");
if (ctx.force || Stat(h3Path.c_str(), &theStat))
{
if (ctx.progressCB)
ctx.progressCB("h3.bin", 0.f);
auto ws = NewFileIO(h3Path)->beginWriteStream();
if (!ws)
return false;
ws->write(m_h3Data.get(), 0x18000);
} }
return true; return true;
@ -447,7 +549,7 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
} parts[4]; } parts[4];
PartInfo(IDiscIO& dio, bool& err) PartInfo(IDiscIO& dio, bool& err)
{ {
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000); std::unique_ptr<IReadStream> s = dio.beginReadStream(0x40000);
if (!s) if (!s)
{ {
err = true; err = true;
@ -496,20 +598,54 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
DiscBuilderWii DiscWii::makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB) DiscBuilderWii DiscWii::makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
{ {
return DiscBuilderWii(outPath, m_header.m_gameID, m_header.m_gameTitle, return DiscBuilderWii(outPath, dualLayer, progressCB);
dualLayer, progressCB);
} }
bool DiscWii::writeOutDataPartitionHeader(const SystemChar* pathOut) const bool DiscWii::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const
{ {
for (const std::unique_ptr<IPartition>& part : m_partitions) if (Mkdir((path + _S("/DATA/disc")).c_str(), 0755) && errno != EEXIST)
{ {
if (part->getKind() == IPartition::Kind::Data) LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/disc'"), path.c_str());
{ return false;
return static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
}
} }
return false;
Sstat theStat;
/* Extract Header */
SystemString headerPath = path + _S("/DATA/disc/header.bin");
if (ctx.force || Stat(headerPath.c_str(), &theStat))
{
if (ctx.progressCB)
ctx.progressCB("header.bin", 0.f);
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x0);
if (!rs)
return false;
Header header;
header.read(*rs);
auto ws = NewFileIO(headerPath)->beginWriteStream();
if (!ws)
return false;
header.write(*ws);
}
/* Extract Region info */
SystemString regionPath = path + _S("/DATA/disc/region.bin");
if (ctx.force || Stat(regionPath.c_str(), &theStat))
{
if (ctx.progressCB)
ctx.progressCB("header.bin", 0.f);
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x4E000);
if (!rs)
return false;
std::unique_ptr<uint8_t[]> buf(new uint8_t[0x20]);
rs->read(buf.get(), 0x20);
auto ws = NewFileIO(regionPath)->beginWriteStream();
if (!ws)
return false;
ws->write(buf.get(), 0x20);
}
return true;
} }
static const uint8_t ZEROIV[16] = {0}; static const uint8_t ZEROIV[16] = {0};
@ -557,32 +693,32 @@ public:
{ {
sha1_init(&sha); sha1_init(&sha);
sha1_write(&sha, ptr0 + (j+1)*0x400, 0x400); sha1_write(&sha, ptr0 + (j+1)*0x400, 0x400);
memcpy(h0[j], sha1_result(&sha), 20); memmove(h0[j], sha1_result(&sha), 20);
} }
sha1_init(&sha); sha1_init(&sha);
sha1_write(&sha, (char*)h0, 0x26C); sha1_write(&sha, (char*)h0, 0x26C);
memcpy(h1[c], sha1_result(&sha), 20); memmove(h1[c], sha1_result(&sha), 20);
memcpy(ptr0, h0, 0x26C); memmove(ptr0, h0, 0x26C);
memset(ptr0+0x26C, 0, 0x014); memset(ptr0+0x26C, 0, 0x014);
} }
sha1_init(&sha); sha1_init(&sha);
sha1_write(&sha, (char*)h1, 0x0A0); sha1_write(&sha, (char*)h1, 0x0A0);
memcpy(h2[s], sha1_result(&sha), 20); memmove(h2[s], sha1_result(&sha), 20);
for (int c=0 ; c<8 ; ++c) for (int c=0 ; c<8 ; ++c)
{ {
char* ptr0 = ptr1 + c*0x8000; char* ptr0 = ptr1 + c*0x8000;
memcpy(ptr0+0x280, h1, 0x0A0); memmove(ptr0+0x280, h1, 0x0A0);
memset(ptr0+0x320, 0, 0x020); memset(ptr0+0x320, 0, 0x020);
} }
} }
sha1_init(&sha); sha1_init(&sha);
sha1_write(&sha, (char*)h2, 0x0A0); sha1_write(&sha, (char*)h2, 0x0A0);
memcpy(h3Out, sha1_result(&sha), 20); memmove(h3Out, sha1_result(&sha), 20);
for (int s=0 ; s<8 ; ++s) for (int s=0 ; s<8 ; ++s)
{ {
@ -590,7 +726,7 @@ public:
for (int c=0 ; c<8 ; ++c) for (int c=0 ; c<8 ; ++c)
{ {
char* ptr0 = ptr1 + c*0x8000; char* ptr0 = ptr1 + c*0x8000;
memcpy(ptr0+0x340, h2, 0x0A0); memmove(ptr0+0x340, h2, 0x0A0);
memset(ptr0+0x3E0, 0, 0x020); memset(ptr0+0x3E0, 0, 0x020);
m_parent.m_aes->encrypt(ZEROIV, (uint8_t*)ptr0, (uint8_t*)ptr0, 0x400); m_parent.m_aes->encrypt(ZEROIV, (uint8_t*)ptr0, (uint8_t*)ptr0, 0x400);
m_parent.m_aes->encrypt((uint8_t*)(ptr0+0x3D0), (uint8_t*)(ptr0+0x400), (uint8_t*)(ptr0+0x400), 0x7c00); m_parent.m_aes->encrypt((uint8_t*)(ptr0+0x3D0), (uint8_t*)(ptr0+0x400), (uint8_t*)(ptr0+0x400), 0x7c00);
@ -659,7 +795,7 @@ public:
if (src) if (src)
{ {
memcpy(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize); memmove(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize);
src += cacheSize; src += cacheSize;
} }
else else
@ -680,9 +816,8 @@ public:
} }
}; };
PartitionBuilderWii(DiscBuilderBase& parent, Kind kind, PartitionBuilderWii(DiscBuilderBase& parent, Kind kind, uint64_t baseOffset)
const char gameID[6], const char* gameTitle, uint64_t baseOffset) : DiscBuilderBase::PartitionBuilderBase(parent, kind),
: DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle),
m_baseOffset(baseOffset), m_aes(NewAES()) {} m_baseOffset(baseOffset), m_aes(NewAES()) {}
uint64_t getCurUserEnd() const {return m_curUser;} uint64_t getCurUserEnd() const {return m_curUser;}
@ -723,101 +858,28 @@ public:
return ret; return ret;
} }
uint64_t _build(const std::function<bool(IPartWriteStream&)>& contentFunc, uint64_t _build(const std::function<bool(IFileIO::IWriteStream&, uint32_t& h3Off, uint32_t& dataOff,
uint8_t& ccIdx, uint8_t tkey[16], uint8_t tkeyiv[16],
std::unique_ptr<uint8_t[]>& tmdData, size_t& tmdSz)>& cryptoFunc,
const std::function<bool(IPartWriteStream&, uint32_t, uint32_t, uint32_t)>& headerFunc,
const std::function<bool(IPartWriteStream&)>& bi2Func,
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc, const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc,
const uint8_t* phBuf, size_t phSz, size_t apploaderSz) const std::function<bool(IPartWriteStream&)>& contentFunc,
size_t apploaderSz)
{ {
/* Read head and validate key members */ /* Write partition head up to H3 table */
uint8_t tkey[16];
{
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 (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 (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::Error, _S("common key index may only be 0 or 1"));
return -1;
}
}
uint32_t tmdSz;
{
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 (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 (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 (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); std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
if (!ws) if (!ws)
return -1; return -1;
size_t copySz = std::min(phSz, size_t(h3Off)); uint32_t h3Off, dataOff;
ws->write(phBuf, copySz); uint8_t tkey[16], tkeyiv[16];
size_t remCopy = (h3Off > phSz) ? (h3Off - copySz) : 0; uint8_t ccIdx;
for (size_t i=0 ; i<remCopy ; ++i) std::unique_ptr<uint8_t[]> tmdData;
ws->write("", 1); size_t tmdSz;
if (!cryptoFunc(*ws, h3Off, dataOff, ccIdx, tkey, tkeyiv, tmdData, tmdSz))
return -1;
m_userOffset = dataOff;
/* Prepare crypto pass */ /* Prepare crypto pass */
m_aes->setKey(COMMON_KEYS[ccIdx]); m_aes->setKey(COMMON_KEYS[ccIdx]);
@ -847,8 +909,6 @@ public:
cws = beginWriteStream(0); cws = beginWriteStream(0);
if (!cws) if (!cws)
return -1; return -1;
Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0);
header.write(*cws);
/* Compute boot table members and write */ /* Compute boot table members and write */
size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz); size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz);
@ -863,13 +923,11 @@ public:
return -1; return -1;
} }
cws->write(nullptr, 0x420 - sizeof(Header)); if (!headerFunc(*cws, m_dolOffset, fstOff, fstSz))
uint32_t vals[4]; return -1;
vals[0] = SBig(uint32_t(m_dolOffset >> uint64_t(2)));
vals[1] = SBig(uint32_t(fstOff >> uint64_t(2))); if (!bi2Func(*cws))
vals[2] = SBig(uint32_t(fstSz)); return -1;
vals[3] = SBig(uint32_t(fstSz));
cws->write(vals, 16);
size_t xferSz = 0; size_t xferSz = 0;
if (!apploaderFunc(*cws, xferSz)) if (!apploaderFunc(*cws, xferSz))
@ -958,42 +1016,151 @@ public:
return m_baseOffset + dataOff + groupCount * 0x200000; return m_baseOffset + dataOff + groupCount * 0x200000;
} }
uint64_t buildFromDirectory(const SystemChar* dirIn, uint64_t buildFromDirectory(const SystemChar* dirIn)
const SystemChar* dolIn,
const SystemChar* apploaderIn,
const SystemChar* partHeadIn)
{ {
std::unique_ptr<IFileIO> ph = NewFileIO(partHeadIn); SystemString dirStr(dirIn);
size_t phSz = ph->size();
std::unique_ptr<uint8_t[]> phBuf(new uint8_t[phSz]); /* Check Ticket */
SystemString ticketIn = dirStr + _S("/ticket.bin");
Sstat theStat;
if (Stat(ticketIn.c_str(), &theStat))
{ {
auto rs = ph->beginReadStream(); LogModule.report(logvisor::Error, _S("unable to stat %s"), ticketIn.c_str());
if (!rs) return -1;
return -1;
rs->read(phBuf.get(), phSz);
} }
/* Get Apploader Size */ /* Check TMD */
Sstat theStat; SystemString tmdIn = dirStr + _S("/tmd.bin");
if (Stat(apploaderIn, &theStat)) Sstat tmdStat;
if (Stat(tmdIn.c_str(), &tmdStat))
{ {
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn); LogModule.report(logvisor::Error, _S("unable to stat %s"), tmdIn.c_str());
return -1;
}
/* Check Cert */
SystemString certIn = dirStr + _S("/cert.bin");
Sstat certStat;
if (Stat(certIn.c_str(), &certStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), certIn.c_str());
return -1;
}
/* Check Apploader */
SystemString apploaderIn = dirStr + _S("/DATA/sys/apploader.img");
Sstat apploaderStat;
if (Stat(apploaderIn.c_str(), &apploaderStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn.c_str());
return -1;
}
/* Check Boot */
SystemString bootIn = dirStr + _S("/DATA/sys/boot.bin");
Sstat bootStat;
if (Stat(bootIn.c_str(), &bootStat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), bootIn.c_str());
return -1;
}
/* Check BI2 */
SystemString bi2In = dirStr + _S("/DATA/sys/bi2.bin");
Sstat bi2Stat;
if (Stat(bi2In.c_str(), &bi2Stat))
{
LogModule.report(logvisor::Error, _S("unable to stat %s"), bi2In.c_str());
return -1; return -1;
} }
return _build( return _build(
[this, dirIn, dolIn, apploaderIn](IPartWriteStream& cws) -> bool [&](IFileIO::IWriteStream& ws, uint32_t& h3OffOut, uint32_t& dataOffOut,
uint8_t& ccIdx, uint8_t tkey[16], uint8_t tkeyiv[16],
std::unique_ptr<uint8_t[]>& tmdData, size_t& tmdSzOut) -> bool
{ {
return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn, dolIn, apploaderIn); h3OffOut = 0x8000;
dataOffOut = 0x20000;
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(ticketIn.c_str())->beginReadStream();
if (!rs)
return false;
uint8_t buf[0x2A4];
memset(buf, 0, 0x2A4);
rs->read(buf, 0x2A4);
ws.write(buf, 0x2A4);
ccIdx = buf[0x1F1];
memmove(tkey, buf + 0x1BF, 16);
memmove(tkeyiv, buf + 0x1DC, 8);
memset(tkeyiv + 8, 0, 8);
uint32_t curOff = 0x2C0;
uint32_t tmdSz = SBig(uint32_t(tmdStat.st_size));
ws.write(&tmdSz, 4);
uint32_t tmdOff = SBig(curOff >> 2);
ws.write(&tmdOff, 4);
curOff += ROUND_UP_32(tmdStat.st_size);
uint32_t certSz = SBig(uint32_t(certStat.st_size));
ws.write(&certSz, 4);
uint32_t certOff = SBig(curOff >> 2);
ws.write(&certOff, 4);
curOff += ROUND_UP_32(certStat.st_size);
uint32_t h3Off = SBig(0x8000 >> 2);
ws.write(&h3Off, 4);
uint32_t dataOff = SBig(0x20000 >> 2);
ws.write(&dataOff, 4);
uint32_t dataSz = 0;
ws.write(&dataSz, 4);
rs = NewFileIO(tmdIn.c_str())->beginReadStream();
tmdData.reset(new uint8_t[tmdStat.st_size]);
tmdSzOut = tmdStat.st_size;
rs->read(tmdData.get(), tmdStat.st_size);
ws.write(tmdData.get(), tmdStat.st_size);
uint32_t tmdPadding = ROUND_UP_32(tmdStat.st_size) - tmdStat.st_size;
for (int i=0 ; i<tmdPadding ; ++i)
ws.write("", 1);
rs = NewFileIO(certIn.c_str())->beginReadStream();
std::unique_ptr<uint8_t[]> certBuf(new uint8_t[certStat.st_size]);
rs->read(certBuf.get(), certStat.st_size);
ws.write(certBuf.get(), certStat.st_size);
return true;
}, },
[this, apploaderIn](IPartWriteStream& cws, size_t& xferSz) -> bool [this, &bootIn](IPartWriteStream& cws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz) -> bool
{ {
cws.write(nullptr, 0x2440 - 0x430); std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream(); if (!rs)
return false;
Header header;
header.read(*rs);
header.m_dolOff = uint32_t(dolOff >> 2);
header.m_fstOff = uint32_t(fstOff >> 2);
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.write(cws);
return true;
},
[this, &bi2In](IPartWriteStream& cws) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
if (!rs)
return false;
BI2Header bi2;
bi2.read(*rs);
bi2.write(cws);
return true;
},
[this, &apploaderIn](IPartWriteStream& cws, size_t& xferSz) -> bool
{
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
if (!rs) if (!rs)
return false; return false;
char buf[8192]; char buf[8192];
SystemString apploaderName(apploaderIn);
while (true) while (true)
{ {
size_t rdSz = rs->read(buf, 8192); size_t rdSz = rs->read(buf, 8192);
@ -1007,11 +1174,15 @@ public:
"apploader flows into user area (one or the other is too big)"); "apploader flows into user area (one or the other is too big)");
return false; return false;
} }
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
} }
++m_parent.m_progressIdx; ++m_parent.m_progressIdx;
return true; return true;
}, phBuf.get(), phSz, theStat.st_size); },
[this, dirIn](IPartWriteStream& cws) -> bool
{
return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn);
}, apploaderStat.st_size);
} }
bool mergeFromDirectory(const PartitionWii* partIn, const SystemChar* dirIn) bool mergeFromDirectory(const PartitionWii* partIn, const SystemChar* dirIn)
@ -1020,13 +1191,44 @@ public:
std::unique_ptr<uint8_t[]> phBuf = partIn->readPartitionHeaderBuf(phSz); std::unique_ptr<uint8_t[]> phBuf = partIn->readPartitionHeaderBuf(phSz);
return _build( return _build(
[this, partIn, dirIn](IPartWriteStream& cws) -> bool [&](IFileIO::IWriteStream& ws, uint32_t& h3OffOut, uint32_t& dataOffOut,
uint8_t& ccIdx, uint8_t tkey[16], uint8_t tkeyiv[16],
std::unique_ptr<uint8_t[]>& tmdData, size_t& tmdSz) -> bool
{ {
return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn); h3OffOut = SBig(*reinterpret_cast<uint32_t*>(&phBuf[0x2B4])) << 2;
dataOffOut = SBig(*reinterpret_cast<uint32_t*>(&phBuf[0x2B8])) << 2;
ccIdx = phBuf[0x1F1];
memmove(tkey, phBuf.get() + 0x1BF, 16);
memmove(tkeyiv, phBuf.get() + 0x1DC, 8);
memset(tkeyiv + 8, 0, 8);
tmdSz = SBig(*reinterpret_cast<uint32_t*>(&phBuf[0x2A4]));
tmdData.reset(new uint8_t[tmdSz]);
memmove(tmdData.get(), phBuf.get() + 0x2C0, tmdSz);
size_t copySz = std::min(phSz, size_t(h3OffOut));
ws.write(phBuf.get(), copySz);
return true;
},
[this, partIn](IPartWriteStream& cws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz) -> bool
{
Header header = partIn->getHeader();
header.m_dolOff = uint32_t(dolOff >> uint64_t(2));
header.m_fstOff = uint32_t(fstOff >> uint64_t(2));
header.m_fstSz = fstSz;
header.m_fstMaxSz = fstSz;
header.write(cws);
return true;
},
[this, partIn](IPartWriteStream& cws) -> bool
{
partIn->getBI2().write(cws);
return true;
}, },
[this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool [this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool
{ {
cws.write(nullptr, 0x2440 - 0x430);
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf(); std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
size_t apploaderSz = partIn->getApploaderSize(); size_t apploaderSz = partIn->getApploaderSize();
SystemString apploaderName(_S("<apploader>")); SystemString apploaderName(_S("<apploader>"));
@ -1041,13 +1243,18 @@ public:
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
++m_parent.m_progressIdx; ++m_parent.m_progressIdx;
return true; return true;
}, phBuf.get(), phSz, partIn->getApploaderSize()); },
[this, partIn, dirIn](IPartWriteStream& cws) -> bool
{
return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn);
}, partIn->getApploaderSize());
} }
}; };
EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn)
const SystemChar* apploaderIn, const SystemChar* partHeadIn)
{ {
SystemString dirStr(dirIn);
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]); PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]);
uint64_t filledSz = pb.m_baseOffset; uint64_t filledSz = pb.m_baseOffset;
if (!m_fileIO->beginWriteStream()) if (!m_fileIO->beginWriteStream())
@ -1066,7 +1273,7 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
ws->write("", 1); ws->write("", 1);
/* Assemble image */ /* Assemble image */
filledSz = pb.buildFromDirectory(dirIn, dolIn, apploaderIn, partHeadIn); filledSz = pb.buildFromDirectory(dirIn);
if (filledSz == -1) if (filledSz == -1)
return EBuildResult::Failed; return EBuildResult::Failed;
else if (filledSz >= uint64_t(m_discCapacity)) else if (filledSz >= uint64_t(m_discCapacity))
@ -1082,7 +1289,12 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
ws = m_fileIO->beginWriteStream(0); ws = m_fileIO->beginWriteStream(0);
if (!ws) if (!ws)
return EBuildResult::Failed; return EBuildResult::Failed;
Header header(pb.getGameID(), pb.getGameTitle().c_str(), true, 0, 0, 0); SystemString headerPath = dirStr + _S("/DATA/disc/header.bin");
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(headerPath.c_str())->beginReadStream();
if (!rs)
return EBuildResult::Failed;
Header header;
header.read(*rs);
header.write(*ws); header.write(*ws);
/* Populate partition info */ /* Populate partition info */
@ -1099,24 +1311,16 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
ws->write(vals, 4); ws->write(vals, 4);
/* Populate region info */ /* Populate region info */
SystemString regionPath = dirStr + _S("/DATA/disc/region.bin");
rs = NewFileIO(regionPath.c_str())->beginReadStream();
if (!rs)
return EBuildResult::Failed;
uint8_t regionBuf[0x20];
rs->read(regionBuf, 0x20);
ws = m_fileIO->beginWriteStream(0x4E000); ws = m_fileIO->beginWriteStream(0x4E000);
if (!ws) if (!ws)
return EBuildResult::Failed; return EBuildResult::Failed;
const char* gameID = pb.getGameID(); ws->write(regionBuf, 0x20);
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_fileIO->beginWriteStream(0x4E010);
if (!ws)
return EBuildResult::Failed;
for (int i=0 ; i<16 ; ++i)
ws->write("\x80", 1);
/* Fill image to end */ /* Fill image to end */
ws = m_fileIO->beginWriteStream(filledSz); ws = m_fileIO->beginWriteStream(filledSz);
@ -1139,10 +1343,9 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
return EBuildResult::Success; return EBuildResult::Success;
} }
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn, uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer)
bool& dualLayer)
{ {
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn); uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn);
if (sz == -1) if (sz == -1)
return -1; return -1;
auto szDiv = std::lldiv(sz, 0x1F0000); auto szDiv = std::lldiv(sz, 0x1F0000);
@ -1158,12 +1361,10 @@ uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, con
return sz; return sz;
} }
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
bool dualLayer, FProgress progressCB)
: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer) : DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer)
{ {
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data, PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data, 0x200000);
gameID, gameTitle, 0x200000);
m_partitions.emplace_back(partBuilder); m_partitions.emplace_back(partBuilder);
} }
@ -1224,24 +1425,15 @@ EBuildResult DiscMergerWii::mergeFromDirectory(const SystemChar* dirIn)
ws->write(vals, 4); ws->write(vals, 4);
/* Populate region info */ /* Populate region info */
std::unique_ptr<IReadStream> rs = m_sourceDisc.getDiscIO().beginReadStream(0x4E000);
if (!rs)
return EBuildResult::Failed;
uint8_t regionBuf[0x20];
rs->read(regionBuf, 0x20);
ws = m_builder.m_fileIO->beginWriteStream(0x4E000); ws = m_builder.m_fileIO->beginWriteStream(0x4E000);
if (!ws) if (!ws)
return EBuildResult::Failed; return EBuildResult::Failed;
const char* gameID = pb.getGameID(); ws->write(regionBuf, 0x20);
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 EBuildResult::Failed;
for (int i=0 ; i<16 ; ++i)
ws->write("\x80", 1);
/* Fill image to end */ /* Fill image to end */
ws = m_builder.m_fileIO->beginWriteStream(filledSz); ws = m_builder.m_fileIO->beginWriteStream(filledSz);

View File

@ -128,7 +128,7 @@ public:
{ {
FSeek(fp, offset, whence); FSeek(fp, offset, whence);
} }
int64_t position() uint64_t position() const
{ {
return FTell(fp); return FTell(fp);
} }

View File

@ -145,7 +145,7 @@ public:
li.QuadPart = offset; li.QuadPart = offset;
SetFilePointerEx(fp, li, nullptr, whence); SetFilePointerEx(fp, li, nullptr, whence);
} }
int64_t position() uint64_t position() const
{ {
LARGE_INTEGER li = {}; LARGE_INTEGER li = {};
LARGE_INTEGER res; LARGE_INTEGER res;