From be8409681fd5e921be72a04f460e87f2de5f6280 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 7 Dec 2018 19:21:47 -1000 Subject: [PATCH] New code style refactor --- driver/main.cpp | 438 ++--- include/nod/DirectoryEnumerator.hpp | 75 +- include/nod/DiscBase.hpp | 830 ++++----- include/nod/DiscGCN.hpp | 45 +- include/nod/DiscWii.hpp | 40 +- include/nod/IDiscIO.hpp | 79 +- include/nod/IFileIO.hpp | 121 +- include/nod/Util.hpp | 347 ++-- include/nod/aes.hpp | 17 +- include/nod/nod.hpp | 13 +- lib/DirectoryEnumerator.cpp | 481 +++-- lib/DiscBase.cpp | 1546 ++++++++-------- lib/DiscGCN.cpp | 812 ++++----- lib/DiscIOISO.cpp | 98 +- lib/DiscIOWBFS.cpp | 501 +++-- lib/DiscWii.cpp | 2634 +++++++++++++-------------- lib/FileIOFILE.cpp | 286 ++- lib/FileIOWin32.cpp | 340 ++-- lib/aes.cpp | 941 +++++----- lib/nod.cpp | 120 +- logvisor | 2 +- 21 files changed, 4545 insertions(+), 5221 deletions(-) diff --git a/driver/main.cpp b/driver/main.cpp index 6295bda..6313d50 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -3,14 +3,14 @@ #include "logvisor/logvisor.hpp" #include "nod/nod.hpp" -static void printHelp() -{ - fprintf(stderr, "Usage:\n" - " nodtool extract [-f] []\n" - " nodtool makegcn []\n" - " nodtool makewii []\n" - " nodtool mergegcn []\n" - " nodtool mergewii []\n"); +static void printHelp() { + fprintf(stderr, + "Usage:\n" + " nodtool extract [-f] []\n" + " nodtool makegcn []\n" + " nodtool makewii []\n" + " nodtool mergegcn []\n" + " nodtool mergewii []\n"); } #if NOD_UCS2 @@ -25,241 +25,203 @@ int wmain(int argc, wchar_t* argv[]) int main(int argc, char* argv[]) #endif { - if (argc < 3 || - (!strcasecmp(argv[1], _SYS_STR("makegcn")) && argc < 3) || - (!strcasecmp(argv[1], _SYS_STR("makewii")) && argc < 3) || - (!strcasecmp(argv[1], _SYS_STR("mergegcn")) && argc < 4) || - (!strcasecmp(argv[1], _SYS_STR("mergewii")) && argc < 4)) - { - printHelp(); - return 1; - } + if (argc < 3 || (!strcasecmp(argv[1], _SYS_STR("makegcn")) && argc < 3) || + (!strcasecmp(argv[1], _SYS_STR("makewii")) && argc < 3) || + (!strcasecmp(argv[1], _SYS_STR("mergegcn")) && argc < 4) || + (!strcasecmp(argv[1], _SYS_STR("mergewii")) && argc < 4)) { + printHelp(); + return 1; + } - /* Enable logging to console */ - logvisor::RegisterStandardExceptions(); - logvisor::RegisterConsoleLogger(); + /* Enable logging to console */ + logvisor::RegisterStandardExceptions(); + logvisor::RegisterConsoleLogger(); - bool verbose = false; - nod::ExtractionContext ctx = {true, - [&](std::string_view str, float c) { - if (verbose) - fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.data(), c * 100.f); - }}; - const nod::SystemChar* inDir = nullptr; - const nod::SystemChar* outDir = _SYS_STR("."); + bool verbose = false; + nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) { + if (verbose) + fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.data(), + c * 100.f); + }}; + const nod::SystemChar* inDir = nullptr; + const nod::SystemChar* outDir = _SYS_STR("."); - for (int a=2 ; a disc = nod::OpenDiscFromImage(inDir, isWii); - if (!disc) - return 1; - - nod::Mkdir(outDir, 0755); - - nod::IPartition* dataPart = disc->getDataPartition(); - if (!dataPart) - return 1; - - if (!dataPart->extractToDirectory(outDir, ctx)) - return 1; - } - else if (!strcasecmp(argv[1], _SYS_STR("makegcn"))) - { - /* Pre-validate path */ - nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[2]); - return 1; - } - - if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1) - return 1; - - nod::EBuildResult ret; - - if (argc < 4) - { - nod::SystemString outPath(argv[2]); - outPath.append(_SYS_STR(".iso")); - nod::DiscBuilderGCN b(outPath, progFunc); - ret = b.buildFromDirectory(argv[2]); - } - else - { - nod::DiscBuilderGCN b(argv[3], progFunc); - ret = b.buildFromDirectory(argv[2]); - } - - printf("\n"); - if (ret != nod::EBuildResult::Success) - return 1; - } - else if (!strcasecmp(argv[1], _SYS_STR("makewii"))) - { - /* Pre-validate path */ - nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[4]); - return 1; - } - - bool dual = false; - if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1) - return 1; - - nod::EBuildResult ret; - - if (argc < 4) - { - nod::SystemString outPath(argv[2]); - outPath.append(_SYS_STR(".iso")); - nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc); - ret = b.buildFromDirectory(argv[2]); - } - else - { - nod::DiscBuilderWii b(argv[3], dual, progFunc); - ret = b.buildFromDirectory(argv[2]); - } - - printf("\n"); - if (ret != nod::EBuildResult::Success) - return 1; - } - else if (!strcasecmp(argv[1], _SYS_STR("mergegcn"))) - { - /* Pre-validate paths */ - nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[2]); - return 1; - } - if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as file"), argv[3]); - return 1; - } - - bool isWii; - std::unique_ptr disc = nod::OpenDiscFromImage(argv[3], isWii); - if (!disc) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to open image %s"), argv[3]); - return 1; - } - if (isWii) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("Wii images should be merged with 'mergewii'")); - return 1; - } - - if (nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast(*disc), argv[2]) == -1) - return 1; - - nod::EBuildResult ret; - - if (argc < 5) - { - nod::SystemString outPath(argv[2]); - outPath.append(_SYS_STR(".iso")); - nod::DiscMergerGCN b(outPath.c_str(), static_cast(*disc), progFunc); - ret = b.mergeFromDirectory(argv[2]); - } - else - { - nod::DiscMergerGCN b(argv[4], static_cast(*disc), progFunc); - ret = b.mergeFromDirectory(argv[2]); - } - - printf("\n"); - if (ret != nod::EBuildResult::Success) - return 1; - } - else if (!strcasecmp(argv[1], _SYS_STR("mergewii"))) - { - /* Pre-validate paths */ - nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[2]); - return 1; - } - if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as file"), argv[3]); - return 1; - } - - bool isWii; - std::unique_ptr disc = nod::OpenDiscFromImage(argv[3], isWii); - if (!disc) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("unable to open image %s"), argv[3]); - return 1; - } - if (!isWii) - { - nod::LogModule.report(logvisor::Error, _SYS_STR("GameCube images should be merged with 'mergegcn'")); - return 1; - } - - bool dual = false; - if (nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast(*disc), argv[2], dual) == -1) - return 1; - - nod::EBuildResult ret; - - if (argc < 5) - { - nod::SystemString outPath(argv[2]); - outPath.append(_SYS_STR(".iso")); - nod::DiscMergerWii b(outPath.c_str(), static_cast(*disc), dual, progFunc); - ret = b.mergeFromDirectory(argv[2]); - } - else - { - nod::DiscMergerWii b(argv[4], static_cast(*disc), dual, progFunc); - ret = b.mergeFromDirectory(argv[2]); - } - - printf("\n"); - if (ret != nod::EBuildResult::Success) - return 1; - } + else if (!inDir) + inDir = argv[a]; else - { - printHelp(); - return 1; + outDir = argv[a]; + } + + auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) { + nod::Printf(_SYS_STR("\r ")); + if (bytes != -1) + nod::Printf(_SYS_STR("\r%g%% %s %" PRISize " B"), prog * 100.f, name.data(), bytes); + else + nod::Printf(_SYS_STR("\r%g%% %s"), prog * 100.f, name.data()); + fflush(stdout); + }; + + if (!strcasecmp(argv[1], _SYS_STR("extract"))) { + bool isWii; + std::unique_ptr disc = nod::OpenDiscFromImage(inDir, isWii); + if (!disc) + return 1; + + nod::Mkdir(outDir, 0755); + + nod::IPartition* dataPart = disc->getDataPartition(); + if (!dataPart) + return 1; + + if (!dataPart->extractToDirectory(outDir, ctx)) + return 1; + } else if (!strcasecmp(argv[1], _SYS_STR("makegcn"))) { + /* Pre-validate path */ + nod::Sstat theStat; + if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[2]); + return 1; } - nod::LogModule.report(logvisor::Info, _SYS_STR("Success!")); - return 0; -} + if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1) + return 1; + nod::EBuildResult ret; + + if (argc < 4) { + nod::SystemString outPath(argv[2]); + outPath.append(_SYS_STR(".iso")); + nod::DiscBuilderGCN b(outPath, progFunc); + ret = b.buildFromDirectory(argv[2]); + } else { + nod::DiscBuilderGCN b(argv[3], progFunc); + ret = b.buildFromDirectory(argv[2]); + } + + printf("\n"); + if (ret != nod::EBuildResult::Success) + return 1; + } else if (!strcasecmp(argv[1], _SYS_STR("makewii"))) { + /* Pre-validate path */ + nod::Sstat theStat; + if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[4]); + return 1; + } + + bool dual = false; + if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1) + return 1; + + nod::EBuildResult ret; + + if (argc < 4) { + nod::SystemString outPath(argv[2]); + outPath.append(_SYS_STR(".iso")); + nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc); + ret = b.buildFromDirectory(argv[2]); + } else { + nod::DiscBuilderWii b(argv[3], dual, progFunc); + ret = b.buildFromDirectory(argv[2]); + } + + printf("\n"); + if (ret != nod::EBuildResult::Success) + return 1; + } else if (!strcasecmp(argv[1], _SYS_STR("mergegcn"))) { + /* Pre-validate paths */ + nod::Sstat theStat; + if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[2]); + return 1; + } + if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as file"), argv[3]); + return 1; + } + + bool isWii; + std::unique_ptr disc = nod::OpenDiscFromImage(argv[3], isWii); + if (!disc) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to open image %s"), argv[3]); + return 1; + } + if (isWii) { + nod::LogModule.report(logvisor::Error, _SYS_STR("Wii images should be merged with 'mergewii'")); + return 1; + } + + if (nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast(*disc), argv[2]) == -1) + return 1; + + nod::EBuildResult ret; + + if (argc < 5) { + nod::SystemString outPath(argv[2]); + outPath.append(_SYS_STR(".iso")); + nod::DiscMergerGCN b(outPath.c_str(), static_cast(*disc), progFunc); + ret = b.mergeFromDirectory(argv[2]); + } else { + nod::DiscMergerGCN b(argv[4], static_cast(*disc), progFunc); + ret = b.mergeFromDirectory(argv[2]); + } + + printf("\n"); + if (ret != nod::EBuildResult::Success) + return 1; + } else if (!strcasecmp(argv[1], _SYS_STR("mergewii"))) { + /* Pre-validate paths */ + nod::Sstat theStat; + if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as directory"), argv[2]); + return 1; + } + if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s as file"), argv[3]); + return 1; + } + + bool isWii; + std::unique_ptr disc = nod::OpenDiscFromImage(argv[3], isWii); + if (!disc) { + nod::LogModule.report(logvisor::Error, _SYS_STR("unable to open image %s"), argv[3]); + return 1; + } + if (!isWii) { + nod::LogModule.report(logvisor::Error, _SYS_STR("GameCube images should be merged with 'mergegcn'")); + return 1; + } + + bool dual = false; + if (nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast(*disc), argv[2], dual) == -1) + return 1; + + nod::EBuildResult ret; + + if (argc < 5) { + nod::SystemString outPath(argv[2]); + outPath.append(_SYS_STR(".iso")); + nod::DiscMergerWii b(outPath.c_str(), static_cast(*disc), dual, progFunc); + ret = b.mergeFromDirectory(argv[2]); + } else { + nod::DiscMergerWii b(argv[4], static_cast(*disc), dual, progFunc); + ret = b.mergeFromDirectory(argv[2]); + } + + printf("\n"); + if (ret != nod::EBuildResult::Success) + return 1; + } else { + printHelp(); + return 1; + } + + nod::LogModule.report(logvisor::Info, _SYS_STR("Success!")); + return 0; +} diff --git a/include/nod/DirectoryEnumerator.hpp b/include/nod/DirectoryEnumerator.hpp index 4f6e949..6c93f79 100644 --- a/include/nod/DirectoryEnumerator.hpp +++ b/include/nod/DirectoryEnumerator.hpp @@ -2,65 +2,52 @@ #include "Util.hpp" -namespace nod -{ +namespace nod { -struct CaseInsensitiveCompare -{ - bool operator()(std::string_view lhs, std::string_view rhs) const - { +struct CaseInsensitiveCompare { + bool operator()(std::string_view lhs, std::string_view rhs) const { #if _WIN32 - if (_stricmp(lhs.data(), rhs.data()) < 0) + if (_stricmp(lhs.data(), rhs.data()) < 0) #else - if (strcasecmp(lhs.data(), rhs.data()) < 0) + if (strcasecmp(lhs.data(), rhs.data()) < 0) #endif - return true; - return false; - } + return true; + return false; + } #if _WIN32 - bool operator()(std::wstring_view lhs, std::wstring_view rhs) const - { - if (_wcsicmp(lhs.data(), rhs.data()) < 0) - return true; - return false; - } + bool operator()(std::wstring_view lhs, std::wstring_view rhs) const { + if (_wcsicmp(lhs.data(), rhs.data()) < 0) + return true; + return false; + } #endif }; -class DirectoryEnumerator -{ +class DirectoryEnumerator { public: - enum class Mode - { - Native, - DirsSorted, - FilesSorted, - DirsThenFilesSorted - }; - struct Entry - { - SystemString m_path; - SystemString m_name; - size_t m_fileSz; - bool m_isDir; + enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted }; + struct Entry { + SystemString m_path; + SystemString m_name; + size_t m_fileSz; + bool m_isDir; - Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir) - : m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {} - }; + Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir) + : m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {} + }; private: - std::vector m_entries; + std::vector m_entries; public: - DirectoryEnumerator(SystemStringView path, Mode mode=Mode::DirsThenFilesSorted, - bool sizeSort=false, bool reverse=false, bool noHidden=false); + DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false, + bool reverse = false, bool noHidden = false); - operator bool() const {return m_entries.size() != 0;} - size_t size() const {return m_entries.size();} - std::vector::const_iterator begin() const {return m_entries.cbegin();} - std::vector::const_iterator end() const {return m_entries.cend();} + operator bool() const { return m_entries.size() != 0; } + size_t size() const { return m_entries.size(); } + std::vector::const_iterator begin() const { return m_entries.cbegin(); } + std::vector::const_iterator end() const { return m_entries.cend(); } }; -} - +} // namespace nod diff --git a/include/nod/DiscBase.hpp b/include/nod/DiscBase.hpp index 7be6544..5bae1eb 100644 --- a/include/nod/DiscBase.hpp +++ b/include/nod/DiscBase.hpp @@ -11,495 +11,453 @@ #include "IDiscIO.hpp" #include "IFileIO.hpp" -namespace nod -{ +namespace nod { using FProgress = std::function; -enum class EBuildResult -{ - Success, - Failed, - DiskFull -}; +enum class EBuildResult { Success, Failed, DiskFull }; -enum class PartitionKind : uint32_t -{ - Data, - Update, - Channel -}; +enum class PartitionKind : uint32_t { Data, Update, Channel }; const SystemChar* getKindString(PartitionKind kind); -class FSTNode -{ - uint32_t typeAndNameOffset; - uint32_t offset; - uint32_t length; +class FSTNode { + uint32_t typeAndNameOffset; + uint32_t offset; + uint32_t length; + public: - FSTNode(bool isDir, uint32_t nameOff, uint32_t off, uint32_t len) - { - typeAndNameOffset = nameOff & 0xffffff; - typeAndNameOffset |= isDir << 24; - typeAndNameOffset = SBig(typeAndNameOffset); - offset = SBig(off); - length = SBig(len); - } - inline bool isDir() const {return ((SBig(typeAndNameOffset) >> 24) != 0);} - inline uint32_t getNameOffset() const {return SBig(typeAndNameOffset) & 0xffffff;} - inline uint32_t getOffset() const {return SBig(offset);} - inline uint32_t getLength() const {return SBig(length);} - void incrementLength() - { - uint32_t orig = SBig(length); - ++orig; - length = SBig(orig); - } + FSTNode(bool isDir, uint32_t nameOff, uint32_t off, uint32_t len) { + typeAndNameOffset = nameOff & 0xffffff; + typeAndNameOffset |= isDir << 24; + typeAndNameOffset = SBig(typeAndNameOffset); + offset = SBig(off); + length = SBig(len); + } + inline bool isDir() const { return ((SBig(typeAndNameOffset) >> 24) != 0); } + inline uint32_t getNameOffset() const { return SBig(typeAndNameOffset) & 0xffffff; } + inline uint32_t getOffset() const { return SBig(offset); } + inline uint32_t getLength() const { return SBig(length); } + void incrementLength() { + uint32_t orig = SBig(length); + ++orig; + length = SBig(orig); + } }; -struct Header -{ - char m_gameID[6]; - char m_discNum; - char m_discVersion; - char m_audioStreaming; - char m_streamBufSz; - char m_unk1[14]; - uint32_t m_wiiMagic; - uint32_t m_gcnMagic; - char m_gameTitle[64]; - char m_disableHashVerification; - char m_disableDiscEnc; - char m_unk2[0x39e]; - 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]; +struct Header { + char m_gameID[6]; + char m_discNum; + char m_discVersion; + char m_audioStreaming; + char m_streamBufSz; + char m_unk1[14]; + uint32_t m_wiiMagic; + uint32_t m_gcnMagic; + char m_gameTitle[64]; + char m_disableHashVerification; + char m_disableDiscEnc; + char m_unk2[0x39e]; + 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) - { - auto rs = dio.beginReadStream(); - if (!rs) - { - err = true; - return; - } - read(*rs); + Header() = default; + Header(IDiscIO& dio, bool& err) { + auto rs = dio.beginReadStream(); + if (!rs) { + err = true; + return; } + 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); - } + 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); + } - 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)); - } + 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]; +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 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)); - } + 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 IPartition; class DiscBase; -class Node -{ +class Node { public: - enum class Kind - { - File, - Directory - }; + enum class Kind { File, Directory }; + private: - friend class IPartition; - const IPartition& m_parent; - Kind m_kind; + friend class IPartition; + const IPartition& m_parent; + Kind m_kind; - uint64_t m_discOffset; - uint64_t m_discLength; - std::string m_name; + uint64_t m_discOffset; + uint64_t m_discLength; + std::string m_name; - std::vector::iterator m_childrenBegin; - std::vector::iterator m_childrenEnd; + std::vector::iterator m_childrenBegin; + std::vector::iterator m_childrenEnd; public: - Node(const IPartition& parent, const FSTNode& node, std::string_view name); - inline Kind getKind() const {return m_kind;} - inline std::string_view getName() const {return m_name;} - inline uint64_t size() const {return m_discLength;} - std::unique_ptr beginReadStream(uint64_t offset=0) const; - std::unique_ptr getBuf() const; - inline std::vector::iterator rawBegin() const {return m_childrenBegin;} - inline std::vector::iterator rawEnd() const {return m_childrenEnd;} + Node(const IPartition& parent, const FSTNode& node, std::string_view name); + inline Kind getKind() const { return m_kind; } + inline std::string_view getName() const { return m_name; } + inline uint64_t size() const { return m_discLength; } + std::unique_ptr beginReadStream(uint64_t offset = 0) const; + std::unique_ptr getBuf() const; + inline std::vector::iterator rawBegin() const { return m_childrenBegin; } + inline std::vector::iterator rawEnd() const { return m_childrenEnd; } - class DirectoryIterator - { - friend class Node; - std::vector::iterator m_it; - DirectoryIterator(const std::vector::iterator& it) : m_it(it) {} - public: - using iterator_category = std::forward_iterator_tag; - using value_type = Node; - using difference_type = std::ptrdiff_t; - using pointer = Node*; - using reference = Node&; + class DirectoryIterator { + friend class Node; + std::vector::iterator m_it; + DirectoryIterator(const std::vector::iterator& it) : m_it(it) {} - inline bool operator!=(const DirectoryIterator& other) {return m_it != other.m_it;} - inline bool operator==(const DirectoryIterator& other) {return m_it == other.m_it;} - inline DirectoryIterator& operator++() - { - if (m_it->m_kind == Kind::Directory) - m_it = m_it->rawEnd(); - else - ++m_it; - return *this; - } - inline Node& operator*() {return *m_it;} - inline Node* operator->() {return &*m_it;} - }; - inline DirectoryIterator begin() const {return DirectoryIterator(m_childrenBegin);} - inline DirectoryIterator end() const {return DirectoryIterator(m_childrenEnd);} - inline DirectoryIterator find(std::string_view name) const - { - if (m_kind == Kind::Directory) - { - DirectoryIterator it=begin(); - for (; it != end() ; ++it) - { - if (!it->getName().compare(name)) - return it; - } - return it; - } - return end(); + public: + using iterator_category = std::forward_iterator_tag; + using value_type = Node; + using difference_type = std::ptrdiff_t; + using pointer = Node*; + using reference = Node&; + + inline bool operator!=(const DirectoryIterator& other) { return m_it != other.m_it; } + inline bool operator==(const DirectoryIterator& other) { return m_it == other.m_it; } + inline DirectoryIterator& operator++() { + if (m_it->m_kind == Kind::Directory) + m_it = m_it->rawEnd(); + else + ++m_it; + return *this; } + inline Node& operator*() { return *m_it; } + inline Node* operator->() { return &*m_it; } + }; + inline DirectoryIterator begin() const { return DirectoryIterator(m_childrenBegin); } + inline DirectoryIterator end() const { return DirectoryIterator(m_childrenEnd); } + inline DirectoryIterator find(std::string_view name) const { + if (m_kind == Kind::Directory) { + DirectoryIterator it = begin(); + for (; it != end(); ++it) { + if (!it->getName().compare(name)) + return it; + } + return it; + } + return end(); + } - bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const; + bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const; }; -class IPartition -{ +class IPartition { public: - virtual ~IPartition() = default; - struct DOLHeader - { - uint32_t textOff[7]; - uint32_t dataOff[11]; - uint32_t textStarts[7]; - uint32_t dataStarts[11]; - uint32_t textSizes[7]; - uint32_t dataSizes[11]; - uint32_t bssStart; - uint32_t bssSize; - uint32_t entryPoint; - }; + virtual ~IPartition() = default; + struct DOLHeader { + uint32_t textOff[7]; + uint32_t dataOff[11]; + uint32_t textStarts[7]; + uint32_t dataStarts[11]; + uint32_t textSizes[7]; + uint32_t dataSizes[11]; + uint32_t bssStart; + uint32_t bssSize; + uint32_t entryPoint; + }; protected: - Header m_header; - BI2Header m_bi2Header; - uint64_t m_dolOff; - uint64_t m_fstOff; - uint64_t m_fstSz; - uint64_t m_apploaderSz; - std::vector m_nodes; - void parseFST(IPartReadStream& s); + Header m_header; + BI2Header m_bi2Header; + uint64_t m_dolOff; + uint64_t m_fstOff; + uint64_t m_fstSz; + uint64_t m_apploaderSz; + std::vector m_nodes; + void parseFST(IPartReadStream& s); + std::vector m_buildNodes; + std::vector m_buildNames; + size_t m_buildNameOff = 0; + + uint64_t m_dolSz; + void parseDOL(IPartReadStream& s); + + const DiscBase& m_parent; + PartitionKind m_kind; + uint64_t m_offset; + bool m_isWii; + +public: + mutable size_t m_curNodeIdx = 0; + float getProgressFactor() const { return getNodeCount() ? m_curNodeIdx / float(getNodeCount()) : 0.f; } + float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const { + if (!getNodeCount()) + return 0.f; + + if (totalBytes) + return (m_curNodeIdx + (curByte / float(totalBytes))) / float(getNodeCount()); + else + return m_curNodeIdx / float(getNodeCount()); + } + + IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset) + : m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {} + virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; } + inline PartitionKind getKind() const { return m_kind; } + inline bool isWii() const { return m_isWii; } + inline uint64_t getDiscOffset() const { return m_offset; } + virtual std::unique_ptr beginReadStream(uint64_t offset = 0) const = 0; + inline std::unique_ptr beginDOLReadStream(uint64_t offset = 0) const { + return beginReadStream(m_dolOff + offset); + } + inline std::unique_ptr beginFSTReadStream(uint64_t offset = 0) const { + return beginReadStream(m_fstOff + offset); + } + inline std::unique_ptr beginApploaderReadStream(uint64_t offset = 0) const { + return beginReadStream(0x2440 + offset); + } + inline const Node& getFSTRoot() const { return m_nodes[0]; } + inline Node& getFSTRoot() { return m_nodes[0]; } + bool extractToDirectory(SystemStringView path, const ExtractionContext& ctx); + + inline uint64_t getDOLSize() const { return m_dolSz; } + inline std::unique_ptr getDOLBuf() const { + std::unique_ptr buf(new uint8_t[m_dolSz]); + beginDOLReadStream()->read(buf.get(), m_dolSz); + return buf; + } + + inline uint64_t getFSTSize() const { return m_fstSz; } + inline std::unique_ptr getFSTBuf() const { + std::unique_ptr buf(new uint8_t[m_fstSz]); + beginFSTReadStream()->read(buf.get(), m_fstSz); + return buf; + } + + inline uint64_t getApploaderSize() const { return m_apploaderSz; } + inline std::unique_ptr getApploaderBuf() const { + std::unique_ptr buf(new uint8_t[m_apploaderSz]); + beginApploaderReadStream()->read(buf.get(), m_apploaderSz); + return buf; + } + + inline size_t getNodeCount() const { return m_nodes.size(); } + inline const Header& getHeader() const { return m_header; } + inline const BI2Header& getBI2() const { return m_bi2Header; } + virtual bool extractCryptoFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; } + bool extractSysFiles(SystemStringView path, const ExtractionContext& ctx) const; +}; + +class DiscBase { +public: + virtual ~DiscBase() = default; + +protected: + std::unique_ptr m_discIO; + Header m_header; + std::vector> m_partitions; + +public: + DiscBase(std::unique_ptr&& dio, bool& err) : m_discIO(std::move(dio)), m_header(*m_discIO, err) {} + + inline const Header& getHeader() const { return m_header; } + inline const IDiscIO& getDiscIO() const { return *m_discIO; } + inline size_t getPartitonNodeCount(size_t partition = 0) const { + if (partition > m_partitions.size()) + return -1; + return m_partitions[partition]->getNodeCount(); + } + + inline IPartition* getDataPartition() { + for (const std::unique_ptr& part : m_partitions) + if (part->getKind() == PartitionKind::Data) + return part.get(); + return nullptr; + } + + inline IPartition* getUpdatePartition() { + for (const std::unique_ptr& part : m_partitions) + if (part->getKind() == PartitionKind::Update) + return part.get(); + return nullptr; + } + + inline void extractToDirectory(SystemStringView path, const ExtractionContext& ctx) { + for (std::unique_ptr& part : m_partitions) + part->extractToDirectory(path, ctx); + } + + virtual bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const = 0; +}; + +class DiscBuilderBase { + friend class DiscMergerWii; + +public: + class PartitionBuilderBase { + public: + virtual ~PartitionBuilderBase() = default; + + protected: + std::unordered_map> m_fileOffsetsSizes; std::vector m_buildNodes; std::vector m_buildNames; size_t m_buildNameOff = 0; + virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) = 0; + virtual uint32_t packOffset(uint64_t offset) const = 0; - uint64_t m_dolSz; - void parseDOL(IPartReadStream& s); + void recursiveBuildNodesPre(SystemStringView dirIn); + bool recursiveBuildNodes(IPartWriteStream& ws, bool system, SystemStringView dirIn); - const DiscBase& m_parent; + bool recursiveBuildFST(SystemStringView dirIn, std::function incParents, size_t parentDirIdx); + + void recursiveMergeNodesPre(const Node* nodeIn, SystemStringView dirIn); + bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, SystemStringView dirIn, + SystemStringView keyPath); + bool recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, std::function incParents, + SystemStringView keyPath); + + static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, SystemStringView dirIn); + + void addBuildName(SystemStringView str) { + SystemUTF8Conv utf8View(str); + m_buildNames.emplace_back(utf8View.utf8_str()); + m_buildNameOff += str.size() + 1; + } + + DiscBuilderBase& m_parent; PartitionKind m_kind; - uint64_t m_offset; + uint64_t m_dolOffset = 0; + uint64_t m_dolSize = 0; bool m_isWii; -public: - mutable size_t m_curNodeIdx = 0; - float getProgressFactor() const { return getNodeCount() ? m_curNodeIdx / float(getNodeCount()) : 0.f; } - float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const - { - if (!getNodeCount()) - return 0.f; - if (totalBytes) - return (m_curNodeIdx + (curByte / float(totalBytes))) / float(getNodeCount()); - else - return m_curNodeIdx / float(getNodeCount()); - } + public: + PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii) + : m_parent(parent), m_kind(kind), m_isWii(isWii) {} + virtual std::unique_ptr beginWriteStream(uint64_t offset) = 0; + bool buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn); + static uint64_t CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, bool isWii); + bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, SystemStringView dirIn); + static uint64_t CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn); + }; - IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset) - : m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {} - virtual uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset;} - inline PartitionKind getKind() const {return m_kind;} - inline bool isWii() const {return m_isWii;} - inline uint64_t getDiscOffset() const {return m_offset;} - virtual std::unique_ptr beginReadStream(uint64_t offset=0) const=0; - inline std::unique_ptr beginDOLReadStream(uint64_t offset=0) const - {return beginReadStream(m_dolOff + offset);} - inline std::unique_ptr beginFSTReadStream(uint64_t offset=0) const - {return beginReadStream(m_fstOff + offset);} - inline std::unique_ptr beginApploaderReadStream(uint64_t offset=0) const - {return beginReadStream(0x2440 + offset);} - inline const Node& getFSTRoot() const {return m_nodes[0];} - inline Node& getFSTRoot() {return m_nodes[0];} - bool extractToDirectory(SystemStringView path, const ExtractionContext& ctx); - - inline uint64_t getDOLSize() const {return m_dolSz;} - inline std::unique_ptr getDOLBuf() const - { - std::unique_ptr buf(new uint8_t[m_dolSz]); - beginDOLReadStream()->read(buf.get(), m_dolSz); - return buf; - } - - inline uint64_t getFSTSize() const {return m_fstSz;} - inline std::unique_ptr getFSTBuf() const - { - std::unique_ptr buf(new uint8_t[m_fstSz]); - beginFSTReadStream()->read(buf.get(), m_fstSz); - return buf; - } - - inline uint64_t getApploaderSize() const {return m_apploaderSz;} - inline std::unique_ptr getApploaderBuf() const - { - std::unique_ptr buf(new uint8_t[m_apploaderSz]); - beginApploaderReadStream()->read(buf.get(), m_apploaderSz); - return buf; - } - - inline size_t getNodeCount() const { return m_nodes.size(); } - inline const Header& getHeader() const { return m_header; } - inline const BI2Header& getBI2() const { return m_bi2Header; } - virtual bool extractCryptoFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; } - bool extractSysFiles(SystemStringView path, const ExtractionContext& ctx) const; -}; - -class DiscBase -{ -public: - virtual ~DiscBase() = default; protected: - std::unique_ptr m_discIO; - Header m_header; - std::vector> m_partitions; + SystemString m_outPath; + std::unique_ptr m_fileIO; + std::vector> m_partitions; + int64_t m_discCapacity; + public: - DiscBase(std::unique_ptr&& dio, bool& err) - : m_discIO(std::move(dio)), m_header(*m_discIO, err) {} + FProgress m_progressCB; + size_t m_progressIdx = 0; + size_t m_progressTotal = 0; + float getProgressFactor() const { + return m_progressTotal ? std::min(1.f, m_progressIdx / float(m_progressTotal)) : 0.f; + } + float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const { + if (!m_progressTotal) + return 0.f; - inline const Header& getHeader() const {return m_header;} - inline const IDiscIO& getDiscIO() const {return *m_discIO;} - inline size_t getPartitonNodeCount(size_t partition = 0) const - { - if (partition > m_partitions.size()) - return -1; - return m_partitions[partition]->getNodeCount(); - } + if (totalBytes) + return (m_progressIdx + (curByte / float(totalBytes))) / float(m_progressTotal); + else + return m_progressIdx / float(m_progressTotal); + } - inline IPartition* getDataPartition() - { - for (const std::unique_ptr& part : m_partitions) - if (part->getKind() == PartitionKind::Data) - return part.get(); - return nullptr; - } + virtual ~DiscBuilderBase() = default; + DiscBuilderBase(SystemStringView 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; - inline IPartition* getUpdatePartition() - { - for (const std::unique_ptr& part : m_partitions) - if (part->getKind() == PartitionKind::Update) - return part.get(); - return nullptr; - } - - inline void extractToDirectory(SystemStringView path, const ExtractionContext& ctx) - { - for (std::unique_ptr& part : m_partitions) - part->extractToDirectory(path, ctx); - } - - virtual bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const=0; + IFileIO& getFileIO() { return *m_fileIO; } }; -class DiscBuilderBase -{ - friend class DiscMergerWii; -public: - class PartitionBuilderBase - { - public: - virtual ~PartitionBuilderBase() = default; - protected: - std::unordered_map> m_fileOffsetsSizes; - std::vector m_buildNodes; - std::vector m_buildNames; - size_t m_buildNameOff = 0; - virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0; - virtual uint32_t packOffset(uint64_t offset) const=0; - - void recursiveBuildNodesPre(SystemStringView dirIn); - bool recursiveBuildNodes(IPartWriteStream& ws, bool system, SystemStringView dirIn); - - bool recursiveBuildFST(SystemStringView dirIn, - std::function incParents, - size_t parentDirIdx); - - void recursiveMergeNodesPre(const Node* nodeIn, SystemStringView dirIn); - bool recursiveMergeNodes(IPartWriteStream& ws, bool system, - const Node* nodeIn, SystemStringView dirIn, - SystemStringView keyPath); - bool recursiveMergeFST(const Node* nodeIn, - SystemStringView dirIn, std::function incParents, - SystemStringView keyPath); - - static bool RecursiveCalculateTotalSize(uint64_t& totalSz, - const Node* nodeIn, - SystemStringView dirIn); - - void addBuildName(SystemStringView str) - { - SystemUTF8Conv utf8View(str); - m_buildNames.emplace_back(utf8View.utf8_str()); - m_buildNameOff += str.size() + 1; - } - - DiscBuilderBase& m_parent; - PartitionKind m_kind; - uint64_t m_dolOffset = 0; - uint64_t m_dolSize = 0; - bool m_isWii; - public: - PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii) - : m_parent(parent), m_kind(kind), m_isWii(isWii) - {} - virtual std::unique_ptr beginWriteStream(uint64_t offset)=0; - bool buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn); - static uint64_t CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, bool isWii); - bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, SystemStringView dirIn); - static uint64_t CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn); - }; -protected: - SystemString m_outPath; - std::unique_ptr m_fileIO; - std::vector> m_partitions; - int64_t m_discCapacity; -public: - FProgress m_progressCB; - size_t m_progressIdx = 0; - size_t m_progressTotal = 0; - float getProgressFactor() const - { - return m_progressTotal ? std::min(1.f, m_progressIdx / float(m_progressTotal)) : 0.f; - } - float getProgressFactorMidFile(size_t curByte, size_t totalBytes) const - { - if (!m_progressTotal) - return 0.f; - - if (totalBytes) - return (m_progressIdx + (curByte / float(totalBytes))) / float(m_progressTotal); - else - return m_progressIdx / float(m_progressTotal); - } - - virtual ~DiscBuilderBase() = default; - DiscBuilderBase(SystemStringView 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; } -}; - -} - +} // namespace nod diff --git a/include/nod/DiscGCN.hpp b/include/nod/DiscGCN.hpp index 394e5f7..880afa6 100644 --- a/include/nod/DiscGCN.hpp +++ b/include/nod/DiscGCN.hpp @@ -2,38 +2,35 @@ #include "DiscBase.hpp" -namespace nod -{ +namespace nod { class DiscBuilderGCN; -class DiscGCN : public DiscBase -{ - friend class DiscMergerGCN; - DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB); +class DiscGCN : public DiscBase { + friend class DiscMergerGCN; + DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB); + public: - DiscGCN(std::unique_ptr&& dio, bool& err); - bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const; + DiscGCN(std::unique_ptr&& dio, bool& err); + bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const; }; -class DiscBuilderGCN : public DiscBuilderBase -{ - friend class DiscMergerGCN; +class DiscBuilderGCN : public DiscBuilderBase { + friend class DiscMergerGCN; + public: - DiscBuilderGCN(SystemStringView outPath, FProgress progressCB); - EBuildResult buildFromDirectory(SystemStringView dirIn); - static uint64_t CalculateTotalSizeRequired(SystemStringView dirIn); + DiscBuilderGCN(SystemStringView outPath, FProgress progressCB); + EBuildResult buildFromDirectory(SystemStringView dirIn); + static uint64_t CalculateTotalSizeRequired(SystemStringView dirIn); }; -class DiscMergerGCN -{ - DiscGCN& m_sourceDisc; - DiscBuilderGCN m_builder; +class DiscMergerGCN { + DiscGCN& m_sourceDisc; + DiscBuilderGCN m_builder; + public: - DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB); - EBuildResult mergeFromDirectory(SystemStringView dirIn); - static uint64_t CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn); + DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB); + EBuildResult mergeFromDirectory(SystemStringView dirIn); + static uint64_t CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn); }; -} - - +} // namespace nod diff --git a/include/nod/DiscWii.hpp b/include/nod/DiscWii.hpp index ff4caf4..783012d 100644 --- a/include/nod/DiscWii.hpp +++ b/include/nod/DiscWii.hpp @@ -2,37 +2,31 @@ #include "DiscBase.hpp" -namespace nod -{ +namespace nod { class DiscBuilderWii; -class DiscWii : public DiscBase -{ +class DiscWii : public DiscBase { public: - DiscWii(std::unique_ptr&& dio, bool& err); - DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB); - bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const; + DiscWii(std::unique_ptr&& dio, bool& err); + DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB); + bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const; }; -class DiscBuilderWii : public DiscBuilderBase -{ +class DiscBuilderWii : public DiscBuilderBase { public: - DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB); - EBuildResult buildFromDirectory(SystemStringView dirIn); - static uint64_t CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer); + DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB); + EBuildResult buildFromDirectory(SystemStringView dirIn); + static uint64_t CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer); }; -class DiscMergerWii -{ - DiscWii& m_sourceDisc; - DiscBuilderWii m_builder; +class DiscMergerWii { + DiscWii& m_sourceDisc; + DiscBuilderWii m_builder; + public: - DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, - bool dualLayer, FProgress progressCB); - EBuildResult mergeFromDirectory(SystemStringView dirIn); - static uint64_t CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, - bool& dualLayer); + DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB); + EBuildResult mergeFromDirectory(SystemStringView dirIn); + static uint64_t CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer); }; -} - +} // namespace nod diff --git a/include/nod/IDiscIO.hpp b/include/nod/IDiscIO.hpp index 35f172f..64f3ec0 100644 --- a/include/nod/IDiscIO.hpp +++ b/include/nod/IDiscIO.hpp @@ -10,64 +10,59 @@ #include #endif -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 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; +struct IWriteStream { + virtual ~IWriteStream() = default; + virtual uint64_t write(const void* buf, uint64_t length) = 0; }; -class IDiscIO -{ +class IDiscIO { public: - virtual ~IDiscIO() = default; - virtual std::unique_ptr beginReadStream(uint64_t offset=0) const=0; - virtual std::unique_ptr beginWriteStream(uint64_t offset=0) const=0; + virtual ~IDiscIO() = default; + virtual std::unique_ptr beginReadStream(uint64_t offset = 0) const = 0; + virtual std::unique_ptr beginWriteStream(uint64_t offset = 0) const = 0; }; -struct IPartReadStream : IReadStream -{ - virtual ~IPartReadStream() = default; +struct IPartReadStream : IReadStream { + virtual ~IPartReadStream() = default; }; -struct IPartWriteStream : IWriteStream -{ - virtual ~IPartWriteStream() = default; - virtual void close()=0; - virtual uint64_t position() const=0; +struct IPartWriteStream : IWriteStream { + virtual ~IPartWriteStream() = default; + virtual void close() = 0; + virtual uint64_t position() const = 0; }; #if NOD_ATHENA -class AthenaPartReadStream : public athena::io::IStreamReader -{ - std::unique_ptr m_rs; -public: - AthenaPartReadStream(std::unique_ptr&& rs) : m_rs(std::move(rs)) {} +class AthenaPartReadStream : public athena::io::IStreamReader { + std::unique_ptr m_rs; - inline void seek(atInt64 off, athena::SeekOrigin origin) - { - if (origin == athena::Begin) - m_rs->seek(off, SEEK_SET); - else if (origin == athena::Current) - m_rs->seek(off, SEEK_CUR); - } - inline atUint64 position() const {return m_rs->position();} - inline atUint64 length() const {return 0;} - inline atUint64 readUBytesToBuf(void* buf, atUint64 sz) {m_rs->read(buf, sz); return sz;} +public: + AthenaPartReadStream(std::unique_ptr&& rs) : m_rs(std::move(rs)) {} + + inline void seek(atInt64 off, athena::SeekOrigin origin) { + if (origin == athena::Begin) + m_rs->seek(off, SEEK_SET); + else if (origin == athena::Current) + m_rs->seek(off, SEEK_CUR); + } + inline atUint64 position() const { return m_rs->position(); } + inline atUint64 length() const { return 0; } + inline atUint64 readUBytesToBuf(void* buf, atUint64 sz) { + m_rs->read(buf, sz); + return sz; + } }; #endif -} - +} // namespace nod diff --git a/include/nod/IFileIO.hpp b/include/nod/IFileIO.hpp index 5e2e219..3a9b4ce 100644 --- a/include/nod/IFileIO.hpp +++ b/include/nod/IFileIO.hpp @@ -6,79 +6,66 @@ #include "IDiscIO.hpp" #include "Util.hpp" -namespace nod -{ +namespace nod { -class IFileIO -{ +class IFileIO { public: - virtual ~IFileIO() = default; - virtual bool exists()=0; - virtual uint64_t size()=0; + virtual ~IFileIO() = default; + virtual bool exists() = 0; + virtual uint64_t size() = 0; - struct IWriteStream : nod::IWriteStream - { - uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length) - { - uint64_t read = 0; - uint8_t buf[0x7c00]; - while (length) - { - uint64_t thisSz = nod::min(uint64_t(0x7c00), length); - uint64_t readSz = discio.read(buf, thisSz); - if (thisSz != readSz) - { - LogModule.report(logvisor::Error, "unable to read enough from disc"); - return read; - } - if (write(buf, readSz) != readSz) - { - LogModule.report(logvisor::Error, "unable to write in file"); - return read; - } - length -= thisSz; - read += thisSz; - } - return read; + struct IWriteStream : nod::IWriteStream { + uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length) { + uint64_t read = 0; + uint8_t buf[0x7c00]; + while (length) { + uint64_t thisSz = nod::min(uint64_t(0x7c00), length); + uint64_t readSz = discio.read(buf, thisSz); + if (thisSz != readSz) { + LogModule.report(logvisor::Error, "unable to read enough from disc"); + return read; } - uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length, const std::function& prog) - { - uint64_t read = 0; - uint8_t buf[0x7c00]; - uint64_t total = length; - while (length) - { - uint64_t thisSz = nod::min(uint64_t(0x7c00), length); - uint64_t readSz = discio.read(buf, thisSz); - if (thisSz != readSz) - { - LogModule.report(logvisor::Error, "unable to read enough from disc"); - return read; - } - if (write(buf, readSz) != readSz) - { - LogModule.report(logvisor::Error, "unable to write in file"); - return read; - } - length -= thisSz; - read += thisSz; - prog(read / float(total)); - } - return read; + if (write(buf, readSz) != readSz) { + LogModule.report(logvisor::Error, "unable to write in file"); + return read; } - }; - virtual std::unique_ptr beginWriteStream() const=0; - virtual std::unique_ptr beginWriteStream(uint64_t offset) const=0; + length -= thisSz; + read += thisSz; + } + return read; + } + uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length, const std::function& prog) { + uint64_t read = 0; + uint8_t buf[0x7c00]; + uint64_t total = length; + while (length) { + uint64_t thisSz = nod::min(uint64_t(0x7c00), length); + uint64_t readSz = discio.read(buf, thisSz); + if (thisSz != readSz) { + LogModule.report(logvisor::Error, "unable to read enough from disc"); + return read; + } + if (write(buf, readSz) != readSz) { + LogModule.report(logvisor::Error, "unable to write in file"); + return read; + } + length -= thisSz; + read += thisSz; + prog(read / float(total)); + } + return read; + } + }; + virtual std::unique_ptr beginWriteStream() const = 0; + virtual std::unique_ptr beginWriteStream(uint64_t offset) const = 0; - struct IReadStream : nod::IReadStream - { - virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length)=0; - }; - virtual std::unique_ptr beginReadStream() const=0; - virtual std::unique_ptr beginReadStream(uint64_t offset) const=0; + struct IReadStream : nod::IReadStream { + virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length) = 0; + }; + virtual std::unique_ptr beginReadStream() const = 0; + virtual std::unique_ptr beginReadStream(uint64_t offset) const = 0; }; -std::unique_ptr NewFileIO(SystemStringView path, int64_t maxWriteSize=-1); - -} +std::unique_ptr NewFileIO(SystemStringView path, int64_t maxWriteSize = -1); +} // namespace nod diff --git a/include/nod/Util.hpp b/include/nod/Util.hpp index 89de01e..1477c5b 100644 --- a/include/nod/Util.hpp +++ b/include/nod/Util.hpp @@ -37,11 +37,11 @@ #include #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) #endif #if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) #endif #if !defined(S_ISLNK) @@ -52,13 +52,16 @@ #undef min #undef max -namespace nod -{ +namespace nod { /* define our own min/max to avoid MSVC BS */ -template -inline T min(T a, T b) { return a < b ? a : b; } -template -inline T max(T a, T b) { return a > b ? a : b; } +template +inline T min(T a, T b) { + return a < b ? a : b; +} +template +inline T max(T a, T b) { + return a > b ? a : b; +} /* Log Module */ extern logvisor::Module LogModule; @@ -67,12 +70,12 @@ extern logvisor::Module LogModule; #if _WIN32 && UNICODE #define NOD_UCS2 1 typedef struct _stat Sstat; -static inline int Mkdir(const wchar_t* path, int) {return _wmkdir(path);} -static inline int Stat(const wchar_t* path, Sstat* statout) {return _wstat(path, statout);} +static inline int Mkdir(const wchar_t* path, int) { return _wmkdir(path); } +static inline int Stat(const wchar_t* path, Sstat* statout) { return _wstat(path, statout); } #else typedef struct stat Sstat; -static inline int Mkdir(const char* path, mode_t mode) {return mkdir(path, mode);} -static inline int Stat(const char* path, Sstat* statout) {return stat(path, statout);} +static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode); } +static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); } #endif /* String-converting views */ @@ -80,96 +83,85 @@ static inline int Stat(const char* path, Sstat* statout) {return stat(path, stat typedef wchar_t SystemChar; typedef std::wstring SystemString; typedef std::wstring_view SystemStringView; -static inline void ToLower(SystemString& str) -{std::transform(str.begin(), str.end(), str.begin(), towlower);} -static inline void ToUpper(SystemString& str) -{std::transform(str.begin(), str.end(), str.begin(), towupper);} -static inline size_t StrLen(const SystemChar* str) {return wcslen(str);} -class SystemUTF8Conv -{ - std::string m_utf8; +static inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towlower); } +static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towupper); } +static inline size_t StrLen(const SystemChar* str) { return wcslen(str); } +class SystemUTF8Conv { + std::string m_utf8; + public: - explicit SystemUTF8Conv(SystemStringView str) - { - int len = WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), nullptr, 0, nullptr, nullptr); - m_utf8.assign(len, '\0'); - WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &m_utf8[0], len, nullptr, nullptr); - } - inline std::string_view utf8_str() const {return m_utf8;} - inline const char* c_str() const {return m_utf8.c_str();} + explicit SystemUTF8Conv(SystemStringView str) { + int len = WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), nullptr, 0, nullptr, nullptr); + m_utf8.assign(len, '\0'); + WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &m_utf8[0], len, nullptr, nullptr); + } + inline std::string_view utf8_str() const { return m_utf8; } + inline const char* c_str() const { return m_utf8.c_str(); } }; -class SystemStringConv -{ - std::wstring m_sys; +class SystemStringConv { + std::wstring m_sys; + public: - explicit SystemStringConv(std::string_view str) - { - int len = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0); - m_sys.assign(len, L'\0'); - MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &m_sys[0], len); - } - inline SystemStringView sys_str() const {return m_sys;} - inline const SystemChar* c_str() const {return m_sys.c_str();} + explicit SystemStringConv(std::string_view str) { + int len = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0); + m_sys.assign(len, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &m_sys[0], len); + } + inline SystemStringView sys_str() const { return m_sys; } + inline const SystemChar* c_str() const { return m_sys.c_str(); } }; #ifndef _SYS_STR -#define _SYS_STR(val) L ## val +#define _SYS_STR(val) L##val #endif #else typedef char SystemChar; typedef std::string SystemString; typedef std::string_view SystemStringView; -static inline void ToLower(SystemString& str) -{std::transform(str.begin(), str.end(), str.begin(), tolower);} -static inline void ToUpper(SystemString& str) -{std::transform(str.begin(), str.end(), str.begin(), toupper);} -static inline size_t StrLen(const SystemChar* str) {return strlen(str);} -class SystemUTF8Conv -{ - std::string_view m_utf8; +static inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), tolower); } +static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), toupper); } +static inline size_t StrLen(const SystemChar* str) { return strlen(str); } +class SystemUTF8Conv { + std::string_view m_utf8; + public: - explicit SystemUTF8Conv(SystemStringView str) - : m_utf8(str) {} - inline std::string_view utf8_str() const {return m_utf8;} - inline const char* c_str() const {return m_utf8.data();} + explicit SystemUTF8Conv(SystemStringView str) : m_utf8(str) {} + inline std::string_view utf8_str() const { return m_utf8; } + inline const char* c_str() const { return m_utf8.data(); } }; -class SystemStringConv -{ - SystemStringView m_sys; +class SystemStringConv { + SystemStringView m_sys; + public: - explicit SystemStringConv(std::string_view str) - : m_sys(str) {} - inline SystemStringView sys_str() const {return m_sys;} - inline const SystemChar* c_str() const {return m_sys.data();} + explicit SystemStringConv(std::string_view str) : m_sys(str) {} + inline SystemStringView sys_str() const { return m_sys; } + inline const SystemChar* c_str() const { return m_sys.data(); } }; #ifndef _SYS_STR #define _SYS_STR(val) val #endif #endif -static inline void Unlink(const SystemChar* file) -{ +static inline void Unlink(const SystemChar* file) { #if NOD_UCS2 - _wunlink(file); + _wunlink(file); #else - unlink(file); + unlink(file); #endif } -static inline int StrCmp(const SystemChar* str1, const SystemChar* str2) -{ +static inline int StrCmp(const SystemChar* str1, const SystemChar* str2) { #if NOD_UCS2 - return wcscmp(str1, str2); + return wcscmp(str1, str2); #else - return strcmp(str1, str2); + return strcmp(str1, str2); #endif } -static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2) -{ +static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2) { #if NOD_UCS2 - return _wcsicmp(str1, str2); + return _wcsicmp(str1, str2); #else - return strcasecmp(str1, str2); + return strcasecmp(str1, str2); #endif } @@ -178,79 +170,71 @@ static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2) #undef bswap64 /* Type-sensitive byte swappers */ template -static inline T bswap16(T val) -{ +static inline T bswap16(T val) { #if __GNUC__ - return __builtin_bswap16(val); + return __builtin_bswap16(val); #elif _WIN32 - return _byteswap_ushort(val); + return _byteswap_ushort(val); #else - return (val = (val << 8) | ((val >> 8) & 0xFF)); + return (val = (val << 8) | ((val >> 8) & 0xFF)); #endif } template -static inline T bswap32(T val) -{ +static inline T bswap32(T val) { #if __GNUC__ - return __builtin_bswap32(val); + return __builtin_bswap32(val); #elif _WIN32 - return _byteswap_ulong(val); + return _byteswap_ulong(val); #else - val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; - val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; - return val; + val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; + val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; + return val; #endif } template -static inline T bswap64(T val) -{ +static inline T bswap64(T val) { #if __GNUC__ - return __builtin_bswap64(val); + return __builtin_bswap64(val); #elif _WIN32 - return _byteswap_uint64(val); + return _byteswap_uint64(val); #else - return ((val & 0xFF00000000000000ULL) >> 56) | - ((val & 0x00FF000000000000ULL) >> 40) | - ((val & 0x0000FF0000000000ULL) >> 24) | - ((val & 0x000000FF00000000ULL) >> 8) | - ((val & 0x00000000FF000000ULL) << 8) | - ((val & 0x0000000000FF0000ULL) << 24) | - ((val & 0x000000000000FF00ULL) << 40) | - ((val & 0x00000000000000FFULL) << 56); + return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) | + ((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) | + ((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) | + ((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56); #endif } - #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -static inline int16_t SBig(int16_t val) {return bswap16(val);} -static inline uint16_t SBig(uint16_t val) {return bswap16(val);} -static inline int32_t SBig(int32_t val) {return bswap32(val);} -static inline uint32_t SBig(uint32_t val) {return bswap32(val);} -static inline int64_t SBig(int64_t val) {return bswap64(val);} -static inline uint64_t SBig(uint64_t val) {return bswap64(val);} +static inline int16_t SBig(int16_t val) { return bswap16(val); } +static inline uint16_t SBig(uint16_t val) { return bswap16(val); } +static inline int32_t SBig(int32_t val) { return bswap32(val); } +static inline uint32_t SBig(uint32_t val) { return bswap32(val); } +static inline int64_t SBig(int64_t val) { return bswap64(val); } +static inline uint64_t SBig(uint64_t val) { return bswap64(val); } -static inline int16_t SLittle(int16_t val) {return val;} -static inline uint16_t SLittle(uint16_t val) {return val;} -static inline int32_t SLittle(int32_t val) {return val;} -static inline uint32_t SLittle(uint32_t val) {return val;} -static inline int64_t SLittle(int64_t val) {return val;} -static inline uint64_t SLittle(uint64_t val) {return val;} +static inline int16_t SLittle(int16_t val) { return val; } +static inline uint16_t SLittle(uint16_t val) { return val; } +static inline int32_t SLittle(int32_t val) { return val; } +static inline uint32_t SLittle(uint32_t val) { return val; } +static inline int64_t SLittle(int64_t val) { return val; } +static inline uint64_t SLittle(uint64_t val) { return val; } #else -static inline int16_t SLittle(int16_t val) {return bswap16(val);} -static inline uint16_t SLittle(uint16_t val) {return bswap16(val);} -static inline int32_t SLittle(int32_t val) {return bswap32(val);} -static inline uint32_t SLittle(uint32_t val) {return bswap32(val);} -static inline int64_t SLittle(int64_t val) {return bswap64(val);} -static inline uint64_t SLittle(uint64_t val) {return bswap64(val);} +static inline int16_t SLittle(int16_t val) { return bswap16(val); } +static inline uint16_t SLittle(uint16_t val) { return bswap16(val); } +static inline int32_t SLittle(int32_t val) { return bswap32(val); } +static inline uint32_t SLittle(uint32_t val) { return bswap32(val); } +static inline int64_t SLittle(int64_t val) { return bswap64(val); } +static inline uint64_t SLittle(uint64_t val) { return bswap64(val); } -static inline int16_t SBig(int16_t val) {return val;} -static inline uint16_t SBig(uint16_t val) {return val;} -static inline int32_t SBig(int32_t val) {return val;} -static inline uint32_t SBig(uint32_t val) {return val;} -static inline int64_t SBig(int64_t val) {return val;} -static inline uint64_t SBig(uint64_t val) {return val;} +static inline int16_t SBig(int16_t val) { return val; } +static inline uint16_t SBig(uint16_t val) { return val; } +static inline int32_t SBig(int32_t val) { return val; } +static inline uint32_t SBig(uint32_t val) { return val; } +static inline int64_t SBig(int64_t val) { return val; } +static inline uint64_t SBig(uint64_t val) { return val; } #endif #ifndef ROUND_UP_32 @@ -258,105 +242,92 @@ static inline uint64_t SBig(uint64_t val) {return val;} #define ROUND_UP_16(val) (((val) + 15) & ~15) #endif -enum class FileLockType -{ - None = 0, - Read, - Write -}; -static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=FileLockType::None) -{ +enum class FileLockType { None = 0, Read, Write }; +static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock = FileLockType::None) { #if NOD_UCS2 - FILE* fp = _wfopen(path, mode); - if (!fp) - return nullptr; + FILE* fp = _wfopen(path, mode); + if (!fp) + return nullptr; #else - FILE* fp = fopen(path, mode); - if (!fp) - return nullptr; + FILE* fp = fopen(path, mode); + if (!fp) + return nullptr; #endif - if (lock != FileLockType::None) - { + if (lock != FileLockType::None) { #if _WIN32 - OVERLAPPED ov = {}; - LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov); + OVERLAPPED ov = {}; + LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, + &ov); #else - if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB)) - LogModule.report(logvisor::Error, "flock %s: %s", path, strerror(errno)); + if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB)) + LogModule.report(logvisor::Error, "flock %s: %s", path, strerror(errno)); #endif - } + } - return fp; + return fp; } -static inline int FSeek(FILE* fp, int64_t offset, int whence) -{ +static inline int FSeek(FILE* fp, int64_t offset, int whence) { #if _WIN32 - return _fseeki64(fp, offset, whence); + return _fseeki64(fp, offset, whence); #elif __APPLE__ || __FreeBSD__ - return fseeko(fp, offset, whence); + return fseeko(fp, offset, whence); #else - return fseeko64(fp, offset, whence); + return fseeko64(fp, offset, whence); #endif } -static inline int64_t FTell(FILE* fp) -{ +static inline int64_t FTell(FILE* fp) { #if _WIN32 - return _ftelli64(fp); + return _ftelli64(fp); #elif __APPLE__ || __FreeBSD__ - return ftello(fp); + return ftello(fp); #else - return ftello64(fp); + return ftello64(fp); #endif } -static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz) -{ +static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz) { #if _WIN32 - ULARGE_INTEGER freeBytes; - wchar_t buf[1024]; - wchar_t* end; - DWORD ret = GetFullPathNameW(path, 1024, buf, &end); - if (!ret || ret > 1024) - { - LogModule.report(logvisor::Error, _SYS_STR("GetFullPathNameW %s"), path); - return false; - } - if (end) - end[0] = L'\0'; - if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr)) - { - LogModule.report(logvisor::Error, _SYS_STR("GetDiskFreeSpaceExW %s: %d"), path, GetLastError()); - return false; - } - return reqSz < freeBytes.QuadPart; + ULARGE_INTEGER freeBytes; + wchar_t buf[1024]; + wchar_t* end; + DWORD ret = GetFullPathNameW(path, 1024, buf, &end); + if (!ret || ret > 1024) { + LogModule.report(logvisor::Error, _SYS_STR("GetFullPathNameW %s"), path); + return false; + } + if (end) + end[0] = L'\0'; + if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr)) { + LogModule.report(logvisor::Error, _SYS_STR("GetDiskFreeSpaceExW %s: %d"), path, GetLastError()); + return false; + } + return reqSz < freeBytes.QuadPart; #else - struct statvfs svfs; - if (statvfs(path, &svfs)) - { - LogModule.report(logvisor::Error, "statvfs %s: %s", path, strerror(errno)); - return false; - } - return reqSz < svfs.f_frsize * svfs.f_bavail; + struct statvfs svfs; + if (statvfs(path, &svfs)) { + LogModule.report(logvisor::Error, "statvfs %s: %s", path, strerror(errno)); + return false; + } + return reqSz < svfs.f_frsize * svfs.f_bavail; #endif } #if __GNUC__ -__attribute__((__format__ (__printf__, 1, 2))) +__attribute__((__format__(__printf__, 1, 2))) #endif -static inline void Printf(const SystemChar* fmt, ...) -{ - va_list args; - va_start(args, fmt); +static inline void +Printf(const SystemChar* fmt, ...) { + va_list args; + va_start(args, fmt); #if NOD_UCS2 - vwprintf(fmt, args); + vwprintf(fmt, args); #else - vprintf(fmt, args); + vprintf(fmt, args); #endif - va_end(args); -} - + va_end(args); } +} // namespace nod diff --git a/include/nod/aes.hpp b/include/nod/aes.hpp index 4db2a30..9bcb666 100644 --- a/include/nod/aes.hpp +++ b/include/nod/aes.hpp @@ -4,19 +4,16 @@ #include #include -namespace nod -{ +namespace nod { -class IAES -{ +class IAES { public: - 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; + 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; }; std::unique_ptr NewAES(); -} - +} // namespace nod diff --git a/include/nod/nod.hpp b/include/nod/nod.hpp index 9152cb2..6042f34 100644 --- a/include/nod/nod.hpp +++ b/include/nod/nod.hpp @@ -5,23 +5,20 @@ #include "logvisor/logvisor.hpp" #include "Util.hpp" -namespace nod -{ +namespace nod { class DiscBase; -struct ExtractionContext final -{ - bool force : 1; - std::function progressCB; +struct ExtractionContext final { + bool force : 1; + std::function progressCB; }; std::unique_ptr OpenDiscFromImage(SystemStringView path); std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii); -} +} // namespace nod #include "DiscGCN.hpp" #include "DiscWii.hpp" #include "IDiscIO.hpp" - diff --git a/lib/DirectoryEnumerator.cpp b/lib/DirectoryEnumerator.cpp index 83978ff..a4a882e 100644 --- a/lib/DirectoryEnumerator.cpp +++ b/lib/DirectoryEnumerator.cpp @@ -9,275 +9,252 @@ #include "nod/DirectoryEnumerator.hpp" -namespace nod -{ +namespace nod { -DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, - bool sizeSort, bool reverse, bool noHidden) -{ - Sstat theStat; - if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode)) - return; +DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, bool sizeSort, bool reverse, bool noHidden) { + Sstat theStat; + if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode)) + return; #if _WIN32 - SystemString wc(path); - wc += _SYS_STR("/*"); - WIN32_FIND_DATAW d; - HANDLE dir = FindFirstFileW(wc.c_str(), &d); - if (dir == INVALID_HANDLE_VALUE) - return; - switch (mode) - { - case Mode::Native: - do - { - if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) - continue; - if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) - continue; - SystemString fp(path); - fp += _SYS_STR('/'); - fp += d.cFileName; - Sstat st; - if (Stat(fp.c_str(), &st)) - continue; + SystemString wc(path); + wc += _SYS_STR("/*"); + WIN32_FIND_DATAW d; + HANDLE dir = FindFirstFileW(wc.c_str(), &d); + if (dir == INVALID_HANDLE_VALUE) + return; + switch (mode) { + case Mode::Native: + do { + if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) + continue; + if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) + continue; + SystemString fp(path); + fp += _SYS_STR('/'); + fp += d.cFileName; + Sstat st; + if (Stat(fp.c_str(), &st)) + continue; - size_t sz = 0; - bool isDir = false; - if (S_ISDIR(st.st_mode)) - isDir = true; - else if (S_ISREG(st.st_mode)) - sz = st.st_size; - else - continue; + size_t sz = 0; + bool isDir = false; + if (S_ISDIR(st.st_mode)) + isDir = true; + else if (S_ISREG(st.st_mode)) + sz = st.st_size; + else + continue; - m_entries.push_back(Entry(fp, d.cFileName, sz, isDir)); - } while (FindNextFileW(dir, &d)); - break; - case Mode::DirsThenFilesSorted: - case Mode::DirsSorted: - { - std::map sort; - do - { - if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) - continue; - if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) - continue; - SystemString fp(path); - fp +=_SYS_STR('/'); - fp += d.cFileName; - Sstat st; - if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) - continue; - sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, 0, true))); - } while (FindNextFileW(dir, &d)); + m_entries.push_back(Entry(fp, d.cFileName, sz, isDir)); + } while (FindNextFileW(dir, &d)); + break; + case Mode::DirsThenFilesSorted: + case Mode::DirsSorted: { + std::map sort; + do { + if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) + continue; + if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) + continue; + SystemString fp(path); + fp += _SYS_STR('/'); + fp += d.cFileName; + Sstat st; + if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) + continue; + sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, 0, true))); + } while (FindNextFileW(dir, &d)); - m_entries.reserve(sort.size()); - if (reverse) - for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) - m_entries.push_back(std::move(it->second)); - else - for (auto& e : sort) - m_entries.push_back(std::move(e.second)); + m_entries.reserve(sort.size()); + if (reverse) + for (auto it = sort.crbegin(); it != sort.crend(); ++it) + m_entries.push_back(std::move(it->second)); + else + for (auto& e : sort) + m_entries.push_back(std::move(e.second)); - if (mode == Mode::DirsSorted) - break; - FindClose(dir); - dir = FindFirstFileW(wc.c_str(), &d); - } - case Mode::FilesSorted: - { - if (mode == Mode::FilesSorted) - m_entries.clear(); - - if (sizeSort) - { - std::multimap sort; - do - { - if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) - continue; - if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) - continue; - SystemString fp(path); - fp += _SYS_STR('/'); - fp += d.cFileName; - Sstat st; - if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) - continue; - sort.emplace(std::make_pair(st.st_size, Entry(fp, d.cFileName, st.st_size, false))); - } while (FindNextFileW(dir, &d)); - - m_entries.reserve(sort.size()); - if (reverse) - for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) - m_entries.push_back(std::move(it->second)); - else - for (auto& e : sort) - m_entries.push_back(std::move(e.second)); - } - else - { - std::map sort; - do - { - if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) - continue; - if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) - continue; - SystemString fp(path); - fp += _SYS_STR('/'); - fp += d.cFileName; - Sstat st; - if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) - continue; - sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, st.st_size, false))); - } while (FindNextFileW(dir, &d)); - - m_entries.reserve(sort.size()); - if (reverse) - for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) - m_entries.push_back(std::move(it->second)); - else - for (auto& e : sort) - m_entries.push_back(std::move(e.second)); - } - - break; - } - } + if (mode == Mode::DirsSorted) + break; FindClose(dir); + dir = FindFirstFileW(wc.c_str(), &d); + } + case Mode::FilesSorted: { + if (mode == Mode::FilesSorted) + m_entries.clear(); + + if (sizeSort) { + std::multimap sort; + do { + if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) + continue; + if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) + continue; + SystemString fp(path); + fp += _SYS_STR('/'); + fp += d.cFileName; + Sstat st; + if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) + continue; + sort.emplace(std::make_pair(st.st_size, Entry(fp, d.cFileName, st.st_size, false))); + } while (FindNextFileW(dir, &d)); + + m_entries.reserve(sort.size()); + if (reverse) + for (auto it = sort.crbegin(); it != sort.crend(); ++it) + m_entries.push_back(std::move(it->second)); + else + for (auto& e : sort) + m_entries.push_back(std::move(e.second)); + } else { + std::map sort; + do { + if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR(".."))) + continue; + if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) + continue; + SystemString fp(path); + fp += _SYS_STR('/'); + fp += d.cFileName; + Sstat st; + if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) + continue; + sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, st.st_size, false))); + } while (FindNextFileW(dir, &d)); + + m_entries.reserve(sort.size()); + if (reverse) + for (auto it = sort.crbegin(); it != sort.crend(); ++it) + m_entries.push_back(std::move(it->second)); + else + for (auto& e : sort) + m_entries.push_back(std::move(e.second)); + } + + break; + } + } + FindClose(dir); #else - DIR* dir = opendir(path.data()); - if (!dir) - return; - const dirent* d; - switch (mode) - { - case Mode::Native: - while ((d = readdir(dir))) - { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - if (noHidden && d->d_name[0] == '.') - continue; - SystemString fp(path); - fp += '/'; - fp += d->d_name; - Sstat st; - if (Stat(fp.c_str(), &st)) - continue; + DIR* dir = opendir(path.data()); + if (!dir) + return; + const dirent* d; + switch (mode) { + case Mode::Native: + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if (noHidden && d->d_name[0] == '.') + continue; + SystemString fp(path); + fp += '/'; + fp += d->d_name; + Sstat st; + if (Stat(fp.c_str(), &st)) + continue; - size_t sz = 0; - bool isDir = false; - if (S_ISDIR(st.st_mode)) - isDir = true; - else if (S_ISREG(st.st_mode)) - sz = st.st_size; - else - continue; + size_t sz = 0; + bool isDir = false; + if (S_ISDIR(st.st_mode)) + isDir = true; + else if (S_ISREG(st.st_mode)) + sz = st.st_size; + else + continue; - m_entries.push_back(Entry(fp, d->d_name, sz, isDir)); - } - break; - case Mode::DirsThenFilesSorted: - case Mode::DirsSorted: - { - std::map sort; - while ((d = readdir(dir))) - { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - if (noHidden && d->d_name[0] == '.') - continue; - SystemString fp(path); - fp += '/'; - fp += d->d_name; - Sstat st; - if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) - continue; - sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, 0, true))); - } - - m_entries.reserve(sort.size()); - if (reverse) - for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) - m_entries.push_back(std::move(it->second)); - else - for (auto& e : sort) - m_entries.push_back(std::move(e.second)); - - if (mode == Mode::DirsSorted) - break; - rewinddir(dir); + m_entries.push_back(Entry(fp, d->d_name, sz, isDir)); } - case Mode::FilesSorted: - { - if (mode == Mode::FilesSorted) - m_entries.clear(); - - if (sizeSort) - { - std::multimap sort; - while ((d = readdir(dir))) - { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - if (noHidden && d->d_name[0] == '.') - continue; - SystemString fp(path); - fp += '/'; - fp += d->d_name; - Sstat st; - if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) - continue; - sort.emplace(std::make_pair(st.st_size, Entry(fp, d->d_name, st.st_size, false))); - } - - m_entries.reserve(sort.size()); - if (reverse) - for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) - m_entries.push_back(std::move(it->second)); - else - for (auto& e : sort) - m_entries.push_back(std::move(e.second)); - } - else - { - std::map sort; - while ((d = readdir(dir))) - { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - if (noHidden && d->d_name[0] == '.') - continue; - SystemString fp(path); - fp += '/'; - fp += d->d_name; - Sstat st; - if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) - continue; - sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, st.st_size, false))); - } - - m_entries.reserve(sort.size()); - if (reverse) - for (auto it=sort.crbegin() ; it != sort.crend() ; ++it) - m_entries.push_back(std::move(it->second)); - else - for (auto& e : sort) - m_entries.push_back(std::move(e.second)); - } - - break; + break; + case Mode::DirsThenFilesSorted: + case Mode::DirsSorted: { + std::map sort; + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if (noHidden && d->d_name[0] == '.') + continue; + SystemString fp(path); + fp += '/'; + fp += d->d_name; + Sstat st; + if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode)) + continue; + sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, 0, true))); } + + m_entries.reserve(sort.size()); + if (reverse) + for (auto it = sort.crbegin(); it != sort.crend(); ++it) + m_entries.push_back(std::move(it->second)); + else + for (auto& e : sort) + m_entries.push_back(std::move(e.second)); + + if (mode == Mode::DirsSorted) + break; + rewinddir(dir); + } + case Mode::FilesSorted: { + if (mode == Mode::FilesSorted) + m_entries.clear(); + + if (sizeSort) { + std::multimap sort; + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if (noHidden && d->d_name[0] == '.') + continue; + SystemString fp(path); + fp += '/'; + fp += d->d_name; + Sstat st; + if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) + continue; + sort.emplace(std::make_pair(st.st_size, Entry(fp, d->d_name, st.st_size, false))); + } + + m_entries.reserve(sort.size()); + if (reverse) + for (auto it = sort.crbegin(); it != sort.crend(); ++it) + m_entries.push_back(std::move(it->second)); + else + for (auto& e : sort) + m_entries.push_back(std::move(e.second)); + } else { + std::map sort; + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if (noHidden && d->d_name[0] == '.') + continue; + SystemString fp(path); + fp += '/'; + fp += d->d_name; + Sstat st; + if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode)) + continue; + sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, st.st_size, false))); + } + + m_entries.reserve(sort.size()); + if (reverse) + for (auto it = sort.crbegin(); it != sort.crend(); ++it) + m_entries.push_back(std::move(it->second)); + else + for (auto& e : sort) + m_entries.push_back(std::move(e.second)); } - closedir(dir); + + break; + } + } + closedir(dir); #endif } -} +} // namespace nod diff --git a/lib/DiscBase.cpp b/lib/DiscBase.cpp index 15b3092..bdbae75 100644 --- a/lib/DiscBase.cpp +++ b/lib/DiscBase.cpp @@ -9,957 +9,841 @@ #ifndef _WIN32 #include #else -static void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen) -{ - int needle_first; - const uint8_t *p = static_cast(haystack); - size_t plen = hlen; - - if (!nlen) - return NULL; - - needle_first = *(unsigned char *)needle; - - while (plen >= nlen && (p = static_cast(memchr(p, needle_first, plen - nlen + 1)))) - { - if (!memcmp(p, needle, nlen)) - return (void *)p; - - p++; - plen = hlen - (p - static_cast(haystack)); - } +static void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen) { + int needle_first; + const uint8_t* p = static_cast(haystack); + size_t plen = hlen; + if (!nlen) return NULL; + + needle_first = *(unsigned char*)needle; + + while (plen >= nlen && (p = static_cast(memchr(p, needle_first, plen - nlen + 1)))) { + if (!memcmp(p, needle, nlen)) + return (void*)p; + + p++; + plen = hlen - (p - static_cast(haystack)); + } + + return NULL; } #endif #include -namespace nod -{ +namespace nod { -const SystemChar* getKindString(PartitionKind kind) -{ - switch (kind) - { - case PartitionKind::Data: - return _SYS_STR("DATA"); - case PartitionKind::Update: - return _SYS_STR("UPDATE"); - case PartitionKind::Channel: - return _SYS_STR("CHANNEL"); - default: - return nullptr; - } +const SystemChar* getKindString(PartitionKind kind) { + switch (kind) { + case PartitionKind::Data: + return _SYS_STR("DATA"); + case PartitionKind::Update: + return _SYS_STR("UPDATE"); + case PartitionKind::Channel: + return _SYS_STR("CHANNEL"); + default: + return nullptr; + } } -void IPartition::parseFST(IPartReadStream& s) -{ - std::unique_ptr fst(new uint8_t[m_fstSz]); - s.seek(m_fstOff); - s.read(fst.get(), m_fstSz); +void IPartition::parseFST(IPartReadStream& s) { + std::unique_ptr fst(new uint8_t[m_fstSz]); + s.seek(m_fstOff); + s.read(fst.get(), m_fstSz); - const FSTNode* nodes = (FSTNode*)fst.get(); + const FSTNode* nodes = (FSTNode*)fst.get(); - /* Root node indicates the count of all contained nodes */ - uint32_t nodeCount = nodes[0].getLength(); - const char* names = (char*)fst.get() + 12 * nodeCount; - m_nodes.clear(); - m_nodes.reserve(nodeCount); + /* Root node indicates the count of all contained nodes */ + uint32_t nodeCount = nodes[0].getLength(); + const char* names = (char*)fst.get() + 12 * nodeCount; + m_nodes.clear(); + m_nodes.reserve(nodeCount); - /* Construct nodes */ - for (uint32_t n=0 ; n::iterator it=m_nodes.begin(); - it != m_nodes.end(); - ++it) - { - Node& node = *it; - if (node.m_kind == Node::Kind::Directory) - { - node.m_childrenBegin = it + 1; - node.m_childrenEnd = m_nodes.begin() + node.m_discLength; - } + /* Construct nodes */ + for (uint32_t n = 0; n < nodeCount; ++n) { + const FSTNode& node = nodes[n]; + m_nodes.emplace_back(*this, node, n ? names + node.getNameOffset() : ""); + } + + /* Setup dir-child iterators */ + for (std::vector::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it) { + Node& node = *it; + if (node.m_kind == Node::Kind::Directory) { + node.m_childrenBegin = it + 1; + node.m_childrenEnd = m_nodes.begin() + node.m_discLength; } + } } -void IPartition::parseDOL(IPartReadStream& s) -{ - /* Read Dol header */ - DOLHeader dolHeader; - s.read(&dolHeader, sizeof(DOLHeader)); +void IPartition::parseDOL(IPartReadStream& s) { + /* Read Dol header */ + DOLHeader dolHeader; + s.read(&dolHeader, sizeof(DOLHeader)); - /* Calculate Dol size */ - uint32_t dolSize = SBig(dolHeader.textOff[0]); - for (uint32_t i = 0 ; i < 7 ; i++) - dolSize += SBig(dolHeader.textSizes[i]); - for (uint32_t i = 0 ; i < 11 ; i++) - dolSize += SBig(dolHeader.dataSizes[i]); + /* Calculate Dol size */ + uint32_t dolSize = SBig(dolHeader.textOff[0]); + for (uint32_t i = 0; i < 7; i++) + dolSize += SBig(dolHeader.textSizes[i]); + for (uint32_t i = 0; i < 11; i++) + dolSize += SBig(dolHeader.dataSizes[i]); - m_dolSz = dolSize; + m_dolSz = dolSize; } Node::Node(const IPartition& parent, const FSTNode& node, std::string_view name) -: m_parent(parent), - m_kind(node.isDir() ? Kind::Directory : Kind::File), - m_discOffset(parent.normalizeOffset(node.getOffset())), - m_discLength(node.getLength()), - m_name(name) {} +: m_parent(parent) +, m_kind(node.isDir() ? Kind::Directory : Kind::File) +, m_discOffset(parent.normalizeOffset(node.getOffset())) +, m_discLength(node.getLength()) +, m_name(name) {} -std::unique_ptr Node::beginReadStream(uint64_t offset) const -{ - if (m_kind != Kind::File) - { - LogModule.report(logvisor::Error, "unable to stream a non-file %s", m_name.c_str()); - return std::unique_ptr(); - } - return m_parent.beginReadStream(m_discOffset + offset); +std::unique_ptr Node::beginReadStream(uint64_t offset) const { + if (m_kind != Kind::File) { + LogModule.report(logvisor::Error, "unable to stream a non-file %s", m_name.c_str()); + return std::unique_ptr(); + } + return m_parent.beginReadStream(m_discOffset + offset); } -std::unique_ptr Node::getBuf() const -{ - if (m_kind != Kind::File) - { - LogModule.report(logvisor::Error, "unable to buffer a non-file %s", m_name.c_str()); - return std::unique_ptr(); - } - uint8_t* buf = new uint8_t[m_discLength]; - beginReadStream()->read(buf, m_discLength); - return std::unique_ptr(buf); +std::unique_ptr Node::getBuf() const { + if (m_kind != Kind::File) { + LogModule.report(logvisor::Error, "unable to buffer a non-file %s", m_name.c_str()); + return std::unique_ptr(); + } + uint8_t* buf = new uint8_t[m_discLength]; + beginReadStream()->read(buf, m_discLength); + return std::unique_ptr(buf); } -bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const -{ - SystemStringConv nameView(getName()); - SystemString path = SystemString(basePath) + _SYS_STR('/') + nameView.sys_str().data(); +bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const { + SystemStringConv nameView(getName()); + SystemString path = SystemString(basePath) + _SYS_STR('/') + nameView.sys_str().data(); - if (m_kind == Kind::Directory) - { - ++m_parent.m_curNodeIdx; - if (ctx.progressCB && !getName().empty()) - ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); - if (Mkdir(path.c_str(), 0755) && errno != EEXIST) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), path.c_str()); - return false; - } - for (Node& subnode : *this) - if (!subnode.extractToDirectory(path, ctx)) - return false; + if (m_kind == Kind::Directory) { + ++m_parent.m_curNodeIdx; + if (ctx.progressCB && !getName().empty()) + ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); + if (Mkdir(path.c_str(), 0755) && errno != EEXIST) { + LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), path.c_str()); + return false; } - else if (m_kind == Kind::File) - { - Sstat theStat; - if (ctx.progressCB) - ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); - - if (ctx.force || Stat(path.c_str(), &theStat)) - { - std::unique_ptr rs = beginReadStream(); - std::unique_ptr ws = NewFileIO(path)->beginWriteStream(); - if (!rs || !ws) - return false; - ws->copyFromDisc(*rs, m_discLength, - [&](float prog) - { - if (ctx.progressCB) - ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount())); - }); - } - ++m_parent.m_curNodeIdx; - } - return true; -} - -bool IPartition::extractToDirectory(SystemStringView path, const ExtractionContext& ctx) -{ - m_curNodeIdx = 0; - if (Mkdir(path.data(), 0755) && errno != EEXIST) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), path.data()); + for (Node& subnode : *this) + if (!subnode.extractToDirectory(path, ctx)) return false; - } - - SystemString basePath = m_isWii ? SystemString(path) + _SYS_STR("/") + getKindString(m_kind) : SystemString(path); - if (m_isWii) - { - if (Mkdir(basePath.c_str(), 0755) && errno != EEXIST) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), basePath.c_str()); - return false; - } - } - - /* Extract Disc Files */ - if (!m_parent.extractDiscHeaderFiles(basePath, ctx)) - return false; - - /* Extract Crypto Files */ - if (!extractCryptoFiles(basePath, ctx)) - return false; - - if (!extractSysFiles(basePath, ctx)) - return false; - - /* Extract Filesystem */ - SystemString fsPath = basePath + _SYS_STR("/files"); - if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), fsPath.c_str()); - return false; - } - - return m_nodes[0].extractToDirectory(fsPath, ctx); -} - -bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionContext& ctx) const -{ - SystemString basePathStr(basePath); - if (Mkdir((basePathStr + _SYS_STR("/sys")).c_str(), 0755) && errno != EEXIST) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s/sys'"), basePath.data()); - return false; - } - + } else if (m_kind == Kind::File) { Sstat theStat; - /* Extract Apploader */ - SystemString apploaderPath = basePathStr + _SYS_STR("/sys/apploader.img"); - if (ctx.force || Stat(apploaderPath.c_str(), &theStat)) - { + if (ctx.progressCB) + ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); + + if (ctx.force || Stat(path.c_str(), &theStat)) { + std::unique_ptr rs = beginReadStream(); + std::unique_ptr ws = NewFileIO(path)->beginWriteStream(); + if (!rs || !ws) + return false; + ws->copyFromDisc(*rs, m_discLength, [&](float prog) { if (ctx.progressCB) - ctx.progressCB("apploader.bin", 0.f); - std::unique_ptr buf = getApploaderBuf(); - auto ws = NewFileIO(apploaderPath)->beginWriteStream(); - if (!ws) - return false; - ws->write(buf.get(), m_apploaderSz); + ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount())); + }); } - - /* Extract Dol */ - SystemString dolPath = basePathStr + _SYS_STR("/sys/main.dol"); - if (ctx.force || Stat(dolPath.c_str(), &theStat)) - { - if (ctx.progressCB) - ctx.progressCB("main.dol", 0.f); - std::unique_ptr buf = getDOLBuf(); - auto ws = NewFileIO(dolPath)->beginWriteStream(); - if (!ws) - return false; - ws->write(buf.get(), m_dolSz); - } - - /* Extract Boot info */ - SystemString bootPath = basePathStr + _SYS_STR("/sys/boot.bin"); - if (ctx.force || Stat(bootPath.c_str(), &theStat)) - { - if (ctx.progressCB) - ctx.progressCB("boot.bin", 0.f); - auto ws = NewFileIO(bootPath)->beginWriteStream(); - if (!ws) - return false; - m_header.write(*ws.get()); - } - - /* Extract BI2 info */ - SystemString bi2Path = basePathStr + _SYS_STR("/sys/bi2.bin"); - if (ctx.force || Stat(bi2Path.c_str(), &theStat)) - { - if (ctx.progressCB) - ctx.progressCB("bi2.bin", 0.f); - - auto ws = NewFileIO(bi2Path)->beginWriteStream(); - if (!ws) - return false; - m_bi2Header.write(*ws); - } - - return true; + ++m_parent.m_curNodeIdx; + } + return true; } -static bool IsSystemFile(SystemStringView name, bool& isDol) -{ - isDol = false; - if (name.size() < 4) - return false; - - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".dol"))) - { - isDol = true; - return true; - } - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".rel"))) - return true; - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".rso"))) - return true; - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".sel"))) - return true; - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".bnr"))) - return true; - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".elf"))) - return true; - if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".wad"))) - return true; - +bool IPartition::extractToDirectory(SystemStringView path, const ExtractionContext& ctx) { + m_curNodeIdx = 0; + if (Mkdir(path.data(), 0755) && errno != EEXIST) { + LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), path.data()); return false; + } + + SystemString basePath = m_isWii ? SystemString(path) + _SYS_STR("/") + getKindString(m_kind) : SystemString(path); + if (m_isWii) { + if (Mkdir(basePath.c_str(), 0755) && errno != EEXIST) { + LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), basePath.c_str()); + return false; + } + } + + /* Extract Disc Files */ + if (!m_parent.extractDiscHeaderFiles(basePath, ctx)) + return false; + + /* Extract Crypto Files */ + if (!extractCryptoFiles(basePath, ctx)) + return false; + + if (!extractSysFiles(basePath, ctx)) + return false; + + /* Extract Filesystem */ + SystemString fsPath = basePath + _SYS_STR("/files"); + if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST) { + LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s'"), fsPath.c_str()); + return false; + } + + return m_nodes[0].extractToDirectory(fsPath, ctx); +} + +bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionContext& ctx) const { + SystemString basePathStr(basePath); + if (Mkdir((basePathStr + _SYS_STR("/sys")).c_str(), 0755) && errno != EEXIST) { + LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s/sys'"), basePath.data()); + return false; + } + + Sstat theStat; + /* Extract Apploader */ + SystemString apploaderPath = basePathStr + _SYS_STR("/sys/apploader.img"); + if (ctx.force || Stat(apploaderPath.c_str(), &theStat)) { + if (ctx.progressCB) + ctx.progressCB("apploader.bin", 0.f); + std::unique_ptr buf = getApploaderBuf(); + auto ws = NewFileIO(apploaderPath)->beginWriteStream(); + if (!ws) + return false; + ws->write(buf.get(), m_apploaderSz); + } + + /* Extract Dol */ + SystemString dolPath = basePathStr + _SYS_STR("/sys/main.dol"); + if (ctx.force || Stat(dolPath.c_str(), &theStat)) { + if (ctx.progressCB) + ctx.progressCB("main.dol", 0.f); + std::unique_ptr buf = getDOLBuf(); + auto ws = NewFileIO(dolPath)->beginWriteStream(); + if (!ws) + return false; + ws->write(buf.get(), m_dolSz); + } + + /* Extract Boot info */ + SystemString bootPath = basePathStr + _SYS_STR("/sys/boot.bin"); + if (ctx.force || Stat(bootPath.c_str(), &theStat)) { + if (ctx.progressCB) + ctx.progressCB("boot.bin", 0.f); + auto ws = NewFileIO(bootPath)->beginWriteStream(); + if (!ws) + return false; + m_header.write(*ws.get()); + } + + /* Extract BI2 info */ + SystemString bi2Path = basePathStr + _SYS_STR("/sys/bi2.bin"); + if (ctx.force || Stat(bi2Path.c_str(), &theStat)) { + if (ctx.progressCB) + ctx.progressCB("bi2.bin", 0.f); + + auto ws = NewFileIO(bi2Path)->beginWriteStream(); + if (!ws) + return false; + m_bi2Header.write(*ws); + } + + return true; +} + +static bool IsSystemFile(SystemStringView name, bool& isDol) { + isDol = false; + if (name.size() < 4) + return false; + + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".dol"))) { + isDol = true; + return true; + } + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".rel"))) + return true; + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".rso"))) + return true; + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".sel"))) + return true; + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".bnr"))) + return true; + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".elf"))) + return true; + if (!StrCaseCmp((&*(name.cend() - 4)), _SYS_STR(".wad"))) + return true; + + return false; } /** Patches out pesky #001 integrity check performed by game's OSInit. * This is required for multi-DOL games, but doesn't harm functionality otherwise */ -static void PatchDOL(std::unique_ptr& buf, size_t sz, bool& patched) -{ - patched = false; - uint8_t* found = static_cast(memmem(buf.get(), sz, - "\x3C\x03\xF8\x00\x28\x00\x00\x00\x40\x82\x00\x0C" - "\x38\x60\x00\x01\x48\x00\x02\x44\x38\x61\x00\x18\x48", 25)); - if (found) - { - found[11] = '\x04'; - patched = true; - } +static void PatchDOL(std::unique_ptr& buf, size_t sz, bool& patched) { + patched = false; + uint8_t* found = static_cast(memmem(buf.get(), sz, + "\x3C\x03\xF8\x00\x28\x00\x00\x00\x40\x82\x00\x0C" + "\x38\x60\x00\x01\x48\x00\x02\x44\x38\x61\x00\x18\x48", + 25)); + if (found) { + found[11] = '\x04'; + patched = true; + } } -static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t sz, bool& patched) -{ - std::unique_ptr buf(new uint8_t[sz]); - sz = in.read(buf.get(), sz); - PatchDOL(buf, sz, patched); - return out.write(buf.get(), sz); +static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t sz, bool& patched) { + std::unique_ptr buf(new uint8_t[sz]); + sz = in.read(buf.get(), sz); + PatchDOL(buf, sz, patched); + return out.write(buf.get(), sz); } -void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(SystemStringView filesIn) -{ - 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()); - else - ++m_parent.m_progressTotal; - } +void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(SystemStringView filesIn) { + 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()); + else + ++m_parent.m_progressTotal; + } } -bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws, - bool system, - SystemStringView filesIn) -{ - 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())) - return false; - } - else - { - bool isDol; - bool isSys = IsSystemFile(e.m_name, isDol); - if (system ^ isSys) - continue; - - size_t fileSz = ROUND_UP_32(e.m_fileSz); - uint64_t fileOff = userAllocate(fileSz, ws); - if (fileOff == -1) - return false; - m_fileOffsetsSizes[e.m_path] = std::make_pair(fileOff, fileSz); - std::unique_ptr rs = NewFileIO(e.m_path)->beginReadStream(); - if (!rs) - return false; - size_t xferSz = 0; - if (isDol) - { - bool patched; - xferSz = PatchDOL(*rs, ws, e.m_fileSz, patched); - m_parent.m_progressCB(m_parent.getProgressFactor(), e.m_name + (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); - ++m_parent.m_progressIdx; - } - else - { - char buf[0x8000]; - while (xferSz < e.m_fileSz) - { - size_t rdSz = rs->read(buf, nod::min(size_t(0x8000ul), e.m_fileSz - xferSz)); - if (!rdSz) - break; - ws.write(buf, rdSz); - xferSz += rdSz; - m_parent.m_progressCB(m_parent.getProgressFactorMidFile(xferSz, e.m_fileSz), e.m_name, xferSz); - } - ++m_parent.m_progressIdx; - } - for (size_t i=0 ; i rs = NewFileIO(e.m_path)->beginReadStream(); + if (!rs) + return false; + size_t xferSz = 0; + if (isDol) { + bool patched; + xferSz = PatchDOL(*rs, ws, e.m_fileSz, patched); + m_parent.m_progressCB(m_parent.getProgressFactor(), + e.m_name + (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); + ++m_parent.m_progressIdx; + } else { + char buf[0x8000]; + while (xferSz < e.m_fileSz) { + size_t rdSz = rs->read(buf, nod::min(size_t(0x8000ul), e.m_fileSz - xferSz)); + if (!rdSz) + break; + ws.write(buf, rdSz); + xferSz += rdSz; + m_parent.m_progressCB(m_parent.getProgressFactorMidFile(xferSz, e.m_fileSz), e.m_name, xferSz); } + ++m_parent.m_progressIdx; + } + for (size_t i = 0; i < fileSz - xferSz; ++i) + ws.write("\xff", 1); } + } - return true; + return true; } bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(SystemStringView filesIn, std::function incParents, - size_t parentDirIdx) -{ - DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); - for (const DirectoryEnumerator::Entry& e : dEnum) - { - if (e.m_isDir) - { - size_t dirNodeIdx = m_buildNodes.size(); - m_buildNodes.emplace_back(true, m_buildNameOff, parentDirIdx, dirNodeIdx+1); - addBuildName(e.m_name); - incParents(); - if (!recursiveBuildFST(e.m_path.c_str(), - [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}, - dirNodeIdx)) - return false; - } - else - { - 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); - incParents(); - } + size_t parentDirIdx) { + DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + for (const DirectoryEnumerator::Entry& e : dEnum) { + if (e.m_isDir) { + size_t dirNodeIdx = m_buildNodes.size(); + m_buildNodes.emplace_back(true, m_buildNameOff, parentDirIdx, dirNodeIdx + 1); + addBuildName(e.m_name); + incParents(); + if (!recursiveBuildFST(e.m_path.c_str(), + [&]() { + m_buildNodes[dirNodeIdx].incrementLength(); + incParents(); + }, + dirNodeIdx)) + return false; + } else { + 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); + incParents(); } + } - return true; + return true; } -void DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodesPre(const Node* nodeIn, SystemStringView dirIn) -{ - /* Build map of existing nodes to write-through later */ - std::unordered_map fileNodes; - std::unordered_map dirNodes; - if (nodeIn) - { - fileNodes.reserve(nodeIn->size()); - dirNodes.reserve(nodeIn->size()); - for (const Node& ch : *nodeIn) - { - if (ch.getKind() == Node::Kind::File) - fileNodes.insert(std::make_pair(ch.getName(), &ch)); - else if (ch.getKind() == Node::Kind::Directory) - dirNodes.insert(std::make_pair(ch.getName(), &ch)); +void DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodesPre(const Node* nodeIn, SystemStringView dirIn) { + /* Build map of existing nodes to write-through later */ + std::unordered_map fileNodes; + std::unordered_map dirNodes; + if (nodeIn) { + fileNodes.reserve(nodeIn->size()); + dirNodes.reserve(nodeIn->size()); + for (const Node& ch : *nodeIn) { + if (ch.getKind() == Node::Kind::File) + fileNodes.insert(std::make_pair(ch.getName(), &ch)); + else if (ch.getKind() == Node::Kind::Directory) + dirNodes.insert(std::make_pair(ch.getName(), &ch)); + } + } + + /* Merge this directory's files */ + if (!dirIn.empty()) { + DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + for (const DirectoryEnumerator::Entry& e : dEnum) { + SystemUTF8Conv nameView(e.m_name); + + if (e.m_isDir) { + auto search = dirNodes.find(nameView.utf8_str().data()); + if (search != dirNodes.cend()) { + recursiveMergeNodesPre(search->second, e.m_path.c_str()); + dirNodes.erase(search); + } else { + recursiveMergeNodesPre(nullptr, e.m_path.c_str()); } + } else { + fileNodes.erase(nameView.utf8_str().data()); + ++m_parent.m_progressTotal; + } } + } - /* Merge this directory's files */ - if (!dirIn.empty()) - { - DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); - for (const DirectoryEnumerator::Entry& e : dEnum) - { - SystemUTF8Conv nameView(e.m_name); + /* Write-through remaining dir nodes */ + for (const auto& p : dirNodes) { + recursiveMergeNodesPre(p.second, nullptr); + } - if (e.m_isDir) - { - auto search = dirNodes.find(nameView.utf8_str().data()); - if (search != dirNodes.cend()) - { - recursiveMergeNodesPre(search->second, e.m_path.c_str()); - dirNodes.erase(search); - } - else - { - recursiveMergeNodesPre(nullptr, e.m_path.c_str()); - } - } - else - { - fileNodes.erase(nameView.utf8_str().data()); - ++m_parent.m_progressTotal; - } - } - } - - /* Write-through remaining dir nodes */ - for (const auto& p : dirNodes) - { - recursiveMergeNodesPre(p.second, nullptr); - } - - /* Write-through remaining file nodes */ - m_parent.m_progressTotal += fileNodes.size(); + /* Write-through remaining file nodes */ + m_parent.m_progressTotal += fileNodes.size(); } -bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream& ws, - bool system, - const Node* nodeIn, - SystemStringView dirIn, - SystemStringView keyPath) -{ - /* Build map of existing nodes to write-through later */ - std::unordered_map fileNodes; - std::unordered_map dirNodes; - if (nodeIn) - { - fileNodes.reserve(nodeIn->size()); - dirNodes.reserve(nodeIn->size()); - for (const Node& ch : *nodeIn) - { - if (ch.getKind() == Node::Kind::File) - fileNodes.insert(std::make_pair(ch.getName(), &ch)); - else if (ch.getKind() == Node::Kind::Directory) - dirNodes.insert(std::make_pair(ch.getName(), &ch)); - } +bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, + SystemStringView dirIn, SystemStringView keyPath) { + /* Build map of existing nodes to write-through later */ + std::unordered_map fileNodes; + std::unordered_map dirNodes; + if (nodeIn) { + fileNodes.reserve(nodeIn->size()); + dirNodes.reserve(nodeIn->size()); + for (const Node& ch : *nodeIn) { + if (ch.getKind() == Node::Kind::File) + fileNodes.insert(std::make_pair(ch.getName(), &ch)); + else if (ch.getKind() == Node::Kind::Directory) + dirNodes.insert(std::make_pair(ch.getName(), &ch)); } + } - /* Merge this directory's files */ - if (!dirIn.empty()) - { - DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); - for (const DirectoryEnumerator::Entry& e : dEnum) - { - SystemUTF8Conv nameView(e.m_name); - SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name; + /* Merge this directory's files */ + if (!dirIn.empty()) { + DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + for (const DirectoryEnumerator::Entry& e : dEnum) { + SystemUTF8Conv nameView(e.m_name); + SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name; - if (e.m_isDir) - { - auto search = dirNodes.find(nameView.utf8_str().data()); - if (search != dirNodes.cend()) - { - if (!recursiveMergeNodes(ws, system, search->second, e.m_path.c_str(), chKeyPath)) - return false; - dirNodes.erase(search); - } - else - { - if (!recursiveMergeNodes(ws, system, nullptr, e.m_path.c_str(), chKeyPath)) - return false; - } - } - else - { - bool isDol; - bool isSys = IsSystemFile(e.m_name, isDol); - if (system ^ isSys) - continue; - - fileNodes.erase(nameView.utf8_str().data()); - - size_t fileSz = ROUND_UP_32(e.m_fileSz); - uint64_t fileOff = userAllocate(fileSz, ws); - if (fileOff == -1) - return false; - m_fileOffsetsSizes[chKeyPath] = std::make_pair(fileOff, fileSz); - std::unique_ptr rs = NewFileIO(e.m_path)->beginReadStream(); - if (!rs) - return false; - size_t xferSz = 0; - if (isDol) - { - bool patched; - xferSz = PatchDOL(*rs, ws, e.m_fileSz, patched); - m_parent.m_progressCB(m_parent.getProgressFactor(), e.m_name + - (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); - ++m_parent.m_progressIdx; - } - else - { - char buf[0x8000]; - while (xferSz < e.m_fileSz) - { - size_t rdSz = rs->read(buf, nod::min(size_t(0x8000ul), e.m_fileSz - xferSz)); - if (!rdSz) - break; - ws.write(buf, rdSz); - xferSz += rdSz; - m_parent.m_progressCB(m_parent.getProgressFactorMidFile(xferSz, e.m_fileSz), e.m_name, xferSz); - } - ++m_parent.m_progressIdx; - } - for (size_t i=0 ; igetName()); - SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); - if (!recursiveMergeNodes(ws, system, p.second, nullptr, chKeyPath)) + if (e.m_isDir) { + auto search = dirNodes.find(nameView.utf8_str().data()); + if (search != dirNodes.cend()) { + if (!recursiveMergeNodes(ws, system, search->second, e.m_path.c_str(), chKeyPath)) return false; - } - - /* Write-through remaining file nodes */ - for (const auto& p : fileNodes) - { - const Node& ch = *p.second; - SystemStringConv sysName(ch.getName()); - SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); - + dirNodes.erase(search); + } else { + if (!recursiveMergeNodes(ws, system, nullptr, e.m_path.c_str(), chKeyPath)) + return false; + } + } else { bool isDol; - bool isSys = IsSystemFile(sysName.sys_str(), isDol); + bool isSys = IsSystemFile(e.m_name, isDol); if (system ^ isSys) - continue; + continue; - size_t fileSz = ROUND_UP_32(ch.size()); + fileNodes.erase(nameView.utf8_str().data()); + + size_t fileSz = ROUND_UP_32(e.m_fileSz); uint64_t fileOff = userAllocate(fileSz, ws); if (fileOff == -1) - return false; + return false; m_fileOffsetsSizes[chKeyPath] = std::make_pair(fileOff, fileSz); - std::unique_ptr rs = ch.beginReadStream(); + std::unique_ptr rs = NewFileIO(e.m_path)->beginReadStream(); if (!rs) - return false; + return false; size_t xferSz = 0; - if (isDol) - { - xferSz = ch.size(); - std::unique_ptr dolBuf = ch.getBuf(); - bool patched; - PatchDOL(dolBuf, xferSz, patched); - ws.write(dolBuf.get(), xferSz); - m_parent.m_progressCB(m_parent.getProgressFactor(), SystemString(sysName.sys_str()) + - (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); - ++m_parent.m_progressIdx; + if (isDol) { + bool patched; + xferSz = PatchDOL(*rs, ws, e.m_fileSz, patched); + m_parent.m_progressCB(m_parent.getProgressFactor(), + e.m_name + (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); + ++m_parent.m_progressIdx; + } else { + char buf[0x8000]; + while (xferSz < e.m_fileSz) { + size_t rdSz = rs->read(buf, nod::min(size_t(0x8000ul), e.m_fileSz - xferSz)); + if (!rdSz) + break; + ws.write(buf, rdSz); + xferSz += rdSz; + m_parent.m_progressCB(m_parent.getProgressFactorMidFile(xferSz, e.m_fileSz), e.m_name, xferSz); + } + ++m_parent.m_progressIdx; } - else - { - char buf[0x8000]; - while (xferSz < ch.size()) - { - size_t rdSz = rs->read(buf, nod::min(size_t(0x8000), size_t(ch.size() - xferSz))); - if (!rdSz) - break; - ws.write(buf, rdSz); - xferSz += rdSz; - m_parent.m_progressCB(m_parent.getProgressFactorMidFile(xferSz, ch.size()), sysName.sys_str(), xferSz); - } - ++m_parent.m_progressIdx; - } - for (size_t i=0 ; igetName()); + SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); + if (!recursiveMergeNodes(ws, system, p.second, nullptr, chKeyPath)) + return false; + } + + /* Write-through remaining file nodes */ + for (const auto& p : fileNodes) { + const Node& ch = *p.second; + SystemStringConv sysName(ch.getName()); + SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); + + bool isDol; + bool isSys = IsSystemFile(sysName.sys_str(), isDol); + if (system ^ isSys) + continue; + + size_t fileSz = ROUND_UP_32(ch.size()); + uint64_t fileOff = userAllocate(fileSz, ws); + if (fileOff == -1) + return false; + m_fileOffsetsSizes[chKeyPath] = std::make_pair(fileOff, fileSz); + std::unique_ptr rs = ch.beginReadStream(); + if (!rs) + return false; + size_t xferSz = 0; + if (isDol) { + xferSz = ch.size(); + std::unique_ptr dolBuf = ch.getBuf(); + bool patched; + PatchDOL(dolBuf, xferSz, patched); + ws.write(dolBuf.get(), xferSz); + m_parent.m_progressCB(m_parent.getProgressFactor(), + SystemString(sysName.sys_str()) + (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), + xferSz); + ++m_parent.m_progressIdx; + } else { + char buf[0x8000]; + while (xferSz < ch.size()) { + size_t rdSz = rs->read(buf, nod::min(size_t(0x8000), size_t(ch.size() - xferSz))); + if (!rdSz) + break; + ws.write(buf, rdSz); + xferSz += rdSz; + m_parent.m_progressCB(m_parent.getProgressFactorMidFile(xferSz, ch.size()), sysName.sys_str(), xferSz); + } + ++m_parent.m_progressIdx; + } + for (size_t i = 0; i < fileSz - xferSz; ++i) + ws.write("\xff", 1); + } + + return true; } -bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn, - SystemStringView dirIn, +bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, std::function incParents, - SystemStringView keyPath) -{ - /* Build map of existing nodes to write-through later */ - std::unordered_map fileNodes; - std::unordered_map dirNodes; - if (nodeIn) - { - fileNodes.reserve(nodeIn->size()); - dirNodes.reserve(nodeIn->size()); - for (const Node& ch : *nodeIn) - { - if (ch.getKind() == Node::Kind::File) - fileNodes.insert(std::make_pair(ch.getName(), &ch)); - else if (ch.getKind() == Node::Kind::Directory) - dirNodes.insert(std::make_pair(ch.getName(), &ch)); - } + SystemStringView keyPath) { + /* Build map of existing nodes to write-through later */ + std::unordered_map fileNodes; + std::unordered_map dirNodes; + if (nodeIn) { + fileNodes.reserve(nodeIn->size()); + dirNodes.reserve(nodeIn->size()); + for (const Node& ch : *nodeIn) { + if (ch.getKind() == Node::Kind::File) + fileNodes.insert(std::make_pair(ch.getName(), &ch)); + else if (ch.getKind() == Node::Kind::Directory) + dirNodes.insert(std::make_pair(ch.getName(), &ch)); } + } - /* Merge this directory's files */ - if (!dirIn.empty()) - { - DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); - for (const DirectoryEnumerator::Entry& e : dEnum) - { - SystemUTF8Conv nameView(e.m_name); - SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name; - - if (e.m_isDir) - { - size_t dirNodeIdx = m_buildNodes.size(); - m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1); - addBuildName(e.m_name); - incParents(); - - auto search = dirNodes.find(nameView.utf8_str().data()); - if (search != dirNodes.cend()) - { - if (!recursiveMergeFST(search->second, e.m_path.c_str(), - [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}, - chKeyPath)) - return false; - dirNodes.erase(search); - } - else - { - if (!recursiveMergeFST(nullptr, e.m_path.c_str(), - [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}, - chKeyPath)) - return false; - } - } - else - { - fileNodes.erase(nameView.utf8_str().data()); - std::pair fileOffSz = m_fileOffsetsSizes.at(chKeyPath); - m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second); - addBuildName(e.m_name); - incParents(); - } - } - } - - - /* Write-through remaining dir nodes */ - for (const auto& p : dirNodes) - { - SystemStringConv sysName(p.second->getName()); - SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data(); + /* Merge this directory's files */ + if (!dirIn.empty()) { + DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + for (const DirectoryEnumerator::Entry& e : dEnum) { + SystemUTF8Conv nameView(e.m_name); + SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name; + if (e.m_isDir) { size_t dirNodeIdx = m_buildNodes.size(); - m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1); - addBuildName(sysName.sys_str()); + m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx + 1); + addBuildName(e.m_name); incParents(); - if (!recursiveMergeFST(p.second, nullptr, - [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}, - chKeyPath)) + auto search = dirNodes.find(nameView.utf8_str().data()); + if (search != dirNodes.cend()) { + if (!recursiveMergeFST(search->second, e.m_path.c_str(), + [&]() { + m_buildNodes[dirNodeIdx].incrementLength(); + incParents(); + }, + chKeyPath)) return false; - } - - /* Write-through remaining file nodes */ - for (const auto& p : fileNodes) - { - const Node& ch = *p.second; - SystemStringConv sysName(ch.getName()); - SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data(); - - std::pair fileOffSz = m_fileOffsetsSizes.at(chKeyPath); + dirNodes.erase(search); + } else { + if (!recursiveMergeFST(nullptr, e.m_path.c_str(), + [&]() { + m_buildNodes[dirNodeIdx].incrementLength(); + incParents(); + }, + chKeyPath)) + return false; + } + } else { + fileNodes.erase(nameView.utf8_str().data()); + std::pair fileOffSz = m_fileOffsetsSizes.at(chKeyPath); m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second); - addBuildName(sysName.sys_str()); + addBuildName(e.m_name); incParents(); + } } + } - return true; + /* Write-through remaining dir nodes */ + for (const auto& p : dirNodes) { + SystemStringConv sysName(p.second->getName()); + SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data(); + + size_t dirNodeIdx = m_buildNodes.size(); + m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx + 1); + addBuildName(sysName.sys_str()); + incParents(); + + if (!recursiveMergeFST(p.second, nullptr, + [&]() { + m_buildNodes[dirNodeIdx].incrementLength(); + incParents(); + }, + chKeyPath)) + return false; + } + + /* Write-through remaining file nodes */ + for (const auto& p : fileNodes) { + const Node& ch = *p.second; + SystemStringConv sysName(ch.getName()); + SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data(); + + std::pair fileOffSz = m_fileOffsetsSizes.at(chKeyPath); + m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second); + addBuildName(sysName.sys_str()); + incParents(); + } + + return true; } -bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t& totalSz, - const Node* nodeIn, - SystemStringView dirIn) -{ - /* Build map of existing nodes to write-through later */ - std::unordered_map fileNodes; - std::unordered_map dirNodes; - if (nodeIn) - { - fileNodes.reserve(nodeIn->size()); - dirNodes.reserve(nodeIn->size()); - for (const Node& ch : *nodeIn) - { - if (ch.getKind() == Node::Kind::File) - fileNodes.insert(std::make_pair(ch.getName(), &ch)); - else if (ch.getKind() == Node::Kind::Directory) - dirNodes.insert(std::make_pair(ch.getName(), &ch)); - } +bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, + SystemStringView dirIn) { + /* Build map of existing nodes to write-through later */ + std::unordered_map fileNodes; + std::unordered_map dirNodes; + if (nodeIn) { + fileNodes.reserve(nodeIn->size()); + dirNodes.reserve(nodeIn->size()); + for (const Node& ch : *nodeIn) { + if (ch.getKind() == Node::Kind::File) + fileNodes.insert(std::make_pair(ch.getName(), &ch)); + else if (ch.getKind() == Node::Kind::Directory) + dirNodes.insert(std::make_pair(ch.getName(), &ch)); } + } - /* Merge this directory's files */ - if (!dirIn.empty()) - { - DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); - for (const DirectoryEnumerator::Entry& e : dEnum) - { - SystemUTF8Conv nameView(e.m_name); + /* Merge this directory's files */ + if (!dirIn.empty()) { + DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); + for (const DirectoryEnumerator::Entry& e : dEnum) { + SystemUTF8Conv nameView(e.m_name); - if (e.m_isDir) - { - auto search = dirNodes.find(nameView.utf8_str().data()); - if (search != dirNodes.cend()) - { - if (!RecursiveCalculateTotalSize(totalSz, search->second, e.m_path.c_str())) - return false; - dirNodes.erase(search); - } - else - { - if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str())) - return false; - } - } - else - { - fileNodes.erase(nameView.utf8_str().data()); - totalSz += ROUND_UP_32(e.m_fileSz); - } - } - } - - /* Write-through remaining dir nodes */ - for (const auto& p : dirNodes) - { - if (!RecursiveCalculateTotalSize(totalSz, p.second, nullptr)) + if (e.m_isDir) { + auto search = dirNodes.find(nameView.utf8_str().data()); + if (search != dirNodes.cend()) { + if (!RecursiveCalculateTotalSize(totalSz, search->second, e.m_path.c_str())) return false; + dirNodes.erase(search); + } else { + if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str())) + return false; + } + } else { + fileNodes.erase(nameView.utf8_str().data()); + totalSz += ROUND_UP_32(e.m_fileSz); + } } + } - /* Write-through remaining file nodes */ - for (const auto& p : fileNodes) - { - const Node& ch = *p.second; - totalSz += ROUND_UP_32(ch.size()); - } + /* Write-through remaining dir nodes */ + for (const auto& p : dirNodes) { + if (!RecursiveCalculateTotalSize(totalSz, p.second, nullptr)) + return false; + } - return true; + /* Write-through remaining file nodes */ + for (const auto& p : fileNodes) { + const Node& ch = *p.second; + totalSz += ROUND_UP_32(ch.size()); + } + + return true; } -bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws, - SystemStringView dirIn) -{ - if (dirIn.empty()) - { - LogModule.report(logvisor::Error, _SYS_STR("all arguments must be supplied to buildFromDirectory()")); - return false; - } +bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn) { + if (dirIn.empty()) { + LogModule.report(logvisor::Error, _SYS_STR("all arguments must be supplied to buildFromDirectory()")); + return false; + } - SystemString dirStr(dirIn); - SystemString basePath = m_isWii ? dirStr + _SYS_STR("/") + getKindString(m_kind) : dirStr; - SystemString dolIn = basePath + _SYS_STR("/sys/main.dol"); - SystemString filesIn = basePath + _SYS_STR("/files"); + SystemString dirStr(dirIn); + SystemString basePath = m_isWii ? dirStr + _SYS_STR("/") + getKindString(m_kind) : dirStr; + SystemString dolIn = basePath + _SYS_STR("/sys/main.dol"); + SystemString filesIn = basePath + _SYS_STR("/files"); - /* 1st pass - Tally up total progress steps */ - m_parent.m_progressTotal += 2; /* Prep and DOL */ - recursiveBuildNodesPre(filesIn.c_str()); + /* 1st pass - Tally up total progress steps */ + m_parent.m_progressTotal += 2; /* Prep and DOL */ + recursiveBuildNodesPre(filesIn.c_str()); - /* Clear file */ - m_parent.m_progressCB(m_parent.getProgressFactor(), _SYS_STR("Preparing output image"), -1); - ++m_parent.m_progressIdx; + /* Clear file */ + m_parent.m_progressCB(m_parent.getProgressFactor(), _SYS_STR("Preparing output image"), -1); + ++m_parent.m_progressIdx; - /* Add root node */ - m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1); - addBuildName(_SYS_STR("")); - - /* Write Boot DOL first (first thing seeked to after Apploader) */ - { - Sstat dolStat; - if (Stat(dolIn.c_str(), &dolStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), dolIn.c_str()); - return false; - } - size_t fileSz = ROUND_UP_32(dolStat.st_size); - uint64_t fileOff = userAllocate(fileSz, ws); - if (fileOff == -1) - return false; - m_dolOffset = fileOff; - m_dolSize = fileSz; - std::unique_ptr 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(), dolIn + - (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); - ++m_parent.m_progressIdx; - for (size_t i=0 ; i")); + /* Write Boot DOL first (first thing seeked to after Apploader) */ + { Sstat dolStat; - if (Stat(dolIn.c_str(), &dolStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), dolIn.c_str()); - return -1; + if (Stat(dolIn.c_str(), &dolStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), dolIn.c_str()); + return false; } - uint64_t totalSz = ROUND_UP_32(dolStat.st_size); - if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str())) - return -1; - return totalSz; + size_t fileSz = ROUND_UP_32(dolStat.st_size); + uint64_t fileOff = userAllocate(fileSz, ws); + if (fileOff == -1) + return false; + m_dolOffset = fileOff; + m_dolSize = fileSz; + std::unique_ptr 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(), dolIn + (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), + xferSz); + ++m_parent.m_progressIdx; + for (size_t i = 0; i < fileSz - xferSz; ++i) + ws.write("\xff", 1); + } + + /* Gather files in root directory */ + if (!recursiveBuildNodes(ws, true, filesIn.c_str())) + return false; + if (!recursiveBuildNodes(ws, false, filesIn.c_str())) + return false; + if (!recursiveBuildFST(filesIn.c_str(), [&]() { m_buildNodes[0].incrementLength(); }, 0)) + return false; + + return true; } -bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& ws, - const IPartition* partIn, - SystemStringView dirIn) -{ - if (dirIn.empty()) - { - LogModule.report(logvisor::Error, _SYS_STR("all arguments must be supplied to mergeFromDirectory()")); - return false; - } +uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, + bool isWii) { + SystemString dirStr(dirIn); + SystemString basePath = isWii ? dirStr + _SYS_STR("/") + getKindString(kind) : dirStr; + SystemString dolIn = basePath + _SYS_STR("/sys/main.dol"); + SystemString filesIn = basePath + _SYS_STR("/files"); - SystemString dirStr(dirIn); - SystemString basePath = m_isWii ? dirStr + _SYS_STR("/") + getKindString(m_kind) : dirStr; - SystemString filesIn = basePath + _SYS_STR("/files"); + Sstat dolStat; + if (Stat(dolIn.c_str(), &dolStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), dolIn.c_str()); + return -1; + } + uint64_t totalSz = ROUND_UP_32(dolStat.st_size); + if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str())) + return -1; + return totalSz; +} - /* 1st pass - Tally up total progress steps */ - m_parent.m_progressTotal += 2; /* Prep and DOL */ - recursiveMergeNodesPre(&partIn->getFSTRoot(), filesIn.c_str()); +bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, + SystemStringView dirIn) { + if (dirIn.empty()) { + LogModule.report(logvisor::Error, _SYS_STR("all arguments must be supplied to mergeFromDirectory()")); + return false; + } - /* Clear file */ - m_parent.m_progressCB(m_parent.getProgressFactor(), _SYS_STR("Preparing output image"), -1); + SystemString dirStr(dirIn); + SystemString basePath = m_isWii ? dirStr + _SYS_STR("/") + getKindString(m_kind) : dirStr; + SystemString filesIn = basePath + _SYS_STR("/files"); + + /* 1st pass - Tally up total progress steps */ + m_parent.m_progressTotal += 2; /* Prep and DOL */ + recursiveMergeNodesPre(&partIn->getFSTRoot(), filesIn.c_str()); + + /* Clear file */ + m_parent.m_progressCB(m_parent.getProgressFactor(), _SYS_STR("Preparing output image"), -1); + ++m_parent.m_progressIdx; + + /* Add root node */ + m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1); + addBuildName(_SYS_STR("")); + + /* Write Boot DOL first (first thing seeked to after Apploader) */ + { + size_t xferSz = partIn->getDOLSize(); + size_t fileSz = ROUND_UP_32(xferSz); + uint64_t fileOff = userAllocate(fileSz, ws); + if (fileOff == -1) + return false; + m_dolOffset = fileOff; + m_dolSize = fileSz; + std::unique_ptr dolBuf = partIn->getDOLBuf(); + bool patched; + PatchDOL(dolBuf, xferSz, patched); + ws.write(dolBuf.get(), xferSz); + m_parent.m_progressCB(m_parent.getProgressFactor(), + SystemString(_SYS_STR("")) + (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), + xferSz); ++m_parent.m_progressIdx; + for (size_t i = 0; i < fileSz - xferSz; ++i) + ws.write("\xff", 1); + } - /* Add root node */ - m_buildNodes.emplace_back(true, m_buildNameOff, 0, 1); - addBuildName(_SYS_STR("")); + /* Gather files in root directory */ + SystemString keyPath; + if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), filesIn.c_str(), keyPath)) + return false; + if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath)) + return false; + if (!recursiveMergeFST(&partIn->getFSTRoot(), filesIn.c_str(), [&]() { m_buildNodes[0].incrementLength(); }, keyPath)) + return false; - /* Write Boot DOL first (first thing seeked to after Apploader) */ - { - size_t xferSz = partIn->getDOLSize(); - size_t fileSz = ROUND_UP_32(xferSz); - uint64_t fileOff = userAllocate(fileSz, ws); - if (fileOff == -1) - return false; - m_dolOffset = fileOff; - m_dolSize = fileSz; - std::unique_ptr dolBuf = partIn->getDOLBuf(); - bool patched; - PatchDOL(dolBuf, xferSz, patched); - ws.write(dolBuf.get(), xferSz); - m_parent.m_progressCB(m_parent.getProgressFactor(), SystemString(_SYS_STR("")) + - (patched ? _SYS_STR(" [PATCHED]") : _SYS_STR("")), xferSz); - ++m_parent.m_progressIdx; - for (size_t i=0 ; igetFSTRoot(), filesIn.c_str(), keyPath)) - return false; - if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath)) - return false; - if (!recursiveMergeFST(&partIn->getFSTRoot(), filesIn.c_str(), [&](){m_buildNodes[0].incrementLength();}, keyPath)) - return false; - - return true; + return true; } uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const IPartition* partIn, - SystemStringView dirIn) -{ - SystemString dirStr(dirIn); - SystemString basePath = partIn->isWii() ? dirStr + _SYS_STR("/") + getKindString(partIn->getKind()) : dirStr; - SystemString filesIn = basePath + _SYS_STR("/files"); + SystemStringView dirIn) { + SystemString dirStr(dirIn); + SystemString basePath = partIn->isWii() ? dirStr + _SYS_STR("/") + getKindString(partIn->getKind()) : dirStr; + SystemString filesIn = basePath + _SYS_STR("/files"); - uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize()); - if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), filesIn.c_str())) - return -1; - return totalSz; + uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize()); + if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), filesIn.c_str())) + return -1; + return totalSz; } -} +} // namespace nod diff --git a/lib/DiscGCN.cpp b/lib/DiscGCN.cpp index f9c2f18..84298b5 100644 --- a/lib/DiscGCN.cpp +++ b/lib/DiscGCN.cpp @@ -3,474 +3,414 @@ #include #define BUFFER_SZ 0x8000 -namespace nod -{ +namespace nod { -class PartitionGCN : public IPartition -{ +class PartitionGCN : public IPartition { public: - PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err) - : IPartition(parent, PartitionKind::Data, false, offset) - { - /* GCN-specific header reads */ - std::unique_ptr s = beginReadStream(0x0); - if (!s) - { - err = true; - return; - } - 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); - m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]); - - /* Yay files!! */ - parseFST(*s); - - /* Also make DOL header and size handy */ - s->seek(m_dolOff); - parseDOL(*s); + PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err) + : IPartition(parent, PartitionKind::Data, false, offset) { + /* GCN-specific header reads */ + std::unique_ptr s = beginReadStream(0x0); + if (!s) { + err = true; + return; } + 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); + m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]); - class PartReadStream : public IPartReadStream - { - const PartitionGCN& m_parent; - std::unique_ptr m_dio; + /* Yay files!! */ + parseFST(*s); - uint64_t m_offset; - size_t m_curBlock = SIZE_MAX; - uint8_t m_buf[BUFFER_SZ]; + /* Also make DOL header and size handy */ + s->seek(m_dolOff); + parseDOL(*s); + } - public: - PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err) - : m_parent(parent), m_offset(offset) - { - size_t block = m_offset / BUFFER_SZ; - m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ); - if (!m_dio) - { - err = true; - return; - } - m_dio->read(m_buf, BUFFER_SZ); - m_curBlock = block; - } - void seek(int64_t offset, int whence) - { - if (whence == SEEK_SET) - m_offset = offset; - else if (whence == SEEK_CUR) - m_offset += offset; - else - return; - size_t block = m_offset / BUFFER_SZ; - if (block != m_curBlock) - { - m_dio->seek(block * BUFFER_SZ); - m_dio->read(m_buf, BUFFER_SZ); - m_curBlock = block; - } - } - uint64_t position() const {return m_offset;} - uint64_t read(void* buf, uint64_t length) - { - size_t block = m_offset / BUFFER_SZ; - size_t cacheOffset = m_offset % BUFFER_SZ; - uint64_t cacheSize; - uint64_t rem = length; - uint8_t* dst = (uint8_t*)buf; + class PartReadStream : public IPartReadStream { + const PartitionGCN& m_parent; + std::unique_ptr m_dio; - while (rem) - { - if (block != m_curBlock) - { - m_dio->read(m_buf, BUFFER_SZ); - m_curBlock = block; - } + uint64_t m_offset; + size_t m_curBlock = SIZE_MAX; + uint8_t m_buf[BUFFER_SZ]; - cacheSize = rem; - if (cacheSize + cacheOffset > BUFFER_SZ) - cacheSize = BUFFER_SZ - cacheOffset; - - memmove(dst, m_buf + cacheOffset, cacheSize); - dst += cacheSize; - rem -= cacheSize; - cacheOffset = 0; - ++block; - } - - m_offset += length; - return dst - (uint8_t*)buf; - } - }; - - std::unique_ptr beginReadStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new PartReadStream(*this, offset, Err)); - if (Err) - return {}; - return ret; - } -}; - -DiscGCN::DiscGCN(std::unique_ptr&& dio, bool& err) -: DiscBase(std::move(dio), err) -{ - if (err) + public: + PartReadStream(const PartitionGCN& parent, uint64_t offset, bool& err) : m_parent(parent), m_offset(offset) { + size_t block = m_offset / BUFFER_SZ; + m_dio = m_parent.m_parent.getDiscIO().beginReadStream(block * BUFFER_SZ); + if (!m_dio) { + err = true; return; - - /* One lone partition for GCN */ - m_partitions.emplace_back(new PartitionGCN(*this, 0, err)); -} - -DiscBuilderGCN DiscGCN::makeMergeBuilder(SystemStringView outPath, FProgress progressCB) -{ - return DiscBuilderGCN(outPath, progressCB); -} - -bool DiscGCN::extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const -{ - return true; -} - -class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase -{ - uint64_t m_curUser = 0x57058000; - -public: - class PartWriteStream : public IPartWriteStream - { - const PartitionBuilderGCN& m_parent; - uint64_t m_offset; - std::unique_ptr m_fio; - - public: - PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err) - : m_parent(parent), m_offset(offset) - { - m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset); - if (!m_fio) - err = true; - } - void close() {m_fio.reset();} - uint64_t position() const {return m_offset;} - uint64_t write(const void* buf, uint64_t length) - { - uint64_t len = m_fio->write(buf, length); - m_offset += len; - return len; - } - void seek(size_t off) - { - m_offset = off; - m_fio = m_parent.m_parent.getFileIO().beginWriteStream(off); - } - }; - - PartitionBuilderGCN(DiscBuilderBase& parent) - : DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {} - - uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) - { - m_curUser -= reqSz; - m_curUser &= 0xfffffffffffffff0; - if (m_curUser < 0x30000) - { - LogModule.report(logvisor::Error, "user area low mark reached"); - return -1; - } - static_cast(ws).seek(m_curUser); - return m_curUser; + } + m_dio->read(m_buf, BUFFER_SZ); + m_curBlock = block; } - - uint32_t packOffset(uint64_t offset) const - { - return offset; + void seek(int64_t offset, int whence) { + if (whence == SEEK_SET) + m_offset = offset; + else if (whence == SEEK_CUR) + m_offset += offset; + else + return; + size_t block = m_offset / BUFFER_SZ; + if (block != m_curBlock) { + m_dio->seek(block * BUFFER_SZ); + m_dio->read(m_buf, BUFFER_SZ); + m_curBlock = block; + } } + uint64_t position() const { return m_offset; } + uint64_t read(void* buf, uint64_t length) { + size_t block = m_offset / BUFFER_SZ; + size_t cacheOffset = m_offset % BUFFER_SZ; + uint64_t cacheSize; + uint64_t rem = length; + uint8_t* dst = (uint8_t*)buf; - std::unique_ptr beginWriteStream(uint64_t offset) - { - bool Err = false; - std::unique_ptr ret = std::make_unique(*this, offset, Err); - if (Err) - return {}; - return ret; - } - - bool _build(const std::function& headerFunc, - const std::function& bi2Func, - const std::function& apploaderFunc) - { - std::unique_ptr ws = beginWriteStream(0x2440); - if (!ws) - return false; - size_t xferSz = 0; - if (!apploaderFunc(*ws, xferSz)) - return false; - - size_t fstOff = ROUND_UP_32(xferSz); - size_t fstSz = sizeof(FSTNode) * m_buildNodes.size(); - for (size_t i=0 ; iwrite("\xff", 1); - fstOff += 0x2440; - ws->write(m_buildNodes.data(), fstSz); - for (const std::string& str : m_buildNames) - ws->write(str.data(), str.size()+1); - fstSz += m_buildNameOff; - fstSz = ROUND_UP_32(fstSz); - - if (fstOff + fstSz >= m_curUser) - { - LogModule.report(logvisor::Error, - "FST flows into user area (one or the other is too big)"); - return false; + while (rem) { + if (block != m_curBlock) { + m_dio->read(m_buf, BUFFER_SZ); + m_curBlock = block; } - ws = beginWriteStream(0); - if (!ws) - return false; - if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser)) - return false; - if (!bi2Func(*ws)) - return false; + cacheSize = rem; + if (cacheSize + cacheOffset > BUFFER_SZ) + cacheSize = BUFFER_SZ - cacheOffset; - return true; + memmove(dst, m_buf + cacheOffset, cacheSize); + dst += cacheSize; + rem -= cacheSize; + cacheOffset = 0; + ++block; + } + + m_offset += length; + return dst - (uint8_t*)buf; } + }; - bool buildFromDirectory(SystemStringView dirIn) - { - std::unique_ptr ws = beginWriteStream(0); - if (!ws) - return false; - bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn); - if (!result) - return false; - - SystemString dirStr(dirIn); - - /* Check Apploader */ - SystemString apploaderIn = dirStr + _SYS_STR("/sys/apploader.img"); - Sstat apploaderStat; - if (Stat(apploaderIn.c_str(), &apploaderStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), apploaderIn.c_str()); - return false; - } - - /* Check Boot */ - SystemString bootIn = dirStr + _SYS_STR("/sys/boot.bin"); - Sstat bootStat; - if (Stat(bootIn.c_str(), &bootStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bootIn.c_str()); - return false; - } - - /* Check BI2 */ - SystemString bi2In = dirStr + _SYS_STR("/sys/bi2.bin"); - Sstat bi2Stat; - if (Stat(bi2In.c_str(), &bi2Stat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bi2In.c_str()); - return false; - } - - return _build( - [&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; - }, - [&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]; - while (true) - { - size_t rdSz = rs->read(buf, 8192); - if (!rdSz) - break; - ws.write(buf, rdSz); - xferSz += rdSz; - if (0x2440 + xferSz >= m_curUser) - { - LogModule.report(logvisor::Error, - "apploader flows into user area (one or the other is too big)"); - return false; - } - m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); - } - ++m_parent.m_progressIdx; - return true; - }); - } - - bool mergeFromDirectory(const PartitionGCN* partIn, SystemStringView dirIn) - { - std::unique_ptr ws = beginWriteStream(0); - if (!ws) - return false; - bool result = DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(*ws, partIn, dirIn); - if (!result) - return false; - - return _build( - [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; - }, - [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(); - SystemString apploaderName(_SYS_STR("")); - ws.write(apploaderBuf.get(), apploaderSz); - xferSz += apploaderSz; - if (0x2440 + xferSz >= m_curUser) - { - LogModule.report(logvisor::Error, - "apploader flows into user area (one or the other is too big)"); - return false; - } - m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); - ++m_parent.m_progressIdx; - return true; - }); - } + std::unique_ptr beginReadStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new PartReadStream(*this, offset, Err)); + if (Err) + return {}; + return ret; + } }; -EBuildResult DiscBuilderGCN::buildFromDirectory(SystemStringView dirIn) -{ - if (!m_fileIO->beginWriteStream()) - return EBuildResult::Failed; - if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) - { - LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_outPath.c_str()); - return EBuildResult::DiskFull; - } - m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1); - ++m_progressIdx; - { - auto ws = m_fileIO->beginWriteStream(0); - if (!ws) - return EBuildResult::Failed; - char zeroBytes[1024] = {}; - for (uint64_t i = 0; i < 0x57058000; i += 1024) - ws->write(zeroBytes, 1024); - } +DiscGCN::DiscGCN(std::unique_ptr&& dio, bool& err) : DiscBase(std::move(dio), err) { + if (err) + return; - PartitionBuilderGCN& pb = static_cast(*m_partitions[0]); - return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed; + /* One lone partition for GCN */ + m_partitions.emplace_back(new PartitionGCN(*this, 0, err)); } -uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn) -{ - uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false); - if (sz == -1) - return -1; - sz += 0x30000; - if (sz > 0x57058000) - { - LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000); - return -1; +DiscBuilderGCN DiscGCN::makeMergeBuilder(SystemStringView outPath, FProgress progressCB) { + return DiscBuilderGCN(outPath, progressCB); +} + +bool DiscGCN::extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; } + +class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase { + uint64_t m_curUser = 0x57058000; + +public: + class PartWriteStream : public IPartWriteStream { + const PartitionBuilderGCN& m_parent; + uint64_t m_offset; + std::unique_ptr m_fio; + + public: + PartWriteStream(const PartitionBuilderGCN& parent, uint64_t offset, bool& err) + : m_parent(parent), m_offset(offset) { + m_fio = m_parent.m_parent.getFileIO().beginWriteStream(offset); + if (!m_fio) + err = true; } - return sz; + void close() { m_fio.reset(); } + uint64_t position() const { return m_offset; } + uint64_t write(const void* buf, uint64_t length) { + uint64_t len = m_fio->write(buf, length); + m_offset += len; + return len; + } + void seek(size_t off) { + m_offset = off; + m_fio = m_parent.m_parent.getFileIO().beginWriteStream(off); + } + }; + + PartitionBuilderGCN(DiscBuilderBase& parent) + : DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {} + + uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) { + m_curUser -= reqSz; + m_curUser &= 0xfffffffffffffff0; + if (m_curUser < 0x30000) { + LogModule.report(logvisor::Error, "user area low mark reached"); + return -1; + } + static_cast(ws).seek(m_curUser); + return m_curUser; + } + + uint32_t packOffset(uint64_t offset) const { return offset; } + + std::unique_ptr beginWriteStream(uint64_t offset) { + bool Err = false; + std::unique_ptr ret = std::make_unique(*this, offset, Err); + if (Err) + return {}; + return ret; + } + + bool + _build(const std::function& headerFunc, + const std::function& bi2Func, + const std::function& apploaderFunc) { + std::unique_ptr ws = beginWriteStream(0x2440); + if (!ws) + return false; + size_t xferSz = 0; + if (!apploaderFunc(*ws, xferSz)) + return false; + + size_t fstOff = ROUND_UP_32(xferSz); + size_t fstSz = sizeof(FSTNode) * m_buildNodes.size(); + for (size_t i = 0; i < fstOff - xferSz; ++i) + ws->write("\xff", 1); + fstOff += 0x2440; + ws->write(m_buildNodes.data(), fstSz); + for (const std::string& str : m_buildNames) + ws->write(str.data(), str.size() + 1); + fstSz += m_buildNameOff; + fstSz = ROUND_UP_32(fstSz); + + if (fstOff + fstSz >= m_curUser) { + LogModule.report(logvisor::Error, "FST flows into user area (one or the other is too big)"); + return false; + } + + ws = beginWriteStream(0); + if (!ws) + return false; + if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser)) + return false; + if (!bi2Func(*ws)) + return false; + + return true; + } + + bool buildFromDirectory(SystemStringView dirIn) { + std::unique_ptr ws = beginWriteStream(0); + if (!ws) + return false; + bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn); + if (!result) + return false; + + SystemString dirStr(dirIn); + + /* Check Apploader */ + SystemString apploaderIn = dirStr + _SYS_STR("/sys/apploader.img"); + Sstat apploaderStat; + if (Stat(apploaderIn.c_str(), &apploaderStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), apploaderIn.c_str()); + return false; + } + + /* Check Boot */ + SystemString bootIn = dirStr + _SYS_STR("/sys/boot.bin"); + Sstat bootStat; + if (Stat(bootIn.c_str(), &bootStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bootIn.c_str()); + return false; + } + + /* Check BI2 */ + SystemString bi2In = dirStr + _SYS_STR("/sys/bi2.bin"); + Sstat bi2Stat; + if (Stat(bi2In.c_str(), &bi2Stat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bi2In.c_str()); + return false; + } + + return _build( + [&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; + }, + [&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]; + while (true) { + size_t rdSz = rs->read(buf, 8192); + if (!rdSz) + break; + ws.write(buf, rdSz); + xferSz += rdSz; + if (0x2440 + xferSz >= m_curUser) { + LogModule.report(logvisor::Error, "apploader flows into user area (one or the other is too big)"); + return false; + } + m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); + } + ++m_parent.m_progressIdx; + return true; + }); + } + + bool mergeFromDirectory(const PartitionGCN* partIn, SystemStringView dirIn) { + std::unique_ptr ws = beginWriteStream(0); + if (!ws) + return false; + bool result = DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(*ws, partIn, dirIn); + if (!result) + return false; + + return _build( + [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; + }, + [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(); + SystemString apploaderName(_SYS_STR("")); + ws.write(apploaderBuf.get(), apploaderSz); + xferSz += apploaderSz; + if (0x2440 + xferSz >= m_curUser) { + LogModule.report(logvisor::Error, "apploader flows into user area (one or the other is too big)"); + return false; + } + m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); + ++m_parent.m_progressIdx; + return true; + }); + } +}; + +EBuildResult DiscBuilderGCN::buildFromDirectory(SystemStringView dirIn) { + if (!m_fileIO->beginWriteStream()) + return EBuildResult::Failed; + if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) { + LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_outPath.c_str()); + return EBuildResult::DiskFull; + } + m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1); + ++m_progressIdx; + { + auto ws = m_fileIO->beginWriteStream(0); + if (!ws) + return EBuildResult::Failed; + char zeroBytes[1024] = {}; + for (uint64_t i = 0; i < 0x57058000; i += 1024) + ws->write(zeroBytes, 1024); + } + + PartitionBuilderGCN& pb = static_cast(*m_partitions[0]); + return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed; +} + +uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn) { + uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false); + if (sz == -1) + return -1; + sz += 0x30000; + if (sz > 0x57058000) { + LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000); + return -1; + } + return sz; } DiscBuilderGCN::DiscBuilderGCN(SystemStringView outPath, FProgress progressCB) -: DiscBuilderBase(outPath, 0x57058000, progressCB) -{ - PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this); - m_partitions.emplace_back(partBuilder); +: DiscBuilderBase(outPath, 0x57058000, progressCB) { + PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this); + m_partitions.emplace_back(partBuilder); } DiscMergerGCN::DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB) -: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) -{} +: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) {} -EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) -{ - if (!m_builder.getFileIO().beginWriteStream()) - return EBuildResult::Failed; - if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) - { - LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_builder.m_outPath.c_str()); - return EBuildResult::DiskFull; - } - m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1); - ++m_builder.m_progressIdx; - { - auto ws = m_builder.m_fileIO->beginWriteStream(0); - if (!ws) - return EBuildResult::Failed; - char zeroBytes[1024] = {}; - for (uint64_t i = 0; i < 0x57058000; i += 1024) - ws->write(zeroBytes, 1024); - } +EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) { + if (!m_builder.getFileIO().beginWriteStream()) + return EBuildResult::Failed; + if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) { + LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_builder.m_outPath.c_str()); + return EBuildResult::DiskFull; + } + m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1); + ++m_builder.m_progressIdx; + { + auto ws = m_builder.m_fileIO->beginWriteStream(0); + if (!ws) + return EBuildResult::Failed; + char zeroBytes[1024] = {}; + for (uint64_t i = 0; i < 0x57058000; i += 1024) + ws->write(zeroBytes, 1024); + } - PartitionBuilderGCN& pb = static_cast(*m_builder.m_partitions[0]); - return pb.mergeFromDirectory(static_cast(m_sourceDisc.getDataPartition()), dirIn) ? - EBuildResult::Success : EBuildResult::Failed; + PartitionBuilderGCN& pb = static_cast(*m_builder.m_partitions[0]); + return pb.mergeFromDirectory(static_cast(m_sourceDisc.getDataPartition()), dirIn) + ? EBuildResult::Success + : EBuildResult::Failed; } -uint64_t DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn) -{ - uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge( - sourceDisc.getDataPartition(), dirIn); - if (sz == -1) - return -1; - sz += 0x30000; - if (sz > 0x57058000) - { - LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000); - return -1; - } - return sz; +uint64_t DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn) { + uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn); + if (sz == -1) + return -1; + sz += 0x30000; + if (sz > 0x57058000) { + LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x57058000); + return -1; + } + return sz; } -} +} // namespace nod diff --git a/lib/DiscIOISO.cpp b/lib/DiscIOISO.cpp index a4c706b..1107213 100644 --- a/lib/DiscIOISO.cpp +++ b/lib/DiscIOISO.cpp @@ -3,67 +3,57 @@ #include "nod/IDiscIO.hpp" #include "nod/IFileIO.hpp" -namespace nod -{ +namespace nod { + +class DiscIOISO : public IDiscIO { + std::unique_ptr m_fio; -class DiscIOISO : public IDiscIO -{ - std::unique_ptr m_fio; public: - DiscIOISO(SystemStringView fpin) - : m_fio(NewFileIO(fpin)) {} + DiscIOISO(SystemStringView fpin) : m_fio(NewFileIO(fpin)) {} - class ReadStream : public IReadStream - { - friend class DiscIOISO; - std::unique_ptr fp; - ReadStream(std::unique_ptr&& fpin, bool& err) - : fp(std::move(fpin)) { if (!fp) err = true; } - public: - uint64_t read(void* buf, uint64_t length) - {return fp->read(buf, length);} - uint64_t position() const - {return fp->position();} - void seek(int64_t offset, int whence) - {fp->seek(offset, whence);} - }; - - std::unique_ptr beginReadStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new ReadStream(m_fio->beginReadStream(offset), Err)); - if (Err) - return {}; - return ret; + class ReadStream : public IReadStream { + friend class DiscIOISO; + std::unique_ptr fp; + ReadStream(std::unique_ptr&& fpin, bool& err) : fp(std::move(fpin)) { + if (!fp) + err = true; } - class WriteStream : public IWriteStream - { - friend class DiscIOISO; - std::unique_ptr fp; - WriteStream(std::unique_ptr&& fpin, bool& err) - : fp(std::move(fpin)) { if (!fp) err = true; } - public: - uint64_t write(const void* buf, uint64_t length) - { - return fp->write(buf, length); - } - }; + public: + uint64_t read(void* buf, uint64_t length) { return fp->read(buf, length); } + uint64_t position() const { return fp->position(); } + void seek(int64_t offset, int whence) { fp->seek(offset, whence); } + }; - std::unique_ptr beginWriteStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new WriteStream(m_fio->beginWriteStream(offset), Err)); - if (Err) - return {}; - return ret; + std::unique_ptr beginReadStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new ReadStream(m_fio->beginReadStream(offset), Err)); + if (Err) + return {}; + return ret; + } + + class WriteStream : public IWriteStream { + friend class DiscIOISO; + std::unique_ptr fp; + WriteStream(std::unique_ptr&& fpin, bool& err) : fp(std::move(fpin)) { + if (!fp) + err = true; } + + public: + uint64_t write(const void* buf, uint64_t length) { return fp->write(buf, length); } + }; + + std::unique_ptr beginWriteStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new WriteStream(m_fio->beginWriteStream(offset), Err)); + if (Err) + return {}; + return ret; + } }; -std::unique_ptr NewDiscIOISO(SystemStringView path) -{ - return std::unique_ptr(new DiscIOISO(path)); -} - -} +std::unique_ptr NewDiscIOISO(SystemStringView path) { return std::unique_ptr(new DiscIOISO(path)); } +} // namespace nod diff --git a/lib/DiscIOWBFS.cpp b/lib/DiscIOWBFS.cpp index 94da4b9..b15580f 100644 --- a/lib/DiscIOWBFS.cpp +++ b/lib/DiscIOWBFS.cpp @@ -4,302 +4,271 @@ #include "nod/IDiscIO.hpp" #include "nod/IFileIO.hpp" -namespace nod -{ +namespace nod { -#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1))) +#define ALIGN_LBA(x) (((x) + p->hd_sec_sz - 1) & (~(p->hd_sec_sz - 1))) -static uint8_t size_to_shift(uint32_t size) -{ - uint8_t ret = 0; - while (size) - { - ret++; - size>>=1; - } - return ret-1; +static uint8_t size_to_shift(uint32_t size) { + uint8_t ret = 0; + while (size) { + ret++; + size >>= 1; + } + return ret - 1; } -class DiscIOWBFS : public IDiscIO -{ - SystemString filepath; +class DiscIOWBFS : public IDiscIO { + SystemString filepath; - struct WBFSHead - { - uint32_t magic; - // parameters copied in the partition for easy dumping, and bug reports - uint32_t n_hd_sec; // total number of hd_sec in this partition - uint8_t hd_sec_sz_s; // sector size in this partition - uint8_t wbfs_sec_sz_s; // size of a wbfs sec - uint8_t padding3[2]; - uint8_t disc_table[0]; // size depends on hd sector size - }; - std::unique_ptr wbfsHead; + struct WBFSHead { + uint32_t magic; + // parameters copied in the partition for easy dumping, and bug reports + uint32_t n_hd_sec; // total number of hd_sec in this partition + uint8_t hd_sec_sz_s; // sector size in this partition + uint8_t wbfs_sec_sz_s; // size of a wbfs sec + uint8_t padding3[2]; + uint8_t disc_table[0]; // size depends on hd sector size + }; + std::unique_ptr wbfsHead; - struct WBFSDiscInfo - { - uint8_t disc_header_copy[0x100]; - uint16_t wlba_table[0]; - }; - std::unique_ptr wbfsDiscInfo; + struct WBFSDiscInfo { + uint8_t disc_header_copy[0x100]; + uint16_t wlba_table[0]; + }; + std::unique_ptr wbfsDiscInfo; - struct WBFS - { - /* hdsectors, the size of the sector provided by the hosting hard drive */ - uint32_t hd_sec_sz; - uint8_t hd_sec_sz_s; // the power of two of the last number - uint32_t n_hd_sec; // the number of hd sector in the wbfs partition + struct WBFS { + /* hdsectors, the size of the sector provided by the hosting hard drive */ + uint32_t hd_sec_sz; + uint8_t hd_sec_sz_s; // the power of two of the last number + uint32_t n_hd_sec; // the number of hd sector in the wbfs partition - /* standard wii sector (0x8000 bytes) */ - uint32_t wii_sec_sz; - uint8_t wii_sec_sz_s; - uint32_t n_wii_sec; - uint32_t n_wii_sec_per_disc; + /* standard wii sector (0x8000 bytes) */ + uint32_t wii_sec_sz; + uint8_t wii_sec_sz_s; + uint32_t n_wii_sec; + uint32_t n_wii_sec_per_disc; - /* The size of a wbfs sector */ - uint32_t wbfs_sec_sz; - uint32_t wbfs_sec_sz_s; - uint16_t n_wbfs_sec; // this must fit in 16 bit! - uint16_t n_wbfs_sec_per_disc; // size of the lookup table + /* The size of a wbfs sector */ + uint32_t wbfs_sec_sz; + uint32_t wbfs_sec_sz_s; + uint16_t n_wbfs_sec; // this must fit in 16 bit! + uint16_t n_wbfs_sec_per_disc; // size of the lookup table - uint32_t part_lba; + uint32_t part_lba; - uint16_t max_disc; - uint32_t freeblks_lba; - uint32_t *freeblks; - uint16_t disc_info_sz; + uint16_t max_disc; + uint32_t freeblks_lba; + uint32_t* freeblks; + uint16_t disc_info_sz; - uint32_t n_disc_open; + uint32_t n_disc_open; - } wbfs; + } wbfs; - static int _wbfsReadSector(IFileIO::IReadStream& rs, uint32_t lba, uint32_t count, void* buf) - { - uint64_t off = lba; - off*=512ULL; - rs.seek(off, SEEK_SET); - if (rs.read(buf, count*512ULL) != count*512ULL) - { - LogModule.report(logvisor::Error, "error reading disc"); - return 1; - } - return 0; + static int _wbfsReadSector(IFileIO::IReadStream& rs, uint32_t lba, uint32_t count, void* buf) { + uint64_t off = lba; + off *= 512ULL; + rs.seek(off, SEEK_SET); + if (rs.read(buf, count * 512ULL) != count * 512ULL) { + LogModule.report(logvisor::Error, "error reading disc"); + return 1; } + return 0; + } public: - DiscIOWBFS(SystemStringView fpin) - : filepath(fpin) - { - /* Temporary file handle to read LBA table */ - std::unique_ptr fio = NewFileIO(filepath); - std::unique_ptr rs = fio->beginReadStream(); - if (!rs) - return; + DiscIOWBFS(SystemStringView fpin) : filepath(fpin) { + /* Temporary file handle to read LBA table */ + std::unique_ptr fio = NewFileIO(filepath); + std::unique_ptr rs = fio->beginReadStream(); + if (!rs) + return; - WBFS* p = &wbfs; - WBFSHead tmpHead; - if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) { - LogModule.report(logvisor::Error, "unable to read WBFS head"); - return; - } - unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s; - unsigned num_hd_sector = SBig(tmpHead.n_hd_sec); + WBFS* p = &wbfs; + WBFSHead tmpHead; + if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) { + LogModule.report(logvisor::Error, "unable to read WBFS head"); + return; + } + unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s; + unsigned num_hd_sector = SBig(tmpHead.n_hd_sec); - wbfsHead.reset(new uint8_t[hd_sector_size]); - WBFSHead* head = (WBFSHead*)wbfsHead.get(); - rs->seek(0, SEEK_SET); - if (rs->read(head, hd_sector_size) != hd_sector_size) { - LogModule.report(logvisor::Error, "unable to read WBFS head"); - return; - } - - //constants, but put here for consistancy - p->wii_sec_sz = 0x8000; - p->wii_sec_sz_s = size_to_shift(0x8000); - p->n_wii_sec = (num_hd_sector/0x8000)*hd_sector_size; - p->n_wii_sec_per_disc = 143432*2;//support for double layers discs.. - p->part_lba = 0; - if (_wbfsReadSector(*rs, p->part_lba, 1, head)) - return; - if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) { - LogModule.report(logvisor::Error, "hd sector size doesn't match"); - return; - } - if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) { - LogModule.report(logvisor::Error, "hd num sector doesn't match"); - return; - } - p->hd_sec_sz = 1<hd_sec_sz_s; - p->hd_sec_sz_s = head->hd_sec_sz_s; - p->n_hd_sec = SBig(head->n_hd_sec); - - p->n_wii_sec = (p->n_hd_sec/p->wii_sec_sz)*(p->hd_sec_sz); - - p->wbfs_sec_sz_s = head->wbfs_sec_sz_s; - p->wbfs_sec_sz = 1<wbfs_sec_sz_s; - p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - p->disc_info_sz = ALIGN_LBA(uint16_t(sizeof(WBFSDiscInfo)) + p->n_wbfs_sec_per_disc*2); - - p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec/8)>>p->hd_sec_sz_s; - - p->freeblks = 0; // will alloc and read only if needed - p->max_disc = (p->freeblks_lba-1)/(p->disc_info_sz>>p->hd_sec_sz_s); - if(p->max_disc > p->hd_sec_sz - sizeof(WBFSHead)) - p->max_disc = p->hd_sec_sz - sizeof(WBFSHead); - - p->n_disc_open = 0; - - int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; - if (head->disc_table[0]) - { - wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]); - if (!wbfsDiscInfo) { - LogModule.report(logvisor::Error, "allocating memory"); - return; - } - if (_wbfsReadSector(*rs, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get())) - return; - p->n_disc_open++; - //for(i=0;in_wbfs_sec_per_disc;i++) - // printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); - } + wbfsHead.reset(new uint8_t[hd_sector_size]); + WBFSHead* head = (WBFSHead*)wbfsHead.get(); + rs->seek(0, SEEK_SET); + if (rs->read(head, hd_sector_size) != hd_sector_size) { + LogModule.report(logvisor::Error, "unable to read WBFS head"); + return; } - class ReadStream : public IReadStream - { - friend class DiscIOWBFS; - const DiscIOWBFS& m_parent; - std::unique_ptr fp; - uint64_t m_offset; - std::unique_ptr m_tmpBuffer; + // constants, but put here for consistancy + p->wii_sec_sz = 0x8000; + p->wii_sec_sz_s = size_to_shift(0x8000); + p->n_wii_sec = (num_hd_sector / 0x8000) * hd_sector_size; + p->n_wii_sec_per_disc = 143432 * 2; // support for double layers discs.. + p->part_lba = 0; + if (_wbfsReadSector(*rs, p->part_lba, 1, head)) + return; + if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) { + LogModule.report(logvisor::Error, "hd sector size doesn't match"); + return; + } + if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) { + LogModule.report(logvisor::Error, "hd num sector doesn't match"); + return; + } + p->hd_sec_sz = 1 << head->hd_sec_sz_s; + p->hd_sec_sz_s = head->hd_sec_sz_s; + p->n_hd_sec = SBig(head->n_hd_sec); - ReadStream(const DiscIOWBFS& parent, std::unique_ptr&& fpin, uint64_t offset, bool& err) - : m_parent(parent), - fp(std::move(fpin)), - m_offset(offset), - m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) { if (!fp) err = true; } + p->n_wii_sec = (p->n_hd_sec / p->wii_sec_sz) * (p->hd_sec_sz); - int wbfsReadSector(uint32_t lba, uint32_t count, void* buf) - {return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf);} + p->wbfs_sec_sz_s = head->wbfs_sec_sz_s; + p->wbfs_sec_sz = 1 << p->wbfs_sec_sz_s; + p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->disc_info_sz = ALIGN_LBA(uint16_t(sizeof(WBFSDiscInfo)) + p->n_wbfs_sec_per_disc * 2); - int wbfsDiscRead(uint32_t offset, uint8_t *data, uint64_t len) - { - const WBFS* p = &m_parent.wbfs; - const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get(); - uint16_t wlba = offset>>(p->wbfs_sec_sz_s-2); - uint32_t iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s; - uint32_t lba_mask = (p->wbfs_sec_sz-1)>>(p->hd_sec_sz_s); - uint64_t lba = (offset>>(p->hd_sec_sz_s-2))&lba_mask; - uint64_t off = offset&((p->hd_sec_sz>>2)-1); - uint16_t iwlba = SBig(d->wlba_table[wlba]); - uint64_t len_copied; - int err = 0; - uint8_t *ptr = data; - if (!iwlba) - return 1; - if (off) - { - off*=4; - err = wbfsReadSector(p->part_lba + (iwlba<hd_sec_sz - off; - if (len < len_copied) - len_copied = len; - memcpy(ptr, m_tmpBuffer.get() + off, len_copied); - len -= len_copied; - ptr += len_copied; - lba++; - if (lba>lba_mask && len) - { - lba=0; - iwlba = SBig(d->wlba_table[++wlba]); - if (!iwlba) - return 1; - } - } - while (len>=p->hd_sec_sz) - { - uint32_t nlb = len>>(p->hd_sec_sz_s); + p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec / 8) >> p->hd_sec_sz_s; - if (lba + nlb > p->wbfs_sec_sz) // dont cross wbfs sectors.. - nlb = p->wbfs_sec_sz-lba; - err = wbfsReadSector(p->part_lba + (iwlba<hd_sec_sz_s; - ptr += nlb<hd_sec_sz_s; - lba += nlb; - if (lba>lba_mask && len) - { - lba = 0; - iwlba = SBig(d->wlba_table[++wlba]); - if (!iwlba) - return 1; - } - } - if (len) - { - err = wbfsReadSector(p->part_lba + (iwlba<freeblks = 0; // will alloc and read only if needed + p->max_disc = (p->freeblks_lba - 1) / (p->disc_info_sz >> p->hd_sec_sz_s); + if (p->max_disc > p->hd_sec_sz - sizeof(WBFSHead)) + p->max_disc = p->hd_sec_sz - sizeof(WBFSHead); - public: - uint64_t read(void* buf, uint64_t length) - { - uint8_t extra[4]; - uint64_t rem_offset = m_offset % 4; - if (rem_offset) - { - uint64_t rem_rem = 4 - rem_offset; - if (wbfsDiscRead((uint32_t)(m_offset / 4), extra, 4)) - return 0; - memcpy(buf, extra + rem_offset, rem_rem); - if (wbfsDiscRead((uint32_t)(m_offset / 4 + 1), (uint8_t*)buf + rem_rem, length - rem_rem)) - return 0; - } - else - { - if (wbfsDiscRead((uint32_t)(m_offset / 4), (uint8_t*)buf, length)) - return 0; - } - m_offset += length; - return length; - } - uint64_t position() const {return m_offset;} - void seek(int64_t offset, int whence) - { - if (whence == SEEK_SET) - m_offset = offset; - else if (whence == SEEK_CUR) - m_offset += offset; - } - }; + p->n_disc_open = 0; - std::unique_ptr beginReadStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset, Err)); - if (Err) - return {}; - return ret; + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + if (head->disc_table[0]) { + wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]); + if (!wbfsDiscInfo) { + LogModule.report(logvisor::Error, "allocating memory"); + return; + } + if (_wbfsReadSector(*rs, p->part_lba + 1, disc_info_sz_lba, wbfsDiscInfo.get())) + return; + p->n_disc_open++; + // for(i=0;in_wbfs_sec_per_disc;i++) + // printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); + } + } + + class ReadStream : public IReadStream { + friend class DiscIOWBFS; + const DiscIOWBFS& m_parent; + std::unique_ptr fp; + uint64_t m_offset; + std::unique_ptr m_tmpBuffer; + + ReadStream(const DiscIOWBFS& parent, std::unique_ptr&& fpin, uint64_t offset, bool& err) + : m_parent(parent), fp(std::move(fpin)), m_offset(offset), m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) { + if (!fp) + err = true; } - std::unique_ptr beginWriteStream(uint64_t offset) const - { - return std::unique_ptr(); + int wbfsReadSector(uint32_t lba, uint32_t count, void* buf) { + return DiscIOWBFS::_wbfsReadSector(*fp, lba, count, buf); } + + int wbfsDiscRead(uint32_t offset, uint8_t* data, uint64_t len) { + const WBFS* p = &m_parent.wbfs; + const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get(); + uint16_t wlba = offset >> (p->wbfs_sec_sz_s - 2); + uint32_t iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s; + uint32_t lba_mask = (p->wbfs_sec_sz - 1) >> (p->hd_sec_sz_s); + uint64_t lba = (offset >> (p->hd_sec_sz_s - 2)) & lba_mask; + uint64_t off = offset & ((p->hd_sec_sz >> 2) - 1); + uint16_t iwlba = SBig(d->wlba_table[wlba]); + uint64_t len_copied; + int err = 0; + uint8_t* ptr = data; + if (!iwlba) + return 1; + if (off) { + off *= 4; + err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, 1, m_tmpBuffer.get()); + if (err) + return err; + len_copied = p->hd_sec_sz - off; + if (len < len_copied) + len_copied = len; + memcpy(ptr, m_tmpBuffer.get() + off, len_copied); + len -= len_copied; + ptr += len_copied; + lba++; + if (lba > lba_mask && len) { + lba = 0; + iwlba = SBig(d->wlba_table[++wlba]); + if (!iwlba) + return 1; + } + } + while (len >= p->hd_sec_sz) { + uint32_t nlb = len >> (p->hd_sec_sz_s); + + if (lba + nlb > p->wbfs_sec_sz) // dont cross wbfs sectors.. + nlb = p->wbfs_sec_sz - lba; + err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, nlb, ptr); + if (err) + return err; + len -= nlb << p->hd_sec_sz_s; + ptr += nlb << p->hd_sec_sz_s; + lba += nlb; + if (lba > lba_mask && len) { + lba = 0; + iwlba = SBig(d->wlba_table[++wlba]); + if (!iwlba) + return 1; + } + } + if (len) { + err = wbfsReadSector(p->part_lba + (iwlba << iwlba_shift) + lba, 1, m_tmpBuffer.get()); + if (err) + return err; + memcpy(ptr, m_tmpBuffer.get(), len); + } + return 0; + } + + public: + uint64_t read(void* buf, uint64_t length) { + uint8_t extra[4]; + uint64_t rem_offset = m_offset % 4; + if (rem_offset) { + uint64_t rem_rem = 4 - rem_offset; + if (wbfsDiscRead((uint32_t)(m_offset / 4), extra, 4)) + return 0; + memcpy(buf, extra + rem_offset, rem_rem); + if (wbfsDiscRead((uint32_t)(m_offset / 4 + 1), (uint8_t*)buf + rem_rem, length - rem_rem)) + return 0; + } else { + if (wbfsDiscRead((uint32_t)(m_offset / 4), (uint8_t*)buf, length)) + return 0; + } + m_offset += length; + return length; + } + uint64_t position() const { return m_offset; } + void seek(int64_t offset, int whence) { + if (whence == SEEK_SET) + m_offset = offset; + else if (whence == SEEK_CUR) + m_offset += offset; + } + }; + + std::unique_ptr beginReadStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset, Err)); + if (Err) + return {}; + return ret; + } + + std::unique_ptr beginWriteStream(uint64_t offset) const { return std::unique_ptr(); } }; -std::unique_ptr NewDiscIOWBFS(SystemStringView path) -{ - return std::unique_ptr(new DiscIOWBFS(path)); -} - -} +std::unique_ptr NewDiscIOWBFS(SystemStringView path) { return std::unique_ptr(new DiscIOWBFS(path)); } +} // namespace nod diff --git a/lib/DiscWii.cpp b/lib/DiscWii.cpp index da2e445..c44d950 100644 --- a/lib/DiscWii.cpp +++ b/lib/DiscWii.cpp @@ -7,1486 +7,1340 @@ #include "nod/sha1.h" #include "nod/nod.hpp" -namespace nod -{ +namespace nod { -static const uint8_t COMMON_KEYS[2][16] = -{ +static const uint8_t COMMON_KEYS[2][16] = { /* Normal */ - {0xeb, 0xe4, 0x2a, 0x22, - 0x5e, 0x85, 0x93, 0xe4, - 0x48, 0xd9, 0xc5, 0x45, - 0x73, 0x81, 0xaa, 0xf7}, + {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}, /* Korean */ - {0x63, 0xb8, 0x2b, 0xb4, - 0xf4, 0x61, 0x4e, 0x2e, - 0x13, 0xf2, 0xfe, 0xfb, - 0xba, 0x4c, 0x9b, 0x7e} -}; + {0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e, 0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e}}; -class PartitionWii : public IPartition -{ - enum class SigType : uint32_t - { - RSA_4096 = 0x00010000, - RSA_2048 = 0x00010001, - ELIPTICAL_CURVE = 0x00010002 +class PartitionWii : public IPartition { + enum class SigType : uint32_t { RSA_4096 = 0x00010000, RSA_2048 = 0x00010001, ELIPTICAL_CURVE = 0x00010002 }; + + enum class KeyType : uint32_t { RSA_4096 = 0x00000000, RSA_2048 = 0x00000001 }; + + struct Ticket { + uint32_t sigType; + char sig[256]; + char padding[60]; + char sigIssuer[64]; + char ecdh[60]; + char padding1[3]; + unsigned char encKey[16]; + char padding2; + char ticketId[8]; + char consoleId[4]; + char titleId[8]; + char padding3[2]; + uint16_t ticketVersion; + uint32_t permittedTitlesMask; + uint32_t permitMask; + char titleExportAllowed; + char commonKeyIdx; + char padding4[48]; + char contentAccessPermissions[64]; + char padding5[2]; + struct TimeLimit { + uint32_t enableTimeLimit; + uint32_t timeLimit; + } timeLimits[8]; + + void read(IReadStream& s) { + s.read(this, 676); + sigType = SBig(sigType); + ticketVersion = SBig(ticketVersion); + permittedTitlesMask = SBig(permittedTitlesMask); + permitMask = SBig(permitMask); + for (size_t t = 0; t < 8; ++t) { + timeLimits[t].enableTimeLimit = SBig(timeLimits[t].enableTimeLimit); + 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 { + SigType sigType; + char sig[256]; + char padding[60]; + char sigIssuer[64]; + char version; + char caCrlVersion; + char signerCrlVersion; + char padding1; + uint32_t iosIdMajor; + uint32_t iosIdMinor; + uint32_t titleIdMajor; + char titleIdMinor[4]; + uint32_t titleType; + uint16_t groupId; + char padding2[62]; + uint32_t accessFlags; + uint16_t titleVersion; + uint16_t numContents; + uint16_t bootIdx; + uint16_t padding3; + + struct Content { + uint32_t id; + uint16_t index; + uint16_t type; + uint64_t size; + char hash[20]; + + void read(IReadStream& s) { + s.read(this, 36); + id = SBig(id); + index = SBig(index); + 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; - enum class KeyType : uint32_t - { - RSA_4096 = 0x00000000, - RSA_2048 = 0x00000001 - }; + void read(IReadStream& s) { + s.read(this, 484); + sigType = SigType(SBig(uint32_t(sigType))); + iosIdMajor = SBig(iosIdMajor); + iosIdMinor = SBig(iosIdMinor); + titleIdMajor = SBig(titleIdMajor); + titleType = SBig(titleType); + groupId = SBig(groupId); + accessFlags = SBig(accessFlags); + titleVersion = SBig(titleVersion); + numContents = SBig(numContents); + bootIdx = SBig(bootIdx); + contents.clear(); + contents.reserve(numContents); + for (uint16_t c = 0; c < numContents; ++c) { + contents.emplace_back(); + contents.back().read(s); + } + } - struct Ticket - { - uint32_t sigType; - char sig[256]; - char padding[60]; - char sigIssuer[64]; - char ecdh[60]; - char padding1[3]; - unsigned char encKey[16]; - char padding2; - char ticketId[8]; - char consoleId[4]; - char titleId[8]; - char padding3[2]; - uint16_t ticketVersion; - uint32_t permittedTitlesMask; - uint32_t permitMask; - char titleExportAllowed; - char commonKeyIdx; - char padding4[48]; - char contentAccessPermissions[64]; - char padding5[2]; - struct TimeLimit - { - uint32_t enableTimeLimit; - uint32_t timeLimit; - } timeLimits[8]; + 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; - void read(IReadStream& s) - { - s.read(this, 676); - sigType = SBig(sigType); - ticketVersion = SBig(ticketVersion); - permittedTitlesMask = SBig(permittedTitlesMask); - permitMask = SBig(permitMask); - for (size_t t=0 ; t<8 ; ++t) - { - timeLimits[t].enableTimeLimit = SBig(timeLimits[t].enableTimeLimit); - timeLimits[t].timeLimit = SBig(timeLimits[t].timeLimit); - } - } + struct Certificate { + SigType sigType; + char sig[512]; + char issuer[64]; + KeyType keyType; + char subject[64]; + char key[512]; + uint32_t modulus; + uint32_t pubExp; - 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; + void read(IReadStream& s) { + s.read(&sigType, 4); + sigType = SigType(SBig(uint32_t(sigType))); + if (sigType == SigType::RSA_4096) + s.read(sig, 512); + else if (sigType == SigType::RSA_2048) + s.read(sig, 256); + else if (sigType == SigType::ELIPTICAL_CURVE) + s.read(sig, 64); + s.seek(60, SEEK_CUR); - struct TMD - { - SigType sigType; - char sig[256]; - char padding[60]; - char sigIssuer[64]; - char version; - char caCrlVersion; - char signerCrlVersion; - char padding1; - uint32_t iosIdMajor; - uint32_t iosIdMinor; - uint32_t titleIdMajor; - char titleIdMinor[4]; - uint32_t titleType; - uint16_t groupId; - char padding2[62]; - uint32_t accessFlags; - uint16_t titleVersion; - uint16_t numContents; - uint16_t bootIdx; - uint16_t padding3; + s.read(issuer, 64); + s.read(&keyType, 4); + s.read(subject, 64); + keyType = KeyType(SBig(uint32_t(keyType))); + if (keyType == KeyType::RSA_4096) + s.read(key, 512); + else if (keyType == KeyType::RSA_2048) + s.read(key, 256); - struct Content - { - uint32_t id; - uint16_t index; - uint16_t type; - uint64_t size; - char hash[20]; + s.read(&modulus, 8); + modulus = SBig(modulus); + pubExp = SBig(pubExp); - void read(IReadStream& s) - { - s.read(this, 36); - id = SBig(id); - index = SBig(index); - type = SBig(type); - size = SBig(size); - } + s.seek(52, SEEK_CUR); + } - 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 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); - void read(IReadStream& s) - { - s.read(this, 484); - sigType = SigType(SBig(uint32_t(sigType))); - iosIdMajor = SBig(iosIdMajor); - iosIdMinor = SBig(iosIdMinor); - titleIdMajor = SBig(titleIdMajor); - titleType = SBig(titleType); - groupId = SBig(groupId); - accessFlags = SBig(accessFlags); - titleVersion = SBig(titleVersion); - numContents = SBig(numContents); - bootIdx = SBig(bootIdx); - contents.clear(); - contents.reserve(numContents); - for (uint16_t c=0 ; c m_h3Data; - s.read(&modulus, 8); - modulus = SBig(modulus); - pubExp = SBig(pubExp); - - 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_tmdCert; - Certificate m_ticketCert; - - std::unique_ptr m_h3Data; - - uint64_t m_dataOff; - uint8_t m_decKey[16]; + uint64_t m_dataOff; + uint8_t m_decKey[16]; public: - PartitionWii(const DiscWii& parent, PartitionKind kind, uint64_t offset, bool& err) - : IPartition(parent, kind, true, offset) - { - std::unique_ptr s = parent.getDiscIO().beginReadStream(offset); - if (!s) - { - err = true; - return; - } - - m_ticket.read(*s); - - uint32_t tmdSize; - s->read(&tmdSize, 4); - tmdSize = SBig(tmdSize); - uint32_t tmdOff; - s->read(&tmdOff, 4); - tmdOff = SBig(tmdOff) << 2; - - uint32_t certChainSize; - s->read(&certChainSize, 4); - certChainSize = SBig(certChainSize); - uint32_t certChainOff; - s->read(&certChainOff, 4); - certChainOff = SBig(certChainOff) << 2; - - uint32_t globalHashTableOff; - s->read(&globalHashTableOff, 4); - globalHashTableOff = SBig(globalHashTableOff) << 2; - - uint32_t dataOff; - s->read(&dataOff, 4); - dataOff = SBig(dataOff) << 2; - m_dataOff = offset + dataOff; - uint32_t dataSize; - s->read(&dataSize, 4); - dataSize = SBig(dataSize) << 2; - - s->seek(offset + tmdOff); - m_tmd.read(*s); - - s->seek(offset + certChainOff); - m_caCert.read(*s); - 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] = {}; - 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(0x0); - if (!ds) - { - err = true; - return; - } - 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); - m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]); - - /* Yay files!! */ - parseFST(*ds); - - /* Also make DOL header and size handy */ - ds->seek(m_dolOff); - parseDOL(*ds); + PartitionWii(const DiscWii& parent, PartitionKind kind, uint64_t offset, bool& err) + : IPartition(parent, kind, true, offset) { + std::unique_ptr s = parent.getDiscIO().beginReadStream(offset); + if (!s) { + err = true; + return; } - class PartReadStream : public IPartReadStream - { - std::unique_ptr m_aes; - const PartitionWii& m_parent; - uint64_t m_baseOffset; - uint64_t m_offset; - std::unique_ptr m_dio; + m_ticket.read(*s); - size_t m_curBlock = SIZE_MAX; - uint8_t m_encBuf[0x8000]; - uint8_t m_decBuf[0x7c00]; + uint32_t tmdSize; + s->read(&tmdSize, 4); + tmdSize = SBig(tmdSize); + uint32_t tmdOff; + s->read(&tmdOff, 4); + tmdOff = SBig(tmdOff) << 2; - void decryptBlock() - { - m_dio->read(m_encBuf, 0x8000); - m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00); - } - public: - PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset, bool& err) - : m_aes(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) - { - m_aes->setKey(parent.m_decKey); - size_t block = m_offset / 0x7c00; - m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000); - if (!m_dio) - { - err = true; - return; - } - decryptBlock(); - m_curBlock = block; - } - void seek(int64_t offset, int whence) - { - if (whence == SEEK_SET) - m_offset = offset; - else if (whence == SEEK_CUR) - m_offset += offset; - else - return; - size_t block = m_offset / 0x7c00; - if (block != m_curBlock) - { - m_dio->seek(m_baseOffset + block * 0x8000); - decryptBlock(); - m_curBlock = block; - } - } - uint64_t position() const {return m_offset;} - uint64_t read(void* buf, uint64_t length) - { - size_t block = m_offset / 0x7c00; - size_t cacheOffset = m_offset % 0x7c00; - uint64_t cacheSize; - uint64_t rem = length; - uint8_t* dst = (uint8_t*)buf; + uint32_t certChainSize; + s->read(&certChainSize, 4); + certChainSize = SBig(certChainSize); + uint32_t certChainOff; + s->read(&certChainOff, 4); + certChainOff = SBig(certChainOff) << 2; - while (rem) - { - if (block != m_curBlock) - { - decryptBlock(); - m_curBlock = block; - } + uint32_t globalHashTableOff; + s->read(&globalHashTableOff, 4); + globalHashTableOff = SBig(globalHashTableOff) << 2; - cacheSize = rem; - if (cacheSize + cacheOffset > 0x7c00) - cacheSize = 0x7c00 - cacheOffset; + uint32_t dataOff; + s->read(&dataOff, 4); + dataOff = SBig(dataOff) << 2; + m_dataOff = offset + dataOff; + uint32_t dataSize; + s->read(&dataSize, 4); + dataSize = SBig(dataSize) << 2; - memmove(dst, m_decBuf + cacheOffset, cacheSize); - dst += cacheSize; - rem -= cacheSize; - cacheOffset = 0; - ++block; - } + s->seek(offset + tmdOff); + m_tmd.read(*s); - m_offset += length; - return dst - (uint8_t*)buf; - } - }; + s->seek(offset + certChainOff); + m_caCert.read(*s); + m_tmdCert.read(*s); + m_ticketCert.read(*s); - std::unique_ptr beginReadStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new PartReadStream(*this, m_dataOff, offset, Err)); - if (Err) - return {}; - return ret; + 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] = {}; + 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(0x0); + if (!ds) { + err = true; + return; + } + 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); + m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]); + + /* Yay files!! */ + parseFST(*ds); + + /* Also make DOL header and size handy */ + ds->seek(m_dolOff); + parseDOL(*ds); + } + + class PartReadStream : public IPartReadStream { + std::unique_ptr m_aes; + const PartitionWii& m_parent; + uint64_t m_baseOffset; + uint64_t m_offset; + std::unique_ptr m_dio; + + size_t m_curBlock = SIZE_MAX; + uint8_t m_encBuf[0x8000]; + uint8_t m_decBuf[0x7c00]; + + void decryptBlock() { + m_dio->read(m_encBuf, 0x8000); + m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00); } - uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset << 2;} - - std::unique_ptr readPartitionHeaderBuf(size_t& szOut) const - { - { - std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4); - if (!rs) - return {}; - - uint32_t h3; - if (rs->read(&h3, 4) != 4) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to read H3 offset apploader")); - return {}; - } - h3 = SBig(h3); - szOut = uint64_t(h3) << 2; - } - - std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset); - if (!rs) - return {}; - - std::unique_ptr buf(new uint8_t[szOut]); - rs->read(buf.get(), szOut); - - return buf; - } - - bool extractCryptoFiles(SystemStringView basePath, const ExtractionContext& ctx) const - { - Sstat theStat; - SystemString basePathStr(basePath); - - /* Extract Ticket */ - SystemString ticketPath = basePathStr + _SYS_STR("/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; - m_ticket.write(*ws); - } - - /* Extract TMD */ - SystemString tmdPath = basePathStr + _SYS_STR("/tmd.bin"); - if (ctx.force || Stat(tmdPath.c_str(), &theStat)) - { - 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 = basePathStr + _SYS_STR("/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 = basePathStr + _SYS_STR("/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; - } -}; - -DiscWii::DiscWii(std::unique_ptr&& dio, bool& err) -: DiscBase(std::move(dio), err) -{ - if (err) + public: + PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset, bool& err) + : m_aes(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) { + m_aes->setKey(parent.m_decKey); + size_t block = m_offset / 0x7c00; + m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000); + if (!m_dio) { + err = true; return; - - /* Read partition info */ - struct PartInfo - { - uint32_t partCount; - uint32_t partInfoOff; - struct Part - { - uint32_t partDataOff; - PartitionKind partType; - } parts[4]; - PartInfo(IDiscIO& dio, bool& err) - { - std::unique_ptr s = dio.beginReadStream(0x40000); - if (!s) - { - err = true; - return; - } - - s->read(this, 32); - partCount = SBig(partCount); - partInfoOff = SBig(partInfoOff); - - s->seek(partInfoOff << 2); - for (uint32_t p=0 ; pread(&parts[p], 8); - parts[p].partDataOff = SBig(parts[p].partDataOff); - parts[p].partType = PartitionKind(SBig(uint32_t(parts[p].partType))); - } - } - } partInfo(*m_discIO, err); - if (err) - return; - - /* Iterate for data partition */ - m_partitions.reserve(partInfo.partCount); - for (uint32_t p=0 ; pseek(m_baseOffset + block * 0x8000); + decryptBlock(); + m_curBlock = block; + } + } + uint64_t position() const { return m_offset; } + uint64_t read(void* buf, uint64_t length) { + size_t block = m_offset / 0x7c00; + size_t cacheOffset = m_offset % 0x7c00; + uint64_t cacheSize; + uint64_t rem = length; + uint8_t* dst = (uint8_t*)buf; -DiscBuilderWii DiscWii::makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB) -{ - return DiscBuilderWii(outPath, dualLayer, progressCB); -} + while (rem) { + if (block != m_curBlock) { + decryptBlock(); + m_curBlock = block; + } -bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const ExtractionContext& ctx) const -{ + cacheSize = rem; + if (cacheSize + cacheOffset > 0x7c00) + cacheSize = 0x7c00 - cacheOffset; + + memmove(dst, m_decBuf + cacheOffset, cacheSize); + dst += cacheSize; + rem -= cacheSize; + cacheOffset = 0; + ++block; + } + + m_offset += length; + return dst - (uint8_t*)buf; + } + }; + + std::unique_ptr beginReadStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new PartReadStream(*this, m_dataOff, offset, Err)); + if (Err) + return {}; + return ret; + } + + uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset << 2; } + + std::unique_ptr readPartitionHeaderBuf(size_t& szOut) const { + { + std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4); + if (!rs) + return {}; + + uint32_t h3; + if (rs->read(&h3, 4) != 4) { + LogModule.report(logvisor::Error, _SYS_STR("unable to read H3 offset apploader")); + return {}; + } + h3 = SBig(h3); + szOut = uint64_t(h3) << 2; + } + + std::unique_ptr rs = m_parent.getDiscIO().beginReadStream(m_offset); + if (!rs) + return {}; + + std::unique_ptr buf(new uint8_t[szOut]); + rs->read(buf.get(), szOut); + + return buf; + } + + bool extractCryptoFiles(SystemStringView basePath, const ExtractionContext& ctx) const { + Sstat theStat; SystemString basePathStr(basePath); - if (Mkdir((basePathStr + _SYS_STR("/disc")).c_str(), 0755) && errno != EEXIST) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s/disc'"), basePathStr.c_str()); + /* Extract Ticket */ + SystemString ticketPath = basePathStr + _SYS_STR("/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; + m_ticket.write(*ws); } - Sstat theStat; - - /* Extract Header */ - SystemString headerPath = basePathStr + _SYS_STR("/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 TMD */ + SystemString tmdPath = basePathStr + _SYS_STR("/tmd.bin"); + if (ctx.force || Stat(tmdPath.c_str(), &theStat)) { + if (ctx.progressCB) + ctx.progressCB("tmd.bin", 0.f); + auto ws = NewFileIO(tmdPath)->beginWriteStream(); + if (!ws) + return false; + m_tmd.write(*ws); } - /* Extract Region info */ - SystemString regionPath = basePathStr + _SYS_STR("/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); + /* Extract Certs */ + SystemString certPath = basePathStr + _SYS_STR("/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 = basePathStr + _SYS_STR("/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; + } +}; + +DiscWii::DiscWii(std::unique_ptr&& dio, bool& err) : DiscBase(std::move(dio), err) { + if (err) + return; + + /* Read partition info */ + struct PartInfo { + uint32_t partCount; + uint32_t partInfoOff; + struct Part { + uint32_t partDataOff; + PartitionKind partType; + } parts[4]; + PartInfo(IDiscIO& dio, bool& err) { + std::unique_ptr s = dio.beginReadStream(0x40000); + if (!s) { + err = true; + return; + } + + s->read(this, 32); + partCount = SBig(partCount); + partInfoOff = SBig(partInfoOff); + + s->seek(partInfoOff << 2); + for (uint32_t p = 0; p < partCount && p < 4; ++p) { + s->read(&parts[p], 8); + parts[p].partDataOff = SBig(parts[p].partDataOff); + parts[p].partType = PartitionKind(SBig(uint32_t(parts[p].partType))); + } + } + } partInfo(*m_discIO, err); + if (err) + return; + + /* Iterate for data partition */ + m_partitions.reserve(partInfo.partCount); + for (uint32_t p = 0; p < partInfo.partCount && p < 4; ++p) { + PartInfo::Part& part = partInfo.parts[p]; + PartitionKind kind; + switch (part.partType) { + case PartitionKind::Data: + case PartitionKind::Update: + case PartitionKind::Channel: + kind = part.partType; + break; + default: + LogModule.report(logvisor::Error, "invalid partition type %d", part.partType); + err = true; + return; + } + m_partitions.emplace_back(new PartitionWii(*this, kind, part.partDataOff << 2, err)); + if (err) + return; + } +} + +DiscBuilderWii DiscWii::makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB) { + return DiscBuilderWii(outPath, dualLayer, progressCB); +} + +bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const ExtractionContext& ctx) const { + SystemString basePathStr(basePath); + + if (Mkdir((basePathStr + _SYS_STR("/disc")).c_str(), 0755) && errno != EEXIST) { + LogModule.report(logvisor::Error, _SYS_STR("unable to mkdir '%s/disc'"), basePathStr.c_str()); + return false; + } + + Sstat theStat; + + /* Extract Header */ + SystemString headerPath = basePathStr + _SYS_STR("/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 = basePathStr + _SYS_STR("/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}; -class PartitionBuilderWii : public DiscBuilderBase::PartitionBuilderBase -{ - friend class DiscBuilderWii; - friend class DiscMergerWii; +class PartitionBuilderWii : public DiscBuilderBase::PartitionBuilderBase { + friend class DiscBuilderWii; + friend class DiscMergerWii; - uint64_t m_baseOffset; - uint64_t m_userOffset = 0; - uint64_t m_curUser = 0x1F0000; - std::unique_ptr m_aes; - uint8_t m_h3[4916][20] = {}; + uint64_t m_baseOffset; + uint64_t m_userOffset = 0; + uint64_t m_curUser = 0x1F0000; + std::unique_ptr m_aes; + uint8_t m_h3[4916][20] = {}; public: - class PartWriteStream : public IPartWriteStream - { - friend class PartitionBuilderWii; - PartitionBuilderWii& m_parent; - uint64_t m_baseOffset; - uint64_t m_offset; - std::unique_ptr m_fio; - bool m_closed = false; + class PartWriteStream : public IPartWriteStream { + friend class PartitionBuilderWii; + PartitionBuilderWii& m_parent; + uint64_t m_baseOffset; + uint64_t m_offset; + std::unique_ptr m_fio; + bool m_closed = false; - size_t m_curGroup = SIZE_MAX; - char m_buf[0x200000]; + size_t m_curGroup = SIZE_MAX; + char m_buf[0x200000]; - void encryptGroup(uint8_t h3Out[20]) - { - sha1nfo sha; - uint8_t h2[8][20]; + void encryptGroup(uint8_t h3Out[20]) { + sha1nfo sha; + uint8_t h2[8][20]; - for (int s=0 ; s<8 ; ++s) - { - char* ptr1 = m_buf + s*0x40000; - uint8_t h1[8][20]; + for (int s = 0; s < 8; ++s) { + char* ptr1 = m_buf + s * 0x40000; + uint8_t h1[8][20]; - for (int c=0 ; c<8 ; ++c) - { - char* ptr0 = ptr1 + c*0x8000; - uint8_t h0[31][20]; - - for (int j=0 ; j<31 ; ++j) - { - sha1_init(&sha); - sha1_write(&sha, ptr0 + (j+1)*0x400, 0x400); - memmove(h0[j], sha1_result(&sha), 20); - } - - sha1_init(&sha); - sha1_write(&sha, (char*)h0, 0x26C); - memmove(h1[c], sha1_result(&sha), 20); - - memmove(ptr0, h0, 0x26C); - memset(ptr0+0x26C, 0, 0x014); - } - - sha1_init(&sha); - sha1_write(&sha, (char*)h1, 0x0A0); - memmove(h2[s], sha1_result(&sha), 20); - - for (int c=0 ; c<8 ; ++c) - { - char* ptr0 = ptr1 + c*0x8000; - memmove(ptr0+0x280, h1, 0x0A0); - memset(ptr0+0x320, 0, 0x020); - } - } + for (int c = 0; c < 8; ++c) { + char* ptr0 = ptr1 + c * 0x8000; + uint8_t h0[31][20]; + for (int j = 0; j < 31; ++j) { sha1_init(&sha); - sha1_write(&sha, (char*)h2, 0x0A0); - memmove(h3Out, sha1_result(&sha), 20); + sha1_write(&sha, ptr0 + (j + 1) * 0x400, 0x400); + memmove(h0[j], sha1_result(&sha), 20); + } - for (int s=0 ; s<8 ; ++s) - { - char* ptr1 = m_buf + s*0x40000; - for (int c=0 ; c<8 ; ++c) - { - char* ptr0 = ptr1 + c*0x8000; - 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); - } - } + sha1_init(&sha); + sha1_write(&sha, (char*)h0, 0x26C); + memmove(h1[c], sha1_result(&sha), 20); - if (m_fio->write(m_buf, 0x200000) != 0x200000) - { - LogModule.report(logvisor::Error, "unable to write full disc group"); - return; - } + memmove(ptr0, h0, 0x26C); + memset(ptr0 + 0x26C, 0, 0x014); } - public: - PartWriteStream(PartitionBuilderWii& parent, uint64_t baseOffset, uint64_t offset, bool& err) - : m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) - { - if (offset % 0x1F0000) - { - LogModule.report(logvisor::Error, "partition write stream MUST begin on 0x1F0000-aligned boundary"); - err = true; - return; - } - size_t group = m_offset / 0x1F0000; - m_fio = m_parent.m_parent.getFileIO().beginWriteStream(m_baseOffset + group * 0x200000); - if (!m_fio) - err = true; - m_curGroup = group; - } - ~PartWriteStream() {close();} - void close() - { - if (m_closed) - return; - m_closed = true; - size_t rem = m_offset % 0x1F0000; - if (rem) - { - rem = 0x1F0000 - rem; - write(nullptr, rem); - } - encryptGroup(m_parent.m_h3[m_curGroup]); - m_fio.reset(); - } - uint64_t position() const {return m_offset;} - uint64_t write(const void* buf, uint64_t length) - { - size_t group = m_offset / 0x1F0000; - size_t block = (m_offset - group * 0x1F0000) / 0x7c00; - size_t cacheOffset = m_offset % 0x7c00; - uint64_t cacheSize; - uint64_t rem = length; - const uint8_t* src = (uint8_t*)buf; - - while (rem) - { - if (group != m_curGroup) - { - encryptGroup(m_parent.m_h3[m_curGroup]); - m_curGroup = group; - } - - cacheSize = rem; - if (cacheSize + cacheOffset > 0x7c00) - cacheSize = 0x7c00 - cacheOffset; - - if (src) - { - memmove(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize); - src += cacheSize; - } - else - memset(m_buf + block * 0x8000 + 0x400 + cacheOffset, 0, cacheSize); - - rem -= cacheSize; - cacheOffset = 0; - ++block; - if (block == 64) - { - block = 0; - ++group; - } - } - - m_offset += length; - return length; - } - }; - - PartitionBuilderWii(DiscBuilderBase& parent, PartitionKind kind, uint64_t baseOffset) - : DiscBuilderBase::PartitionBuilderBase(parent, kind, true), - m_baseOffset(baseOffset), m_aes(NewAES()) {} - - uint64_t getCurUserEnd() const {return m_curUser;} - - uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) - { - reqSz = ROUND_UP_32(reqSz); - if (m_curUser + reqSz >= 0x1FB450000) - { - LogModule.report(logvisor::Error, "partition exceeds maximum single-partition capacity"); - return -1; - } - uint64_t ret = m_curUser; - PartWriteStream& cws = static_cast(ws); - if (cws.m_offset > ret) - { - LogModule.report(logvisor::Error, "partition overwrite error"); - return -1; - } - while (cws.m_offset < ret) - cws.write("\xff", 1); - m_curUser += reqSz; - return ret; - } - - uint32_t packOffset(uint64_t offset) const - { - return uint32_t(offset >> uint64_t(2)); - } - - std::unique_ptr beginWriteStream(uint64_t offset) - { - bool Err = false; - std::unique_ptr ret = - std::make_unique(*this, m_baseOffset + m_userOffset, offset, Err); - if (Err) - return {}; - return ret; - } - - uint64_t _build(const std::function& tmdData, size_t& tmdSz)>& cryptoFunc, - const std::function& headerFunc, - const std::function& bi2Func, - const std::function& apploaderFunc, - const std::function& contentFunc, - size_t apploaderSz) - { - /* Write partition head up to H3 table */ - std::unique_ptr ws = m_parent.getFileIO().beginWriteStream(m_baseOffset); - if (!ws) - return -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]); - m_aes->decrypt(tkeyiv, tkey, tkey, 16); - m_aes->setKey(tkey); - - { - /* Assemble partition data */ - std::unique_ptr cws = beginWriteStream(0x1F0000); - if (!cws) - return -1; - if (!contentFunc(*cws)) - return -1; - - /* Pad out user area to nearest cleartext sector */ - m_curUser = cws->position(); - uint64_t curUserRem = m_curUser % 0x1F0000; - if (curUserRem) - { - curUserRem = 0x1F0000 - curUserRem; - for (size_t i=0 ; iwrite("\xff", 1); - m_curUser += curUserRem; - } - - /* Begin crypto write and add content header */ - cws = beginWriteStream(0); - if (!cws) - return -1; - - /* Compute boot table members and write */ - size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz); - size_t fstSz = sizeof(FSTNode) * m_buildNodes.size(); - fstSz += m_buildNameOff; - fstSz = ROUND_UP_32(fstSz); - - if (fstOff + fstSz >= 0x1F0000) - { - LogModule.report(logvisor::Error, - "FST flows into user area (one or the other is too big)"); - return -1; - } - - if (!headerFunc(*cws, m_dolOffset, fstOff, fstSz)) - return -1; - - if (!bi2Func(*cws)) - return -1; - - size_t xferSz = 0; - if (!apploaderFunc(*cws, xferSz)) - return -1; - - size_t fstOffRel = fstOff - 0x2440; - if (xferSz > fstOffRel) - { - LogModule.report(logvisor::Error, "apploader unexpectedly flows into FST"); - return -1; - } - for (size_t i=0 ; iwrite("\xff", 1); - - /* Write FST */ - cws->write(m_buildNodes.data(), m_buildNodes.size() * sizeof(FSTNode)); - for (const std::string& str : m_buildNames) - cws->write(str.data(), str.size()+1); - } - - /* Write new crypto content size */ - uint64_t groupCount = m_curUser / 0x1F0000; - uint64_t cryptContentSize = (groupCount * 0x200000) >> uint64_t(2); - uint32_t cryptContentSizeBig = SBig(uint32_t(cryptContentSize)); - ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2BC); - if (!ws) - return -1; - ws->write(&cryptContentSizeBig, 0x4); - - /* Write new H3 */ - ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + h3Off); - if (!ws) - return -1; - ws->write(m_h3, 0x18000); - - /* Compute content hash and replace in TMD */ - sha1nfo sha; sha1_init(&sha); - sha1_write(&sha, (char*)m_h3, 0x18000); - memmove(tmdData.get() + 0x1F4, sha1_result(&sha), 20); + sha1_write(&sha, (char*)h1, 0x0A0); + memmove(h2[s], sha1_result(&sha), 20); - /* Same for content size */ - uint64_t contentSize = groupCount * 0x1F0000; - uint64_t contentSizeBig = SBig(contentSize); - memmove(tmdData.get() + 0x1EC, &contentSizeBig, 8); + for (int c = 0; c < 8; ++c) { + char* ptr0 = ptr1 + c * 0x8000; + memmove(ptr0 + 0x280, h1, 0x0A0); + memset(ptr0 + 0x320, 0, 0x020); + } + } - /* Zero-out TMD signature to simplify brute-force */ - memset(tmdData.get() + 0x4, 0, 0x100); + sha1_init(&sha); + sha1_write(&sha, (char*)h2, 0x0A0); + memmove(h3Out, sha1_result(&sha), 20); - /* Brute-force zero-starting hash */ - size_t tmdCheckSz = tmdSz - 0x140; - struct BFWindow - { - uint64_t word[7]; - }* bfWindow = (BFWindow*)(tmdData.get() + 0x19A); - bool good = false; - uint64_t attempts = 0; - SystemString bfName(_SYS_STR("Brute force attempts")); - for (int w=0 ; w<7 ; ++w) - { - for (uint64_t i=0 ; iword[w] = i; - sha1_init(&sha); - sha1_write(&sha, (char*)(tmdData.get() + 0x140), tmdCheckSz); - uint8_t* hash = sha1_result(&sha); - ++attempts; - if (hash[0] == 0) - { - good = true; - break; - } - m_parent.m_progressCB(m_parent.getProgressFactor(), bfName, attempts); - } - if (good) - break; + for (int s = 0; s < 8; ++s) { + char* ptr1 = m_buf + s * 0x40000; + for (int c = 0; c < 8; ++c) { + char* ptr0 = ptr1 + c * 0x8000; + 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); + } + } + + if (m_fio->write(m_buf, 0x200000) != 0x200000) { + LogModule.report(logvisor::Error, "unable to write full disc group"); + return; + } + } + + public: + PartWriteStream(PartitionBuilderWii& parent, uint64_t baseOffset, uint64_t offset, bool& err) + : m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) { + if (offset % 0x1F0000) { + LogModule.report(logvisor::Error, "partition write stream MUST begin on 0x1F0000-aligned boundary"); + err = true; + return; + } + size_t group = m_offset / 0x1F0000; + m_fio = m_parent.m_parent.getFileIO().beginWriteStream(m_baseOffset + group * 0x200000); + if (!m_fio) + err = true; + m_curGroup = group; + } + ~PartWriteStream() { close(); } + void close() { + if (m_closed) + return; + m_closed = true; + size_t rem = m_offset % 0x1F0000; + if (rem) { + rem = 0x1F0000 - rem; + write(nullptr, rem); + } + encryptGroup(m_parent.m_h3[m_curGroup]); + m_fio.reset(); + } + uint64_t position() const { return m_offset; } + uint64_t write(const void* buf, uint64_t length) { + size_t group = m_offset / 0x1F0000; + size_t block = (m_offset - group * 0x1F0000) / 0x7c00; + size_t cacheOffset = m_offset % 0x7c00; + uint64_t cacheSize; + uint64_t rem = length; + const uint8_t* src = (uint8_t*)buf; + + while (rem) { + if (group != m_curGroup) { + encryptGroup(m_parent.m_h3[m_curGroup]); + m_curGroup = group; + } + + cacheSize = rem; + if (cacheSize + cacheOffset > 0x7c00) + cacheSize = 0x7c00 - cacheOffset; + + if (src) { + memmove(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize); + src += cacheSize; + } else + memset(m_buf + block * 0x8000 + 0x400 + cacheOffset, 0, cacheSize); + + rem -= cacheSize; + cacheOffset = 0; + ++block; + if (block == 64) { + block = 0; + ++group; + } + } + + m_offset += length; + return length; + } + }; + + PartitionBuilderWii(DiscBuilderBase& parent, PartitionKind kind, uint64_t baseOffset) + : DiscBuilderBase::PartitionBuilderBase(parent, kind, true), m_baseOffset(baseOffset), m_aes(NewAES()) {} + + uint64_t getCurUserEnd() const { return m_curUser; } + + uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) { + reqSz = ROUND_UP_32(reqSz); + if (m_curUser + reqSz >= 0x1FB450000) { + LogModule.report(logvisor::Error, "partition exceeds maximum single-partition capacity"); + return -1; + } + uint64_t ret = m_curUser; + PartWriteStream& cws = static_cast(ws); + if (cws.m_offset > ret) { + LogModule.report(logvisor::Error, "partition overwrite error"); + return -1; + } + while (cws.m_offset < ret) + cws.write("\xff", 1); + m_curUser += reqSz; + return ret; + } + + uint32_t packOffset(uint64_t offset) const { return uint32_t(offset >> uint64_t(2)); } + + std::unique_ptr beginWriteStream(uint64_t offset) { + bool Err = false; + std::unique_ptr ret = + std::make_unique(*this, m_baseOffset + m_userOffset, offset, Err); + if (Err) + return {}; + return ret; + } + + uint64_t _build(const std::function& tmdData, + size_t& tmdSz)>& cryptoFunc, + const std::function& headerFunc, + const std::function& bi2Func, + const std::function& apploaderFunc, + const std::function& contentFunc, size_t apploaderSz) { + /* Write partition head up to H3 table */ + std::unique_ptr ws = m_parent.getFileIO().beginWriteStream(m_baseOffset); + if (!ws) + return -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]); + m_aes->decrypt(tkeyiv, tkey, tkey, 16); + m_aes->setKey(tkey); + + { + /* Assemble partition data */ + std::unique_ptr cws = beginWriteStream(0x1F0000); + if (!cws) + return -1; + if (!contentFunc(*cws)) + return -1; + + /* Pad out user area to nearest cleartext sector */ + m_curUser = cws->position(); + uint64_t curUserRem = m_curUser % 0x1F0000; + if (curUserRem) { + curUserRem = 0x1F0000 - curUserRem; + for (size_t i = 0; i < curUserRem; ++i) + cws->write("\xff", 1); + m_curUser += curUserRem; + } + + /* Begin crypto write and add content header */ + cws = beginWriteStream(0); + if (!cws) + return -1; + + /* Compute boot table members and write */ + size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz); + size_t fstSz = sizeof(FSTNode) * m_buildNodes.size(); + fstSz += m_buildNameOff; + fstSz = ROUND_UP_32(fstSz); + + if (fstOff + fstSz >= 0x1F0000) { + LogModule.report(logvisor::Error, "FST flows into user area (one or the other is too big)"); + return -1; + } + + if (!headerFunc(*cws, m_dolOffset, fstOff, fstSz)) + return -1; + + if (!bi2Func(*cws)) + return -1; + + size_t xferSz = 0; + if (!apploaderFunc(*cws, xferSz)) + return -1; + + size_t fstOffRel = fstOff - 0x2440; + if (xferSz > fstOffRel) { + LogModule.report(logvisor::Error, "apploader unexpectedly flows into FST"); + return -1; + } + for (size_t i = 0; i < fstOffRel - xferSz; ++i) + cws->write("\xff", 1); + + /* Write FST */ + cws->write(m_buildNodes.data(), m_buildNodes.size() * sizeof(FSTNode)); + for (const std::string& str : m_buildNames) + cws->write(str.data(), str.size() + 1); + } + + /* Write new crypto content size */ + uint64_t groupCount = m_curUser / 0x1F0000; + uint64_t cryptContentSize = (groupCount * 0x200000) >> uint64_t(2); + uint32_t cryptContentSizeBig = SBig(uint32_t(cryptContentSize)); + ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2BC); + if (!ws) + return -1; + ws->write(&cryptContentSizeBig, 0x4); + + /* Write new H3 */ + ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + h3Off); + if (!ws) + return -1; + ws->write(m_h3, 0x18000); + + /* Compute content hash and replace in TMD */ + sha1nfo sha; + sha1_init(&sha); + sha1_write(&sha, (char*)m_h3, 0x18000); + memmove(tmdData.get() + 0x1F4, sha1_result(&sha), 20); + + /* Same for content size */ + uint64_t contentSize = groupCount * 0x1F0000; + uint64_t contentSizeBig = SBig(contentSize); + memmove(tmdData.get() + 0x1EC, &contentSizeBig, 8); + + /* Zero-out TMD signature to simplify brute-force */ + memset(tmdData.get() + 0x4, 0, 0x100); + + /* Brute-force zero-starting hash */ + size_t tmdCheckSz = tmdSz - 0x140; + struct BFWindow { + uint64_t word[7]; + }* bfWindow = (BFWindow*)(tmdData.get() + 0x19A); + bool good = false; + uint64_t attempts = 0; + SystemString bfName(_SYS_STR("Brute force attempts")); + for (int w = 0; w < 7; ++w) { + for (uint64_t i = 0; i < UINT64_MAX; ++i) { + bfWindow->word[w] = i; + sha1_init(&sha); + sha1_write(&sha, (char*)(tmdData.get() + 0x140), tmdCheckSz); + uint8_t* hash = sha1_result(&sha); + ++attempts; + if (hash[0] == 0) { + good = true; + break; } m_parent.m_progressCB(m_parent.getProgressFactor(), bfName, attempts); - ++m_parent.m_progressIdx; - - ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2C0); - if (!ws) - return -1; - ws->write(tmdData.get(), tmdSz); - - return m_baseOffset + dataOff + groupCount * 0x200000; - } - - uint64_t buildFromDirectory(SystemStringView dirIn) - { - SystemString dirStr(dirIn); - SystemString basePath = dirStr + _SYS_STR("/") + getKindString(m_kind); - - /* Check Ticket */ - SystemString ticketIn = basePath + _SYS_STR("/ticket.bin"); - Sstat theStat; - if (Stat(ticketIn.c_str(), &theStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), ticketIn.c_str()); - return -1; - } - - /* Check TMD */ - SystemString tmdIn = basePath + _SYS_STR("/tmd.bin"); - Sstat tmdStat; - if (Stat(tmdIn.c_str(), &tmdStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), tmdIn.c_str()); - return -1; - } - - /* Check Cert */ - SystemString certIn = basePath + _SYS_STR("/cert.bin"); - Sstat certStat; - if (Stat(certIn.c_str(), &certStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), certIn.c_str()); - return -1; - } - - /* Check Apploader */ - SystemString apploaderIn = basePath + _SYS_STR("/sys/apploader.img"); - Sstat apploaderStat; - if (Stat(apploaderIn.c_str(), &apploaderStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), apploaderIn.c_str()); - return -1; - } - - /* Check Boot */ - SystemString bootIn = basePath + _SYS_STR("/sys/boot.bin"); - Sstat bootStat; - if (Stat(bootIn.c_str(), &bootStat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bootIn.c_str()); - return -1; - } - - /* Check BI2 */ - SystemString bi2In = basePath + _SYS_STR("/sys/bi2.bin"); - Sstat bi2Stat; - if (Stat(bi2In.c_str(), &bi2Stat)) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bi2In.c_str()); - return -1; - } - - return _build( - [&](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 - { - 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; - }, - [&bootIn](IPartWriteStream& cws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz) -> bool - { - 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; - }, - [&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]; - while (true) - { - size_t rdSz = rs->read(buf, 8192); - if (!rdSz) - break; - cws.write(buf, rdSz); - xferSz += rdSz; - if (0x2440 + xferSz >= 0x1F0000) - { - LogModule.report(logvisor::Error, - "apploader flows into user area (one or the other is too big)"); - return false; - } - m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); - } - ++m_parent.m_progressIdx; - return true; - }, - [this, dirIn](IPartWriteStream& cws) -> bool - { - return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn); - }, apploaderStat.st_size); - } - - uint64_t mergeFromDirectory(const PartitionWii* partIn, SystemStringView dirIn) - { - size_t phSz; - std::unique_ptr phBuf = partIn->readPartitionHeaderBuf(phSz); - - return _build( - [&](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 - { - 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; - }, - [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; - }, - [partIn](IPartWriteStream& cws) -> bool - { - partIn->getBI2().write(cws); - return true; - }, - [this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool - { - std::unique_ptr apploaderBuf = partIn->getApploaderBuf(); - size_t apploaderSz = partIn->getApploaderSize(); - SystemString apploaderName(_SYS_STR("")); - cws.write(apploaderBuf.get(), apploaderSz); - xferSz += apploaderSz; - if (0x2440 + xferSz >= 0x1F0000) - { - LogModule.report(logvisor::Error, - "apploader flows into user area (one or the other is too big)"); - return false; - } - m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); - ++m_parent.m_progressIdx; - return true; - }, - [this, partIn, dirIn](IPartWriteStream& cws) -> bool - { - return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn); - }, partIn->getApploaderSize()); - } -}; - -EBuildResult DiscBuilderWii::buildFromDirectory(SystemStringView dirIn) -{ - SystemString dirStr(dirIn); - SystemString basePath = SystemString(dirStr) + _SYS_STR("/") + getKindString(PartitionKind::Data); - - PartitionBuilderWii& pb = static_cast(*m_partitions[0]); - uint64_t filledSz = pb.m_baseOffset; - if (!m_fileIO->beginWriteStream()) - return EBuildResult::Failed; - - if (!CheckFreeSpace(m_outPath.c_str(), m_discCapacity)) - { - LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_outPath.c_str()); - return EBuildResult::DiskFull; - } - m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1); - ++m_progressIdx; - { - std::unique_ptr ws = m_fileIO->beginWriteStream(0); - if (!ws) - return EBuildResult::Failed; - char zeroBytes[1024] = {}; - for (uint64_t i = 0; i < m_discCapacity; i += 1024) - ws->write(zeroBytes, 1024); - } - - /* Assemble image */ - filledSz = pb.buildFromDirectory(dirIn); - if (filledSz == -1) - return EBuildResult::Failed; - else if (filledSz >= uint64_t(m_discCapacity)) - { - LogModule.report(logvisor::Error, "data partition exceeds disc capacity"); - return EBuildResult::Failed; - } - - m_progressCB(getProgressFactor(), _SYS_STR("Finishing Disc"), -1); - ++m_progressIdx; - - /* Populate disc header */ - std::unique_ptr ws = m_fileIO->beginWriteStream(0); - if (!ws) - return EBuildResult::Failed; - SystemString headerPath = basePath + _SYS_STR("/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 */ - ws = m_fileIO->beginWriteStream(0x40000); - if (!ws) - return EBuildResult::Failed; - uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))}; - ws->write(vals, 8); - - ws = m_fileIO->beginWriteStream(0x40020); - if (!ws) - return EBuildResult::Failed; - vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2))); - ws->write(vals, 4); - - /* Populate region info */ - SystemString regionPath = basePath + _SYS_STR("/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; - ws->write(regionBuf, 0x20); - - /* Fill image to end */ - ws = m_fileIO->beginWriteStream(filledSz); - if (!ws) - return EBuildResult::Failed; - uint8_t fillBuf[512]; - memset(fillBuf, 0xff, 512); - for (size_t i=m_discCapacity-filledSz ; i>0 ;) - { - if (i >= 512) - { - ws->write(fillBuf, 512); - i -= 512; - continue; - } - ws->write(fillBuf, i); + } + if (good) break; } + m_parent.m_progressCB(m_parent.getProgressFactor(), bfName, attempts); + ++m_parent.m_progressIdx; - return EBuildResult::Success; + ws = m_parent.getFileIO().beginWriteStream(m_baseOffset + 0x2C0); + if (!ws) + return -1; + ws->write(tmdData.get(), tmdSz); + + return m_baseOffset + dataOff + groupCount * 0x200000; + } + + uint64_t buildFromDirectory(SystemStringView dirIn) { + SystemString dirStr(dirIn); + SystemString basePath = dirStr + _SYS_STR("/") + getKindString(m_kind); + + /* Check Ticket */ + SystemString ticketIn = basePath + _SYS_STR("/ticket.bin"); + Sstat theStat; + if (Stat(ticketIn.c_str(), &theStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), ticketIn.c_str()); + return -1; + } + + /* Check TMD */ + SystemString tmdIn = basePath + _SYS_STR("/tmd.bin"); + Sstat tmdStat; + if (Stat(tmdIn.c_str(), &tmdStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), tmdIn.c_str()); + return -1; + } + + /* Check Cert */ + SystemString certIn = basePath + _SYS_STR("/cert.bin"); + Sstat certStat; + if (Stat(certIn.c_str(), &certStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), certIn.c_str()); + return -1; + } + + /* Check Apploader */ + SystemString apploaderIn = basePath + _SYS_STR("/sys/apploader.img"); + Sstat apploaderStat; + if (Stat(apploaderIn.c_str(), &apploaderStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), apploaderIn.c_str()); + return -1; + } + + /* Check Boot */ + SystemString bootIn = basePath + _SYS_STR("/sys/boot.bin"); + Sstat bootStat; + if (Stat(bootIn.c_str(), &bootStat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bootIn.c_str()); + return -1; + } + + /* Check BI2 */ + SystemString bi2In = basePath + _SYS_STR("/sys/bi2.bin"); + Sstat bi2Stat; + if (Stat(bi2In.c_str(), &bi2Stat)) { + LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), bi2In.c_str()); + return -1; + } + + return _build( + [&](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 { + 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; i < tmdPadding; ++i) + ws.write("", 1); + + rs = NewFileIO(certIn.c_str())->beginReadStream(); + 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; + }, + [&bootIn](IPartWriteStream& cws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz) -> bool { + 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; + }, + [&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]; + while (true) { + size_t rdSz = rs->read(buf, 8192); + if (!rdSz) + break; + cws.write(buf, rdSz); + xferSz += rdSz; + if (0x2440 + xferSz >= 0x1F0000) { + LogModule.report(logvisor::Error, "apploader flows into user area (one or the other is too big)"); + return false; + } + m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz); + } + ++m_parent.m_progressIdx; + return true; + }, + [this, dirIn](IPartWriteStream& cws) -> bool { + return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn); + }, + apploaderStat.st_size); + } + + uint64_t mergeFromDirectory(const PartitionWii* partIn, SystemStringView dirIn) { + size_t phSz; + std::unique_ptr phBuf = partIn->readPartitionHeaderBuf(phSz); + + return _build( + [&](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 { + 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; + }, + [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; + }, + [partIn](IPartWriteStream& cws) -> bool { + partIn->getBI2().write(cws); + return true; + }, + [this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool { + std::unique_ptr apploaderBuf = partIn->getApploaderBuf(); + size_t apploaderSz = partIn->getApploaderSize(); + SystemString apploaderName(_SYS_STR("")); + cws.write(apploaderBuf.get(), apploaderSz); + xferSz += apploaderSz; + if (0x2440 + xferSz >= 0x1F0000) { + LogModule.report(logvisor::Error, "apploader flows into user area (one or the other is too big)"); + return false; + } + m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz); + ++m_parent.m_progressIdx; + return true; + }, + [this, partIn, dirIn](IPartWriteStream& cws) -> bool { + return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn); + }, + partIn->getApploaderSize()); + } +}; + +EBuildResult DiscBuilderWii::buildFromDirectory(SystemStringView dirIn) { + SystemString dirStr(dirIn); + SystemString basePath = SystemString(dirStr) + _SYS_STR("/") + getKindString(PartitionKind::Data); + + PartitionBuilderWii& pb = static_cast(*m_partitions[0]); + uint64_t filledSz = pb.m_baseOffset; + if (!m_fileIO->beginWriteStream()) + return EBuildResult::Failed; + + if (!CheckFreeSpace(m_outPath.c_str(), m_discCapacity)) { + LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_outPath.c_str()); + return EBuildResult::DiskFull; + } + m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1); + ++m_progressIdx; + { + std::unique_ptr ws = m_fileIO->beginWriteStream(0); + if (!ws) + return EBuildResult::Failed; + char zeroBytes[1024] = {}; + for (uint64_t i = 0; i < m_discCapacity; i += 1024) + ws->write(zeroBytes, 1024); + } + + /* Assemble image */ + filledSz = pb.buildFromDirectory(dirIn); + if (filledSz == -1) + return EBuildResult::Failed; + else if (filledSz >= uint64_t(m_discCapacity)) { + LogModule.report(logvisor::Error, "data partition exceeds disc capacity"); + return EBuildResult::Failed; + } + + m_progressCB(getProgressFactor(), _SYS_STR("Finishing Disc"), -1); + ++m_progressIdx; + + /* Populate disc header */ + std::unique_ptr ws = m_fileIO->beginWriteStream(0); + if (!ws) + return EBuildResult::Failed; + SystemString headerPath = basePath + _SYS_STR("/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 */ + ws = m_fileIO->beginWriteStream(0x40000); + if (!ws) + return EBuildResult::Failed; + uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))}; + ws->write(vals, 8); + + ws = m_fileIO->beginWriteStream(0x40020); + if (!ws) + return EBuildResult::Failed; + vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2))); + ws->write(vals, 4); + + /* Populate region info */ + SystemString regionPath = basePath + _SYS_STR("/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; + ws->write(regionBuf, 0x20); + + /* Fill image to end */ + ws = m_fileIO->beginWriteStream(filledSz); + if (!ws) + return EBuildResult::Failed; + uint8_t fillBuf[512]; + memset(fillBuf, 0xff, 512); + for (size_t i = m_discCapacity - filledSz; i > 0;) { + if (i >= 512) { + ws->write(fillBuf, 512); + i -= 512; + continue; + } + ws->write(fillBuf, i); + break; + } + + return EBuildResult::Success; } -uint64_t DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer) -{ - uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true); - if (sz == -1) - return -1; - auto szDiv = std::lldiv(sz, 0x1F0000); - if (szDiv.rem) ++szDiv.quot; - sz = szDiv.quot * 0x200000; - sz += 0x200000; - dualLayer = (sz > 0x118240000); - if (sz > 0x1FB4E0000) - { - LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x1FB4E0000); - return -1; - } - return sz; +uint64_t DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer) { + uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true); + if (sz == -1) + return -1; + auto szDiv = std::lldiv(sz, 0x1F0000); + if (szDiv.rem) + ++szDiv.quot; + sz = szDiv.quot * 0x200000; + sz += 0x200000; + dualLayer = (sz > 0x118240000); + if (sz > 0x1FB4E0000) { + LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x1FB4E0000); + return -1; + } + return sz; } DiscBuilderWii::DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB) -: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB) -{ - PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionKind::Data, 0x200000); - m_partitions.emplace_back(partBuilder); +: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB) { + PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionKind::Data, 0x200000); + m_partitions.emplace_back(partBuilder); } -DiscMergerWii::DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, - bool dualLayer, FProgress progressCB) -: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, dualLayer, progressCB)) -{} +DiscMergerWii::DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB) +: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, dualLayer, progressCB)) {} -EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) -{ - PartitionBuilderWii& pb = static_cast(*m_builder.m_partitions[0]); - uint64_t filledSz = pb.m_baseOffset; - if (!m_builder.m_fileIO->beginWriteStream()) - return EBuildResult::Failed; +EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) { + PartitionBuilderWii& pb = static_cast(*m_builder.m_partitions[0]); + uint64_t filledSz = pb.m_baseOffset; + if (!m_builder.m_fileIO->beginWriteStream()) + return EBuildResult::Failed; - if (!CheckFreeSpace(m_builder.m_outPath.c_str(), m_builder.m_discCapacity)) - { - LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_builder.m_outPath.c_str()); - return EBuildResult::DiskFull; - } - m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1); - ++m_builder.m_progressIdx; - { - std::unique_ptr ws = m_builder.m_fileIO->beginWriteStream(0); - if (!ws) - return EBuildResult::Failed; - char zeroBytes[1024] = {}; - for (uint64_t i = 0; i < m_builder.m_discCapacity; i += 1024) - ws->write(zeroBytes, 1024); - } - - /* Assemble image */ - filledSz = pb.mergeFromDirectory(static_cast(m_sourceDisc.getDataPartition()), dirIn); - if (filledSz == -1) - return EBuildResult::Failed; - else if (filledSz >= uint64_t(m_builder.m_discCapacity)) - { - LogModule.report(logvisor::Error, "data partition exceeds disc capacity"); - return EBuildResult::Failed; - } - - m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Finishing Disc"), -1); - ++m_builder.m_progressIdx; - - /* Populate disc header */ + if (!CheckFreeSpace(m_builder.m_outPath.c_str(), m_builder.m_discCapacity)) { + LogModule.report(logvisor::Error, _SYS_STR("not enough free disk space for %s"), m_builder.m_outPath.c_str()); + return EBuildResult::DiskFull; + } + m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1); + ++m_builder.m_progressIdx; + { std::unique_ptr ws = m_builder.m_fileIO->beginWriteStream(0); if (!ws) - return EBuildResult::Failed; - m_sourceDisc.getHeader().write(*ws); + return EBuildResult::Failed; + char zeroBytes[1024] = {}; + for (uint64_t i = 0; i < m_builder.m_discCapacity; i += 1024) + ws->write(zeroBytes, 1024); + } - /* Populate partition info */ - ws = m_builder.m_fileIO->beginWriteStream(0x40000); - if (!ws) - return EBuildResult::Failed; - uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))}; - ws->write(vals, 8); + /* Assemble image */ + filledSz = pb.mergeFromDirectory(static_cast(m_sourceDisc.getDataPartition()), dirIn); + if (filledSz == -1) + return EBuildResult::Failed; + else if (filledSz >= uint64_t(m_builder.m_discCapacity)) { + LogModule.report(logvisor::Error, "data partition exceeds disc capacity"); + return EBuildResult::Failed; + } - ws = m_builder.m_fileIO->beginWriteStream(0x40020); - if (!ws) - return EBuildResult::Failed; - vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2))); - ws->write(vals, 4); + m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Finishing Disc"), -1); + ++m_builder.m_progressIdx; - /* 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; - ws->write(regionBuf, 0x20); + /* Populate disc header */ + std::unique_ptr ws = m_builder.m_fileIO->beginWriteStream(0); + if (!ws) + return EBuildResult::Failed; + m_sourceDisc.getHeader().write(*ws); - /* Fill image to end */ - ws = m_builder.m_fileIO->beginWriteStream(filledSz); - if (!ws) - return EBuildResult::Failed; - uint8_t fillBuf[512]; - memset(fillBuf, 0xff, 512); - for (size_t i=m_builder.m_discCapacity-filledSz ; i>0 ;) - { - if (i >= 512) - { - ws->write(fillBuf, 512); - i -= 512; - continue; - } - ws->write(fillBuf, i); - break; + /* Populate partition info */ + ws = m_builder.m_fileIO->beginWriteStream(0x40000); + if (!ws) + return EBuildResult::Failed; + uint32_t vals[2] = {SBig(uint32_t(1)), SBig(uint32_t(0x40020 >> uint64_t(2)))}; + ws->write(vals, 8); + + ws = m_builder.m_fileIO->beginWriteStream(0x40020); + if (!ws) + return EBuildResult::Failed; + vals[0] = SBig(uint32_t(pb.m_baseOffset >> uint64_t(2))); + 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; + ws->write(regionBuf, 0x20); + + /* Fill image to end */ + ws = m_builder.m_fileIO->beginWriteStream(filledSz); + if (!ws) + return EBuildResult::Failed; + uint8_t fillBuf[512]; + memset(fillBuf, 0xff, 512); + for (size_t i = m_builder.m_discCapacity - filledSz; i > 0;) { + if (i >= 512) { + ws->write(fillBuf, 512); + i -= 512; + continue; } + ws->write(fillBuf, i); + break; + } - return EBuildResult::Success; + return EBuildResult::Success; } -uint64_t DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, - SystemStringView dirIn, bool& dualLayer) -{ - uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge( - sourceDisc.getDataPartition(), dirIn); - if (sz == -1) - return -1; - auto szDiv = std::lldiv(sz, 0x1F0000); - if (szDiv.rem) ++szDiv.quot; - sz = szDiv.quot * 0x200000; - sz += 0x200000; - dualLayer = (sz > 0x118240000); - if (sz > 0x1FB4E0000) - { - LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x1FB4E0000); - return -1; - } - return sz; +uint64_t DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer) { + uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn); + if (sz == -1) + return -1; + auto szDiv = std::lldiv(sz, 0x1F0000); + if (szDiv.rem) + ++szDiv.quot; + sz = szDiv.quot * 0x200000; + sz += 0x200000; + dualLayer = (sz > 0x118240000); + if (sz > 0x1FB4E0000) { + LogModule.report(logvisor::Error, _SYS_STR("disc capacity exceeded [%" PRIu64 " / %" PRIu64 "]"), sz, 0x1FB4E0000); + return -1; + } + return sz; } -} +} // namespace nod diff --git a/lib/FileIOFILE.cpp b/lib/FileIOFILE.cpp index 35019d2..fa70521 100644 --- a/lib/FileIOFILE.cpp +++ b/lib/FileIOFILE.cpp @@ -4,180 +4,140 @@ #include "nod/Util.hpp" #include "nod/IFileIO.hpp" -namespace nod -{ +namespace nod { + +class FileIOFILE : public IFileIO { + SystemString m_path; + int64_t m_maxWriteSize; -class FileIOFILE : public IFileIO -{ - SystemString m_path; - int64_t m_maxWriteSize; public: - FileIOFILE(SystemStringView path, int64_t maxWriteSize) - : m_path(path), m_maxWriteSize(maxWriteSize) {} + FileIOFILE(SystemStringView path, int64_t maxWriteSize) : m_path(path), m_maxWriteSize(maxWriteSize) {} - bool exists() - { - FILE* fp = Fopen(m_path.c_str(), _SYS_STR("rb")); - if (!fp) - return false; - fclose(fp); - return true; - } + bool exists() { + FILE* fp = Fopen(m_path.c_str(), _SYS_STR("rb")); + if (!fp) + return false; + fclose(fp); + return true; + } - uint64_t size() - { - FILE* fp = Fopen(m_path.c_str(), _SYS_STR("rb")); - if (!fp) - return 0; - FSeek(fp, 0, SEEK_END); - uint64_t result = FTell(fp); - fclose(fp); - return result; - } + uint64_t size() { + FILE* fp = Fopen(m_path.c_str(), _SYS_STR("rb")); + if (!fp) + return 0; + FSeek(fp, 0, SEEK_END); + uint64_t result = FTell(fp); + fclose(fp); + return result; + } - struct WriteStream : public IFileIO::IWriteStream - { - FILE* fp; - int64_t m_maxWriteSize; - WriteStream(SystemStringView path, int64_t maxWriteSize, bool& err) - : m_maxWriteSize(maxWriteSize) - { - fp = Fopen(path.data(), _SYS_STR("wb")); - if (!fp) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); - err = true; - } - } - WriteStream(SystemStringView path, uint64_t offset, int64_t maxWriteSize, bool& err) - : m_maxWriteSize(maxWriteSize) - { - fp = Fopen(path.data(), _SYS_STR("ab")); - if (!fp) - goto FailLoc; - fclose(fp); - fp = Fopen(path.data(), _SYS_STR("r+b")); - if (!fp) - goto FailLoc; - FSeek(fp, offset, SEEK_SET); - return; - FailLoc: - LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); - err = true; - } - ~WriteStream() - { - fclose(fp); - } - uint64_t write(const void* buf, uint64_t length) - { - if (m_maxWriteSize >= 0) - { - if (FTell(fp) + length > m_maxWriteSize) - { - LogModule.report(logvisor::Error, _SYS_STR("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize); - return 0; - } - } - return fwrite(buf, 1, length, fp); - } - }; - std::unique_ptr beginWriteStream() const - { - bool Err = false; - auto ret = std::unique_ptr(new WriteStream(m_path, m_maxWriteSize, Err)); - if (Err) - return {}; - return ret; + struct WriteStream : public IFileIO::IWriteStream { + FILE* fp; + int64_t m_maxWriteSize; + WriteStream(SystemStringView path, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) { + fp = Fopen(path.data(), _SYS_STR("wb")); + if (!fp) { + LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); + err = true; + } } - std::unique_ptr beginWriteStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new WriteStream(m_path, offset, m_maxWriteSize, Err)); - if (Err) - return {}; - return ret; + WriteStream(SystemStringView path, uint64_t offset, int64_t maxWriteSize, bool& err) + : m_maxWriteSize(maxWriteSize) { + fp = Fopen(path.data(), _SYS_STR("ab")); + if (!fp) + goto FailLoc; + fclose(fp); + fp = Fopen(path.data(), _SYS_STR("r+b")); + if (!fp) + goto FailLoc; + FSeek(fp, offset, SEEK_SET); + return; + FailLoc: + LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); + err = true; } + ~WriteStream() { fclose(fp); } + uint64_t write(const void* buf, uint64_t length) { + if (m_maxWriteSize >= 0) { + if (FTell(fp) + length > m_maxWriteSize) { + LogModule.report(logvisor::Error, _SYS_STR("write operation exceeds file's %" PRIi64 "-byte limit"), + m_maxWriteSize); + return 0; + } + } + return fwrite(buf, 1, length, fp); + } + }; + std::unique_ptr beginWriteStream() const { + bool Err = false; + auto ret = std::unique_ptr(new WriteStream(m_path, m_maxWriteSize, Err)); + if (Err) + return {}; + return ret; + } + std::unique_ptr beginWriteStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new WriteStream(m_path, offset, m_maxWriteSize, Err)); + if (Err) + return {}; + return ret; + } - struct ReadStream : public IFileIO::IReadStream - { - FILE* fp; - ReadStream(SystemStringView path, bool& err) - { - fp = Fopen(path.data(), _SYS_STR("rb")); - if (!fp) - { - err = true; - LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for reading"), path.data()); - } - } - ReadStream(SystemStringView path, uint64_t offset, bool& err) - : ReadStream(path, err) - { - if (err) - return; - FSeek(fp, offset, SEEK_SET); - } - ~ReadStream() - { - fclose(fp); - } - void seek(int64_t offset, int whence) - { - FSeek(fp, offset, whence); - } - uint64_t position() const - { - return FTell(fp); - } - uint64_t read(void* buf, uint64_t length) - { - return fread(buf, 1, length, fp); - } - uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) - { - uint64_t written = 0; - uint8_t buf[0x7c00]; - while (length) - { - uint64_t thisSz = nod::min(uint64_t(0x7c00), length); - if (read(buf, thisSz) != thisSz) - { - LogModule.report(logvisor::Error, "unable to read enough from file"); - return written; - } - if (discio.write(buf, thisSz) != thisSz) - { - LogModule.report(logvisor::Error, "unable to write enough to disc"); - return written; - } - length -= thisSz; - written += thisSz; - } - return written; - } - }; - std::unique_ptr beginReadStream() const - { - bool Err = false; - auto ret = std::unique_ptr(new ReadStream(m_path, Err)); - if (Err) - return {}; - return ret; + struct ReadStream : public IFileIO::IReadStream { + FILE* fp; + ReadStream(SystemStringView path, bool& err) { + fp = Fopen(path.data(), _SYS_STR("rb")); + if (!fp) { + err = true; + LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for reading"), path.data()); + } } - std::unique_ptr beginReadStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new ReadStream(m_path, offset, Err)); - if (Err) - return {}; - return ret; + ReadStream(SystemStringView path, uint64_t offset, bool& err) : ReadStream(path, err) { + if (err) + return; + FSeek(fp, offset, SEEK_SET); } + ~ReadStream() { fclose(fp); } + void seek(int64_t offset, int whence) { FSeek(fp, offset, whence); } + uint64_t position() const { return FTell(fp); } + uint64_t read(void* buf, uint64_t length) { return fread(buf, 1, length, fp); } + uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) { + uint64_t written = 0; + uint8_t buf[0x7c00]; + while (length) { + uint64_t thisSz = nod::min(uint64_t(0x7c00), length); + if (read(buf, thisSz) != thisSz) { + LogModule.report(logvisor::Error, "unable to read enough from file"); + return written; + } + if (discio.write(buf, thisSz) != thisSz) { + LogModule.report(logvisor::Error, "unable to write enough to disc"); + return written; + } + length -= thisSz; + written += thisSz; + } + return written; + } + }; + std::unique_ptr beginReadStream() const { + bool Err = false; + auto ret = std::unique_ptr(new ReadStream(m_path, Err)); + if (Err) + return {}; + return ret; + } + std::unique_ptr beginReadStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new ReadStream(m_path, offset, Err)); + if (Err) + return {}; + return ret; + } }; -std::unique_ptr NewFileIO(SystemStringView path, int64_t maxWriteSize) -{ - return std::unique_ptr(new FileIOFILE(path, maxWriteSize)); +std::unique_ptr NewFileIO(SystemStringView path, int64_t maxWriteSize) { + return std::unique_ptr(new FileIOFILE(path, maxWriteSize)); } -} +} // namespace nod diff --git a/lib/FileIOWin32.cpp b/lib/FileIOWin32.cpp index 02ab9bd..3026241 100644 --- a/lib/FileIOWin32.cpp +++ b/lib/FileIOWin32.cpp @@ -4,222 +4,186 @@ #include "nod/Util.hpp" #include "nod/IFileIO.hpp" -namespace nod -{ +namespace nod { + +class FileIOWin32 : public IFileIO { + SystemString m_path; + int64_t m_maxWriteSize; -class FileIOWin32 : public IFileIO -{ - SystemString m_path; - int64_t m_maxWriteSize; public: - FileIOWin32(SystemStringView path, int64_t maxWriteSize) - : m_path(path), m_maxWriteSize(maxWriteSize) {} + FileIOWin32(SystemStringView path, int64_t maxWriteSize) : m_path(path), m_maxWriteSize(maxWriteSize) {} - bool exists() - { + bool exists() { #if !WINDOWS_STORE - HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, - nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); #else - HANDLE fp = CreateFile2(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + HANDLE fp = CreateFile2(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); #endif - if (fp == INVALID_HANDLE_VALUE) - return false; - CloseHandle(fp); - return true; - } + if (fp == INVALID_HANDLE_VALUE) + return false; + CloseHandle(fp); + return true; + } - uint64_t size() - { + uint64_t size() { #if !WINDOWS_STORE - HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, - nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + HANDLE fp = CreateFileW(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); #else - HANDLE fp = CreateFile2(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + HANDLE fp = CreateFile2(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); #endif - if (fp == INVALID_HANDLE_VALUE) - return 0; - LARGE_INTEGER sz; - if (!GetFileSizeEx(fp, &sz)) - { - CloseHandle(fp); - return 0; - } - CloseHandle(fp); - return sz.QuadPart; + if (fp == INVALID_HANDLE_VALUE) + return 0; + LARGE_INTEGER sz; + if (!GetFileSizeEx(fp, &sz)) { + CloseHandle(fp); + return 0; } + CloseHandle(fp); + return sz.QuadPart; + } - struct WriteStream : public IFileIO::IWriteStream - { - HANDLE fp; - int64_t m_maxWriteSize; - WriteStream(SystemStringView path, int64_t maxWriteSize, bool& err) - : m_maxWriteSize(maxWriteSize) - { + struct WriteStream : public IFileIO::IWriteStream { + HANDLE fp; + int64_t m_maxWriteSize; + WriteStream(SystemStringView path, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) { #if !WINDOWS_STORE - fp = CreateFileW(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, - nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + fp = CreateFileW(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + nullptr); #else - fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr); + fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr); #endif - if (fp == INVALID_HANDLE_VALUE) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); - err = true; - } - } - WriteStream(SystemStringView path, uint64_t offset, int64_t maxWriteSize, bool& err) - : m_maxWriteSize(maxWriteSize) - { + if (fp == INVALID_HANDLE_VALUE) { + LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); + err = true; + } + } + WriteStream(SystemStringView path, uint64_t offset, int64_t maxWriteSize, bool& err) + : m_maxWriteSize(maxWriteSize) { #if !WINDOWS_STORE - fp = CreateFileW(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, - nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + fp = CreateFileW(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + nullptr); #else - fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, OPEN_ALWAYS, nullptr); + fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, OPEN_ALWAYS, nullptr); #endif - if (fp == INVALID_HANDLE_VALUE) - { - LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); - err = true; - return; - } - LARGE_INTEGER lioffset; - lioffset.QuadPart = offset; - SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN); + if (fp == INVALID_HANDLE_VALUE) { + LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for writing"), path.data()); + err = true; + return; + } + LARGE_INTEGER lioffset; + lioffset.QuadPart = offset; + SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN); + } + ~WriteStream() { CloseHandle(fp); } + uint64_t write(const void* buf, uint64_t length) { + if (m_maxWriteSize >= 0) { + LARGE_INTEGER li = {}; + LARGE_INTEGER res; + SetFilePointerEx(fp, li, &res, FILE_CURRENT); + if (res.QuadPart + int64_t(length) > m_maxWriteSize) { + LogModule.report(logvisor::Error, _SYS_STR("write operation exceeds file's %" PRIi64 "-byte limit"), + m_maxWriteSize); + return 0; } - ~WriteStream() - { - CloseHandle(fp); - } - uint64_t write(const void* buf, uint64_t length) - { - if (m_maxWriteSize >= 0) - { - LARGE_INTEGER li = {}; - LARGE_INTEGER res; - SetFilePointerEx(fp, li, &res, FILE_CURRENT); - if (res.QuadPart + int64_t(length) > m_maxWriteSize) - { - LogModule.report(logvisor::Error, _SYS_STR("write operation exceeds file's %" PRIi64 "-byte limit"), m_maxWriteSize); - return 0; - } - } + } - DWORD ret = 0; - WriteFile(fp, buf, length, &ret, nullptr); - return ret; - } - }; - std::unique_ptr beginWriteStream() const - { - bool Err = false; - auto ret = std::unique_ptr(new WriteStream(m_path, m_maxWriteSize, Err)); - if (Err) - return {}; - return ret; - } - std::unique_ptr beginWriteStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new WriteStream(m_path, offset, m_maxWriteSize, Err)); - if (Err) - return {}; - return ret; + DWORD ret = 0; + WriteFile(fp, buf, length, &ret, nullptr); + return ret; } + }; + std::unique_ptr beginWriteStream() const { + bool Err = false; + auto ret = std::unique_ptr(new WriteStream(m_path, m_maxWriteSize, Err)); + if (Err) + return {}; + return ret; + } + std::unique_ptr beginWriteStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new WriteStream(m_path, offset, m_maxWriteSize, Err)); + if (Err) + return {}; + return ret; + } - struct ReadStream : public IFileIO::IReadStream - { - HANDLE fp; - ReadStream(SystemStringView path, bool& err) - { + struct ReadStream : public IFileIO::IReadStream { + HANDLE fp; + ReadStream(SystemStringView path, bool& err) { #if !WINDOWS_STORE - fp = CreateFileW(path.data(), GENERIC_READ, FILE_SHARE_READ, - nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + fp = CreateFileW(path.data(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + nullptr); #else - fp = CreateFile2(path.data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + fp = CreateFile2(path.data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); #endif - if (fp == INVALID_HANDLE_VALUE) - { - err = true; - LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for reading"), path.data()); - } - } - ReadStream(SystemStringView path, uint64_t offset, bool& err) - : ReadStream(path, err) - { - if (err) - return; - LARGE_INTEGER lioffset; - lioffset.QuadPart = offset; - SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN); - } - ~ReadStream() - { - CloseHandle(fp); - } - void seek(int64_t offset, int whence) - { - LARGE_INTEGER li; - li.QuadPart = offset; - SetFilePointerEx(fp, li, nullptr, whence); - } - uint64_t position() const - { - LARGE_INTEGER li = {}; - LARGE_INTEGER res; - SetFilePointerEx(fp, li, &res, FILE_CURRENT); - return res.QuadPart; - } - uint64_t read(void* buf, uint64_t length) - { - DWORD ret = 0; - ReadFile(fp, buf, length, &ret, nullptr); - return ret; - } - uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) - { - uint64_t written = 0; - uint8_t buf[0x7c00]; - while (length) - { - uint64_t thisSz = nod::min(uint64_t(0x7c00), length); - if (read(buf, thisSz) != thisSz) - { - LogModule.report(logvisor::Error, "unable to read enough from file"); - return written; - } - if (discio.write(buf, thisSz) != thisSz) - { - LogModule.report(logvisor::Error, "unable to write enough to disc"); - return written; - } - length -= thisSz; - written += thisSz; - } - return written; - } - }; - std::unique_ptr beginReadStream() const - { - bool Err = false; - auto ret = std::unique_ptr(new ReadStream(m_path, Err)); - if (Err) - return {}; - return ret; + if (fp == INVALID_HANDLE_VALUE) { + err = true; + LogModule.report(logvisor::Error, _SYS_STR("unable to open '%s' for reading"), path.data()); + } } - std::unique_ptr beginReadStream(uint64_t offset) const - { - bool Err = false; - auto ret = std::unique_ptr(new ReadStream(m_path, offset, Err)); - if (Err) - return {}; - return ret; + ReadStream(SystemStringView path, uint64_t offset, bool& err) : ReadStream(path, err) { + if (err) + return; + LARGE_INTEGER lioffset; + lioffset.QuadPart = offset; + SetFilePointerEx(fp, lioffset, nullptr, FILE_BEGIN); } + ~ReadStream() { CloseHandle(fp); } + void seek(int64_t offset, int whence) { + LARGE_INTEGER li; + li.QuadPart = offset; + SetFilePointerEx(fp, li, nullptr, whence); + } + uint64_t position() const { + LARGE_INTEGER li = {}; + LARGE_INTEGER res; + SetFilePointerEx(fp, li, &res, FILE_CURRENT); + return res.QuadPart; + } + uint64_t read(void* buf, uint64_t length) { + DWORD ret = 0; + ReadFile(fp, buf, length, &ret, nullptr); + return ret; + } + uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) { + uint64_t written = 0; + uint8_t buf[0x7c00]; + while (length) { + uint64_t thisSz = nod::min(uint64_t(0x7c00), length); + if (read(buf, thisSz) != thisSz) { + LogModule.report(logvisor::Error, "unable to read enough from file"); + return written; + } + if (discio.write(buf, thisSz) != thisSz) { + LogModule.report(logvisor::Error, "unable to write enough to disc"); + return written; + } + length -= thisSz; + written += thisSz; + } + return written; + } + }; + std::unique_ptr beginReadStream() const { + bool Err = false; + auto ret = std::unique_ptr(new ReadStream(m_path, Err)); + if (Err) + return {}; + return ret; + } + std::unique_ptr beginReadStream(uint64_t offset) const { + bool Err = false; + auto ret = std::unique_ptr(new ReadStream(m_path, offset, Err)); + if (Err) + return {}; + return ret; + } }; -std::unique_ptr NewFileIO(SystemStringView path, int64_t maxWriteSize) -{ - return std::unique_ptr(new FileIOWin32(path, maxWriteSize)); +std::unique_ptr NewFileIO(SystemStringView path, int64_t maxWriteSize) { + return std::unique_ptr(new FileIOWin32(path, maxWriteSize)); } -} +} // namespace nod diff --git a/lib/aes.cpp b/lib/aes.cpp index de2d804..ac97277 100644 --- a/lib/aes.cpp +++ b/lib/aes.cpp @@ -12,608 +12,563 @@ #define _AES_NI 1 #endif -namespace nod -{ +namespace nod { /* rotates x one bit to the left */ -#define ROTL(x) (((x)>>7)|((x)<<1)) +#define ROTL(x) (((x) >> 7) | ((x) << 1)) /* Rotates 32-bit word left by 1, 2 or 3 byte */ -#define ROTL8(x) (((x)<<8)|((x)>>24)) -#define ROTL16(x) (((x)<<16)|((x)>>16)) -#define ROTL24(x) (((x)<<24)|((x)>>8)) +#define ROTL8(x) (((x) << 8) | ((x) >> 24)) +#define ROTL16(x) (((x) << 16) | ((x) >> 16)) +#define ROTL24(x) (((x) << 24) | ((x) >> 8)) static const uint8_t InCo[4] = {0xB, 0xD, 0x9, 0xE}; /* Inverse Coefficients */ -static inline uint32_t pack(const uint8_t* b) -{ - /* pack bytes into a 32-bit Word */ - return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0]; +static inline uint32_t pack(const uint8_t* b) { + /* pack bytes into a 32-bit Word */ + return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0]; } -static inline void unpack(uint32_t a, uint8_t* b) -{ - /* unpack bytes from a word */ - b[0] = (uint8_t)a; - b[1] = (uint8_t)(a >> 8); - b[2] = (uint8_t)(a >> 16); - b[3] = (uint8_t)(a >> 24); +static inline void unpack(uint32_t a, uint8_t* b) { + /* unpack bytes from a word */ + b[0] = (uint8_t)a; + b[1] = (uint8_t)(a >> 8); + b[2] = (uint8_t)(a >> 16); + b[3] = (uint8_t)(a >> 24); } -static inline uint8_t xtime(uint8_t a) -{ - return ((a << 1) ^ (((a>>7) & 1) * 0x11B)); -} +static inline uint8_t xtime(uint8_t a) { return ((a << 1) ^ (((a >> 7) & 1) * 0x11B)); } -static const struct SoftwareAESTables -{ - uint8_t fbsub[256]; - uint8_t rbsub[256]; - uint8_t ptab[256], ltab[256]; - uint32_t ftable[256]; - uint32_t rtable[256]; - uint32_t rco[30]; +static const struct SoftwareAESTables { + uint8_t fbsub[256]; + uint8_t rbsub[256]; + uint8_t ptab[256], ltab[256]; + uint32_t ftable[256]; + uint32_t rtable[256]; + uint32_t rco[30]; - uint8_t bmul(uint8_t x, uint8_t y) const - { - /* x.y= AntiLog(Log(x) + Log(y)) */ - if (x && y) return ptab[(ltab[x] + ltab[y]) % 255]; - else return 0; + uint8_t bmul(uint8_t x, uint8_t y) const { + /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) + return ptab[(ltab[x] + ltab[y]) % 255]; + else + return 0; + } + + uint32_t SubByte(uint32_t a) const { + uint8_t b[4]; + unpack(a, b); + b[0] = fbsub[b[0]]; + b[1] = fbsub[b[1]]; + b[2] = fbsub[b[2]]; + b[3] = fbsub[b[3]]; + return pack(b); + } + + uint8_t product(uint32_t x, uint32_t y) const { + /* dot product of two 4-byte arrays */ + uint8_t xb[4], yb[4]; + unpack(x, xb); + unpack(y, yb); + return bmul(xb[0], yb[0]) ^ bmul(xb[1], yb[1]) ^ bmul(xb[2], yb[2]) ^ bmul(xb[3], yb[3]); + } + + uint32_t InvMixCol(uint32_t x) const { + /* matrix Multiplication */ + uint32_t y, m; + uint8_t b[4]; + + m = pack(InCo); + b[3] = product(m, x); + m = ROTL24(m); + b[2] = product(m, x); + m = ROTL24(m); + b[1] = product(m, x); + m = ROTL24(m); + b[0] = product(m, x); + y = pack(b); + return y; + } + + uint8_t ByteSub(uint8_t x) const { + uint8_t y = ptab[255 - ltab[x]]; /* multiplicative inverse */ + x = y; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + x = ROTL(x); + y ^= x; + y ^= 0x63; + return y; + } + + SoftwareAESTables() { + /* generate tables */ + int i; + uint8_t y, b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0] = 0; + ptab[0] = 1; + ltab[1] = 0; + ptab[1] = 3; + ltab[3] = 1; + + for (i = 2; i < 256; i++) { + ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]); + ltab[ptab[i]] = i; } - uint32_t SubByte(uint32_t a) const - { - uint8_t b[4]; - unpack(a, b); - b[0] = fbsub[b[0]]; - b[1] = fbsub[b[1]]; - b[2] = fbsub[b[2]]; - b[3] = fbsub[b[3]]; - return pack(b); + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0] = 0x63; + rbsub[0x63] = 0; + + for (i = 1; i < 256; i++) { + y = ByteSub((uint8_t)i); + fbsub[i] = y; + rbsub[y] = i; } - uint8_t product(uint32_t x, uint32_t y) const - { - /* dot product of two 4-byte arrays */ - uint8_t xb[4], yb[4]; - unpack(x, xb); - unpack(y, yb); - return bmul(xb[0], yb[0])^bmul(xb[1], yb[1])^bmul(xb[2], yb[2])^bmul(xb[3], yb[3]); + for (i = 0, y = 1; i < 30; i++) { + rco[i] = y; + y = xtime(y); } - uint32_t InvMixCol(uint32_t x) const - { - /* matrix Multiplication */ - uint32_t y, m; - uint8_t b[4]; + /* calculate forward and reverse tables */ + for (i = 0; i < 256; i++) { + y = fbsub[i]; + b[3] = y ^ xtime(y); + b[2] = y; + b[1] = y; + b[0] = xtime(y); + ftable[i] = pack(b); - m = pack(InCo); - b[3] = product(m, x); - m = ROTL24(m); - b[2] = product(m, x); - m = ROTL24(m); - b[1] = product(m, x); - m = ROTL24(m); - b[0] = product(m, x); - y = pack(b); - return y; - } - - uint8_t ByteSub(uint8_t x) const - { - uint8_t y = ptab[255 - ltab[x]]; /* multiplicative inverse */ - x = y; - x = ROTL(x); - y ^= x; - x = ROTL(x); - y ^= x; - x = ROTL(x); - y ^= x; - x = ROTL(x); - y ^= x; - y ^= 0x63; - return y; - } - - SoftwareAESTables() - { - /* generate tables */ - int i; - uint8_t y, b[4]; - - /* use 3 as primitive root to generate power and log tables */ - - ltab[0] = 0; - ptab[0] = 1; - ltab[1] = 0; - ptab[1] = 3; - ltab[3] = 1; - - for (i = 2; i < 256; i++) - { - ptab[i] = ptab[i - 1] ^ xtime(ptab[i - 1]); - ltab[ptab[i]] = i; - } - - /* affine transformation:- each bit is xored with itself shifted one bit */ - - fbsub[0] = 0x63; - rbsub[0x63] = 0; - - for (i = 1; i < 256; i++) - { - y = ByteSub((uint8_t)i); - fbsub[i] = y; - rbsub[y] = i; - } - - for (i = 0, y = 1; i < 30; i++) - { - rco[i] = y; - y = xtime(y); - } - - /* calculate forward and reverse tables */ - for (i = 0; i < 256; i++) - { - y = fbsub[i]; - b[3] = y ^ xtime(y); - b[2] = y; - b[1] = y; - b[0] = xtime(y); - ftable[i] = pack(b); - - y = rbsub[i]; - b[3] = bmul(InCo[0], y); - b[2] = bmul(InCo[1], y); - b[1] = bmul(InCo[2], y); - b[0] = bmul(InCo[3], y); - rtable[i] = pack(b); - } + y = rbsub[i]; + b[3] = bmul(InCo[0], y); + b[2] = bmul(InCo[1], y); + b[1] = bmul(InCo[2], y); + b[0] = bmul(InCo[3], y); + rtable[i] = pack(b); } + } } AEStb; -class SoftwareAES : public IAES -{ +class SoftwareAES : public IAES { protected: - /* Parameter-dependent data */ - int Nk, Nb, Nr; - uint8_t fi[24], ri[24]; - uint32_t fkey[120]; - uint32_t rkey[120]; + /* Parameter-dependent data */ + int Nk, Nb, Nr; + uint8_t fi[24], ri[24]; + uint32_t fkey[120]; + uint32_t rkey[120]; - void gkey(int nb, int nk, const uint8_t* key); - void _encrypt(uint8_t* buff); - void _decrypt(uint8_t* buff); + void gkey(int nb, int nk, const uint8_t* key); + void _encrypt(uint8_t* buff); + void _decrypt(uint8_t* buff); public: - void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); - void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); - void setKey(const uint8_t* key); + void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); + void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); + void setKey(const uint8_t* key); }; -void SoftwareAES::gkey(int nb, int nk, const uint8_t* key) -{ - /* blocksize=32*nb bits. Key=32*nk bits */ - /* currently nb,bk = 4, 6 or 8 */ - /* key comes as 4*Nk bytes */ - /* Key Scheduler. Create expanded encryption key */ - int i, j, k, m, N; - int C1, C2, C3; - uint32_t CipherKey[8]; +void SoftwareAES::gkey(int nb, int nk, const uint8_t* key) { + /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int i, j, k, m, N; + int C1, C2, C3; + uint32_t CipherKey[8]; - Nb = nb; - Nk = nk; + Nb = nb; + Nk = nk; - /* Nr is number of rounds */ - if (Nb >= Nk) Nr = 6 + Nb; - else Nr = 6 + Nk; + /* Nr is number of rounds */ + if (Nb >= Nk) + Nr = 6 + Nb; + else + Nr = 6 + Nk; - C1 = 1; + C1 = 1; - if (Nb < 8) { C2 = 2; C3 = 3; } - else { C2 = 3; C3 = 4; } + if (Nb < 8) { + C2 = 2; + C3 = 3; + } else { + C2 = 3; + C3 = 4; + } - /* pre-calculate forward and reverse increments */ - for (m = j = 0; j < nb; j++, m += 3) - { - fi[m] = (j + C1) % nb; - fi[m + 1] = (j + C2) % nb; - fi[m + 2] = (j + C3) % nb; - ri[m] = (nb + j - C1) % nb; - ri[m + 1] = (nb + j - C2) % nb; - ri[m + 2] = (nb + j - C3) % nb; + /* pre-calculate forward and reverse increments */ + for (m = j = 0; j < nb; j++, m += 3) { + fi[m] = (j + C1) % nb; + fi[m + 1] = (j + C2) % nb; + fi[m + 2] = (j + C3) % nb; + ri[m] = (nb + j - C1) % nb; + ri[m + 1] = (nb + j - C2) % nb; + ri[m + 2] = (nb + j - C3) % nb; + } + + N = Nb * (Nr + 1); + + for (i = j = 0; i < Nk; i++, j += 4) { + CipherKey[i] = pack(key + j); + } + + for (i = 0; i < Nk; i++) + fkey[i] = CipherKey[i]; + + for (j = Nk, k = 0; j < N; j += Nk, k++) { + fkey[j] = fkey[j - Nk] ^ AEStb.SubByte(ROTL24(fkey[j - 1])) ^ AEStb.rco[k]; + + if (Nk <= 6) { + for (i = 1; i < Nk && (i + j) < N; i++) + fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; + } else { + for (i = 1; i < 4 && (i + j) < N; i++) + fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; + + if ((j + 4) < N) + fkey[j + 4] = fkey[j + 4 - Nk] ^ AEStb.SubByte(fkey[j + 3]); + + for (i = 5; i < Nk && (i + j) < N; i++) + fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; } + } - N = Nb * (Nr + 1); + /* now for the expanded decrypt key in reverse order */ - for (i = j = 0; i < Nk; i++, j += 4) - { - CipherKey[i] = pack(key + j); - } + for (j = 0; j < Nb; j++) + rkey[j + N - Nb] = fkey[j]; - for (i = 0; i < Nk; i++) fkey[i] = CipherKey[i]; + for (i = Nb; i < N - Nb; i += Nb) { + k = N - Nb - i; - for (j = Nk, k = 0; j < N; j += Nk, k++) - { - fkey[j] = fkey[j - Nk] ^ AEStb.SubByte(ROTL24(fkey[j - 1]))^AEStb.rco[k]; + for (j = 0; j < Nb; j++) + rkey[k + j] = AEStb.InvMixCol(fkey[i + j]); + } - if (Nk <= 6) - { - for (i = 1; i < Nk && (i + j) < N; i++) - fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; - } - else - { - for (i = 1; i < 4 && (i + j) < N; i++) - fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; - - if ((j + 4) < N) fkey[j + 4] = fkey[j + 4 - Nk] ^ AEStb.SubByte(fkey[j + 3]); - - for (i = 5; i < Nk && (i + j) < N; i++) - fkey[i + j] = fkey[i + j - Nk] ^ fkey[i + j - 1]; - } - - } - - /* now for the expanded decrypt key in reverse order */ - - for (j = 0; j < Nb; j++) rkey[j + N - Nb] = fkey[j]; - - for (i = Nb; i < N - Nb; i += Nb) - { - k = N - Nb - i; - - for (j = 0; j < Nb; j++) rkey[k + j] = AEStb.InvMixCol(fkey[i + j]); - } - - for (j = N - Nb; j < N; j++) rkey[j - N + Nb] = fkey[j]; + for (j = N - Nb; j < N; j++) + rkey[j - N + Nb] = fkey[j]; } - /* There is an obvious time/space trade-off possible here. * * Instead of just one ftable[], I could have 4, the other * * 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */ -void SoftwareAES::_encrypt(uint8_t* buff) -{ - int i, j, k, m; - uint32_t a[8], b[8], *x, *y, *t; +void SoftwareAES::_encrypt(uint8_t* buff) { + int i, j, k, m; + uint32_t a[8], b[8], *x, *y, *t; - for (i = j = 0; i < Nb; i++, j += 4) - { - a[i] = pack(buff + j); - a[i] ^= fkey[i]; + for (i = j = 0; i < Nb; i++, j += 4) { + a[i] = pack(buff + j); + a[i] ^= fkey[i]; + } + + k = Nb; + x = a; + y = b; + + /* State alternates between a and b */ + for (i = 1; i < Nr; i++) { + /* Nr is number of rounds. May be odd. */ + + /* if Nb is fixed - unroll this next + loop and hard-code in the values of fi[] */ + + for (m = j = 0; j < Nb; j++, m += 3) { + /* deal with each 32-bit element of the State */ + /* This is the time-critical bit */ + y[j] = fkey[k++] ^ AEStb.ftable[(uint8_t)x[j]] ^ ROTL8(AEStb.ftable[(uint8_t)(x[fi[m]] >> 8)]) ^ + ROTL16(AEStb.ftable[(uint8_t)(x[fi[m + 1]] >> 16)]) ^ ROTL24(AEStb.ftable[(uint8_t)(x[fi[m + 2]] >> 24)]); } - k = Nb; - x = a; - y = b; + t = x; + x = y; + y = t; /* swap pointers */ + } - /* State alternates between a and b */ - for (i = 1; i < Nr; i++) - { - /* Nr is number of rounds. May be odd. */ + /* Last Round - unroll if possible */ + for (m = j = 0; j < Nb; j++, m += 3) { + y[j] = fkey[k++] ^ (uint32_t)AEStb.fbsub[(uint8_t)x[j]] ^ ROTL8((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m]] >> 8)]) ^ + ROTL16((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 1]] >> 16)]) ^ + ROTL24((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 2]] >> 24)]); + } - /* if Nb is fixed - unroll this next - loop and hard-code in the values of fi[] */ + for (i = j = 0; i < Nb; i++, j += 4) { + unpack(y[i], (uint8_t*)&buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } - for (m = j = 0; j < Nb; j++, m += 3) - { - /* deal with each 32-bit element of the State */ - /* This is the time-critical bit */ - y[j] = fkey[k++] ^ AEStb.ftable[(uint8_t)x[j]] ^ - ROTL8(AEStb.ftable[(uint8_t)(x[fi[m]] >> 8)])^ - ROTL16(AEStb.ftable[(uint8_t)(x[fi[m + 1]] >> 16)])^ - ROTL24(AEStb.ftable[(uint8_t)(x[fi[m + 2]] >> 24)]); - } - - t = x; - x = y; - y = t; /* swap pointers */ - } - - /* Last Round - unroll if possible */ - for (m = j = 0; j < Nb; j++, m += 3) - { - y[j] = fkey[k++] ^ (uint32_t)AEStb.fbsub[(uint8_t)x[j]] ^ - ROTL8((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m]] >> 8)])^ - ROTL16((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 1]] >> 16)])^ - ROTL24((uint32_t)AEStb.fbsub[(uint8_t)(x[fi[m + 2]] >> 24)]); - } - - for (i = j = 0; i < Nb; i++, j += 4) - { - unpack(y[i], (uint8_t*)&buff[j]); - x[i] = y[i] = 0; /* clean up stack */ - } - - return; + return; } -void SoftwareAES::_decrypt(uint8_t* buff) -{ - int i, j, k, m; - uint32_t a[8], b[8], *x, *y, *t; +void SoftwareAES::_decrypt(uint8_t* buff) { + int i, j, k, m; + uint32_t a[8], b[8], *x, *y, *t; - for (i = j = 0; i < Nb; i++, j += 4) - { - a[i] = pack(buff + j); - a[i] ^= rkey[i]; + for (i = j = 0; i < Nb; i++, j += 4) { + a[i] = pack(buff + j); + a[i] ^= rkey[i]; + } + + k = Nb; + x = a; + y = b; + + /* State alternates between a and b */ + for (i = 1; i < Nr; i++) { + /* Nr is number of rounds. May be odd. */ + + /* if Nb is fixed - unroll this next + loop and hard-code in the values of ri[] */ + + for (m = j = 0; j < Nb; j++, m += 3) { + /* This is the time-critical bit */ + y[j] = rkey[k++] ^ AEStb.rtable[(uint8_t)x[j]] ^ ROTL8(AEStb.rtable[(uint8_t)(x[ri[m]] >> 8)]) ^ + ROTL16(AEStb.rtable[(uint8_t)(x[ri[m + 1]] >> 16)]) ^ ROTL24(AEStb.rtable[(uint8_t)(x[ri[m + 2]] >> 24)]); } - k = Nb; - x = a; - y = b; + t = x; + x = y; + y = t; /* swap pointers */ + } - /* State alternates between a and b */ - for (i = 1; i < Nr; i++) - { - /* Nr is number of rounds. May be odd. */ + /* Last Round - unroll if possible */ + for (m = j = 0; j < Nb; j++, m += 3) { + y[j] = rkey[k++] ^ (uint32_t)AEStb.rbsub[(uint8_t)x[j]] ^ ROTL8((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m]] >> 8)]) ^ + ROTL16((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 1]] >> 16)]) ^ + ROTL24((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 2]] >> 24)]); + } - /* if Nb is fixed - unroll this next - loop and hard-code in the values of ri[] */ + for (i = j = 0; i < Nb; i++, j += 4) { + unpack(y[i], (uint8_t*)&buff[j]); + x[i] = y[i] = 0; /* clean up stack */ + } - for (m = j = 0; j < Nb; j++, m += 3) - { - /* This is the time-critical bit */ - y[j] = rkey[k++] ^ AEStb.rtable[(uint8_t)x[j]] ^ - ROTL8(AEStb.rtable[(uint8_t)(x[ri[m]] >> 8)])^ - ROTL16(AEStb.rtable[(uint8_t)(x[ri[m + 1]] >> 16)])^ - ROTL24(AEStb.rtable[(uint8_t)(x[ri[m + 2]] >> 24)]); - } - - t = x; - x = y; - y = t; /* swap pointers */ - } - - /* Last Round - unroll if possible */ - for (m = j = 0; j < Nb; j++, m += 3) - { - y[j] = rkey[k++] ^ (uint32_t)AEStb.rbsub[(uint8_t)x[j]] ^ - ROTL8((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m]] >> 8)])^ - ROTL16((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 1]] >> 16)])^ - ROTL24((uint32_t)AEStb.rbsub[(uint8_t)(x[ri[m + 2]] >> 24)]); - } - - for (i = j = 0; i < Nb; i++, j += 4) - { - unpack(y[i], (uint8_t*)&buff[j]); - x[i] = y[i] = 0; /* clean up stack */ - } - - return; + return; } -void SoftwareAES::setKey(const uint8_t* key) -{ - gkey(4, 4, key); -} +void SoftwareAES::setKey(const uint8_t* key) { gkey(4, 4, key); } // CBC mode decryption -void SoftwareAES::decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) -{ - uint8_t block[16]; - const uint8_t* ctext_ptr; - unsigned int blockno = 0, i; +void SoftwareAES::decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) { + uint8_t block[16]; + const uint8_t* ctext_ptr; + unsigned int blockno = 0, i; - //fprintf( stderr,"aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len ); - //printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); + // fprintf( stderr,"aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len ); + // printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); - for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) + for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) { + unsigned int fraction; + + if (blockno == (len / sizeof(block))) // last block { - unsigned int fraction; + fraction = len % sizeof(block); - if (blockno == (len / sizeof(block))) // last block - { - fraction = len % sizeof(block); + if (fraction == 0) + break; - if (fraction == 0) break; + memset(block, 0, sizeof(block)); + } else + fraction = 16; - memset(block, 0, sizeof(block)); - } - else fraction = 16; + // debug_printf("block %d: fraction = %d\n", blockno, fraction); + memcpy(block, inbuf + blockno * sizeof(block), fraction); + _decrypt(block); - // debug_printf("block %d: fraction = %d\n", blockno, fraction); - memcpy(block, inbuf + blockno * sizeof(block), fraction); - _decrypt(block); + if (blockno == 0) + ctext_ptr = iv; + else + ctext_ptr = (uint8_t*)(inbuf + (blockno - 1) * sizeof(block)); - if (blockno == 0) ctext_ptr = iv; - else ctext_ptr = (uint8_t*)(inbuf + (blockno - 1) * sizeof(block)); + for (i = 0; i < fraction; i++) + outbuf[blockno * sizeof(block) + i] = ctext_ptr[i] ^ block[i]; - for (i = 0; i < fraction; i++) - outbuf[blockno * sizeof(block) + i] = - ctext_ptr[i] ^ block[i]; - - // debug_printf("Block %d output: ", blockno); - // hexdump(outbuf + blockno*sizeof(block), 16); - } + // debug_printf("Block %d output: ", blockno); + // hexdump(outbuf + blockno*sizeof(block), 16); + } } // CBC mode encryption -void SoftwareAES::encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) -{ - uint8_t block[16]; - uint8_t feedback[16]; - memcpy(feedback, iv, 16); - unsigned int blockno = 0, i; +void SoftwareAES::encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) { + uint8_t block[16]; + uint8_t feedback[16]; + memcpy(feedback, iv, 16); + unsigned int blockno = 0, i; - //printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); - //fprintf( stderr,"aes_encrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); + // printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); + // fprintf( stderr,"aes_encrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len); - for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) + for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) { + unsigned int fraction; + + if (blockno == (len / sizeof(block))) // last block { - unsigned int fraction; + fraction = len % sizeof(block); - if (blockno == (len / sizeof(block))) // last block - { - fraction = len % sizeof(block); + if (fraction == 0) + break; - if (fraction == 0) break; + memset(block, 0, sizeof(block)); + } else + fraction = 16; - memset(block, 0, sizeof(block)); - } - else fraction = 16; + // debug_printf("block %d: fraction = %d\n", blockno, fraction); + memcpy(block, inbuf + blockno * sizeof(block), fraction); - // debug_printf("block %d: fraction = %d\n", blockno, fraction); - memcpy(block, inbuf + blockno * sizeof(block), fraction); + for (i = 0; i < fraction; i++) + block[i] = inbuf[blockno * sizeof(block) + i] ^ feedback[i]; - for (i = 0; i < fraction; i++) - block[i] = inbuf[blockno * sizeof(block) + i] ^ feedback[i]; - - _encrypt(block); - memcpy(feedback, block, sizeof(block)); - memcpy(outbuf + blockno * sizeof(block), block, sizeof(block)); - // debug_printf("Block %d output: ", blockno); - // hexdump(outbuf + blockno*sizeof(block), 16); - } + _encrypt(block); + memcpy(feedback, block, sizeof(block)); + memcpy(outbuf + blockno * sizeof(block), block, sizeof(block)); + // debug_printf("Block %d output: ", blockno); + // hexdump(outbuf + blockno*sizeof(block), 16); + } } #if _AES_NI #include -class NiAES : public IAES -{ - __m128i m_ekey[11]; - __m128i m_dkey[11]; +class NiAES : public IAES { + __m128i m_ekey[11]; + __m128i m_dkey[11]; + public: - void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) - { - __m128i feedback,data; - uint64_t i,j; - if (len%16) - len = len/16+1; - else - len /= 16; - feedback = _mm_loadu_si128((__m128i*)iv); - for (i=0 ; i NewAES() -{ +std::unique_ptr NewAES() { #if _AES_NI - if (HAS_AES_NI == -1) - { + if (HAS_AES_NI == -1) { #if _MSC_VER - int info[4]; - __cpuid(info, 1); - HAS_AES_NI = ((info[2] & 0x2000000) != 0); + int info[4]; + __cpuid(info, 1); + HAS_AES_NI = ((info[2] & 0x2000000) != 0); #else - unsigned int a,b,c,d; - __cpuid(1, a,b,c,d); - HAS_AES_NI = ((c & 0x2000000) != 0); + unsigned int a, b, c, d; + __cpuid(1, a, b, c, d); + HAS_AES_NI = ((c & 0x2000000) != 0); #endif - } - if (HAS_AES_NI) - return std::unique_ptr(new NiAES); - else - return std::unique_ptr(new SoftwareAES); -#else + } + if (HAS_AES_NI) + return std::unique_ptr(new NiAES); + else return std::unique_ptr(new SoftwareAES); +#else + return std::unique_ptr(new SoftwareAES); #endif } -} - +} // namespace nod diff --git a/lib/nod.cpp b/lib/nod.cpp index 680c05e..c075767 100644 --- a/lib/nod.cpp +++ b/lib/nod.cpp @@ -2,87 +2,73 @@ #include "nod/nod.hpp" #include "nod/DiscBase.hpp" -namespace nod -{ +namespace nod { logvisor::Module LogModule("nod"); std::unique_ptr NewDiscIOISO(SystemStringView path); std::unique_ptr NewDiscIOWBFS(SystemStringView path); -std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii) -{ - /* Temporary file handle to determine image type */ - std::unique_ptr fio = NewFileIO(path); - if (!fio->exists()) - { - LogModule.report(logvisor::Error, _SYS_STR("Unable to open '%s'"), path.data()); - return {}; - } - std::unique_ptr rs = fio->beginReadStream(); - if (!rs) - return {}; +std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii) { + /* Temporary file handle to determine image type */ + std::unique_ptr fio = NewFileIO(path); + if (!fio->exists()) { + LogModule.report(logvisor::Error, _SYS_STR("Unable to open '%s'"), path.data()); + return {}; + } + std::unique_ptr rs = fio->beginReadStream(); + if (!rs) + return {}; - isWii = false; - std::unique_ptr discIO; - uint32_t magic = 0; - if (rs->read(&magic, 4) != 4) - { - LogModule.report(logvisor::Error, _SYS_STR("Unable to read magic from '%s'"), path.data()); - return {}; - } + isWii = false; + std::unique_ptr discIO; + uint32_t magic = 0; + if (rs->read(&magic, 4) != 4) { + LogModule.report(logvisor::Error, _SYS_STR("Unable to read magic from '%s'"), path.data()); + return {}; + } - if (magic == nod::SBig((uint32_t)'WBFS')) - { - discIO = NewDiscIOWBFS(path); - isWii = true; - } - else - { - rs->seek(0x18, SEEK_SET); - rs->read(&magic, 4); - magic = nod::SBig(magic); - if (magic == 0x5D1C9EA3) - { - discIO = NewDiscIOISO(path); - isWii = true; - } - else - { - rs->read(&magic, 4); - magic = nod::SBig(magic); - if (magic == 0xC2339F3D) - discIO = NewDiscIOISO(path); - } + if (magic == nod::SBig((uint32_t)'WBFS')) { + discIO = NewDiscIOWBFS(path); + isWii = true; + } else { + rs->seek(0x18, SEEK_SET); + rs->read(&magic, 4); + magic = nod::SBig(magic); + if (magic == 0x5D1C9EA3) { + discIO = NewDiscIOISO(path); + isWii = true; + } else { + rs->read(&magic, 4); + magic = nod::SBig(magic); + if (magic == 0xC2339F3D) + discIO = NewDiscIOISO(path); } + } - if (!discIO) - { - LogModule.report(logvisor::Error, _SYS_STR("'%s' is not a valid image"), path.data()); - return {}; - } + if (!discIO) { + LogModule.report(logvisor::Error, _SYS_STR("'%s' is not a valid image"), path.data()); + return {}; + } - bool Err = false; - std::unique_ptr ret; - if (isWii) - { - ret = std::unique_ptr(new DiscWii(std::move(discIO), Err)); - if (Err) - return {}; - return ret; - } - - ret = std::unique_ptr(new DiscGCN(std::move(discIO), Err)); + bool Err = false; + std::unique_ptr ret; + if (isWii) { + ret = std::unique_ptr(new DiscWii(std::move(discIO), Err)); if (Err) - return {}; + return {}; return ret; + } + + ret = std::unique_ptr(new DiscGCN(std::move(discIO), Err)); + if (Err) + return {}; + return ret; } -std::unique_ptr OpenDiscFromImage(SystemStringView path) -{ - bool isWii; - return OpenDiscFromImage(path, isWii); -} - +std::unique_ptr OpenDiscFromImage(SystemStringView path) { + bool isWii; + return OpenDiscFromImage(path, isWii); } +} // namespace nod diff --git a/logvisor b/logvisor index 1b6c2ae..01e2918 160000 --- a/logvisor +++ b/logvisor @@ -1 +1 @@ -Subproject commit 1b6c2ae7159fd4fd7a80b1951d5ed43e4d1a3676 +Subproject commit 01e291833ba4d7f2a596c32cf6158cb6a9327ad7