diff --git a/driver/main.cpp b/driver/main.cpp index 6d8b82f..3396607 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -1,7 +1,12 @@ +#include #include #include #include #include +#if _WIN32 +#include +#include +#endif #include @@ -10,14 +15,30 @@ #include #include + +constexpr std::array codepages = { + CP_US_ASCII, + CP_UTF8, + CP_SHIFT_JIS, +}; + + static void printHelp() { - fmt::print(stderr, FMT_STRING( - "Usage:\n" - " nodtool extract [-f] []\n" - " nodtool makegcn []\n" - " nodtool makewii []\n" - " nodtool mergegcn []\n" - " nodtool mergewii []\n")); + fmt::print(stderr, FMT_STRING(_SYS_STR( + "Usage:\n" + " nodtool extract [options] []\n" + " nodtool makegcn [options] []\n" + " nodtool makewii [options] []\n" + " nodtool mergegcn [options] []\n" + " nodtool mergewii [options] []\n" + "Options:\n" + " -f Force (extract only)\n" + " -v Verbose details (extract only).\n" + " -c Set multi-byte character set of image(s).\n" + "Available codepage values:\n" + " 0 7-bit ASCII (default)\n" + " 1 UTF-8\n" + " 2 Shift-JIS\n"))); } #if NOD_UCS2 @@ -32,37 +53,57 @@ 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; - } - /* Enable logging to console */ logvisor::RegisterStandardExceptions(); logvisor::RegisterConsoleLogger(); +#if _WIN32 + _setmode(_fileno(stdin), _O_U16TEXT); + _setmode(_fileno(stdout), _O_U16TEXT); + _setmode(_fileno(stderr), _O_U16TEXT); +#endif + int argidx = 1; + nod::SystemString errand; bool verbose = false; - nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) { + nod::Codepage_t discLocale = CP_US_ASCII; + nod::ExtractionContext ctx = {true, [&](nod::SystemStringView str, float c) { if (verbose) - fmt::print(stderr, FMT_STRING("Current node: {}, Extraction {:g}% Complete\n"), str, - c * 100.f); + fmt::print(stderr, FMT_STRING(_SYS_STR("Current node: {}, Extraction {:g}% Complete\n")), + str, c * 100.f); }}; - const nod::SystemChar* inDir = nullptr; - const nod::SystemChar* outDir = _SYS_STR("."); - - for (int a = 2; a < argc; ++a) { - if (argv[a][0] == '-' && argv[a][1] == 'f') + while (argidx < argc) { + if (!strcasecmp(argv[argidx], _SYS_STR("-f"))) { ctx.force = true; - else if (argv[a][0] == '-' && argv[a][1] == 'v') + ++argidx; + continue; + } else if (!strcasecmp(argv[argidx], _SYS_STR("-v"))) { verbose = true; + ++argidx; + continue; + } else if (!strcasecmp(argv[argidx], _SYS_STR("-c"))) { + if (argidx+1 < argc) { + unsigned long cpidx = nod::StrToUL(argv[argidx + 1], NULL, 0); + if (cpidx > codepages.size() - 1) + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unavailable codepage: {}")), cpidx); + discLocale = codepages[cpidx]; + } else { + printHelp(); + return 1; + } + argidx += 2; + continue; + } else if (errand.empty()) { + errand = argv[argidx]; + ++argidx; + continue; + } else { + break; + } + } - else if (!inDir) - inDir = argv[a]; - else - outDir = argv[a]; + if (errand.empty()) { + printHelp(); + return 1; } auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) { @@ -74,89 +115,156 @@ int main(int argc, char* argv[]) fflush(stdout); }; - if (!strcasecmp(argv[1], _SYS_STR("extract"))) { + if (errand == _SYS_STR("extract")) { + nod::SystemString imageIn; + nod::SystemString dirOut; + while (argidx < argc) { + if (imageIn.empty()) { + imageIn = argv[argidx]; + ++argidx; + continue; + } else if (dirOut.empty()) { + dirOut = argv[argidx]; + ++argidx; + continue; + } else { + printHelp(); + return 1; + } + } + if (dirOut.empty()) + dirOut = _SYS_STR("."); + bool isWii; - std::unique_ptr disc = nod::OpenDiscFromImage(inDir, isWii); + std::unique_ptr disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale); if (!disc) return 1; - nod::Mkdir(outDir, 0755); + nod::Mkdir(dirOut.c_str(), 0755); nod::IPartition* dataPart = disc->getDataPartition(); if (!dataPart) return 1; - if (!dataPart->extractToDirectory(outDir, ctx)) + if (!dataPart->extractToDirectory(dirOut, ctx)) return 1; - } else if (!strcasecmp(argv[1], _SYS_STR("makegcn"))) { + } else if (errand == _SYS_STR("makegcn")) { + nod::SystemString fsrootIn; + nod::SystemString imageOut; + while (argidx < argc) { + if (fsrootIn.empty()) { + fsrootIn = argv[argidx]; + ++argidx; + continue; + } else if (imageOut.empty()) { + imageOut = argv[argidx]; + ++argidx; + continue; + } else { + printHelp(); + return 1; + } + } + if (imageOut.empty()) + imageOut = fsrootIn + _SYS_STR(".gcm"); + /* Pre-validate path */ nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[2]); + if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn); return 1; } - if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2])) + if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(fsrootIn, discLocale)) 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]); - } + nod::DiscBuilderGCN b(imageOut, progFunc, discLocale); + ret = b.buildFromDirectory(fsrootIn); - fmt::print(FMT_STRING("\n")); + fmt::print(FMT_STRING(_SYS_STR("\n"))); if (ret != nod::EBuildResult::Success) return 1; - } else if (!strcasecmp(argv[1], _SYS_STR("makewii"))) { + } else if (errand == _SYS_STR("makewii")) { + nod::SystemString fsrootIn; + nod::SystemString imageOut; + while (argidx < argc) { + if (fsrootIn.empty()) { + fsrootIn = argv[argidx]; + ++argidx; + continue; + } else if (imageOut.empty()) { + imageOut = argv[argidx]; + ++argidx; + continue; + } else { + printHelp(); + return 1; + } + } + if (imageOut.empty()) + imageOut = fsrootIn + _SYS_STR(".iso"); + /* Pre-validate path */ nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[4]); + if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn); return 1; } bool dual = false; - if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual)) + if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(fsrootIn, dual, discLocale)) 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]); - } + nod::DiscBuilderWii b(imageOut, dual, progFunc, discLocale); + ret = b.buildFromDirectory(fsrootIn); - fmt::print(FMT_STRING("\n")); + fmt::print(FMT_STRING(_SYS_STR("\n"))); if (ret != nod::EBuildResult::Success) return 1; - } else if (!strcasecmp(argv[1], _SYS_STR("mergegcn"))) { + } else if (errand == _SYS_STR("mergegcn")) { + nod::SystemString fsrootIn; + nod::SystemString imageIn; + nod::SystemString imageOut; + while (argidx < argc) { + if (fsrootIn.empty()) { + fsrootIn = argv[argidx]; + ++argidx; + continue; + } else if (imageIn.empty()) { + imageIn = argv[argidx]; + ++argidx; + continue; + } else if (imageOut.empty()) { + imageOut = argv[argidx]; + ++argidx; + continue; + } else { + printHelp(); + return 1; + } + } + if (imageOut.empty()) + imageOut = fsrootIn + _SYS_STR(".gcm"); + /* Pre-validate paths */ nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[2]); + if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn); return 1; } - if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), argv[3]); + if (nod::Stat(imageIn.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), imageIn); return 1; } bool isWii; - std::unique_ptr disc = nod::OpenDiscFromImage(argv[3], isWii); + std::unique_ptr disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale); if (!disc) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), argv[3]); + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), imageIn); return 1; } if (isWii) { @@ -164,38 +272,55 @@ int main(int argc, char* argv[]) return 1; } - if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast(*disc), argv[2])) + if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast(*disc), fsrootIn, discLocale)) 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]); - } + nod::DiscMergerGCN b(imageOut, static_cast(*disc), progFunc, discLocale); + ret = b.mergeFromDirectory(fsrootIn); - fmt::print(FMT_STRING("\n")); + fmt::print(FMT_STRING(_SYS_STR("\n"))); if (ret != nod::EBuildResult::Success) return 1; - } else if (!strcasecmp(argv[1], _SYS_STR("mergewii"))) { + } else if (errand == _SYS_STR("mergewii")) { + nod::SystemString fsrootIn; + nod::SystemString imageIn; + nod::SystemString imageOut; + while (argidx < argc) { + if (fsrootIn.empty()) { + fsrootIn = argv[argidx]; + ++argidx; + continue; + } else if (imageIn.empty()) { + imageIn = argv[argidx]; + ++argidx; + continue; + } else if (imageOut.empty()) { + imageOut = argv[argidx]; + ++argidx; + continue; + } else { + printHelp(); + return 1; + } + } + if (imageOut.empty()) + imageOut = fsrootIn + _SYS_STR(".iso"); + /* Pre-validate paths */ nod::Sstat theStat; - if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[2]); + if (nod::Stat(fsrootIn.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn); return 1; } - if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) { - nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), argv[3]); + if (nod::Stat(imageIn.c_str(), &theStat) || !S_ISREG(theStat.st_mode)) { + nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), imageIn); return 1; } bool isWii; - std::unique_ptr disc = nod::OpenDiscFromImage(argv[3], isWii); + std::unique_ptr disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale); if (!disc) { nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), argv[3]); return 1; @@ -206,29 +331,22 @@ int main(int argc, char* argv[]) } bool dual = false; - if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast(*disc), argv[2], dual)) + if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast(*disc), fsrootIn, dual, discLocale)) 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]); - } + nod::DiscMergerWii b(imageOut, static_cast(*disc), dual, progFunc, discLocale); + ret = b.mergeFromDirectory(fsrootIn); - fmt::print(FMT_STRING("\n")); + fmt::print(FMT_STRING(_SYS_STR("\n"))); if (ret != nod::EBuildResult::Success) return 1; } else { printHelp(); return 1; } - + nod::LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Success!"))); return 0; } diff --git a/include/nod/DiscBase.hpp b/include/nod/DiscBase.hpp index 22462ee..3411b20 100644 --- a/include/nod/DiscBase.hpp +++ b/include/nod/DiscBase.hpp @@ -236,7 +236,7 @@ public: return end(); } - bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const; + bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx, Codepage_t codepage) const; }; class IPartition { @@ -275,6 +275,7 @@ protected: PartitionKind m_kind; uint64_t m_offset; bool m_isWii; + Codepage_t m_codepage; public: mutable size_t m_curNodeIdx = 0; @@ -289,8 +290,8 @@ public: 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) {} + IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset, Codepage_t codepage) + : m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii), m_codepage(codepage) {} virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; } PartitionKind getKind() const { return m_kind; } bool isWii() const { return m_isWii; } @@ -405,14 +406,14 @@ public: 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); + size_t parentDirIdx, SystemStringView keyPath); - static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, SystemStringView dirIn); + static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, SystemStringView dirIn, Codepage_t codepage); void addBuildName(SystemStringView str) { - SystemUTF8Conv utf8View(str); - m_buildNames.emplace_back(utf8View.utf8_str()); - m_buildNameOff += str.size() + 1; + SystemToDiscLocConv nameView(str, m_codepage); + m_buildNames.emplace_back(nameView.disc_str()); + m_buildNameOff += nameView.disc_str().size() + 1; } DiscBuilderBase& m_parent; @@ -420,15 +421,16 @@ public: uint64_t m_dolOffset = 0; uint64_t m_dolSize = 0; bool m_isWii; + Codepage_t m_codepage; public: - PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii) - : m_parent(parent), m_kind(kind), m_isWii(isWii) {} + PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii, Codepage_t codepage) + : m_parent(parent), m_kind(kind), m_isWii(isWii), m_codepage(codepage) {} virtual std::unique_ptr beginWriteStream(uint64_t offset) = 0; bool buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn); - static std::optional CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, bool isWii); + static std::optional CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, bool isWii, Codepage_t codepage); bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, SystemStringView dirIn); - static std::optional CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn); + static std::optional CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn, Codepage_t codepage); }; protected: diff --git a/include/nod/DiscGCN.hpp b/include/nod/DiscGCN.hpp index a16b897..55d798f 100644 --- a/include/nod/DiscGCN.hpp +++ b/include/nod/DiscGCN.hpp @@ -7,10 +7,10 @@ class DiscBuilderGCN; class DiscGCN : public DiscBase { friend class DiscMergerGCN; - DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB); + DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB, Codepage_t codepage); public: - DiscGCN(std::unique_ptr&& dio, bool& err); + DiscGCN(std::unique_ptr&& dio, bool& err, Codepage_t codepage = CP_US_ASCII); bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override; }; @@ -18,9 +18,9 @@ class DiscBuilderGCN : public DiscBuilderBase { friend class DiscMergerGCN; public: - DiscBuilderGCN(SystemStringView outPath, FProgress progressCB); + DiscBuilderGCN(SystemStringView outPath, FProgress progressCB, Codepage_t codepage = CP_US_ASCII); EBuildResult buildFromDirectory(SystemStringView dirIn); - static std::optional CalculateTotalSizeRequired(SystemStringView dirIn); + static std::optional CalculateTotalSizeRequired(SystemStringView dirIn, Codepage_t codepage = CP_US_ASCII); }; class DiscMergerGCN { @@ -28,9 +28,9 @@ class DiscMergerGCN { DiscBuilderGCN m_builder; public: - DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB); + DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB, Codepage_t codepage = CP_US_ASCII); EBuildResult mergeFromDirectory(SystemStringView dirIn); - static std::optional CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn); + static std::optional CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn, Codepage_t codepage = CP_US_ASCII); }; } // namespace nod diff --git a/include/nod/DiscWii.hpp b/include/nod/DiscWii.hpp index ee9abc4..9bc16c6 100644 --- a/include/nod/DiscWii.hpp +++ b/include/nod/DiscWii.hpp @@ -7,16 +7,16 @@ class DiscBuilderWii; class DiscWii : public DiscBase { public: - DiscWii(std::unique_ptr&& dio, bool& err); - DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB); + DiscWii(std::unique_ptr&& dio, bool& err, Codepage_t codepage = CP_US_ASCII); + DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB, Codepage_t codepage); bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override; }; class DiscBuilderWii : public DiscBuilderBase { public: - DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB); + DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB, Codepage_t codepage = CP_US_ASCII); EBuildResult buildFromDirectory(SystemStringView dirIn); - static std::optional CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer); + static std::optional CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer, Codepage_t codepage = CP_US_ASCII); }; class DiscMergerWii { @@ -24,9 +24,9 @@ class DiscMergerWii { DiscBuilderWii m_builder; public: - DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB); + DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB, Codepage_t codepage = CP_US_ASCII); EBuildResult mergeFromDirectory(SystemStringView dirIn); - static std::optional CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer); + static std::optional CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer, Codepage_t codepage = CP_US_ASCII); }; } // namespace nod diff --git a/include/nod/Util.hpp b/include/nod/Util.hpp index 6bad58a..4a24699 100644 --- a/include/nod/Util.hpp +++ b/include/nod/Util.hpp @@ -18,6 +18,7 @@ #else #include #include +#include #include #include #include @@ -75,9 +76,9 @@ extern logvisor::Module LogModule; /* filesystem char type */ #if _WIN32 && UNICODE #define NOD_UCS2 1 -typedef struct _stat Sstat; +typedef struct _stat64 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 Stat(const wchar_t* path, Sstat* statout) { return _wstati64(path, statout); } #else typedef struct stat Sstat; static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode); } @@ -86,65 +87,164 @@ static inline int Stat(const char* path, Sstat* statout) { return stat(path, sta /* String-converting views */ #if NOD_UCS2 -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; - -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); - } - std::string_view utf8_str() const { return m_utf8; } - const char* c_str() const { return m_utf8.c_str(); } -}; -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); - } - SystemStringView sys_str() const { return m_sys; } - const SystemChar* c_str() const { return m_sys.c_str(); } -}; +#define CP_US_ASCII 20127 +#define CP_SHIFT_JIS 932 +#define CP_GB_18030 54936 #ifndef _SYS_STR #define _SYS_STR(val) L##val #endif +typedef wchar_t SystemChar; +typedef std::wstring SystemString; +typedef std::wstring_view SystemStringView; +typedef UINT Codepage_t; +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); } +static inline unsigned long StrToUL(const SystemChar* str, SystemChar** endptr, int base) {return wcstoul(str, endptr, base); } +class SystemToDiscLocConv { + std::string m_disc_str; + +public: + explicit SystemToDiscLocConv(SystemStringView str, Codepage_t codepage) { + if (!IsValidCodePage(codepage)) + nod::LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid Codepage ({})")), codepage); + + size_t len; + bool failureState = false; + switch (codepage) { + case CP_UTF8: case CP_GB_18030: + len = WideCharToMultiByte(codepage, WC_ERR_INVALID_CHARS, str.data(), str.size(), nullptr, 0, nullptr, nullptr); + if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) + failureState = true; + break; + case CP_UTF7: + // WideCharToMultiByte cannot use WC_ERR_INVALID_CHARS nor lpUsedDefaultChar to check for a bad conversion when converting to UTF-7. + // https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte + len = WideCharToMultiByte(codepage, 0, str.data(), str.size(), nullptr, 0, nullptr, nullptr); + break; + default: + BOOL lpUsedDefaultChar = false; + len = WideCharToMultiByte(codepage, 0, str.data(), str.size(), nullptr, 0, nullptr, &lpUsedDefaultChar); + if (lpUsedDefaultChar) + failureState = true; + break; + } + m_disc_str.assign(len, '\0'); + WideCharToMultiByte(codepage, 0, str.data(), str.size(), &m_disc_str[0], len, nullptr, nullptr); + if (failureState) + nod::LogModule.report(logvisor::Warning, FMT_STRING(_SYS_STR("Bad conversion to codepage {}: \"{}\"")), codepage, str); + + } + std::string_view disc_str() const { return m_disc_str; } + const char* c_str() const { return m_disc_str.c_str(); } +}; +class DiscLocToSystemConv { + SystemString m_sys_str; + +public: + explicit DiscLocToSystemConv(std::string_view str, Codepage_t codepage) { + if (!IsValidCodePage(codepage)) + nod::LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid Codepage ({})")), codepage); + + bool failureState = false; + size_t len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(), str.size(), nullptr, 0); + if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) + failureState = true; + + m_sys_str.assign(len, L'\0'); + MultiByteToWideChar(codepage, 0, str.data(), str.size(), &m_sys_str[0], len); + if (failureState) + // This will probably never happen, but might as well check. + nod::LogModule.report(logvisor::Warning, FMT_STRING(_SYS_STR("Bad conversion from codepage {}: \"{}\"")), codepage, m_sys_str); + } + SystemStringView sys_str() const { return m_sys_str; } + const SystemChar* c_str() const { return m_sys_str.c_str(); } +}; #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; - -public: - explicit SystemUTF8Conv(SystemStringView str) : m_utf8(str) {} - std::string_view utf8_str() const { return m_utf8; } - const char* c_str() const { return m_utf8.data(); } -}; -class SystemStringConv { - SystemStringView m_sys; - -public: - explicit SystemStringConv(std::string_view str) : m_sys(str) {} - SystemStringView sys_str() const { return m_sys; } - const SystemChar* c_str() const { return m_sys.data(); } -}; +#define CP_US_ASCII "US-ASCII" +#define CP_UTF8 "UTF-8" +#define CP_SHIFT_JIS "SHIFT-JIS" #ifndef _SYS_STR #define _SYS_STR(val) val #endif +typedef char SystemChar; +typedef std::string SystemString; +typedef std::string_view SystemStringView; +typedef const char* Codepage_t; +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); } +static inline unsigned long StrToUL(const SystemChar* str, SystemChar** endptr, int base) {return strtoul(str, endptr, base); } +static inline bool CodepageConvert(const iconv_t convDesc, std::string_view input, std::string& output) +{ + bool failureState = false; + size_t const inBytes = input.size(); + size_t const outBufferSize = 4 * inBytes; + + std::string outBuffer; + outBuffer.resize(outBufferSize); + + auto srcBuffer = input.data(); + size_t srcBytes = inBytes; + auto dstBuffer = outBuffer.data(); + size_t dstBytes = outBuffer.size(); + + while (srcBytes != 0) { + size_t const iconvResult = +#if defined(__OpenBSD__) || defined(__NetBSD__) + iconv(convDesc, reinterpret_cast(&srcBuffer), &srcBytes, &dstBuffer, &dstBytes); +#else + iconv(convDesc, const_cast(reinterpret_cast(&srcBuffer)), &srcBytes, &dstBuffer, &dstBytes); +#endif + if ((size_t)-1 == iconvResult) { + failureState = true; + if (EILSEQ == errno || EINVAL == errno) { + // Try to skip the bad character + if (srcBytes != 0) { + --srcBytes; + ++srcBuffer; + } + } else { + break; + } + } + } + outBuffer.resize(outBufferSize - dstBytes); + outBuffer.swap(output); + return failureState; +} +class SystemToDiscLocConv { + std::string m_disc_str; + +public: + explicit SystemToDiscLocConv(SystemStringView str, Codepage_t codepage) { + const iconv_t convDesc = iconv_open(codepage, CP_UTF8); + if (convDesc == (iconv_t)-1) + nod::LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid Codepage \"{}\"")), codepage); + + if (CodepageConvert(convDesc, str, m_disc_str) == true) + nod::LogModule.report(logvisor::Warning, FMT_STRING(_SYS_STR("Bad conversion to codepage \"{}\": \"{}\"")), codepage, str); + iconv_close(convDesc); + } + std::string_view disc_str() const { return m_disc_str; } + const char* c_str() const { return m_disc_str.data(); } +}; +class DiscLocToSystemConv { + SystemString m_sys_str; + +public: + explicit DiscLocToSystemConv(std::string_view str, Codepage_t codepage) { + const iconv_t convDesc = iconv_open(CP_UTF8, codepage); + if (convDesc == (iconv_t)-1) + nod::LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Invalid Codepage \"{}\"")), codepage); + + if (CodepageConvert(convDesc, str, m_sys_str) == true) + nod::LogModule.report(logvisor::Warning, FMT_STRING(_SYS_STR("Bad conversion from codepage \"{}\": \"{}\"")), codepage, m_sys_str); + iconv_close(convDesc); + } + SystemStringView sys_str() const { return m_sys_str; } + const SystemChar* c_str() const { return m_sys_str.data(); } +}; #endif static inline void Unlink(const SystemChar* file) { diff --git a/include/nod/nod.hpp b/include/nod/nod.hpp index 758c44a..83eb8b1 100644 --- a/include/nod/nod.hpp +++ b/include/nod/nod.hpp @@ -12,10 +12,10 @@ class DiscBase; struct ExtractionContext final { bool force : 1; - std::function progressCB; + std::function progressCB; }; std::unique_ptr OpenDiscFromImage(SystemStringView path); -std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii); +std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii, Codepage_t codepage = CP_US_ASCII); } // namespace nod diff --git a/lib/DiscBase.cpp b/lib/DiscBase.cpp index 43044e8..da8d6f2 100644 --- a/lib/DiscBase.cpp +++ b/lib/DiscBase.cpp @@ -126,25 +126,25 @@ std::unique_ptr Node::getBuf() const { return buf; } -bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const { - SystemStringConv nameView(getName()); +bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx, Codepage_t codepage) const { + DiscLocToSystemConv nameView(getName(), codepage); 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())); + ctx.progressCB(nameView.sys_str(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); if (Mkdir(path.c_str(), 0755) && errno != EEXIST) { LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), path); return false; } for (Node& subnode : *this) - if (!subnode.extractToDirectory(path, ctx)) + if (!subnode.extractToDirectory(path, ctx, codepage)) return false; } else if (m_kind == Kind::File) { Sstat theStat; if (ctx.progressCB) - ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); + ctx.progressCB(nameView.sys_str(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount())); if (ctx.force || Stat(path.c_str(), &theStat)) { std::unique_ptr rs = beginReadStream(); @@ -153,7 +153,7 @@ bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext return false; ws->copyFromDisc(*rs, m_discLength, [&](float prog) { if (ctx.progressCB) - ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount())); + ctx.progressCB(nameView.sys_str(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount())); }); } ++m_parent.m_curNodeIdx; @@ -194,7 +194,7 @@ bool IPartition::extractToDirectory(SystemStringView path, const ExtractionConte return false; } - return m_nodes[0].extractToDirectory(fsPath, ctx); + return m_nodes[0].extractToDirectory(fsPath, ctx, m_codepage); } bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionContext& ctx) const { @@ -209,7 +209,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont 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); + ctx.progressCB(_SYS_STR("apploader.bin"), 0.f); std::unique_ptr buf = getApploaderBuf(); auto ws = NewFileIO(apploaderPath)->beginWriteStream(); if (!ws) @@ -221,7 +221,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont 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); + ctx.progressCB(_SYS_STR("main.dol"), 0.f); std::unique_ptr buf = getDOLBuf(); auto ws = NewFileIO(dolPath)->beginWriteStream(); if (!ws) @@ -233,7 +233,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont 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); + ctx.progressCB(_SYS_STR("boot.bin"), 0.f); auto ws = NewFileIO(bootPath)->beginWriteStream(); if (!ws) return false; @@ -244,7 +244,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont 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); + ctx.progressCB(_SYS_STR("bi2.bin"), 0.f); auto ws = NewFileIO(bi2Path)->beginWriteStream(); if (!ws) @@ -406,10 +406,10 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodesPre(const Node* n if (!dirIn.empty()) { DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); for (const DirectoryEnumerator::Entry& e : dEnum) { - SystemUTF8Conv nameView(e.m_name); + SystemToDiscLocConv nameView(e.m_name, m_codepage); if (e.m_isDir) { - auto search = dirNodes.find(nameView.utf8_str().data()); + auto search = dirNodes.find(nameView.disc_str().data()); if (search != dirNodes.cend()) { recursiveMergeNodesPre(search->second, e.m_path.c_str()); dirNodes.erase(search); @@ -417,7 +417,7 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodesPre(const Node* n recursiveMergeNodesPre(nullptr, e.m_path.c_str()); } } else { - fileNodes.erase(nameView.utf8_str().data()); + fileNodes.erase(nameView.disc_str().data()); ++m_parent.m_progressTotal; } } @@ -452,11 +452,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream if (!dirIn.empty()) { DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); for (const DirectoryEnumerator::Entry& e : dEnum) { - SystemUTF8Conv nameView(e.m_name); + SystemToDiscLocConv nameView(e.m_name, m_codepage); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name; if (e.m_isDir) { - auto search = dirNodes.find(nameView.utf8_str().data()); + auto search = dirNodes.find(nameView.disc_str().data()); if (search != dirNodes.cend()) { if (!recursiveMergeNodes(ws, system, search->second, e.m_path.c_str(), chKeyPath)) return false; @@ -471,7 +471,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream if (system ^ isSys) continue; - fileNodes.erase(nameView.utf8_str().data()); + fileNodes.erase(nameView.disc_str().data()); size_t fileSz = ROUND_UP_32(e.m_fileSz); uint64_t fileOff = userAllocate(fileSz, ws); @@ -508,7 +508,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream /* Write-through remaining dir nodes */ for (const auto& p : dirNodes) { - SystemStringConv sysName(p.second->getName()); + DiscLocToSystemConv sysName(p.second->getName(), m_codepage); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); if (!recursiveMergeNodes(ws, system, p.second, {}, chKeyPath)) return false; @@ -517,7 +517,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream /* Write-through remaining file nodes */ for (const auto& p : fileNodes) { const Node& ch = *p.second; - SystemStringConv sysName(ch.getName()); + DiscLocToSystemConv sysName(ch.getName(), m_codepage); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); bool isDol; @@ -565,7 +565,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, std::function incParents, - SystemStringView keyPath) { + size_t parentDirIdx, SystemStringView keyPath) { /* Build map of existing nodes to write-through later */ std::unordered_map fileNodes; std::unordered_map dirNodes; @@ -584,23 +584,23 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn if (!dirIn.empty()) { DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); for (const DirectoryEnumerator::Entry& e : dEnum) { - SystemUTF8Conv nameView(e.m_name); + SystemToDiscLocConv nameView(e.m_name, m_codepage); 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); + m_buildNodes.emplace_back(true, m_buildNameOff, parentDirIdx, dirNodeIdx + 1); addBuildName(e.m_name); incParents(); - auto search = dirNodes.find(nameView.utf8_str().data()); + auto search = dirNodes.find(nameView.disc_str().data()); if (search != dirNodes.cend()) { if (!recursiveMergeFST(search->second, e.m_path.c_str(), [&]() { m_buildNodes[dirNodeIdx].incrementLength(); incParents(); }, - chKeyPath)) + dirNodeIdx, chKeyPath)) return false; dirNodes.erase(search); } else { @@ -609,11 +609,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn m_buildNodes[dirNodeIdx].incrementLength(); incParents(); }, - chKeyPath)) + dirNodeIdx, chKeyPath)) return false; } } else { - fileNodes.erase(nameView.utf8_str().data()); + fileNodes.erase(nameView.disc_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); @@ -624,11 +624,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn /* Write-through remaining dir nodes */ for (const auto& p : dirNodes) { - SystemStringConv sysName(p.second->getName()); + DiscLocToSystemConv sysName(p.second->getName(), m_codepage); 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); + m_buildNodes.emplace_back(true, m_buildNameOff, parentDirIdx, dirNodeIdx + 1); addBuildName(sysName.sys_str()); incParents(); @@ -637,14 +637,14 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn m_buildNodes[dirNodeIdx].incrementLength(); incParents(); }, - chKeyPath)) + dirNodeIdx, chKeyPath)) return false; } /* Write-through remaining file nodes */ for (const auto& p : fileNodes) { const Node& ch = *p.second; - SystemStringConv sysName(ch.getName()); + DiscLocToSystemConv sysName(ch.getName(), m_codepage); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data(); std::pair fileOffSz = m_fileOffsetsSizes.at(chKeyPath); @@ -657,7 +657,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn } bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, - SystemStringView dirIn) { + SystemStringView dirIn, Codepage_t codepage) { /* Build map of existing nodes to write-through later */ std::unordered_map fileNodes; std::unordered_map dirNodes; @@ -676,20 +676,20 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t if (!dirIn.empty()) { DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); for (const DirectoryEnumerator::Entry& e : dEnum) { - SystemUTF8Conv nameView(e.m_name); + SystemToDiscLocConv nameView(e.m_name, codepage); if (e.m_isDir) { - auto search = dirNodes.find(nameView.utf8_str().data()); + auto search = dirNodes.find(nameView.disc_str().data()); if (search != dirNodes.cend()) { - if (!RecursiveCalculateTotalSize(totalSz, search->second, e.m_path.c_str())) + if (!RecursiveCalculateTotalSize(totalSz, search->second, e.m_path.c_str(), codepage)) return false; dirNodes.erase(search); } else { - if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str())) + if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str(), codepage)) return false; } } else { - fileNodes.erase(nameView.utf8_str().data()); + fileNodes.erase(nameView.disc_str().data()); totalSz += ROUND_UP_32(e.m_fileSz); } } @@ -697,7 +697,7 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t /* Write-through remaining dir nodes */ for (const auto& p : dirNodes) { - if (!RecursiveCalculateTotalSize(totalSz, p.second, {})) + if (!RecursiveCalculateTotalSize(totalSz, p.second, {}, codepage)) return false; } @@ -770,7 +770,7 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& } std::optional DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(SystemStringView dirIn, - PartitionKind kind, bool isWii) { + PartitionKind kind, bool isWii, Codepage_t codepage) { SystemString dirStr(dirIn); SystemString basePath = isWii ? dirStr + _SYS_STR("/") + getKindString(kind) : dirStr; SystemString dolIn = basePath + _SYS_STR("/sys/main.dol"); @@ -782,7 +782,7 @@ std::optional DiscBuilderBase::PartitionBuilderBase::CalculateTotalSiz return std::nullopt; } uint64_t totalSz = ROUND_UP_32(dolStat.st_size); - if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str())) + if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str(), codepage)) return std::nullopt; return totalSz; } @@ -837,20 +837,20 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& 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)) + if (!recursiveMergeFST(&partIn->getFSTRoot(), filesIn.c_str(), [&]() { m_buildNodes[0].incrementLength(); }, 0, keyPath)) return false; return true; } std::optional DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const IPartition* partIn, - SystemStringView dirIn) { + SystemStringView dirIn, Codepage_t codepage) { 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())) + if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), filesIn.c_str(), codepage)) return std::nullopt; return totalSz; } diff --git a/lib/DiscGCN.cpp b/lib/DiscGCN.cpp index 9072572..c2f29bc 100644 --- a/lib/DiscGCN.cpp +++ b/lib/DiscGCN.cpp @@ -17,8 +17,8 @@ namespace nod { class PartitionGCN : public IPartition { public: - PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err) - : IPartition(parent, PartitionKind::Data, false, offset) { + PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err, Codepage_t codepage) + : IPartition(parent, PartitionKind::Data, false, offset, codepage) { /* GCN-specific header reads */ std::unique_ptr s = beginReadStream(0x0); if (!s) { @@ -118,16 +118,16 @@ public: } }; -DiscGCN::DiscGCN(std::unique_ptr&& dio, bool& err) : DiscBase(std::move(dio), err) { +DiscGCN::DiscGCN(std::unique_ptr&& dio, bool& err, Codepage_t codepage) : DiscBase(std::move(dio), err) { if (err) return; /* One lone partition for GCN */ - m_partitions.emplace_back(std::make_unique(*this, 0, err)); + m_partitions.emplace_back(std::make_unique(*this, 0, err, codepage)); } -DiscBuilderGCN DiscGCN::makeMergeBuilder(SystemStringView outPath, FProgress progressCB) { - return DiscBuilderGCN(outPath, progressCB); +DiscBuilderGCN DiscGCN::makeMergeBuilder(SystemStringView outPath, FProgress progressCB, Codepage_t codepage) { + return DiscBuilderGCN(outPath, progressCB, codepage); } bool DiscGCN::extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; } @@ -161,8 +161,8 @@ public: } }; - PartitionBuilderGCN(DiscBuilderBase& parent) - : DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {} + PartitionBuilderGCN(DiscBuilderBase& parent, Codepage_t codepage) + : DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false, codepage) {} uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override { m_curUser -= reqSz; @@ -372,8 +372,8 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(SystemStringView dirIn) { return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed; } -std::optional DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn) { - std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false); +std::optional DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn, Codepage_t codepage) { + std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false, codepage); if (!sz) return sz; *sz += 0x30000; @@ -384,13 +384,13 @@ std::optional DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringV return sz; } -DiscBuilderGCN::DiscBuilderGCN(SystemStringView outPath, FProgress progressCB) +DiscBuilderGCN::DiscBuilderGCN(SystemStringView outPath, FProgress progressCB, Codepage_t codepage) : DiscBuilderBase(outPath, 0x57058000, progressCB) { - m_partitions.emplace_back(std::make_unique(*this)); + m_partitions.emplace_back(std::make_unique(*this, codepage)); } -DiscMergerGCN::DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB) -: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) {} +DiscMergerGCN::DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB, Codepage_t codepage) +: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB, codepage)) {} EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) { if (!m_builder.getFileIO().beginWriteStream()) @@ -416,8 +416,8 @@ EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) { : EBuildResult::Failed; } -std::optional DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn) { - std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn); +std::optional DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn, Codepage_t codepage) { + std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn, codepage); if (!sz) return std::nullopt; *sz += 0x30000; diff --git a/lib/DiscWii.cpp b/lib/DiscWii.cpp index 9fff84d..f646256 100644 --- a/lib/DiscWii.cpp +++ b/lib/DiscWii.cpp @@ -241,8 +241,8 @@ class PartitionWii : public IPartition { uint8_t m_decKey[16]; public: - PartitionWii(const DiscWii& parent, PartitionKind kind, uint64_t offset, bool& err) - : IPartition(parent, kind, true, offset) { + PartitionWii(const DiscWii& parent, PartitionKind kind, uint64_t offset, bool& err, Codepage_t codepage) + : IPartition(parent, kind, true, offset, codepage) { std::unique_ptr s = parent.getDiscIO().beginReadStream(offset); if (!s) { err = true; @@ -447,7 +447,7 @@ public: SystemString ticketPath = basePathStr + _SYS_STR("/ticket.bin"); if (ctx.force || Stat(ticketPath.c_str(), &theStat)) { if (ctx.progressCB) - ctx.progressCB("ticket.bin", 0.f); + ctx.progressCB(_SYS_STR("ticket.bin"), 0.f); auto ws = NewFileIO(ticketPath)->beginWriteStream(); if (!ws) return false; @@ -458,7 +458,7 @@ public: SystemString tmdPath = basePathStr + _SYS_STR("/tmd.bin"); if (ctx.force || Stat(tmdPath.c_str(), &theStat)) { if (ctx.progressCB) - ctx.progressCB("tmd.bin", 0.f); + ctx.progressCB(_SYS_STR("tmd.bin"), 0.f); auto ws = NewFileIO(tmdPath)->beginWriteStream(); if (!ws) return false; @@ -469,7 +469,7 @@ public: SystemString certPath = basePathStr + _SYS_STR("/cert.bin"); if (ctx.force || Stat(certPath.c_str(), &theStat)) { if (ctx.progressCB) - ctx.progressCB("cert.bin", 0.f); + ctx.progressCB(_SYS_STR("cert.bin"), 0.f); auto ws = NewFileIO(certPath)->beginWriteStream(); if (!ws) return false; @@ -482,7 +482,7 @@ public: SystemString h3Path = basePathStr + _SYS_STR("/h3.bin"); if (ctx.force || Stat(h3Path.c_str(), &theStat)) { if (ctx.progressCB) - ctx.progressCB("h3.bin", 0.f); + ctx.progressCB(_SYS_STR("h3.bin"), 0.f); auto ws = NewFileIO(h3Path)->beginWriteStream(); if (!ws) return false; @@ -493,7 +493,7 @@ public: } }; -DiscWii::DiscWii(std::unique_ptr&& dio, bool& err) : DiscBase(std::move(dio), err) { +DiscWii::DiscWii(std::unique_ptr&& dio, bool& err, Codepage_t codepage) : DiscBase(std::move(dio), err) { if (err) return; @@ -543,14 +543,14 @@ DiscWii::DiscWii(std::unique_ptr&& dio, bool& err) : DiscBase(std::move err = true; return; } - m_partitions.emplace_back(std::make_unique(*this, kind, part.partDataOff << 2, err)); + m_partitions.emplace_back(std::make_unique(*this, kind, part.partDataOff << 2, err, codepage)); if (err) return; } } -DiscBuilderWii DiscWii::makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB) { - return DiscBuilderWii(outPath, dualLayer, progressCB); +DiscBuilderWii DiscWii::makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB, Codepage_t codepage) { + return DiscBuilderWii(outPath, dualLayer, progressCB, codepage); } bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const ExtractionContext& ctx) const { @@ -567,7 +567,7 @@ bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const Extraction 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); + ctx.progressCB(_SYS_STR("header.bin"), 0.f); std::unique_ptr rs = getDiscIO().beginReadStream(0x0); if (!rs) return false; @@ -583,7 +583,7 @@ bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const Extraction 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); + ctx.progressCB(_SYS_STR("header.bin"), 0.f); std::unique_ptr rs = getDiscIO().beginReadStream(0x4E000); if (!rs) return false; @@ -746,8 +746,8 @@ public: } }; - PartitionBuilderWii(DiscBuilderBase& parent, PartitionKind kind, uint64_t baseOffset) - : DiscBuilderBase::PartitionBuilderBase(parent, kind, true), m_baseOffset(baseOffset), m_aes(NewAES()) {} + PartitionBuilderWii(DiscBuilderBase& parent, PartitionKind kind, uint64_t baseOffset, Codepage_t codepage) + : DiscBuilderBase::PartitionBuilderBase(parent, kind, true, codepage), m_baseOffset(baseOffset), m_aes(NewAES()) {} uint64_t getCurUserEnd() const { return m_curUser; } @@ -1235,8 +1235,8 @@ EBuildResult DiscBuilderWii::buildFromDirectory(SystemStringView dirIn) { return EBuildResult::Success; } -std::optional DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer) { - std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true); +std::optional DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer, Codepage_t codepage) { + std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true, codepage); if (!sz) return sz; auto szDiv = nod::div(*sz, uint64_t(0x1F0000)); @@ -1252,13 +1252,13 @@ std::optional DiscBuilderWii::CalculateTotalSizeRequired(SystemStringV return sz; } -DiscBuilderWii::DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB) +DiscBuilderWii::DiscBuilderWii(SystemStringView outPath, bool dualLayer, FProgress progressCB, Codepage_t codepage) : DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB) { - m_partitions.emplace_back(std::make_unique(*this, PartitionKind::Data, 0x200000)); + m_partitions.emplace_back(std::make_unique(*this, PartitionKind::Data, 0x200000, codepage)); } -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, Codepage_t codepage) +: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, dualLayer, progressCB, codepage)) {} EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) { PartitionBuilderWii& pb = static_cast(*m_builder.m_partitions[0]); @@ -1342,8 +1342,8 @@ EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) { return EBuildResult::Success; } -std::optional DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer) { - std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn); +std::optional DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer, Codepage_t codepage) { + std::optional sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn, codepage); if (!sz) return std::nullopt; auto szDiv = nod::div(*sz, uint64_t(0x1F0000)); diff --git a/lib/nod.cpp b/lib/nod.cpp index 253c7c1..5562b1d 100644 --- a/lib/nod.cpp +++ b/lib/nod.cpp @@ -14,7 +14,7 @@ std::unique_ptr NewDiscIOISO(SystemStringView path); std::unique_ptr NewDiscIOWBFS(SystemStringView path); std::unique_ptr NewDiscIONFS(SystemStringView path); -std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii) { +std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii, Codepage_t codepage) { /* Temporary file handle to determine image type */ std::unique_ptr fio = NewFileIO(path); if (!fio->exists()) { @@ -67,13 +67,13 @@ std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii) bool err = false; std::unique_ptr ret; if (isWii) { - ret = std::make_unique(std::move(discIO), err); + ret = std::make_unique(std::move(discIO), err, codepage); if (err) return {}; return ret; } - ret = std::make_unique(std::move(discIO), err); + ret = std::make_unique(std::move(discIO), err, codepage); if (err) return {}; return ret; @@ -81,7 +81,7 @@ std::unique_ptr OpenDiscFromImage(SystemStringView path, bool& isWii) std::unique_ptr OpenDiscFromImage(SystemStringView path) { bool isWii; - return OpenDiscFromImage(path, isWii); + return OpenDiscFromImage(path, isWii, CP_US_ASCII); } } // namespace nod