The Encoding Update

While Nintendo's own documents claim GameCube and Wii disc file symbol tables only support 7-bit ASCII, this is far from the truth.  Indeed, even some first-party Nintendo games shipped with Shift-JIS encoded file symbol tables.  My guess?  The locale of whatever Windows machine mastered a GameCube or Wii disc influenced how wide character strings (UCS-2) were converted to narrow character strings.  To account for all possibilites, this update adds extensible multi-byte character set options to NOD-Tool.

A rundown of notable changes:
 - "-c XXXXX" option added to set the encoding of the GameCube / Wii ISO(s) being processed.
 - "SystemStringConv" renamed to "DiscLocToSystemConv"
 - "SystemUTF8Conv" renamed to "SystemToDiscLocConv"
 - Help message updated with new info.
 - Bugfix: AddBuildName had a logic error wherein the length of the SystemString was being used instead of length of the disc locale string.  This would corrupt the File Symbol Table if the disc locale string's length was greater than the SystemString's length.
 - Bugfix: recursiveMergeFST was not keeping track of parent indexes at all, meaning nested folders and their contents would be corrupted.  I simply copied the way recursiveBuildFST did things to fix this.
 - Bugfix (Windows): On Windows, for some reason, Sstat was a typedef for _stat (32-bit) instead of _stat64 (64-bit).  This is confounding, because untrimmed Wii ISOs will always be larger than the unsigned 32-bit integer limit (4,699,979,776 bytes vs 4,294,967,295 bytes), meaning the MergeWii errand has never worked for untrimmed ISOs on Windows.  Was this never tested??
 - Bugfix (Windows): Did you know Windows Command Prompt fully supports Unicode?  Stdio streams are now in _O_U16TEXT mode for Windows only.  Previously, attempting to print any character that could not be narrowed to your locale's encoding would either silently fail (std functions), or throw an exception (fmt functions).  As a minor drawback, narrow character print functions can no longer be used when stdio is in _O_U16TEXT mode, necessitating my PR for Logvisor here: (AxioDL/logvisor#7)
 - ExtractionContext::progressCB now uses SystemStringView because widechar printing works correctly on Windows now.
 - progFunc lambda no longer throws exceptions when printing unicode because widechar printing works correctly on Windows now.
 - Top-level constructors and functions with a Codepage_t parameter have also signatures that default to the US-ASCII codepage.
    - DiscGCN constructor
    - DiscBuilderGCN constructor
    - DiscBuilderGCN::CalculateTotalSizeRequired
    - DiscMergerGCN constructor
    - DiscMergerGCN::CalculateTotalSizeRequired
    - DiscWii constructor
    - DiscBuilderWii constructor
    - DiscBuilderWii::CalculateTotalSizeRequired
    - DiscMergerWii constructor
    - DiscMergerWii::CalculateTotalSizeRequired
    - OpenDiscFromImage
 - Conversion between system encoding and disc locale encoding has checks in place to warn the user if string conversion goes awry.
This commit is contained in:
Minty-Meeo 2021-06-27 03:26:20 -05:00
parent 3eccde013d
commit 3c25647b6e
10 changed files with 480 additions and 260 deletions

View File

