Merge branch 'Minty-Meeo-Shift-JIS-fixes'

This commit is contained in:
Phillip Stephens 2021-06-27 10:46:57 -07:00
commit 8127bddb97
Signed by: Antidote
GPG Key ID: F8BEE4C83DACA60D
10 changed files with 480 additions and 260 deletions

View File

@ -1,7 +1,12 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#if _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include <logvisor/logvisor.hpp>
@ -10,14 +15,30 @@
#include <nod/DiscWii.hpp>
#include <nod/nod.hpp>
constexpr std::array<nod::Codepage_t, 3> codepages = {
CP_US_ASCII,
CP_UTF8,
CP_SHIFT_JIS,
};
static void printHelp() {
fmt::print(stderr, FMT_STRING(
"Usage:\n"
" nodtool extract [-f] <image-in> [<dir-out>]\n"
" nodtool makegcn <fsroot-in> [<image-out>]\n"
" nodtool makewii <fsroot-in> [<image-out>]\n"
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n"));
fmt::print(stderr, FMT_STRING(_SYS_STR(
"Usage:\n"
" nodtool extract [options] <image-in> [<dir-out>]\n"
" nodtool makegcn [options] <fsroot-in> [<image-out>]\n"
" nodtool makewii [options] <fsroot-in> [<image-out>]\n"
" nodtool mergegcn [options] <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
@ -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<nod::DiscBase> disc = nod::OpenDiscFromImage(inDir, isWii);
std::unique_ptr<nod::DiscBase> 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<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
std::unique_ptr<nod::DiscBase> 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<nod::DiscGCN&>(*disc), argv[2]))
if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*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<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]);
}
nod::DiscMergerGCN b(imageOut, static_cast<nod::DiscGCN&>(*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<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
std::unique_ptr<nod::DiscBase> 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,22 +331,15 @@ int main(int argc, char* argv[])
}
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;
nod::EBuildResult ret;
if (argc < 5) {
nod::SystemString outPath(argv[2]);
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]);
}
nod::DiscMergerWii b(imageOut, static_cast<nod::DiscWii&>(*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 {

View File

@ -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<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) {
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<IPartWriteStream> beginWriteStream(uint64_t offset) = 0;
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);
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:

View File

@ -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<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;
};
@ -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<uint64_t> CalculateTotalSizeRequired(SystemStringView dirIn);
static std::optional<uint64_t> 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<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

View File

@ -7,16 +7,16 @@ class DiscBuilderWii;
class DiscWii : public DiscBase {
public:
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
DiscBuilderWii makeMergeBuilder(SystemStringView outPath, bool dualLayer, FProgress progressCB);
DiscWii(std::unique_ptr<IDiscIO>&& 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<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 {
@ -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<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

View File

@ -18,6 +18,7 @@
#else
#include <cctype>
#include <cerrno>
#include <iconv.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/statvfs.h>
@ -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<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
static inline void Unlink(const SystemChar* file) {

View File

@ -12,10 +12,10 @@ class DiscBase;
struct ExtractionContext final {
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, bool& isWii);
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii, Codepage_t codepage = CP_US_ASCII);
} // namespace nod

View File

@ -126,25 +126,25 @@ std::unique_ptr<uint8_t[]> 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<IPartReadStream> 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<uint8_t[]> 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<uint8_t[]> 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<void(void)> incParents,
SystemStringView keyPath) {
size_t parentDirIdx, SystemStringView keyPath) {
/* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Node*> fileNodes;
std::unordered_map<std::string, const Node*> 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<uint64_t, uint64_t> 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<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,
SystemStringView dirIn) {
SystemStringView dirIn, Codepage_t codepage) {
/* Build map of existing nodes to write-through later */
std::unordered_map<std::string, const Node*> fileNodes;
std::unordered_map<std::string, const Node*> 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<uint64_t> 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<uint64_t> 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<uint64_t> 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;
}

View File

@ -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<IPartReadStream> s = beginReadStream(0x0);
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)
return;
/* 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) {
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<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false);
std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringView dirIn, Codepage_t codepage) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, false, codepage);
if (!sz)
return sz;
*sz += 0x30000;
@ -384,13 +384,13 @@ std::optional<uint64_t> 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<PartitionBuilderGCN>(*this));
m_partitions.emplace_back(std::make_unique<PartitionBuilderGCN>(*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<uint64_t> DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourceDisc, SystemStringView dirIn) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), 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, codepage);
if (!sz)
return std::nullopt;
*sz += 0x30000;

View File

@ -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<IReadStream> 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<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)
return;
@ -543,14 +543,14 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move
err = true;
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)
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<IReadStream> 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<IReadStream> 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<uint64_t> DiscBuilderWii::CalculateTotalSizeRequired(SystemStringView dirIn, bool& dualLayer) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true);
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, codepage);
if (!sz)
return sz;
auto szDiv = nod::div(*sz, uint64_t(0x1F0000));
@ -1252,13 +1252,13 @@ std::optional<uint64_t> 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<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)
: 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<PartitionBuilderWii&>(*m_builder.m_partitions[0]);
@ -1342,8 +1342,8 @@ EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) {
return EBuildResult::Success;
}
std::optional<uint64_t> DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourceDisc, SystemStringView dirIn, bool& dualLayer) {
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn);
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, codepage);
if (!sz)
return std::nullopt;
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> 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 */
std::unique_ptr<IFileIO> fio = NewFileIO(path);
if (!fio->exists()) {
@ -67,13 +67,13 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
bool err = false;
std::unique_ptr<DiscBase> ret;
if (isWii) {
ret = std::make_unique<DiscWii>(std::move(discIO), err);
ret = std::make_unique<DiscWii>(std::move(discIO), err, codepage);
if (err)
return {};
return ret;
}
ret = std::make_unique<DiscGCN>(std::move(discIO), err);
ret = std::make_unique<DiscGCN>(std::move(discIO), err, codepage);
if (err)
return {};
return ret;
@ -81,7 +81,7 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path) {
bool isWii;
return OpenDiscFromImage(path, isWii);
return OpenDiscFromImage(path, isWii, CP_US_ASCII);
}
} // namespace nod