From 41148a1368f0294addfb5abc5fccdbdfe0c3569b Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 1 Jul 2017 13:36:16 -1000 Subject: [PATCH] Refactor of extracted directory structure and API simplification --- README.md | 13 +- driver/main.cpp | 124 ++------ include/nod/DiscBase.hpp | 184 ++++++----- include/nod/DiscGCN.hpp | 9 +- include/nod/DiscWii.hpp | 13 +- include/nod/IDiscIO.hpp | 42 ++- include/nod/IFileIO.hpp | 13 +- include/nod/aes.hpp | 2 +- include/nod/nod.hpp | 1 - lib/DiscBase.cpp | 142 ++++----- lib/DiscGCN.cpp | 190 +++++++++--- lib/DiscWii.cpp | 654 +++++++++++++++++++++++++-------------- lib/FileIOFILE.cpp | 2 +- lib/FileIOWin32.cpp | 2 +- 14 files changed, 814 insertions(+), 577 deletions(-) diff --git a/README.md b/README.md index d86c49b..368d363 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,12 @@ auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes) }; /* Making a GCN image */ -nod::DiscBuilderGCN b(isoOutPath, gameID, gameTitle, dolLoadAddress, progFunc); -ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath); +nod::DiscBuilderGCN b(isoOutPath, progFunc); +ret = b.buildFromDirectory(fsRootDirPath); /* Making a Wii image */ -nod::DiscBuilderWii b(isoOutPath, gameID, gameTitle, dualLayer, progFunc); -ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath, partitionHeadPath); +nod::DiscBuilderWii b(isoOutPath, dualLayer, progFunc); +ret = b.buildFromDirectory(fsRootDirPath); ``` 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 # Then one of: ->$ nodtool makegcn fsroot boot.dol apploader.bin [] ->$ nodtool makewiisl fsroot boot.dol apploader.bin partition_head.bin [] ->$ nodtool makewiidl fsroot boot.dol apploader.bin partition_head.bin [] +>$ nodtool makegcn fsroot [] +>$ nodtool makewii fsroot [] ``` diff --git a/driver/main.cpp b/driver/main.cpp index c9b1e11..39c8ead 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -7,8 +7,8 @@ static void printHelp() { fprintf(stderr, "Usage:\n" " nodtool extract [-f] []\n" - " nodtool makegcn []\n" - " nodtool makewii []\n" + " nodtool makegcn []\n" + " nodtool makewii []\n" " nodtool mergegcn []\n" " nodtool mergewii []\n"); } @@ -26,8 +26,8 @@ int main(int argc, char* argv[]) #endif { if (argc < 3 || - (!strcasecmp(argv[1], _S("makegcn")) && argc < 7) || - (!strcasecmp(argv[1], _S("makewii")) && argc < 8) || + (!strcasecmp(argv[1], _S("makegcn")) && argc < 3) || + (!strcasecmp(argv[1], _S("makewii")) && argc < 3) || (!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) || (!strcasecmp(argv[1], _S("mergewii")) && argc < 4)) { @@ -39,9 +39,12 @@ int main(int argc, char* argv[]) logvisor::RegisterStandardExceptions(); logvisor::RegisterConsoleLogger(); - nod::ExtractionContext ctx = { true, true, [&](const std::string& str, float c){ - fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f); - }}; + bool verbose = false; + 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* outDir = _S("."); @@ -50,7 +53,7 @@ int main(int argc, char* argv[]) if (argv[a][0] == '-' && argv[a][1] == 'f') ctx.force = true; else if (argv[a][0] == '-' && argv[a][1] == 'v') - ctx.verbose = true; + verbose = true; else if (!inDir) inDir = argv[a]; @@ -77,10 +80,6 @@ int main(int argc, char* argv[]) nod::Mkdir(outDir, 0755); - if (isWii) - static_cast(*disc).writeOutDataPartitionHeader( - (nod::SystemString(outDir) + _S("/partition_head.bin")).c_str()); - nod::Partition* dataPart = disc->getDataPartition(); if (!dataPart) return 1; @@ -90,59 +89,30 @@ int main(int argc, char* argv[]) } else if (!strcasecmp(argv[1], _S("makegcn"))) { -#if NOD_UCS2 - 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 */ + /* Pre-validate path */ 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]); - 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]); + nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]); return 1; } - nod::SystemString gameIdSys(argv[2]); - nod::SystemUTF8View gameId(gameIdSys); - nod::SystemString gameTitleSys(argv[3]); - nod::SystemUTF8View gameTitle(gameTitleSys); - - if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[4], argv[5]) == -1) + if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1) return 1; nod::EBuildResult ret; - if (argc < 8) + if (argc < 4) { - nod::SystemString outPath(argv[4]); + nod::SystemString outPath(argv[2]); outPath.append(_S(".iso")); - nod::DiscBuilderGCN b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc); - ret = b.buildFromDirectory(argv[4], argv[5], argv[6]); + nod::DiscBuilderGCN b(outPath.c_str(), progFunc); + ret = b.buildFromDirectory(argv[2]); } else { - nod::DiscBuilderGCN b(argv[7], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc); - ret = b.buildFromDirectory(argv[4], argv[5], argv[6]); + nod::DiscBuilderGCN b(argv[3], progFunc); + ret = b.buildFromDirectory(argv[2]); } printf("\n"); @@ -151,65 +121,31 @@ int main(int argc, char* argv[]) } else if (!strcasecmp(argv[1], _S("makewii"))) { -#if NOD_UCS2 - 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 */ + /* Pre-validate path */ 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]); 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; - if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[4], argv[5], dual) == -1) + if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1) return 1; nod::EBuildResult ret; - if (argc < 9) + if (argc < 4) { - nod::SystemString outPath(argv[4]); + nod::SystemString outPath(argv[2]); outPath.append(_S(".iso")); - nod::DiscBuilderWii b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc); - ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]); + nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc); + ret = b.buildFromDirectory(argv[2]); } else { - nod::DiscBuilderWii b(argv[8], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc); - ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]); + nod::DiscBuilderWii b(argv[3], dual, progFunc); + ret = b.buildFromDirectory(argv[2]); } printf("\n"); diff --git a/include/nod/DiscBase.hpp b/include/nod/DiscBase.hpp index 106e515..c245787 100644 --- a/include/nod/DiscBase.hpp +++ b/include/nod/DiscBase.hpp @@ -67,60 +67,126 @@ struct Header uint32_t m_debugMonOff; uint32_t m_debugLoadAddr; 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) { - std::unique_ptr s = dio.beginReadStream(0); - if (!s) + auto rs = dio.beginReadStream(); + if (!rs) { err = true; 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_gcnMagic = SBig(m_gcnMagic); m_debugMonOff = SBig(m_debugMonOff); 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, - 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 - void write(WriteStream& ws) const + void write(IWriteStream& ws) const { Header hs(*this); hs.m_wiiMagic = SBig(hs.m_wiiMagic); hs.m_gcnMagic = SBig(hs.m_gcnMagic); hs.m_debugMonOff = SBig(hs.m_debugMonOff); 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)); } }; +/* 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; class DiscBase { public: - virtual ~DiscBase() {} + virtual ~DiscBase() = default; class IPartition { public: - virtual ~IPartition() {} + virtual ~IPartition() = default; enum class Kind : uint32_t { Data, @@ -140,32 +206,6 @@ public: 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 { public: @@ -259,11 +299,11 @@ public: bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const; }; protected: + Header m_header; BI2Header m_bi2Header; uint64_t m_dolOff; uint64_t m_fstOff; uint64_t m_fstSz; - uint64_t m_fstMemoryAddr; uint64_t m_apploaderSz; std::vector m_nodes; void parseFST(IPartReadStream& s); @@ -325,8 +365,6 @@ public: return buf; } - inline uint64_t getFSTMemoryAddr() const {return m_fstMemoryAddr;} - inline uint64_t getApploaderSize() const {return m_apploaderSz;} inline std::unique_ptr getApploaderBuf() const { @@ -336,8 +374,9 @@ public: } inline size_t getNodeCount() const { return m_nodes.size(); } - inline const Header& getHeader() const { return m_parent.getHeader(); } - inline const uint8_t* getBI2Buf() const { return reinterpret_cast(&m_bi2Header); } + inline const Header& getHeader() const { return m_header; } + inline const BI2Header& getBI2() const { return m_bi2Header; } + virtual bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { return true; } }; protected: @@ -364,6 +403,7 @@ public: return part.get(); return nullptr; } + inline IPartition* getUpdatePartition() { for (const std::unique_ptr& part : m_partitions) @@ -371,12 +411,14 @@ public: return part.get(); return nullptr; } + inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx) { for (std::unique_ptr& part : m_partitions) part->extractToDirectory(path, ctx); } + virtual bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const=0; }; class DiscBuilderBase @@ -386,7 +428,7 @@ public: class PartitionBuilderBase { public: - virtual ~PartitionBuilderBase() {} + virtual ~PartitionBuilderBase() = default; enum class Kind : uint32_t { Data, @@ -401,10 +443,10 @@ public: virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0; virtual uint32_t packOffset(uint64_t offset) const=0; - void recursiveBuildNodesPre(const SystemChar* dirIn, uint64_t dolInode); - bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode); + void recursiveBuildNodesPre(const SystemChar* dirIn); + bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn); - bool recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode, + bool recursiveBuildFST(const SystemChar* dirIn, std::function incParents); void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn); @@ -428,32 +470,21 @@ public: DiscBuilderBase& m_parent; Kind m_kind; - - char m_gameID[6]; - std::string m_gameTitle; uint64_t m_dolOffset = 0; uint64_t m_dolSize = 0; public: - PartitionBuilderBase(DiscBuilderBase& parent, Kind kind, - const char gameID[6], const char* gameTitle) - : m_parent(parent), m_kind(kind), m_gameTitle(gameTitle) - { - memcpy(m_gameID, gameID, 6); - } + PartitionBuilderBase(DiscBuilderBase& parent, Kind kind) + : m_parent(parent), m_kind(kind) + {} virtual std::unique_ptr beginWriteStream(uint64_t offset)=0; bool buildFromDirectory(IPartWriteStream& ws, - const SystemChar* dirIn, const SystemChar* dolIn, - const SystemChar* apploaderIn); - static uint64_t CalculateTotalSizeBuild(const SystemChar* dolIn, - const SystemChar* dirIn); + const SystemChar* dirIn); + static uint64_t CalculateTotalSizeBuild(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: SystemString m_outPath; @@ -480,13 +511,14 @@ public: } 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_discCapacity(discCapacity), m_progressCB(progressCB) {} DiscBuilderBase(DiscBuilderBase&&) = default; DiscBuilderBase& operator=(DiscBuilderBase&&) = default; - IFileIO& getFileIO() {return *m_fileIO;} + IFileIO& getFileIO() { return *m_fileIO; } }; using Partition = DiscBase::IPartition; diff --git a/include/nod/DiscGCN.hpp b/include/nod/DiscGCN.hpp index 6582437..8d0eadb 100644 --- a/include/nod/DiscGCN.hpp +++ b/include/nod/DiscGCN.hpp @@ -13,17 +13,16 @@ class DiscGCN : public DiscBase DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB); public: DiscGCN(std::unique_ptr&& dio, bool& err); + bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const; }; class DiscBuilderGCN : public DiscBuilderBase { friend class DiscMergerGCN; public: - DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle, - uint32_t fstMemoryAddr, FProgress progressCB); - EBuildResult buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, - const SystemChar* apploaderIn); - static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn); + DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB); + EBuildResult buildFromDirectory(const SystemChar* dirIn); + static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn); }; class DiscMergerGCN diff --git a/include/nod/DiscWii.hpp b/include/nod/DiscWii.hpp index a6a0547..126b68c 100644 --- a/include/nod/DiscWii.hpp +++ b/include/nod/DiscWii.hpp @@ -12,21 +12,16 @@ class DiscWii : public DiscBase public: DiscWii(std::unique_ptr&& dio, bool& err); 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 { bool m_dualLayer; public: - DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, - bool dualLayer, FProgress progressCB); - EBuildResult 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); + DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB); + EBuildResult buildFromDirectory(const SystemChar* dirIn); + static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer); }; class DiscMergerWii diff --git a/include/nod/IDiscIO.hpp b/include/nod/IDiscIO.hpp index 7858a6c..e051186 100644 --- a/include/nod/IDiscIO.hpp +++ b/include/nod/IDiscIO.hpp @@ -14,42 +14,38 @@ 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 { public: - virtual ~IDiscIO() {} - - 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 ~IDiscIO() = default; virtual std::unique_ptr 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 beginWriteStream(uint64_t offset=0) const=0; }; -struct IPartReadStream +struct IPartReadStream : IReadStream { - virtual ~IPartReadStream() {} - 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; + virtual ~IPartReadStream() = default; }; -struct IPartWriteStream +struct IPartWriteStream : IWriteStream { - virtual ~IPartWriteStream() {} + virtual ~IPartWriteStream() = default; virtual void close()=0; virtual uint64_t position() const=0; - virtual uint64_t write(const void* buf, uint64_t length)=0; }; #if NOD_ATHENA diff --git a/include/nod/IFileIO.hpp b/include/nod/IFileIO.hpp index b5edf00..fab1065 100644 --- a/include/nod/IFileIO.hpp +++ b/include/nod/IFileIO.hpp @@ -13,15 +13,12 @@ namespace nod class IFileIO { public: - virtual ~IFileIO() {} + virtual ~IFileIO() = default; virtual bool exists()=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 read = 0; @@ -74,12 +71,8 @@ public: virtual std::unique_ptr beginWriteStream() const=0; virtual std::unique_ptr 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 std::unique_ptr beginReadStream() const=0; diff --git a/include/nod/aes.hpp b/include/nod/aes.hpp index 72af741..e0ad86b 100644 --- a/include/nod/aes.hpp +++ b/include/nod/aes.hpp @@ -11,7 +11,7 @@ namespace nod class IAES { 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 decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; virtual void setKey(const uint8_t* key)=0; diff --git a/include/nod/nod.hpp b/include/nod/nod.hpp index ad94c60..43bd42c 100644 --- a/include/nod/nod.hpp +++ b/include/nod/nod.hpp @@ -13,7 +13,6 @@ class DiscBase; struct ExtractionContext final { - bool verbose : 1; bool force : 1; std::function progressCB; }; diff --git a/lib/DiscBase.cpp b/lib/DiscBase.cpp index f0631af..9bb8c9a 100644 --- a/lib/DiscBase.cpp +++ b/lib/DiscBase.cpp @@ -98,7 +98,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath if (m_kind == Kind::Directory) { ++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())); 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) { Sstat theStat; - if (ctx.verbose && ctx.progressCB) + if (ctx.progressCB) ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); 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, [&](float prog) { - if (ctx.verbose && ctx.progressCB) + if (ctx.progressCB) 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; } - 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; } + 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 */ - 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.verbose && ctx.progressCB) + if (ctx.progressCB) ctx.progressCB("apploader.bin", 0.f); std::unique_ptr buf = getApploaderBuf(); auto ws = NewFileIO(apploaderPath)->beginWriteStream(); @@ -164,10 +178,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path, } /* 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.verbose && ctx.progressCB) + if (ctx.progressCB) ctx.progressCB("main.dol", 0.f); std::unique_ptr buf = getDOLBuf(); auto ws = NewFileIO(dolPath)->beginWriteStream(); @@ -177,32 +191,32 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path, } /* 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.verbose && ctx.progressCB) + if (ctx.progressCB) ctx.progressCB("boot.bin", 0.f); auto ws = NewFileIO(bootPath)->beginWriteStream(); if (!ws) return false; - getHeader().write(*ws.get()); + m_header.write(*ws.get()); } /* 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.verbose && ctx.progressCB) + if (ctx.progressCB) ctx.progressCB("bi2.bin", 0.f); - const uint8_t* buf = getBI2Buf(); auto ws = NewFileIO(bi2Path)->beginWriteStream(); if (!ws) return false; - ws->write(buf, sizeof(BI2Header)); + m_bi2Header.write(*ws); } + /* Extract Filesystem */ - SystemString fsPath = path + _S("/files"); + SystemString fsPath = path + _S("/DATA/files"); if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST) { 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); } -void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* dirIn, - uint64_t dolInode) +void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* filesIn) { - DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); for (const DirectoryEnumerator::Entry& e : dEnum) { if (e.m_isDir) - { - recursiveBuildNodesPre(e.m_path.c_str(), dolInode); - } + recursiveBuildNodesPre(e.m_path.c_str()); else - { - if (dolInode == GetInode(e.m_path.c_str())) - continue; - ++m_parent.m_progressTotal; - } } } bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws, bool system, - const SystemChar* dirIn, - uint64_t dolInode) + const SystemChar* filesIn) { - DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); for (const DirectoryEnumerator::Entry& e : dEnum) { 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; } else @@ -339,9 +344,6 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream if (system ^ isSys) continue; - if (dolInode == GetInode(e.m_path.c_str())) - continue; - size_t fileSz = ROUND_UP_32(e.m_fileSz); uint64_t fileOff = userAllocate(fileSz, ws); if (fileOff == -1) @@ -380,10 +382,10 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream return true; } -bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode, +bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* filesIn, std::function 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) { if (e.m_isDir) @@ -392,19 +394,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1); addBuildName(e.m_name); 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; } 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 fileOffSz = m_fileOffsetsSizes.at(e.m_path); m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second); addBuildName(e.m_name); @@ -796,20 +790,21 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize( } bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws, - const SystemChar* dirIn, - const SystemChar* dolIn, - const SystemChar* apploaderIn) + const SystemChar* dirIn) { - if (!dirIn || !dolIn || !apploaderIn) + if (!dirIn) { LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()")); 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 */ - uint64_t dolInode = GetInode(dolIn); m_parent.m_progressTotal += 2; /* Prep and DOL */ - recursiveBuildNodesPre(dirIn, dolInode); + recursiveBuildNodesPre(filesIn.c_str()); /* Clear file */ 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) */ { 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; } size_t fileSz = ROUND_UP_32(dolStat.st_size); @@ -833,12 +828,12 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& return false; m_dolOffset = fileOff; m_dolSize = fileSz; - std::unique_ptr rs = NewFileIO(dolIn)->beginReadStream(); + std::unique_ptr rs = NewFileIO(dolIn.c_str())->beginReadStream(); if (!rs) return false; bool 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); ++m_parent.m_progressIdx; for (size_t i=0 ; igetFSTRoot(), dirIn); + recursiveMergeNodesPre(&partIn->getFSTRoot(), filesIn.c_str()); /* Clear file */ 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 */ SystemString keyPath; - if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), dirIn, keyPath)) + if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), filesIn.c_str(), keyPath)) return false; - if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), dirIn, keyPath)) + if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath)) 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 true; @@ -928,8 +929,11 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn, const SystemChar* dirIn) { + SystemString dirStr(dirIn); + SystemString filesIn = dirStr + _S("/DATA/files"); + 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 totalSz; } diff --git a/lib/DiscGCN.cpp b/lib/DiscGCN.cpp index 1fd27bb..7778a55 100644 --- a/lib/DiscGCN.cpp +++ b/lib/DiscGCN.cpp @@ -1,4 +1,5 @@ #include "nod/DiscGCN.hpp" +#include "nod/nod.hpp" #include #define BUFFER_SZ 0x8000 @@ -12,18 +13,17 @@ public: : IPartition(parent, kind, offset) { /* GCN-specific header reads */ - std::unique_ptr s = beginReadStream(0x420); + std::unique_ptr s = beginReadStream(0x0); if (!s) { err = true; return; } - - s->read(&m_bi2Header, sizeof(BI2Header)); - m_dolOff = SBig(m_bi2Header.dolOff); - m_fstOff = SBig(m_bi2Header.fstOff); - m_fstSz = SBig(m_bi2Header.fstSz); - m_fstMemoryAddr = SBig(m_bi2Header.fstMemoryAddress); + m_header.read(*s); + m_bi2Header.read(*s); + m_dolOff = m_header.m_dolOff; + m_fstOff = m_header.m_fstOff; + m_fstSz = m_header.m_fstSz; uint32_t vals[2]; s->seek(0x2440 + 0x14); s->read(vals, 8); @@ -40,7 +40,7 @@ public: class PartReadStream : public IPartReadStream { const PartitionGCN& m_parent; - std::unique_ptr m_dio; + std::unique_ptr m_dio; uint64_t m_offset; size_t m_curBlock = SIZE_MAX; @@ -97,7 +97,7 @@ public: if (cacheSize + cacheOffset > BUFFER_SZ) cacheSize = BUFFER_SZ - cacheOffset; - memcpy(dst, m_buf + cacheOffset, cacheSize); + memmove(dst, m_buf + cacheOffset, cacheSize); dst += cacheSize; rem -= cacheSize; cacheOffset = 0; @@ -131,15 +131,42 @@ DiscGCN::DiscGCN(std::unique_ptr&& dio, bool& 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); + return DiscBuilderGCN(outPath, 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 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 { uint64_t m_curUser = 0x57058000; - uint32_t m_fstMemoryAddr; public: class PartWriteStream : public IPartWriteStream @@ -171,9 +198,8 @@ public: } }; - PartitionBuilderGCN(DiscBuilderBase& parent, Kind kind, - const char gameID[6], const char* gameTitle, uint32_t fstMemoryAddr) - : DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle), m_fstMemoryAddr(fstMemoryAddr) {} + PartitionBuilderGCN(DiscBuilderBase& parent) + : DiscBuilderBase::PartitionBuilderBase(parent, Kind::Data) {} uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) { @@ -202,17 +228,16 @@ public: return ret; } - bool _build(const std::function& func) + bool _build(const std::function& headerFunc, + const std::function& bi2Func, + const std::function& apploaderFunc) { - std::unique_ptr ws = beginWriteStream(0); + std::unique_ptr ws = beginWriteStream(0x2440); if (!ws) return false; - Header header(m_gameID, m_gameTitle.c_str(), false); - header.write(*ws); - - ws = beginWriteStream(0x2440); size_t xferSz = 0; - if (!func(*ws, xferSz)) + if (!apploaderFunc(*ws, xferSz)) return false; size_t fstOff = ROUND_UP_32(xferSz); @@ -233,38 +258,89 @@ public: return false; } - ws = beginWriteStream(0x420); + ws = beginWriteStream(0); if (!ws) return false; - uint32_t vals[7]; - vals[0] = SBig(uint32_t(m_dolOffset)); - vals[1] = SBig(uint32_t(fstOff)); - vals[2] = SBig(uint32_t(fstSz)); - 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)); + if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser)) + return false; + if (!bi2Func(*ws)) + return false; return true; } - bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, const SystemChar* apploaderIn) + bool buildFromDirectory(const SystemChar* dirIn) { std::unique_ptr ws = beginWriteStream(0); if (!ws) return false; - bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn); + bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn); if (!result) 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 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 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 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 rs = NewFileIO(apploaderIn.c_str())->beginReadStream(); if (!rs) return false; char buf[8192]; - SystemString apploaderName(apploaderIn); while (true) { size_t rdSz = rs->read(buf, 8192); @@ -278,7 +354,7 @@ public: "apploader flows into user area (one or the other is too big)"); return false; } - m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); + m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); } ++m_parent.m_progressIdx; return true; @@ -294,7 +370,26 @@ public: if (!result) 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 apploaderBuf = partIn->getApploaderBuf(); size_t apploaderSz = partIn->getApploaderSize(); @@ -314,8 +409,7 @@ public: } }; -EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, - const SystemChar* apploaderIn) +EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn) { if (!m_fileIO->beginWriteStream()) return EBuildResult::Failed; @@ -332,12 +426,12 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const S ws->write("", 1); PartitionBuilderGCN& pb = static_cast(*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) return -1; sz += 0x30000; @@ -349,12 +443,10 @@ uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, con return sz; } -DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle, - uint32_t fstMemoryAddr, FProgress progressCB) +DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB) : DiscBuilderBase(outPath, 0x57058000, progressCB) { - PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this, PartitionBuilderBase::Kind::Data, - gameID, gameTitle, fstMemoryAddr); + PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this); m_partitions.emplace_back(partBuilder); } diff --git a/lib/DiscWii.cpp b/lib/DiscWii.cpp index fd43855..4d09a7a 100644 --- a/lib/DiscWii.cpp +++ b/lib/DiscWii.cpp @@ -5,6 +5,7 @@ #include "nod/DiscWii.hpp" #include "nod/aes.hpp" #include "nod/sha1.h" +#include "nod/nod.hpp" namespace nod { @@ -66,7 +67,7 @@ class PartitionWii : public DiscBase::IPartition uint32_t timeLimit; } timeLimits[8]; - void read(IDiscIO::IReadStream& s) + void read(IReadStream& s) { s.read(this, 676); sigType = SBig(sigType); @@ -79,6 +80,21 @@ class PartitionWii : public DiscBase::IPartition 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; struct TMD @@ -112,7 +128,7 @@ class PartitionWii : public DiscBase::IPartition uint64_t size; char hash[20]; - void read(IDiscIO::IReadStream& s) + void read(IReadStream& s) { s.read(this, 36); id = SBig(id); @@ -120,10 +136,20 @@ class PartitionWii : public DiscBase::IPartition type = SBig(type); 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 contents; - void read(IDiscIO::IReadStream& s) + void read(IReadStream& s) { s.read(this, 484); sigType = SigType(SBig(uint32_t(sigType))); @@ -144,6 +170,24 @@ class PartitionWii : public DiscBase::IPartition 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 m_h3Data; + uint64_t m_dataOff; uint8_t m_decKey[16]; @@ -196,7 +275,7 @@ public: PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err) : IPartition(parent, kind, offset) { - std::unique_ptr s = parent.getDiscIO().beginReadStream(offset); + std::unique_ptr s = parent.getDiscIO().beginReadStream(offset); if (!s) { err = true; @@ -239,25 +318,29 @@ public: m_tmdCert.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 */ std::unique_ptr aes = NewAES(); 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->decrypt(iv, m_ticket.encKey, m_decKey, 16); /* Wii-specific header reads (now using title key to decrypt) */ - std::unique_ptr ds = beginReadStream(0x420); + std::unique_ptr ds = beginReadStream(0x0); if (!ds) { err = true; return; } - - s->read(&m_bi2Header, sizeof(BI2Header)); - m_dolOff = SBig(m_bi2Header.dolOff) << 2; - m_fstOff = SBig(m_bi2Header.fstOff) << 2; - m_fstSz = SBig(m_bi2Header.fstSz) << 2; + m_header.read(*ds); + m_bi2Header.read(*ds); + m_dolOff = m_header.m_dolOff << 2; + m_fstOff = m_header.m_fstOff << 2; + m_fstSz = m_header.m_fstSz << 2; ds->seek(0x2440 + 0x14); uint32_t vals[2]; ds->read(vals, 8); @@ -277,7 +360,7 @@ public: const PartitionWii& m_parent; uint64_t m_baseOffset; uint64_t m_offset; - std::unique_ptr m_dio; + std::unique_ptr m_dio; size_t m_curBlock = SIZE_MAX; uint8_t m_encBuf[0x8000]; @@ -340,7 +423,7 @@ public: if (cacheSize + cacheOffset > 0x7c00) cacheSize = 0x7c00 - cacheOffset; - memcpy(dst, m_decBuf + cacheOffset, cacheSize); + memmove(dst, m_decBuf + cacheOffset, cacheSize); dst += cacheSize; rem -= cacheSize; cacheOffset = 0; @@ -366,7 +449,7 @@ public: std::unique_ptr readPartitionHeaderBuf(size_t& szOut) const { { - std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4); + std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4); if (!rs) return {}; @@ -380,7 +463,7 @@ public: szOut = uint64_t(h3) << 2; } - std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset); + std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset); if (!rs) return {}; @@ -390,39 +473,58 @@ public: return buf; } - bool writeOutPartitionHeader(const SystemChar* pathOut) const + bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { - std::unique_ptr ws = NewFileIO(pathOut)->beginWriteStream(); - if (!ws) - return false; - uint64_t h3Off; - { - std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4); - if (!rs) - return false; + Sstat theStat; - uint32_t h3; - if (rs->read(&h3, 4) != 4) - { - LogModule.report(logvisor::Error, _S("unable to read H3 offset to %s"), pathOut); + /* Extract Ticket */ + SystemString ticketPath = path + _S("/ticket.bin"); + if (ctx.force || Stat(ticketPath.c_str(), &theStat)) + { + if (ctx.progressCB) + ctx.progressCB("ticket.bin", 0.f); + auto ws = NewFileIO(ticketPath)->beginWriteStream(); + if (!ws) return false; - } - h3 = SBig(h3); - h3Off = uint64_t(h3) << 2; + m_ticket.write(*ws); } - char buf[8192]; - size_t rem = h3Off; - std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset); - if (!rs) - return false; - - while (rem) + /* Extract TMD */ + SystemString tmdPath = path + _S("/tmd.bin"); + if (ctx.force || Stat(tmdPath.c_str(), &theStat)) { - size_t rdSz = nod::min(rem, size_t(8192ul)); - rs->read(buf, rdSz); - ws->write(buf, rdSz); - rem -= rdSz; + if (ctx.progressCB) + ctx.progressCB("tmd.bin", 0.f); + auto ws = NewFileIO(tmdPath)->beginWriteStream(); + 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; @@ -447,7 +549,7 @@ DiscWii::DiscWii(std::unique_ptr&& dio, bool& err) } parts[4]; PartInfo(IDiscIO& dio, bool& err) { - std::unique_ptr s = dio.beginReadStream(0x40000); + std::unique_ptr s = dio.beginReadStream(0x40000); if (!s) { err = true; @@ -496,20 +598,54 @@ DiscWii::DiscWii(std::unique_ptr&& dio, bool& err) DiscBuilderWii DiscWii::makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB) { - return DiscBuilderWii(outPath, m_header.m_gameID, m_header.m_gameTitle, - dualLayer, progressCB); + return DiscBuilderWii(outPath, dualLayer, progressCB); } -bool DiscWii::writeOutDataPartitionHeader(const SystemChar* pathOut) const +bool DiscWii::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const { - for (const std::unique_ptr& part : m_partitions) + if (Mkdir((path + _S("/DATA/disc")).c_str(), 0755) && errno != EEXIST) { - if (part->getKind() == IPartition::Kind::Data) - { - return static_cast(*part).writeOutPartitionHeader(pathOut); - } + LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/disc'"), path.c_str()); + return false; } - 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 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 rs = getDiscIO().beginReadStream(0x4E000); + if (!rs) + return false; + std::unique_ptr 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}; @@ -557,32 +693,32 @@ public: { sha1_init(&sha); 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_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); } sha1_init(&sha); 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) { char* ptr0 = ptr1 + c*0x8000; - memcpy(ptr0+0x280, h1, 0x0A0); + memmove(ptr0+0x280, h1, 0x0A0); memset(ptr0+0x320, 0, 0x020); } } sha1_init(&sha); 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) { @@ -590,7 +726,7 @@ public: for (int c=0 ; c<8 ; ++c) { char* ptr0 = ptr1 + c*0x8000; - memcpy(ptr0+0x340, h2, 0x0A0); + memmove(ptr0+0x340, h2, 0x0A0); memset(ptr0+0x3E0, 0, 0x020); 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); @@ -659,7 +795,7 @@ public: if (src) { - memcpy(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize); + memmove(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize); src += cacheSize; } else @@ -680,9 +816,8 @@ public: } }; - PartitionBuilderWii(DiscBuilderBase& parent, Kind kind, - const char gameID[6], const char* gameTitle, uint64_t baseOffset) - : DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle), + PartitionBuilderWii(DiscBuilderBase& parent, Kind kind, uint64_t baseOffset) + : DiscBuilderBase::PartitionBuilderBase(parent, kind), m_baseOffset(baseOffset), m_aes(NewAES()) {} uint64_t getCurUserEnd() const {return m_curUser;} @@ -723,101 +858,28 @@ public: return ret; } - uint64_t _build(const std::function& contentFunc, + uint64_t _build(const std::function& tmdData, size_t& tmdSz)>& cryptoFunc, + const std::function& headerFunc, + const std::function& bi2Func, const std::function& apploaderFunc, - const uint8_t* phBuf, size_t phSz, size_t apploaderSz) + const std::function& contentFunc, + size_t apploaderSz) { - /* Read head and validate key members */ - 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 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 */ + /* Write partition head up to H3 table */ std::unique_ptr ws = m_parent.getFileIO().beginWriteStream(m_baseOffset); 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 ; iwrite("", 1); + uint32_t h3Off, dataOff; + uint8_t tkey[16], tkeyiv[16]; + uint8_t ccIdx; + std::unique_ptr tmdData; + size_t tmdSz; + if (!cryptoFunc(*ws, h3Off, dataOff, ccIdx, tkey, tkeyiv, tmdData, tmdSz)) + return -1; + + m_userOffset = dataOff; /* Prepare crypto pass */ m_aes->setKey(COMMON_KEYS[ccIdx]); @@ -847,8 +909,6 @@ public: cws = beginWriteStream(0); if (!cws) return -1; - Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0); - header.write(*cws); /* Compute boot table members and write */ size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz); @@ -863,13 +923,11 @@ public: return -1; } - cws->write(nullptr, 0x420 - sizeof(Header)); - uint32_t vals[4]; - vals[0] = SBig(uint32_t(m_dolOffset >> uint64_t(2))); - vals[1] = SBig(uint32_t(fstOff >> uint64_t(2))); - vals[2] = SBig(uint32_t(fstSz)); - vals[3] = SBig(uint32_t(fstSz)); - cws->write(vals, 16); + if (!headerFunc(*cws, m_dolOffset, fstOff, fstSz)) + return -1; + + if (!bi2Func(*cws)) + return -1; size_t xferSz = 0; if (!apploaderFunc(*cws, xferSz)) @@ -958,42 +1016,151 @@ public: return m_baseOffset + dataOff + groupCount * 0x200000; } - uint64_t buildFromDirectory(const SystemChar* dirIn, - const SystemChar* dolIn, - const SystemChar* apploaderIn, - const SystemChar* partHeadIn) + uint64_t buildFromDirectory(const SystemChar* dirIn) { - std::unique_ptr ph = NewFileIO(partHeadIn); - size_t phSz = ph->size(); - std::unique_ptr phBuf(new uint8_t[phSz]); + SystemString dirStr(dirIn); + + /* Check Ticket */ + SystemString ticketIn = dirStr + _S("/ticket.bin"); + Sstat theStat; + if (Stat(ticketIn.c_str(), &theStat)) { - auto rs = ph->beginReadStream(); - if (!rs) - return -1; - rs->read(phBuf.get(), phSz); + LogModule.report(logvisor::Error, _S("unable to stat %s"), ticketIn.c_str()); + return -1; } - /* Get Apploader Size */ - Sstat theStat; - if (Stat(apploaderIn, &theStat)) + /* Check TMD */ + SystemString tmdIn = dirStr + _S("/tmd.bin"); + 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 _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& tmdData, size_t& tmdSzOut) -> bool { - return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn, dolIn, apploaderIn); + h3OffOut = 0x8000; + dataOffOut = 0x20000; + + std::unique_ptr 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 ; ibeginReadStream(); + std::unique_ptr 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 rs = NewFileIO(apploaderIn)->beginReadStream(); + std::unique_ptr rs = NewFileIO(bootIn.c_str())->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 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 rs = NewFileIO(apploaderIn.c_str())->beginReadStream(); if (!rs) return false; char buf[8192]; - SystemString apploaderName(apploaderIn); while (true) { size_t rdSz = rs->read(buf, 8192); @@ -1007,11 +1174,15 @@ public: "apploader flows into user area (one or the other is too big)"); return false; } - m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); + m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); } ++m_parent.m_progressIdx; 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) @@ -1020,13 +1191,44 @@ public: std::unique_ptr phBuf = partIn->readPartitionHeaderBuf(phSz); 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& tmdData, size_t& tmdSz) -> bool { - return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn); + h3OffOut = SBig(*reinterpret_cast(&phBuf[0x2B4])) << 2; + dataOffOut = SBig(*reinterpret_cast(&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(&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 { - cws.write(nullptr, 0x2440 - 0x430); std::unique_ptr apploaderBuf = partIn->getApploaderBuf(); size_t apploaderSz = partIn->getApploaderSize(); SystemString apploaderName(_S("")); @@ -1041,13 +1243,18 @@ public: m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); ++m_parent.m_progressIdx; 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, - const SystemChar* apploaderIn, const SystemChar* partHeadIn) +EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn) { + SystemString dirStr(dirIn); + PartitionBuilderWii& pb = static_cast(*m_partitions[0]); uint64_t filledSz = pb.m_baseOffset; if (!m_fileIO->beginWriteStream()) @@ -1066,7 +1273,7 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S ws->write("", 1); /* Assemble image */ - filledSz = pb.buildFromDirectory(dirIn, dolIn, apploaderIn, partHeadIn); + filledSz = pb.buildFromDirectory(dirIn); if (filledSz == -1) return EBuildResult::Failed; else if (filledSz >= uint64_t(m_discCapacity)) @@ -1082,7 +1289,12 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S ws = m_fileIO->beginWriteStream(0); if (!ws) 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 rs = NewFileIO(headerPath.c_str())->beginReadStream(); + if (!rs) + return EBuildResult::Failed; + Header header; + header.read(*rs); header.write(*ws); /* Populate partition info */ @@ -1099,24 +1311,16 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S ws->write(vals, 4); /* 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); if (!ws) return EBuildResult::Failed; - 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_fileIO->beginWriteStream(0x4E010); - if (!ws) - return EBuildResult::Failed; - for (int i=0 ; i<16 ; ++i) - ws->write("\x80", 1); + ws->write(regionBuf, 0x20); /* Fill image to end */ ws = m_fileIO->beginWriteStream(filledSz); @@ -1139,10 +1343,9 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S return EBuildResult::Success; } -uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn, - bool& dualLayer) +uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer) { - uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn); + uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn); if (sz == -1) return -1; auto szDiv = std::lldiv(sz, 0x1F0000); @@ -1158,12 +1361,10 @@ uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, con return sz; } -DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle, - bool dualLayer, FProgress progressCB) +DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB) : DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer) { - PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data, - gameID, gameTitle, 0x200000); + PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data, 0x200000); m_partitions.emplace_back(partBuilder); } @@ -1224,24 +1425,15 @@ EBuildResult DiscMergerWii::mergeFromDirectory(const SystemChar* dirIn) ws->write(vals, 4); /* Populate region info */ + std::unique_ptr 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); if (!ws) return EBuildResult::Failed; - 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 EBuildResult::Failed; - for (int i=0 ; i<16 ; ++i) - ws->write("\x80", 1); + ws->write(regionBuf, 0x20); /* Fill image to end */ ws = m_builder.m_fileIO->beginWriteStream(filledSz); diff --git a/lib/FileIOFILE.cpp b/lib/FileIOFILE.cpp index 56b3f14..ab174a1 100644 --- a/lib/FileIOFILE.cpp +++ b/lib/FileIOFILE.cpp @@ -128,7 +128,7 @@ public: { FSeek(fp, offset, whence); } - int64_t position() + uint64_t position() const { return FTell(fp); } diff --git a/lib/FileIOWin32.cpp b/lib/FileIOWin32.cpp index 776af56..eecc25f 100644 --- a/lib/FileIOWin32.cpp +++ b/lib/FileIOWin32.cpp @@ -145,7 +145,7 @@ public: li.QuadPart = offset; SetFilePointerEx(fp, li, nullptr, whence); } - int64_t position() + uint64_t position() const { LARGE_INTEGER li = {}; LARGE_INTEGER res;