@ -1,7 +1,12 @@
#include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#if _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include <logvisor/logvisor.hpp> #include <logvisor/logvisor.hpp>
@ -10,14 +15,30 @@
#include <nod/DiscWii.hpp> #include <nod/DiscWii.hpp>
#include <nod/nod.hpp> #include <nod/nod.hpp>
constexpr std::array<nod::Codepage_t, 3> codepages = {
CP_US_ASCII,
CP_UTF8,
CP_SHIFT_JIS,
};
static void printHelp() { static void printHelp() {
fmt::print(stderr, FMT_STRING( fmt::print(stderr, FMT_STRING(_SYS_STR(
"Usage:\n" "Usage:\n"
" nodtool extract [-f] <image-in> [<dir-out>]\n" " nodtool extract [options] <image-in> [<dir-out>]\n"
" nodtool makegcn <fsroot-in> [<image-out>]\n" " nodtool makegcn [options] <fsroot-in> [<image-out>]\n"
" nodtool makewii <fsroot-in> [<image-out>]\n" " nodtool makewii [options] <fsroot-in> [<image-out>]\n"
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n" " nodtool mergegcn [options] <fsroot-in> <image-in> [<image-out>]\n"
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n")); " nodtool mergewii [options] <fsroot-in> <image-in> [<image-out>]\n"
"Options:\n"
" -f Force (extract only)\n"
" -v Verbose details (extract only).\n"
" -c <val> 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 #if NOD_UCS2
@ -32,37 +53,57 @@ int wmain(int argc, wchar_t* argv[])
int main(int argc, char* argv[]) int main(int argc, char* argv[])
#endif #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 */ /* Enable logging to console */
logvisor::RegisterStandardExceptions(); logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger(); 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; 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) if (verbose)
fmt::print(stderr, FMT_STRING("Current node: {}, Extraction {:g}% Complete\n"), str, fmt::print(stderr, FMT_STRING(_SYS_STR("Current node: {}, Extraction {:g}% Complete\n")),
c * 100.f); str, c * 100.f);
}}; }};
const nod::SystemChar* inDir = nullptr; while (argidx < argc) {
const nod::SystemChar* outDir = _SYS_STR("."); if (!strcasecmp(argv[argidx], _SYS_STR("-f"))) {
for (int a = 2; a < argc; ++a) {
if (argv[a][0] == '-' && argv[a][1] == 'f')
ctx.force = true; ctx.force = true;
else if (argv[a][0] == '-' && argv[a][1] == 'v') ++argidx;
continue;
} else if (!strcasecmp(argv[argidx], _SYS_STR("-v"))) {
verbose = true; 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) if (errand.empty()) {
inDir = argv[a]; printHelp();
else return 1;
outDir = argv[a];
} }
auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) { auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) {
@ -74,89 +115,156 @@ int main(int argc, char* argv[])
fflush(stdout); 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; bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(inDir, isWii); std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale);
if (!disc) if (!disc)
return 1; return 1;
nod::Mkdir(outDir, 0755); nod::Mkdir(dirOut.c_str(), 0755);
nod::IPartition* dataPart = disc->getDataPartition(); nod::IPartition* dataPart = disc->getDataPartition();
if (!dataPart) if (!dataPart)
return 1; return 1;
if (!dataPart->extractToDirectory(outDir, ctx)) if (!dataPart->extractToDirectory(dirOut, ctx))
return 1; 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 */ /* Pre-validate path */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { 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")), argv[2]); nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn);
return 1; return 1;
} }
if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2])) if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(fsrootIn, discLocale))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 4) { nod::DiscBuilderGCN b(imageOut, progFunc, discLocale);
nod::SystemString outPath(argv[2]); ret = b.buildFromDirectory(fsrootIn);
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]);
}
fmt::print(FMT_STRING("\n")); fmt::print(FMT_STRING(_SYS_STR("\n")));
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; 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 */ /* Pre-validate path */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { 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")), argv[4]); nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn);
return 1; return 1;
} }
bool dual = false; bool dual = false;
if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual)) if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(fsrootIn, dual, discLocale))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 4) { nod::DiscBuilderWii b(imageOut, dual, progFunc, discLocale);
nod::SystemString outPath(argv[2]); ret = b.buildFromDirectory(fsrootIn);
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]);
}
fmt::print(FMT_STRING("\n")); fmt::print(FMT_STRING(_SYS_STR("\n")));
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; 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 */ /* Pre-validate paths */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { 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")), argv[2]); nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn);
return 1; return 1;
} }
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) { 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")), argv[3]); nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), imageIn);
return 1; return 1;
} }
bool isWii; bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii); std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale);
if (!disc) { 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; return 1;
} }
if (isWii) { if (isWii) {
@ -164,38 +272,55 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), argv[2])) if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), fsrootIn, discLocale))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 5) { nod::DiscMergerGCN b(imageOut, static_cast<nod::DiscGCN&>(*disc), progFunc, discLocale);
nod::SystemString outPath(argv[2]); ret = b.mergeFromDirectory(fsrootIn);
outPath.append(_SYS_STR(".iso"));
nod::DiscMergerGCN b(outPath.c_str(), static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(argv[2]);
} else {
nod::DiscMergerGCN b(argv[4], static_cast<nod::DiscGCN&>(*disc), progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
fmt::print(FMT_STRING("\n")); fmt::print(FMT_STRING(_SYS_STR("\n")));
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; 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 */ /* Pre-validate paths */
nod::Sstat theStat; nod::Sstat theStat;
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) { 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")), argv[2]); nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), fsrootIn);
return 1; return 1;
} }
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) { 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")), argv[3]); nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), imageIn);
return 1; return 1;
} }
bool isWii; bool isWii;
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii); std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale);
if (!disc) { 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 {}")), argv[3]);
return 1; return 1;
@ -206,29 +331,22 @@ int main(int argc, char* argv[])
} }
bool dual = false; bool dual = false;
if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), argv[2], dual)) if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), fsrootIn, dual, discLocale))
return 1; return 1;
nod::EBuildResult ret; nod::EBuildResult ret;
if (argc < 5) { nod::DiscMergerWii b(imageOut, static_cast<nod::DiscWii&>(*disc), dual, progFunc, discLocale);
nod::SystemString outPath(argv[2]); ret = b.mergeFromDirectory(fsrootIn);
outPath.append(_SYS_STR(".iso"));
nod::DiscMergerWii b(outPath.c_str(), static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(argv[2]);
} else {
nod::DiscMergerWii b(argv[4], static_cast<nod::DiscWii&>(*disc), dual, progFunc);
ret = b.mergeFromDirectory(argv[2]);
}
fmt::print(FMT_STRING("\n")); fmt::print(FMT_STRING(_SYS_STR("\n")));
if (ret != nod::EBuildResult::Success) if (ret != nod::EBuildResult::Success)
return 1; return 1;
} else { } else {
printHelp(); printHelp();
return 1; return 1;
} }
nod::LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Success!"))); nod::LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Success!")));
return 0; return 0;
} }

View File

@ -236,7 +236,7 @@ public:
return end(); return end();
} }
bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const; bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx, Codepage_t codepage) const;
}; };
class IPartition { class IPartition {
@ -275,6 +275,7 @@ protected:
PartitionKind m_kind; PartitionKind m_kind;
uint64_t m_offset; uint64_t m_offset;
bool m_isWii; bool m_isWii;
Codepage_t m_codepage;
public: public:
mutable size_t m_curNodeIdx = 0; mutable size_t m_curNodeIdx = 0;
@ -289,8 +290,8 @@ public:
return m_curNodeIdx / float(getNodeCount()); return m_curNodeIdx / float(getNodeCount());
} }
IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset) 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_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii), m_codepage(codepage) {}
virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; } virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; }
PartitionKind getKind() const { return m_kind; } PartitionKind getKind() const { return m_kind; }
bool isWii() const { return m_isWii; } bool isWii() const { return m_isWii; }
@ -405,14 +406,14 @@ public:
bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, SystemStringView dirIn, bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, SystemStringView dirIn,
SystemStringView keyPath); SystemStringView keyPath);
bool recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, std::function<void(void)> incParents, bool recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, std::function<void(void)> 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) { void addBuildName(SystemStringView str) {
SystemUTF8Conv utf8View(str); SystemToDiscLocConv nameView(str, m_codepage);
m_buildNames.emplace_back(utf8View.utf8_str()); m_buildNames.emplace_back(nameView.disc_str());
m_buildNameOff += str.size() + 1; m_buildNameOff += nameView.disc_str().size() + 1;
} }
DiscBuilderBase& m_parent; DiscBuilderBase& m_parent;
@ -420,15 +421,16 @@ public:
uint64_t m_dolOffset = 0; uint64_t m_dolOffset = 0;
uint64_t m_dolSize = 0; uint64_t m_dolSize = 0;
bool m_isWii; bool m_isWii;
Codepage_t m_codepage;
public: public:
PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii) PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii, Codepage_t codepage)
: m_parent(parent), m_kind(kind), m_isWii(isWii) {} : m_parent(parent), m_kind(kind), m_isWii(isWii), m_codepage(codepage) {}
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset) = 0; virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset) = 0;
bool buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn); bool buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn);
static std::optional<uint64_t> CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, bool isWii); static std::optional<uint64_t> CalculateTotalSizeBuild(SystemStringView dirIn, PartitionKind kind, bool isWii, Codepage_t codepage);
bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, SystemStringView dirIn); bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, SystemStringView dirIn);
static std::optional<uint64_t> CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn); static std::optional<uint64_t> CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn, Codepage_t codepage);
}; };
protected: protected:

View File

@ -7,10 +7,10 @@ class DiscBuilderGCN;
class DiscGCN : public DiscBase { class DiscGCN : public DiscBase {
friend class DiscMergerGCN; friend class DiscMergerGCN;
DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB); DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB, Codepage_t codepage);
public: public:
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err); DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err, Codepage_t codepage = CP_US_ASCII);
bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override; bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override;
}; };
@ -18,9 +18,9 @@ class DiscBuilderGCN : public DiscBuilderBase {
friend class DiscMergerGCN; friend class DiscMergerGCN;
public: public:
DiscBuilderGCN(SystemStringView outPath, FProgress progressCB); DiscBuilderGCN(SystemStringView outPath, FProgress progressCB, Codepage_t codepage = CP_US_ASCII);
EBuildResult buildFromDirectory(SystemStringView dirIn); EBuildResult buildFromDirectory(SystemStringView dirIn);
static std::optional<uint64_t> CalculateTotalSizeRequired(SystemStringView dirIn); static std::optional<uint64_t> CalculateTotalSizeRequired(SystemStringView dirIn, Codepage_t codepage = CP_US_ASCII);
}; };
class DiscMergerGCN { class DiscMergerGCN {
@ -28,9 +28,9 @@ class DiscMergerGCN {
DiscBuilderGCN m_builder; DiscBuilderGCN m_builder;
public: 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); EBuildResult mergeFromDirectory(SystemStringView dirIn);
static std::optional<uint64_t> CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn); static std::optional<uint64_t> CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn, Codepage_t codepage = CP_US_ASCII);
}; };
} // namespace nod } // namespace nod

View File

@ -7,16 +7,16 @@ class DiscBuilderWii;
class DiscWii : public DiscBase { class DiscWii : public DiscBase {
public: public:
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err); DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err, Codepage_t codepage = CP_US_ASCII);
DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB); DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB, Codepage_t codepage);
bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override; bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override;
}; };
class DiscBuilderWii : public DiscBuilderBase { class DiscBuilderWii : public DiscBuilderBase {
public: 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); EBuildResult buildFromDirectory(SystemStringView dirIn);
static std::optional<uint64_t> CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer); static std::optional<uint64_t> CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer, Codepage_t codepage = CP_US_ASCII);
}; };
class DiscMergerWii { class DiscMergerWii {
@ -24,9 +24,9 @@ class DiscMergerWii {
DiscBuilderWii m_builder; DiscBuilderWii m_builder;
public: 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); EBuildResult mergeFromDirectory(SystemStringView dirIn);
static std::optional<uint64_t> CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer); static std::optional<uint64_t> CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer, Codepage_t codepage = CP_US_ASCII);
}; };
} // namespace nod } // namespace nod

View File

@ -18,6 +18,7 @@
#else #else
#include <cctype> #include <cctype>
#include <cerrno> #include <cerrno>
#include <iconv.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
@ -75,9 +76,9 @@ extern logvisor::Module LogModule;
/* filesystem char type */ /* filesystem char type */
#if _WIN32 && UNICODE #if _WIN32 && UNICODE
#define NOD_UCS2 1 #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 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 #else
typedef struct stat Sstat; typedef struct stat Sstat;
static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode); } 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 */ /* String-converting views */
#if NOD_UCS2 #if NOD_UCS2
typedef wchar_t SystemChar; #define CP_US_ASCII 20127
typedef std::wstring SystemString; #define CP_SHIFT_JIS 932
typedef std::wstring_view SystemStringView; #define CP_GB_18030 54936
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(); }
};
#ifndef _SYS_STR #ifndef _SYS_STR
#define _SYS_STR(val) L##val #define _SYS_STR(val) L##val
#endif #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 #else
typedef char SystemChar; #define CP_US_ASCII "US-ASCII"
typedef std::string SystemString; #define CP_UTF8 "UTF-8"
typedef std::string_view SystemStringView; #define CP_SHIFT_JIS "SHIFT-JIS"
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(); }
};
#ifndef _SYS_STR #ifndef _SYS_STR
#define _SYS_STR(val) val #define _SYS_STR(val) val
#endif #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<const char**>(&srcBuffer), &srcBytes, &dstBuffer, &dstBytes);
#else
iconv(convDesc, const_cast<char**>(reinterpret_cast<const char**>(&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 #endif
static inline void Unlink(const SystemChar* file) { static inline void Unlink(const SystemChar* file) {

View File

@ -12,10 +12,10 @@ class DiscBase;
struct ExtractionContext final { struct ExtractionContext final {
bool force : 1; bool force : 1;
std::function<void(std::string_view, float)> progressCB; std::function<void(nod::SystemStringView, float)> progressCB;
}; };
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path); std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path);
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii); std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii, Codepage_t codepage = CP_US_ASCII);
} // namespace nod } // namespace nod

View File

@ -126,25 +126,25 @@ std::unique_ptr<uint8_t[]> Node::getBuf() const {
return buf; return buf;
} }
bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx) const { bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx, Codepage_t codepage) const {
SystemStringConv nameView(getName()); DiscLocToSystemConv nameView(getName(), codepage);
SystemString path = SystemString(basePath) + _SYS_STR('/') + nameView.sys_str().data(); SystemString path = SystemString(basePath) + _SYS_STR('/') + nameView.sys_str().data();
if (m_kind == Kind::Directory) { if (m_kind == Kind::Directory) {
++m_parent.m_curNodeIdx; ++m_parent.m_curNodeIdx;
if (ctx.progressCB && !getName().empty()) 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) { if (Mkdir(path.c_str(), 0755) && errno != EEXIST) {
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), path); LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), path);
return false; return false;
} }
for (Node& subnode : *this) for (Node& subnode : *this)
if (!subnode.extractToDirectory(path, ctx)) if (!subnode.extractToDirectory(path, ctx, codepage))
return false; return false;
} else if (m_kind == Kind::File) { } else if (m_kind == Kind::File) {
Sstat theStat; Sstat theStat;
if (ctx.progressCB) 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)) { if (ctx.force || Stat(path.c_str(), &theStat)) {
std::unique_ptr<IPartReadStream> rs = beginReadStream(); std::unique_ptr<IPartReadStream> rs = beginReadStream();
@ -153,7 +153,7 @@ bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext
return false; return false;
ws->copyFromDisc(*rs, m_discLength, [&](float prog) { ws->copyFromDisc(*rs, m_discLength, [&](float prog) {
if (ctx.progressCB) 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; ++m_parent.m_curNodeIdx;
@ -194,7 +194,7 @@ bool IPartition::extractToDirectory(SystemStringView path, const ExtractionConte
return false; 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 { 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"); SystemString apploaderPath = basePathStr + _SYS_STR("/sys/apploader.img");
if (ctx.force || Stat(apploaderPath.c_str(), &theStat)) { if (ctx.force || Stat(apploaderPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("apploader.bin", 0.f); ctx.progressCB(_SYS_STR("apploader.bin"), 0.f);
std::unique_ptr<uint8_t[]> buf = getApploaderBuf(); std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
auto ws = NewFileIO(apploaderPath)->beginWriteStream(); auto ws = NewFileIO(apploaderPath)->beginWriteStream();
if (!ws) if (!ws)
@ -221,7 +221,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont
SystemString dolPath = basePathStr + _SYS_STR("/sys/main.dol"); SystemString dolPath = basePathStr + _SYS_STR("/sys/main.dol");
if (ctx.force || Stat(dolPath.c_str(), &theStat)) { if (ctx.force || Stat(dolPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("main.dol", 0.f); ctx.progressCB(_SYS_STR("main.dol"), 0.f);
std::unique_ptr<uint8_t[]> buf = getDOLBuf(); std::unique_ptr<uint8_t[]> buf = getDOLBuf();
auto ws = NewFileIO(dolPath)->beginWriteStream(); auto ws = NewFileIO(dolPath)->beginWriteStream();
if (!ws) if (!ws)
@ -233,7 +233,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont
SystemString bootPath = basePathStr + _SYS_STR("/sys/boot.bin"); SystemString bootPath = basePathStr + _SYS_STR("/sys/boot.bin");
if (ctx.force || Stat(bootPath.c_str(), &theStat)) { if (ctx.force || Stat(bootPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("boot.bin", 0.f); ctx.progressCB(_SYS_STR("boot.bin"), 0.f);
auto ws = NewFileIO(bootPath)->beginWriteStream(); auto ws = NewFileIO(bootPath)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
@ -244,7 +244,7 @@ bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionCont
SystemString bi2Path = basePathStr + _SYS_STR("/sys/bi2.bin"); SystemString bi2Path = basePathStr + _SYS_STR("/sys/bi2.bin");
if (ctx.force || Stat(bi2Path.c_str(), &theStat)) { if (ctx.force || Stat(bi2Path.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("bi2.bin", 0.f); ctx.progressCB(_SYS_STR("bi2.bin"), 0.f);
auto ws = NewFileIO(bi2Path)->beginWriteStream(); auto ws = NewFileIO(bi2Path)->beginWriteStream();
if (!ws) if (!ws)
@ -406,10 +406,10 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodesPre(const Node* n
if (!dirIn.empty()) { if (!dirIn.empty()) {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) { for (const DirectoryEnumerator::Entry& e : dEnum) {
SystemUTF8Conv nameView(e.m_name); SystemToDiscLocConv nameView(e.m_name, m_codepage);
if (e.m_isDir) { 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 (search != dirNodes.cend()) {
recursiveMergeNodesPre(search->second, e.m_path.c_str()); recursiveMergeNodesPre(search->second, e.m_path.c_str());
dirNodes.erase(search); dirNodes.erase(search);
@ -417,7 +417,7 @@ void DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodesPre(const Node* n
recursiveMergeNodesPre(nullptr, e.m_path.c_str()); recursiveMergeNodesPre(nullptr, e.m_path.c_str());
} }
} else { } else {
fileNodes.erase(nameView.utf8_str().data()); fileNodes.erase(nameView.disc_str().data());
++m_parent.m_progressTotal; ++m_parent.m_progressTotal;
} }
} }
@ -452,11 +452,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream
if (!dirIn.empty()) { if (!dirIn.empty()) {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) { 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; SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name;
if (e.m_isDir) { 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 (search != dirNodes.cend()) {
if (!recursiveMergeNodes(ws, system, search->second, e.m_path.c_str(), chKeyPath)) if (!recursiveMergeNodes(ws, system, search->second, e.m_path.c_str(), chKeyPath))
return false; return false;
@ -471,7 +471,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream
if (system ^ isSys) if (system ^ isSys)
continue; continue;
fileNodes.erase(nameView.utf8_str().data()); fileNodes.erase(nameView.disc_str().data());
size_t fileSz = ROUND_UP_32(e.m_fileSz); size_t fileSz = ROUND_UP_32(e.m_fileSz);
uint64_t fileOff = userAllocate(fileSz, ws); uint64_t fileOff = userAllocate(fileSz, ws);
@ -508,7 +508,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream
/* Write-through remaining dir nodes */ /* Write-through remaining dir nodes */
for (const auto& p : dirNodes) { 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(); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str();
if (!recursiveMergeNodes(ws, system, p.second, {}, chKeyPath)) if (!recursiveMergeNodes(ws, system, p.second, {}, chKeyPath))
return false; return false;
@ -517,7 +517,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream
/* Write-through remaining file nodes */ /* Write-through remaining file nodes */
for (const auto& p : fileNodes) { for (const auto& p : fileNodes) {
const Node& ch = *p.second; const Node& ch = *p.second;
SystemStringConv sysName(ch.getName()); DiscLocToSystemConv sysName(ch.getName(), m_codepage);
SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str(); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.c_str();
bool isDol; bool isDol;
@ -565,7 +565,7 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeNodes(IPartWriteStream
bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn,
std::function<void(void)> incParents, std::function<void(void)> incParents,
SystemStringView keyPath) { size_t parentDirIdx, SystemStringView keyPath) {
/* Build map of existing nodes to write-through later */ /* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Node*> fileNodes; std::unordered_map<std::string, const Node*> fileNodes;
std::unordered_map<std::string, const Node*> dirNodes; std::unordered_map<std::string, const Node*> dirNodes;
@ -584,23 +584,23 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn
if (!dirIn.empty()) { if (!dirIn.empty()) {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) { 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; SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + e.m_name;
if (e.m_isDir) { if (e.m_isDir) {
size_t dirNodeIdx = m_buildNodes.size(); 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); addBuildName(e.m_name);
incParents(); incParents();
auto search = dirNodes.find(nameView.utf8_str().data()); auto search = dirNodes.find(nameView.disc_str().data());
if (search != dirNodes.cend()) { if (search != dirNodes.cend()) {
if (!recursiveMergeFST(search->second, e.m_path.c_str(), if (!recursiveMergeFST(search->second, e.m_path.c_str(),
[&]() { [&]() {
m_buildNodes[dirNodeIdx].incrementLength(); m_buildNodes[dirNodeIdx].incrementLength();
incParents(); incParents();
}, },
chKeyPath)) dirNodeIdx, chKeyPath))
return false; return false;
dirNodes.erase(search); dirNodes.erase(search);
} else { } else {
@ -609,11 +609,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn
m_buildNodes[dirNodeIdx].incrementLength(); m_buildNodes[dirNodeIdx].incrementLength();
incParents(); incParents();
}, },
chKeyPath)) dirNodeIdx, chKeyPath))
return false; return false;
} }
} else { } else {
fileNodes.erase(nameView.utf8_str().data()); fileNodes.erase(nameView.disc_str().data());
std::pair<uint64_t, uint64_t> fileOffSz = m_fileOffsetsSizes.at(chKeyPath); std::pair<uint64_t, uint64_t> fileOffSz = m_fileOffsetsSizes.at(chKeyPath);
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second); m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
addBuildName(e.m_name); addBuildName(e.m_name);
@ -624,11 +624,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn
/* Write-through remaining dir nodes */ /* Write-through remaining dir nodes */
for (const auto& p : dirNodes) { 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(); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data();
size_t dirNodeIdx = m_buildNodes.size(); 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()); addBuildName(sysName.sys_str());
incParents(); incParents();
@ -637,14 +637,14 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveMergeFST(const Node* nodeIn
m_buildNodes[dirNodeIdx].incrementLength(); m_buildNodes[dirNodeIdx].incrementLength();
incParents(); incParents();
}, },
chKeyPath)) dirNodeIdx, chKeyPath))
return false; return false;
} }
/* Write-through remaining file nodes */ /* Write-through remaining file nodes */
for (const auto& p : fileNodes) { for (const auto& p : fileNodes) {
const Node& ch = *p.second; 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(); SystemString chKeyPath = SystemString(keyPath) + _SYS_STR('/') + sysName.sys_str().data();
std::pair<uint64_t, uint64_t> fileOffSz = m_fileOffsetsSizes.at(chKeyPath); std::pair<uint64_t, uint64_t> 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, 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 */ /* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Node*> fileNodes; std::unordered_map<std::string, const Node*> fileNodes;
std::unordered_map<std::string, const Node*> dirNodes; std::unordered_map<std::string, const Node*> dirNodes;
@ -676,20 +676,20 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t
if (!dirIn.empty()) { if (!dirIn.empty()) {
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true); DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const DirectoryEnumerator::Entry& e : dEnum) { for (const DirectoryEnumerator::Entry& e : dEnum) {
SystemUTF8Conv nameView(e.m_name); SystemToDiscLocConv nameView(e.m_name, codepage);
if (e.m_isDir) { 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 (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; return false;
dirNodes.erase(search); dirNodes.erase(search);
} else { } else {
if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str())) if (!RecursiveCalculateTotalSize(totalSz, nullptr, e.m_path.c_str(), codepage))
return false; return false;
} }
} else { } else {
fileNodes.erase(nameView.utf8_str().data()); fileNodes.erase(nameView.disc_str().data());
totalSz += ROUND_UP_32(e.m_fileSz); totalSz += ROUND_UP_32(e.m_fileSz);
} }
} }
@ -697,7 +697,7 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t
/* Write-through remaining dir nodes */ /* Write-through remaining dir nodes */
for (const auto& p : dirNodes) { for (const auto& p : dirNodes) {
if (!RecursiveCalculateTotalSize(totalSz, p.second, {})) if (!RecursiveCalculateTotalSize(totalSz, p.second, {}, codepage))
return false; return false;
} }
@ -770,7 +770,7 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
} }
std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(SystemStringView dirIn, std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(SystemStringView dirIn,
PartitionKind kind, bool isWii) { PartitionKind kind, bool isWii, Codepage_t codepage) {
SystemString dirStr(dirIn); SystemString dirStr(dirIn);
SystemString basePath = isWii ? dirStr + _SYS_STR("/") + getKindString(kind) : dirStr; SystemString basePath = isWii ? dirStr + _SYS_STR("/") + getKindString(kind) : dirStr;
SystemString dolIn = basePath + _SYS_STR("/sys/main.dol"); SystemString dolIn = basePath + _SYS_STR("/sys/main.dol");
@ -782,7 +782,7 @@ std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSiz
return std::nullopt; return std::nullopt;
} }
uint64_t totalSz = ROUND_UP_32(dolStat.st_size); 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 std::nullopt;
return totalSz; return totalSz;
} }
@ -837,20 +837,20 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
return false; return false;
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath)) if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
return false; 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 false;
return true; return true;
} }
std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const IPartition* partIn, std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const IPartition* partIn,
SystemStringView dirIn) { SystemStringView dirIn, Codepage_t codepage) {
SystemString dirStr(dirIn); SystemString dirStr(dirIn);
SystemString basePath = partIn->isWii() ? dirStr + _SYS_STR("/") + getKindString(partIn->getKind()) : dirStr; SystemString basePath = partIn->isWii() ? dirStr + _SYS_STR("/") + getKindString(partIn->getKind()) : dirStr;
SystemString filesIn = basePath + _SYS_STR("/files"); SystemString filesIn = basePath + _SYS_STR("/files");
uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize()); 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 std::nullopt;
return totalSz; return totalSz;
} }

View File

@ -17,8 +17,8 @@ namespace nod {
class PartitionGCN : public IPartition { class PartitionGCN : public IPartition {
public: public:
PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err) PartitionGCN(const DiscGCN& parent, uint64_t offset, bool& err, Codepage_t codepage)
: IPartition(parent, PartitionKind::Data, false, offset) { : IPartition(parent, PartitionKind::Data, false, offset, codepage) {
/* GCN-specific header reads */ /* GCN-specific header reads */
std::unique_ptr<IPartReadStream> s = beginReadStream(0x0); std::unique_ptr<IPartReadStream> s = beginReadStream(0x0);
if (!s) { if (!s) {
@ -118,16 +118,16 @@ public:
} }
}; };
DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move(dio), err) { DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err, Codepage_t codepage) : DiscBase(std::move(dio), err) {
if (err) if (err)
return; return;
/* One lone partition for GCN */ /* One lone partition for GCN */
m_partitions.emplace_back(std::make_unique<PartitionGCN>(*this, 0, err)); m_partitions.emplace_back(std::make_unique<PartitionGCN>(*this, 0, err, codepage));
} }
DiscBuilderGCN DiscGCN::makeMergeBuilder(SystemStringView outPath, FProgress progressCB) { DiscBuilderGCN DiscGCN::makeMergeBuilder(SystemStringView outPath, FProgress progressCB, Codepage_t codepage) {
return DiscBuilderGCN(outPath, progressCB); return DiscBuilderGCN(outPath, progressCB, codepage);
} }
bool DiscGCN::extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; } bool DiscGCN::extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; }
@ -161,8 +161,8 @@ public:
} }
}; };
PartitionBuilderGCN(DiscBuilderBase& parent) PartitionBuilderGCN(DiscBuilderBase& parent, Codepage_t codepage)
: DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false) {} : DiscBuilderBase::PartitionBuilderBase(parent, PartitionKind::Data, false, codepage) {}
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override { uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override {
m_curUser -= reqSz; m_curUser -= reqSz;
@ -372,8 +372,8 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(SystemStringView dirIn) {
return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed; return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed;
} }
std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn) { std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn, Codepage_t codepage) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false); std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false, codepage);
if (!sz) if (!sz)
return sz; return sz;
*sz += 0x30000; *sz += 0x30000;
@ -384,13 +384,13 @@ std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringV
return sz; return sz;
} }
DiscBuilderGCN::DiscBuilderGCN(SystemStringView outPath, FProgress progressCB) DiscBuilderGCN::DiscBuilderGCN(SystemStringView outPath, FProgress progressCB, Codepage_t codepage)
: DiscBuilderBase(outPath, 0x57058000, progressCB) { : DiscBuilderBase(outPath, 0x57058000, progressCB) {
m_partitions.emplace_back(std::make_unique<PartitionBuilderGCN>(*this)); m_partitions.emplace_back(std::make_unique<PartitionBuilderGCN>(*this, codepage));
} }
DiscMergerGCN::DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB) DiscMergerGCN::DiscMergerGCN(SystemStringView outPath, DiscGCN& sourceDisc, FProgress progressCB, Codepage_t codepage)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB)) {} : m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, progressCB, codepage)) {}
EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) { EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) {
if (!m_builder.getFileIO().beginWriteStream()) if (!m_builder.getFileIO().beginWriteStream())
@ -416,8 +416,8 @@ EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) {
: EBuildResult::Failed; : EBuildResult::Failed;
} }
std::optional<uint64_t> DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn) { std::optional<uint64_t> DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn, Codepage_t codepage) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn); std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn, codepage);
if (!sz) if (!sz)
return std::nullopt; return std::nullopt;
*sz += 0x30000; *sz += 0x30000;

View File

@ -241,8 +241,8 @@ class PartitionWii : public IPartition {
uint8_t m_decKey[16]; uint8_t m_decKey[16];
public: public:
PartitionWii(const DiscWii& parent, PartitionKind kind, uint64_t offset, bool& err) PartitionWii(const DiscWii& parent, PartitionKind kind, uint64_t offset, bool& err, Codepage_t codepage)
: IPartition(parent, kind, true, offset) { : IPartition(parent, kind, true, offset, codepage) {
std::unique_ptr<IReadStream> s = parent.getDiscIO().beginReadStream(offset); std::unique_ptr<IReadStream> s = parent.getDiscIO().beginReadStream(offset);
if (!s) { if (!s) {
err = true; err = true;
@ -447,7 +447,7 @@ public:
SystemString ticketPath = basePathStr + _SYS_STR("/ticket.bin"); SystemString ticketPath = basePathStr + _SYS_STR("/ticket.bin");
if (ctx.force || Stat(ticketPath.c_str(), &theStat)) { if (ctx.force || Stat(ticketPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("ticket.bin", 0.f); ctx.progressCB(_SYS_STR("ticket.bin"), 0.f);
auto ws = NewFileIO(ticketPath)->beginWriteStream(); auto ws = NewFileIO(ticketPath)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
@ -458,7 +458,7 @@ public:
SystemString tmdPath = basePathStr + _SYS_STR("/tmd.bin"); SystemString tmdPath = basePathStr + _SYS_STR("/tmd.bin");
if (ctx.force || Stat(tmdPath.c_str(), &theStat)) { if (ctx.force || Stat(tmdPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("tmd.bin", 0.f); ctx.progressCB(_SYS_STR("tmd.bin"), 0.f);
auto ws = NewFileIO(tmdPath)->beginWriteStream(); auto ws = NewFileIO(tmdPath)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
@ -469,7 +469,7 @@ public:
SystemString certPath = basePathStr + _SYS_STR("/cert.bin"); SystemString certPath = basePathStr + _SYS_STR("/cert.bin");
if (ctx.force || Stat(certPath.c_str(), &theStat)) { if (ctx.force || Stat(certPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("cert.bin", 0.f); ctx.progressCB(_SYS_STR("cert.bin"), 0.f);
auto ws = NewFileIO(certPath)->beginWriteStream(); auto ws = NewFileIO(certPath)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
@ -482,7 +482,7 @@ public:
SystemString h3Path = basePathStr + _SYS_STR("/h3.bin"); SystemString h3Path = basePathStr + _SYS_STR("/h3.bin");
if (ctx.force || Stat(h3Path.c_str(), &theStat)) { if (ctx.force || Stat(h3Path.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("h3.bin", 0.f); ctx.progressCB(_SYS_STR("h3.bin"), 0.f);
auto ws = NewFileIO(h3Path)->beginWriteStream(); auto ws = NewFileIO(h3Path)->beginWriteStream();
if (!ws) if (!ws)
return false; return false;
@ -493,7 +493,7 @@ public:
} }
}; };
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move(dio), err) { DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err, Codepage_t codepage) : DiscBase(std::move(dio), err) {
if (err) if (err)
return; return;
@ -543,14 +543,14 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move
err = true; err = true;
return; return;
} }
m_partitions.emplace_back(std::make_unique<PartitionWii>(*this, kind, part.partDataOff << 2, err)); m_partitions.emplace_back(std::make_unique<PartitionWii>(*this, kind, part.partDataOff << 2, err, codepage));
if (err) if (err)
return; return;
} }
} }
DiscBuilderWii DiscWii::makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB) { DiscBuilderWii DiscWii::makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB, Codepage_t codepage) {
return DiscBuilderWii(outPath, dualLayer, progressCB); return DiscBuilderWii(outPath, dualLayer, progressCB, codepage);
} }
bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const ExtractionContext& ctx) const { 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"); SystemString headerPath = basePathStr + _SYS_STR("/disc/header.bin");
if (ctx.force || Stat(headerPath.c_str(), &theStat)) { if (ctx.force || Stat(headerPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("header.bin", 0.f); ctx.progressCB(_SYS_STR("header.bin"), 0.f);
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x0); std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x0);
if (!rs) if (!rs)
return false; return false;
@ -583,7 +583,7 @@ bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const Extraction
SystemString regionPath = basePathStr + _SYS_STR("/disc/region.bin"); SystemString regionPath = basePathStr + _SYS_STR("/disc/region.bin");
if (ctx.force || Stat(regionPath.c_str(), &theStat)) { if (ctx.force || Stat(regionPath.c_str(), &theStat)) {
if (ctx.progressCB) if (ctx.progressCB)
ctx.progressCB("header.bin", 0.f); ctx.progressCB(_SYS_STR("header.bin"), 0.f);
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x4E000); std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x4E000);
if (!rs) if (!rs)
return false; return false;
@ -746,8 +746,8 @@ public:
} }
}; };
PartitionBuilderWii(DiscBuilderBase& parent, PartitionKind kind, uint64_t baseOffset) PartitionBuilderWii(DiscBuilderBase& parent, PartitionKind kind, uint64_t baseOffset, Codepage_t codepage)
: DiscBuilderBase::PartitionBuilderBase(parent, kind, true), m_baseOffset(baseOffset), m_aes(NewAES()) {} : DiscBuilderBase::PartitionBuilderBase(parent, kind, true, codepage), m_baseOffset(baseOffset), m_aes(NewAES()) {}
uint64_t getCurUserEnd() const { return m_curUser; } uint64_t getCurUserEnd() const { return m_curUser; }
@ -1235,8 +1235,8 @@ EBuildResult DiscBuilderWii::buildFromDirectory(SystemStringView dirIn) {
return EBuildResult::Success; return EBuildResult::Success;
} }
std::optional<uint64_t> DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer) { std::optional<uint64_t> DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer, Codepage_t codepage) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true); std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true, codepage);
if (!sz) if (!sz)
return sz; return sz;
auto szDiv = nod::div(*sz, uint64_t(0x1F0000)); auto szDiv = nod::div(*sz, uint64_t(0x1F0000));
@ -1252,13 +1252,13 @@ std::optional<uint64_t> DiscBuilderWii::CalculateTotalSizeRequired(SystemStringV
return sz; 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) { : DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB) {
m_partitions.emplace_back(std::make_unique<PartitionBuilderWii>(*this, PartitionKind::Data, 0x200000)); m_partitions.emplace_back(std::make_unique<PartitionBuilderWii>(*this, PartitionKind::Data, 0x200000, codepage));
} }
DiscMergerWii::DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB) DiscMergerWii::DiscMergerWii(SystemStringView outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB, Codepage_t codepage)
: m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, dualLayer, progressCB)) {} : m_sourceDisc(sourceDisc), m_builder(sourceDisc.makeMergeBuilder(outPath, dualLayer, progressCB, codepage)) {}
EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) { EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) {
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_builder.m_partitions[0]); PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_builder.m_partitions[0]);
@ -1342,8 +1342,8 @@ EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) {
return EBuildResult::Success; return EBuildResult::Success;
} }
std::optional<uint64_t> DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer) { std::optional<uint64_t> DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer, Codepage_t codepage) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn); std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn, codepage);
if (!sz) if (!sz)
return std::nullopt; return std::nullopt;
auto szDiv = nod::div(*sz, uint64_t(0x1F0000)); auto szDiv = nod::div(*sz, uint64_t(0x1F0000));

View File

@ -14,7 +14,7 @@ std::unique_ptr<IDiscIO> NewDiscIOISO(SystemStringView path);
std::unique_ptr<IDiscIO> NewDiscIOWBFS(SystemStringView path); std::unique_ptr<IDiscIO> NewDiscIOWBFS(SystemStringView path);
std::unique_ptr<IDiscIO> NewDiscIONFS(SystemStringView path); std::unique_ptr<IDiscIO> NewDiscIONFS(SystemStringView path);
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii) { std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii, Codepage_t codepage) {
/* Temporary file handle to determine image type */ /* Temporary file handle to determine image type */
std::unique_ptr<IFileIO> fio = NewFileIO(path); std::unique_ptr<IFileIO> fio = NewFileIO(path);
if (!fio->exists()) { if (!fio->exists()) {
@ -67,13 +67,13 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
bool err = false; bool err = false;
std::unique_ptr<DiscBase> ret; std::unique_ptr<DiscBase> ret;
if (isWii) { if (isWii) {
ret = std::make_unique<DiscWii>(std::move(discIO), err); ret = std::make_unique<DiscWii>(std::move(discIO), err, codepage);
if (err) if (err)
return {}; return {};
return ret; return ret;
} }
ret = std::make_unique<DiscGCN>(std::move(discIO), err); ret = std::make_unique<DiscGCN>(std::move(discIO), err, codepage);
if (err) if (err)
return {}; return {};
return ret; return ret;
@ -81,7 +81,7 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path) { std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path) {
bool isWii; bool isWii;
return OpenDiscFromImage(path, isWii); return OpenDiscFromImage(path, isWii, CP_US_ASCII);
} }
} // namespace nod } // namespace nod