mirror of https://github.com/AxioDL/nod.git
Use UTF-8 exclusively internally; replace iconv with OSUTF
Filenames are now unconditionally encoded/decoded as Shift-JIS, which provides a reasonable fallback to 7-bit ASCII.
This commit is contained in:
parent
a525f60775
commit
c1635245b8
|
@ -4,3 +4,7 @@ version.h
|
|||
.DS_Store
|
||||
*.autosave
|
||||
docs/*
|
||||
.idea/
|
||||
cmake-build-*
|
||||
build/
|
||||
out/
|
|
@ -9,7 +9,7 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|||
_SCL_SECURE_NO_DEPRECATE=1 _CRT_NONSTDC_NO_WARNINGS=1
|
||||
_ENABLE_EXTENDED_ALIGNED_STORAGE=1 NOMINMAX=1)
|
||||
add_compile_options(/IGNORE:4221 /wd4018 /wd4800 /wd4005 /wd4311 /wd4068
|
||||
/wd4267 /wd4244 /wd4200 /wd4305 /wd4067 /wd4146 /wd4309 /wd4805 ${VS_OPTIONS})
|
||||
/wd4267 /wd4244 /wd4200 /wd4305 /wd4067 /wd4146 /wd4309 /wd4805 /utf-8 ${VS_OPTIONS})
|
||||
|
||||
add_compile_options(
|
||||
# Disable exceptions
|
||||
|
|
|
@ -34,7 +34,7 @@ a content pipeline using the `nod::DiscBuilderBase` interface.
|
|||
```cpp
|
||||
/* Sample logging lambda for progress feedback */
|
||||
size_t lastIdx = -1;
|
||||
auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
|
||||
auto progFunc = [&](size_t idx, const std::string& name, size_t bytes)
|
||||
{
|
||||
if (idx != lastIdx)
|
||||
{
|
||||
|
|
|
@ -3,9 +3,11 @@ add_executable(nodtool main.cpp)
|
|||
target_link_libraries(nodtool nod logvisor)
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(nodtool pthread)
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
target_link_libraries(nodtool execinfo)
|
||||
else()
|
||||
else ()
|
||||
target_link_libraries(nodtool dl)
|
||||
endif()
|
||||
endif()
|
||||
endif ()
|
||||
else ()
|
||||
target_sources(nodtool PRIVATE app.manifest)
|
||||
endif ()
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity type="win32" name="..." version="6.0.0.0"/>
|
||||
<application>
|
||||
<windowsSettings>
|
||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
165
driver/main.cpp
165
driver/main.cpp
|
@ -15,16 +15,8 @@
|
|||
#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(_SYS_STR(
|
||||
fmt::print(stderr, FMT_STRING(
|
||||
"Usage:\n"
|
||||
" nodtool extract [options] <image-in> [<dir-out>]\n"
|
||||
" nodtool makegcn [options] <fsroot-in> [<image-out>]\n"
|
||||
|
@ -33,65 +25,40 @@ static void printHelp() {
|
|||
" 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")));
|
||||
" -v Verbose details (extract only).\n"));
|
||||
}
|
||||
|
||||
#if NOD_UCS2
|
||||
#ifdef strcasecmp
|
||||
#undef strcasecmp
|
||||
#endif
|
||||
#define strcasecmp _wcsicmp
|
||||
#if _MSC_VER
|
||||
#include <nowide/args.hpp>
|
||||
|
||||
#define PRISize "Iu"
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
int main(int argc, char* argv[]) {
|
||||
nowide::args _(argc, argv);
|
||||
#else
|
||||
#define PRISize "zu"
|
||||
int main(int argc, char* argv[])
|
||||
int main(int argc, char* argv[]) {
|
||||
#endif
|
||||
{
|
||||
/* 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;
|
||||
std::string errand;
|
||||
bool verbose = false;
|
||||
nod::Codepage_t discLocale = CP_US_ASCII;
|
||||
nod::ExtractionContext ctx = {true, [&](nod::SystemStringView str, float c) {
|
||||
nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) {
|
||||
if (verbose)
|
||||
fmt::print(stderr, FMT_STRING(_SYS_STR("Current node: {}, Extraction {:g}% Complete\n")),
|
||||
fmt::print(stderr, FMT_STRING("Current node: {}, Extraction {:g}% Complete\n"),
|
||||
str, c * 100.f);
|
||||
}};
|
||||
while (argidx < argc) {
|
||||
if (!strcasecmp(argv[argidx], _SYS_STR("-f"))) {
|
||||
if (!nod::StrCaseCmp(argv[argidx], "-f")) {
|
||||
ctx.force = true;
|
||||
++argidx;
|
||||
continue;
|
||||
} else if (!strcasecmp(argv[argidx], _SYS_STR("-v"))) {
|
||||
} else if (!nod::StrCaseCmp(argv[argidx], "-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;
|
||||
|
@ -106,18 +73,18 @@ int main(int argc, char* argv[])
|
|||
return 1;
|
||||
}
|
||||
|
||||
auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) {
|
||||
fmt::print(FMT_STRING(_SYS_STR("\r ")));
|
||||
auto progFunc = [&](float prog, std::string_view name, size_t bytes) {
|
||||
fmt::print(FMT_STRING("\r "));
|
||||
if (bytes != SIZE_MAX)
|
||||
fmt::print(FMT_STRING(_SYS_STR("\r{:g}% {} {} B")), prog * 100.f, name, bytes);
|
||||
fmt::print(FMT_STRING("\r{:g}% {} {} B"), prog * 100.f, name, bytes);
|
||||
else
|
||||
fmt::print(FMT_STRING(_SYS_STR("\r{:g}% {}")), prog * 100.f, name);
|
||||
fmt::print(FMT_STRING("\r{:g}% {}"), prog * 100.f, name);
|
||||
fflush(stdout);
|
||||
};
|
||||
|
||||
if (errand == _SYS_STR("extract")) {
|
||||
nod::SystemString imageIn;
|
||||
nod::SystemString dirOut;
|
||||
if (errand == "extract") {
|
||||
std::string imageIn;
|
||||
std::string dirOut;
|
||||
while (argidx < argc) {
|
||||
if (imageIn.empty()) {
|
||||
imageIn = argv[argidx];
|
||||
|
@ -133,10 +100,10 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
if (dirOut.empty())
|
||||
dirOut = _SYS_STR(".");
|
||||
dirOut = ".";
|
||||
|
||||
bool isWii;
|
||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale);
|
||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
|
||||
if (!disc)
|
||||
return 1;
|
||||
|
||||
|
@ -148,9 +115,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
if (!dataPart->extractToDirectory(dirOut, ctx))
|
||||
return 1;
|
||||
} else if (errand == _SYS_STR("makegcn")) {
|
||||
nod::SystemString fsrootIn;
|
||||
nod::SystemString imageOut;
|
||||
} else if (errand == "makegcn") {
|
||||
std::string fsrootIn;
|
||||
std::string imageOut;
|
||||
while (argidx < argc) {
|
||||
if (fsrootIn.empty()) {
|
||||
fsrootIn = argv[argidx];
|
||||
|
@ -166,29 +133,29 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
if (imageOut.empty())
|
||||
imageOut = fsrootIn + _SYS_STR(".gcm");
|
||||
imageOut = fsrootIn + ".gcm";
|
||||
|
||||
/* Pre-validate path */
|
||||
nod::Sstat theStat;
|
||||
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);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(fsrootIn, discLocale))
|
||||
if (!nod::DiscBuilderGCN::CalculateTotalSizeRequired(fsrootIn))
|
||||
return 1;
|
||||
|
||||
nod::EBuildResult ret;
|
||||
|
||||
nod::DiscBuilderGCN b(imageOut, progFunc, discLocale);
|
||||
nod::DiscBuilderGCN b(imageOut, progFunc);
|
||||
ret = b.buildFromDirectory(fsrootIn);
|
||||
|
||||
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||
fmt::print(FMT_STRING("\n"));
|
||||
if (ret != nod::EBuildResult::Success)
|
||||
return 1;
|
||||
} else if (errand == _SYS_STR("makewii")) {
|
||||
nod::SystemString fsrootIn;
|
||||
nod::SystemString imageOut;
|
||||
} else if (errand == "makewii") {
|
||||
std::string fsrootIn;
|
||||
std::string imageOut;
|
||||
while (argidx < argc) {
|
||||
if (fsrootIn.empty()) {
|
||||
fsrootIn = argv[argidx];
|
||||
|
@ -204,31 +171,31 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
if (imageOut.empty())
|
||||
imageOut = fsrootIn + _SYS_STR(".iso");
|
||||
imageOut = fsrootIn + ".iso";
|
||||
|
||||
/* Pre-validate path */
|
||||
nod::Sstat theStat;
|
||||
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);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool dual = false;
|
||||
if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(fsrootIn, dual, discLocale))
|
||||
if (!nod::DiscBuilderWii::CalculateTotalSizeRequired(fsrootIn, dual))
|
||||
return 1;
|
||||
|
||||
nod::EBuildResult ret;
|
||||
|
||||
nod::DiscBuilderWii b(imageOut, dual, progFunc, discLocale);
|
||||
nod::DiscBuilderWii b(imageOut, dual, progFunc);
|
||||
ret = b.buildFromDirectory(fsrootIn);
|
||||
|
||||
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||
fmt::print(FMT_STRING("\n"));
|
||||
if (ret != nod::EBuildResult::Success)
|
||||
return 1;
|
||||
} else if (errand == _SYS_STR("mergegcn")) {
|
||||
nod::SystemString fsrootIn;
|
||||
nod::SystemString imageIn;
|
||||
nod::SystemString imageOut;
|
||||
} else if (errand == "mergegcn") {
|
||||
std::string fsrootIn;
|
||||
std::string imageIn;
|
||||
std::string imageOut;
|
||||
while (argidx < argc) {
|
||||
if (fsrootIn.empty()) {
|
||||
fsrootIn = argv[argidx];
|
||||
|
@ -248,45 +215,45 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
if (imageOut.empty())
|
||||
imageOut = fsrootIn + _SYS_STR(".gcm");
|
||||
imageOut = fsrootIn + ".gcm";
|
||||
|
||||
/* Pre-validate paths */
|
||||
nod::Sstat theStat;
|
||||
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);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
|
||||
return 1;
|
||||
}
|
||||
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);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as file"), imageIn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool isWii;
|
||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale);
|
||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
|
||||
if (!disc) {
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), imageIn);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to open image {}"), imageIn);
|
||||
return 1;
|
||||
}
|
||||
if (isWii) {
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Wii images should be merged with 'mergewii'")));
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("Wii images should be merged with 'mergewii'"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), fsrootIn, discLocale))
|
||||
if (!nod::DiscMergerGCN::CalculateTotalSizeRequired(static_cast<nod::DiscGCN&>(*disc), fsrootIn))
|
||||
return 1;
|
||||
|
||||
nod::EBuildResult ret;
|
||||
|
||||
nod::DiscMergerGCN b(imageOut, static_cast<nod::DiscGCN&>(*disc), progFunc, discLocale);
|
||||
nod::DiscMergerGCN b(imageOut, static_cast<nod::DiscGCN&>(*disc), progFunc);
|
||||
ret = b.mergeFromDirectory(fsrootIn);
|
||||
|
||||
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||
fmt::print(FMT_STRING("\n"));
|
||||
if (ret != nod::EBuildResult::Success)
|
||||
return 1;
|
||||
} else if (errand == _SYS_STR("mergewii")) {
|
||||
nod::SystemString fsrootIn;
|
||||
nod::SystemString imageIn;
|
||||
nod::SystemString imageOut;
|
||||
} else if (errand == "mergewii") {
|
||||
std::string fsrootIn;
|
||||
std::string imageIn;
|
||||
std::string imageOut;
|
||||
while (argidx < argc) {
|
||||
if (fsrootIn.empty()) {
|
||||
fsrootIn = argv[argidx];
|
||||
|
@ -306,47 +273,47 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
if (imageOut.empty())
|
||||
imageOut = fsrootIn + _SYS_STR(".iso");
|
||||
imageOut = fsrootIn + ".iso";
|
||||
|
||||
/* Pre-validate paths */
|
||||
nod::Sstat theStat;
|
||||
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);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as directory"), fsrootIn);
|
||||
return 1;
|
||||
}
|
||||
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);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to stat {} as file"), imageIn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool isWii;
|
||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii, discLocale);
|
||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(imageIn, isWii);
|
||||
if (!disc) {
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), argv[3]);
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("unable to open image {}"), argv[3]);
|
||||
return 1;
|
||||
}
|
||||
if (!isWii) {
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("GameCube images should be merged with 'mergegcn'")));
|
||||
nod::LogModule.report(logvisor::Error, FMT_STRING("GameCube images should be merged with 'mergegcn'"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool dual = false;
|
||||
if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), fsrootIn, dual, discLocale))
|
||||
if (!nod::DiscMergerWii::CalculateTotalSizeRequired(static_cast<nod::DiscWii&>(*disc), fsrootIn, dual))
|
||||
return 1;
|
||||
|
||||
nod::EBuildResult ret;
|
||||
|
||||
nod::DiscMergerWii b(imageOut, static_cast<nod::DiscWii&>(*disc), dual, progFunc, discLocale);
|
||||
nod::DiscMergerWii b(imageOut, static_cast<nod::DiscWii&>(*disc), dual, progFunc);
|
||||
ret = b.mergeFromDirectory(fsrootIn);
|
||||
|
||||
fmt::print(FMT_STRING(_SYS_STR("\n")));
|
||||
fmt::print(FMT_STRING("\n"));
|
||||
if (ret != nod::EBuildResult::Success)
|
||||
return 1;
|
||||
} else {
|
||||
printHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
nod::LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Success!")));
|
||||
|
||||
nod::LogModule.report(logvisor::Info, FMT_STRING("Success!"));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,46 +9,44 @@
|
|||
|
||||
namespace nod {
|
||||
|
||||
/**
|
||||
* @brief Case-insensitive comparator for std::map sorting
|
||||
*/
|
||||
struct CaseInsensitiveCompare {
|
||||
// Allow heterogeneous lookup with maps that use this comparator.
|
||||
using is_transparent = void;
|
||||
|
||||
bool operator()(std::string_view lhs, std::string_view rhs) const {
|
||||
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char lhs, char rhs) {
|
||||
return std::tolower(static_cast<unsigned char>(lhs)) < std::tolower(static_cast<unsigned char>(rhs));
|
||||
});
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const {
|
||||
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](wchar_t lhs, wchar_t rhs) {
|
||||
return std::towlower(lhs) < std::towlower(rhs);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class DirectoryEnumerator {
|
||||
public:
|
||||
enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
|
||||
struct Entry {
|
||||
SystemString m_path;
|
||||
SystemString m_name;
|
||||
std::string m_path;
|
||||
std::string m_name;
|
||||
size_t m_fileSz;
|
||||
bool m_isDir;
|
||||
|
||||
Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir)
|
||||
: m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
|
||||
Entry(std::string path, std::string name, size_t sz, bool isDir)
|
||||
: m_path(std::move(path)), m_name(std::move(name)), m_fileSz(sz), m_isDir(isDir) {}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Entry> m_entries;
|
||||
|
||||
public:
|
||||
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
|
||||
DirectoryEnumerator(std::string_view path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
|
||||
bool reverse = false, bool noHidden = false);
|
||||
|
||||
operator bool() const { return m_entries.size() != 0; }
|
||||
size_t size() const { return m_entries.size(); }
|
||||
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
|
||||
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
|
||||
explicit operator bool() const { return !m_entries.empty(); }
|
||||
[[nodiscard]] size_t size() const { return m_entries.size(); }
|
||||
[[nodiscard]] std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
|
||||
[[nodiscard]] std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
|
||||
};
|
||||
|
||||
} // namespace nod
|
||||
|
|
|
@ -12,16 +12,17 @@
|
|||
|
||||
#include "nod/IDiscIO.hpp"
|
||||
#include "nod/IFileIO.hpp"
|
||||
#include "nod/OSUTF.h"
|
||||
#include "nod/Util.hpp"
|
||||
|
||||
namespace nod {
|
||||
|
||||
using FProgress = std::function<void(float totalProg, SystemStringView fileName, size_t fileBytesXfered)>;
|
||||
using FProgress = std::function<void(float totalProg, std::string_view fileName, size_t fileBytesXfered)>;
|
||||
|
||||
enum class EBuildResult { Success, Failed, DiskFull };
|
||||
|
||||
enum class PartitionKind : uint32_t { Data, Update, Channel };
|
||||
const SystemChar* getKindString(PartitionKind kind);
|
||||
const char* getKindString(PartitionKind kind);
|
||||
|
||||
class FSTNode {
|
||||
uint32_t typeAndNameOffset;
|
||||
|
@ -236,7 +237,7 @@ public:
|
|||
return end();
|
||||
}
|
||||
|
||||
bool extractToDirectory(SystemStringView basePath, const ExtractionContext& ctx, Codepage_t codepage = CP_US_ASCII) const;
|
||||
bool extractToDirectory(std::string_view basePath, const ExtractionContext& ctx) const;
|
||||
};
|
||||
|
||||
class IPartition {
|
||||
|
@ -275,7 +276,6 @@ protected:
|
|||
PartitionKind m_kind;
|
||||
uint64_t m_offset;
|
||||
bool m_isWii;
|
||||
Codepage_t m_codepage;
|
||||
|
||||
public:
|
||||
mutable size_t m_curNodeIdx = 0;
|
||||
|
@ -290,8 +290,8 @@ public:
|
|||
return m_curNodeIdx / float(getNodeCount());
|
||||
}
|
||||
|
||||
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) {}
|
||||
IPartition(const DiscBase& parent, PartitionKind kind, bool isWii, uint64_t offset)
|
||||
: m_parent(parent), m_kind(kind), m_offset(offset), m_isWii(isWii) {}
|
||||
virtual uint64_t normalizeOffset(uint64_t anOffset) const { return anOffset; }
|
||||
PartitionKind getKind() const { return m_kind; }
|
||||
bool isWii() const { return m_isWii; }
|
||||
|
@ -308,7 +308,7 @@ public:
|
|||
}
|
||||
const Node& getFSTRoot() const { return m_nodes[0]; }
|
||||
Node& getFSTRoot() { return m_nodes[0]; }
|
||||
bool extractToDirectory(SystemStringView path, const ExtractionContext& ctx);
|
||||
bool extractToDirectory(std::string_view path, const ExtractionContext& ctx);
|
||||
|
||||
uint64_t getDOLSize() const { return m_dolSz; }
|
||||
std::unique_ptr<uint8_t[]> getDOLBuf() const {
|
||||
|
@ -334,8 +334,8 @@ public:
|
|||
size_t getNodeCount() const { return m_nodes.size(); }
|
||||
const Header& getHeader() const { return m_header; }
|
||||
const BI2Header& getBI2() const { return m_bi2Header; }
|
||||
virtual bool extractCryptoFiles(SystemStringView path, const ExtractionContext& ctx) const { return true; }
|
||||
bool extractSysFiles(SystemStringView path, const ExtractionContext& ctx) const;
|
||||
virtual bool extractCryptoFiles(std::string_view path, const ExtractionContext& ctx) const { return true; }
|
||||
bool extractSysFiles(std::string_view path, const ExtractionContext& ctx) const;
|
||||
};
|
||||
|
||||
class DiscBase {
|
||||
|
@ -373,12 +373,12 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void extractToDirectory(SystemStringView path, const ExtractionContext& ctx) {
|
||||
void extractToDirectory(std::string_view path, const ExtractionContext& ctx) {
|
||||
for (std::unique_ptr<IPartition>& part : m_partitions)
|
||||
part->extractToDirectory(path, ctx);
|
||||
}
|
||||
|
||||
virtual bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const = 0;
|
||||
virtual bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const = 0;
|
||||
};
|
||||
|
||||
class DiscBuilderBase {
|
||||
|
@ -390,30 +390,30 @@ public:
|
|||
virtual ~PartitionBuilderBase() = default;
|
||||
|
||||
protected:
|
||||
std::unordered_map<SystemString, std::pair<uint64_t, uint64_t>> m_fileOffsetsSizes;
|
||||
std::unordered_map<std::string, std::pair<uint64_t, uint64_t>> m_fileOffsetsSizes;
|
||||
std::vector<FSTNode> m_buildNodes;
|
||||
std::vector<std::string> m_buildNames;
|
||||
size_t m_buildNameOff = 0;
|
||||
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) = 0;
|
||||
virtual uint32_t packOffset(uint64_t offset) const = 0;
|
||||
|
||||
void recursiveBuildNodesPre(SystemStringView dirIn);
|
||||
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, SystemStringView dirIn);
|
||||
void recursiveBuildNodesPre(std::string_view dirIn);
|
||||
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, std::string_view dirIn);
|
||||
|
||||
bool recursiveBuildFST(SystemStringView dirIn, std::function<void(void)> incParents, size_t parentDirIdx);
|
||||
bool recursiveBuildFST(std::string_view dirIn, std::function<void(void)> incParents, size_t parentDirIdx);
|
||||
|
||||
void recursiveMergeNodesPre(const Node* nodeIn, SystemStringView dirIn);
|
||||
bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, SystemStringView dirIn,
|
||||
SystemStringView keyPath);
|
||||
bool recursiveMergeFST(const Node* nodeIn, SystemStringView dirIn, std::function<void(void)> incParents,
|
||||
size_t parentDirIdx, SystemStringView keyPath);
|
||||
void recursiveMergeNodesPre(const Node* nodeIn, std::string_view dirIn);
|
||||
bool recursiveMergeNodes(IPartWriteStream& ws, bool system, const Node* nodeIn, std::string_view dirIn,
|
||||
std::string_view keyPath);
|
||||
bool recursiveMergeFST(const Node* nodeIn, std::string_view dirIn, std::function<void(void)> incParents,
|
||||
size_t parentDirIdx, std::string_view keyPath);
|
||||
|
||||
static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, SystemStringView dirIn, Codepage_t codepage);
|
||||
static bool RecursiveCalculateTotalSize(uint64_t& totalSz, const Node* nodeIn, std::string_view dirIn);
|
||||
|
||||
void addBuildName(SystemStringView str) {
|
||||
SystemToDiscLocConv nameView(str, m_codepage);
|
||||
m_buildNames.emplace_back(nameView.disc_str());
|
||||
m_buildNameOff += nameView.disc_str().size() + 1;
|
||||
void addBuildName(std::string_view str) {
|
||||
UTF8ToSJIS nameView(str);
|
||||
m_buildNames.emplace_back(nameView.str());
|
||||
m_buildNameOff += nameView.str().size() + 1;
|
||||
}
|
||||
|
||||
DiscBuilderBase& m_parent;
|
||||
|
@ -421,20 +421,19 @@ 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, Codepage_t codepage)
|
||||
: m_parent(parent), m_kind(kind), m_isWii(isWii), m_codepage(codepage) {}
|
||||
PartitionBuilderBase(DiscBuilderBase& parent, PartitionKind kind, bool isWii)
|
||||
: m_parent(parent), m_kind(kind), m_isWii(isWii) {}
|
||||
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, Codepage_t codepage);
|
||||
bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, SystemStringView dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeMerge(const IPartition* partIn, SystemStringView dirIn, Codepage_t codepage);
|
||||
bool buildFromDirectory(IPartWriteStream& ws, std::string_view dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeBuild(std::string_view dirIn, PartitionKind kind, bool isWii);
|
||||
bool mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn, std::string_view dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeMerge(const IPartition* partIn, std::string_view dirIn);
|
||||
};
|
||||
|
||||
protected:
|
||||
SystemString m_outPath;
|
||||
std::string m_outPath;
|
||||
std::unique_ptr<IFileIO> m_fileIO;
|
||||
std::vector<std::unique_ptr<PartitionBuilderBase>> m_partitions;
|
||||
int64_t m_discCapacity;
|
||||
|
@ -457,7 +456,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ~DiscBuilderBase() = default;
|
||||
DiscBuilderBase(SystemStringView outPath, int64_t discCapacity, FProgress progressCB)
|
||||
DiscBuilderBase(std::string_view outPath, int64_t discCapacity, FProgress progressCB)
|
||||
: m_outPath(outPath)
|
||||
, m_fileIO(NewFileIO(outPath, discCapacity))
|
||||
, m_discCapacity(discCapacity)
|
||||
|
|
|
@ -7,20 +7,20 @@ class DiscBuilderGCN;
|
|||
|
||||
class DiscGCN : public DiscBase {
|
||||
friend class DiscMergerGCN;
|
||||
DiscBuilderGCN makeMergeBuilder(SystemStringView outPath, FProgress progressCB, Codepage_t codepage);
|
||||
DiscBuilderGCN makeMergeBuilder(std::string_view outPath, FProgress progressCB);
|
||||
|
||||
public:
|
||||
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err, Codepage_t codepage = CP_US_ASCII);
|
||||
bool extractDiscHeaderFiles(SystemStringView path, const ExtractionContext& ctx) const override;
|
||||
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||
bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const override;
|
||||
};
|
||||
|
||||
class DiscBuilderGCN : public DiscBuilderBase {
|
||||
friend class DiscMergerGCN;
|
||||
|
||||
public:
|
||||
DiscBuilderGCN(SystemStringView outPath, FProgress progressCB, Codepage_t codepage = CP_US_ASCII);
|
||||
EBuildResult buildFromDirectory(SystemStringView dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeRequired(SystemStringView dirIn, Codepage_t codepage = CP_US_ASCII);
|
||||
DiscBuilderGCN(std::string_view outPath, FProgress progressCB);
|
||||
EBuildResult buildFromDirectory(std::string_view dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeRequired(std::string_view dirIn);
|
||||
};
|
||||
|
||||
class DiscMergerGCN {
|
||||
|
@ -28,9 +28,9 @@ class DiscMergerGCN {
|
|||
DiscBuilderGCN m_builder;
|
||||
|
||||
public:
|
||||
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, Codepage_t codepage = CP_US_ASCII);
|
||||
DiscMergerGCN(std::string_view outPath, DiscGCN& sourceDisc, FProgress progressCB);
|
||||
EBuildResult mergeFromDirectory(std::string_view dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeRequired(DiscGCN& sourceDisc, std::string_view dirIn);
|
||||
};
|
||||
|
||||
} // namespace nod
|
||||
|
|
|
@ -7,16 +7,16 @@ class DiscBuilderWii;
|
|||
|
||||
class DiscWii : public DiscBase {
|
||||
public:
|
||||
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;
|
||||
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||
DiscBuilderWii makeMergeBuilder(std::string_view outPath, bool dualLayer, FProgress progressCB);
|
||||
bool extractDiscHeaderFiles(std::string_view path, const ExtractionContext& ctx) const override;
|
||||
};
|
||||
|
||||
class DiscBuilderWii : public DiscBuilderBase {
|
||||
public:
|
||||
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, Codepage_t codepage = CP_US_ASCII);
|
||||
DiscBuilderWii(std::string_view outPath, bool dualLayer, FProgress progressCB);
|
||||
EBuildResult buildFromDirectory(std::string_view dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeRequired(std::string_view dirIn, bool& dualLayer);
|
||||
};
|
||||
|
||||
class DiscMergerWii {
|
||||
|
@ -24,9 +24,9 @@ class DiscMergerWii {
|
|||
DiscBuilderWii m_builder;
|
||||
|
||||
public:
|
||||
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, Codepage_t codepage = CP_US_ASCII);
|
||||
DiscMergerWii(std::string_view outPath, DiscWii& sourceDisc, bool dualLayer, FProgress progressCB);
|
||||
EBuildResult mergeFromDirectory(std::string_view dirIn);
|
||||
static std::optional<uint64_t> CalculateTotalSizeRequired(DiscWii& sourceDisc, std::string_view dirIn, bool& dualLayer);
|
||||
};
|
||||
|
||||
} // namespace nod
|
||||
|
|
|
@ -69,6 +69,6 @@ public:
|
|||
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IFileIO> NewFileIO(SystemStringView path, int64_t maxWriteSize = -1);
|
||||
std::unique_ptr<IFileIO> NewFileIO(std::string_view path, int64_t maxWriteSize = -1);
|
||||
|
||||
} // namespace nod
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#define OS_CONSTEXPR constexpr
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define OS_CONSTEXPR
|
||||
typedef uint8_t char8_t;
|
||||
typedef uint16_t char16_t;
|
||||
typedef uint32_t char32_t;
|
||||
#endif
|
||||
|
||||
OS_CONSTEXPR inline bool IsSjisLeadByte(char8_t c) { return (c > 0x80 && c < 0xa0) || (c > 0xdf && c < 0xfd); }
|
||||
|
||||
OS_CONSTEXPR inline bool IsSjisTrailByte(char8_t c) { return c > 0x3f && c < 0xfd && c != 0x7f; }
|
||||
|
||||
char32_t OSSJISToUTF32(char16_t sjis);
|
||||
|
||||
char16_t OSUTF32ToSJIS(char32_t utf32);
|
||||
|
||||
char8_t OSUTF32ToAnsi(char32_t utf32);
|
||||
|
||||
char16_t* OSUTF32To16(char32_t utf32, char16_t* utf16);
|
||||
|
||||
char8_t* OSUTF32To8(char32_t utf32, char8_t* utf8);
|
||||
|
||||
const char16_t* OSUTF16To32(const char16_t* utf16, char32_t* utf32);
|
||||
|
||||
const char8_t* OSUTF8To32(const char8_t* utf8, char32_t* utf32);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
class SJISToUTF8 {
|
||||
private:
|
||||
std::string out;
|
||||
|
||||
public:
|
||||
SJISToUTF8(std::string_view sv) {
|
||||
const auto* in = reinterpret_cast<const char8_t*>(sv.data());
|
||||
const auto* end = in + sv.size();
|
||||
std::array<char8_t, 4> u8arr{};
|
||||
while (in < end) {
|
||||
if (IsSjisLeadByte(*in)) {
|
||||
char16_t sjis = static_cast<char16_t>(*in) << 8 | *(in + 1);
|
||||
in += 2;
|
||||
char32_t utf32 = OSSJISToUTF32(sjis);
|
||||
if (utf32 == 0) {
|
||||
continue;
|
||||
}
|
||||
char8_t* u8out = u8arr.data();
|
||||
char8_t* u8end = OSUTF32To8(utf32, u8out);
|
||||
if (u8end == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto length = static_cast<size_t>(u8end - u8out);
|
||||
out.append(std::string_view{reinterpret_cast<char*>(u8out), length});
|
||||
} else {
|
||||
out.push_back(static_cast<char>(*in++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& str() const { return out; }
|
||||
|
||||
[[nodiscard]] std::string& str() { return out; }
|
||||
|
||||
[[nodiscard]] const char* c_str() const { return out.c_str(); }
|
||||
};
|
||||
|
||||
class UTF8ToSJIS {
|
||||
private:
|
||||
std::string out;
|
||||
|
||||
public:
|
||||
UTF8ToSJIS(std::string_view sv) {
|
||||
const auto* in = reinterpret_cast<const char8_t*>(sv.data());
|
||||
const auto* end = in + sv.size();
|
||||
while (in < end) {
|
||||
char32_t utf32 = 0;
|
||||
const char8_t* next = OSUTF8To32(in, &utf32);
|
||||
if (next == nullptr) {
|
||||
utf32 = *in;
|
||||
in++;
|
||||
} else {
|
||||
in = next;
|
||||
}
|
||||
char16_t sjis = OSUTF32ToSJIS(utf32);
|
||||
char8_t lead = (sjis >> 8) & 0xFF;
|
||||
if (IsSjisLeadByte(lead)) {
|
||||
out.push_back(static_cast<char>(lead));
|
||||
}
|
||||
out.push_back(static_cast<char>(sjis & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& str() const { return out; }
|
||||
|
||||
[[nodiscard]] std::string& str() { return out; }
|
||||
|
||||
[[nodiscard]] const char* c_str() const { return out.c_str(); }
|
||||
};
|
||||
#endif
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <nowide/stackstring.hpp>
|
||||
#include <windows.h>
|
||||
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
|
||||
#define WINDOWS_STORE 1
|
||||
|
@ -18,7 +19,6 @@
|
|||
#else
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <iconv.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
@ -66,7 +66,9 @@ constexpr T max(T a, T b) {
|
|||
/* template-based div for flexible typing and avoiding a library call */
|
||||
template <typename T>
|
||||
constexpr auto div(T a, T b) {
|
||||
struct DivTp { T quot, rem; };
|
||||
struct DivTp {
|
||||
T quot, rem;
|
||||
};
|
||||
return DivTp{a / b, a % b};
|
||||
}
|
||||
|
||||
|
@ -74,198 +76,27 @@ constexpr auto div(T a, T b) {
|
|||
extern logvisor::Module LogModule;
|
||||
|
||||
/* filesystem char type */
|
||||
#if _WIN32 && UNICODE
|
||||
#define NOD_UCS2 1
|
||||
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 _wstati64(path, statout); }
|
||||
#if _WIN32
|
||||
static inline int Mkdir(const char* path, int) {
|
||||
const nowide::wstackstring str(path);
|
||||
return _wmkdir(str.get());
|
||||
}
|
||||
|
||||
using Sstat = struct ::_stat64;
|
||||
static inline int Stat(const char* path, Sstat* statout) {
|
||||
const nowide::wstackstring wpath(path);
|
||||
return _wstat64(wpath.get(), statout);
|
||||
}
|
||||
#else
|
||||
static inline int Mkdir(const char* path, mode_t mode) { return CreateDirectoryA(path, mode); }
|
||||
|
||||
typedef struct stat Sstat;
|
||||
static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode); }
|
||||
static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); }
|
||||
#endif
|
||||
|
||||
/* String-converting views */
|
||||
#if NOD_UCS2
|
||||
#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
|
||||
#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) {
|
||||
#if NOD_UCS2
|
||||
_wunlink(file);
|
||||
#else
|
||||
unlink(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int StrCmp(const SystemChar* str1, const SystemChar* str2) {
|
||||
#if NOD_UCS2
|
||||
return wcscmp(str1, str2);
|
||||
#else
|
||||
return strcmp(str1, str2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2) {
|
||||
#if NOD_UCS2
|
||||
return _wcsicmp(str1, str2);
|
||||
static inline int StrCaseCmp(const char* str1, const char* str2) {
|
||||
#ifdef _MSC_VER
|
||||
return _stricmp(str1, str2);
|
||||
#else
|
||||
return strcasecmp(str1, str2);
|
||||
#endif
|
||||
|
@ -349,9 +180,11 @@ static inline uint64_t SBig(uint64_t val) { return val; }
|
|||
#endif
|
||||
|
||||
enum class FileLockType { None = 0, Read, Write };
|
||||
static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock = FileLockType::None) {
|
||||
#if NOD_UCS2
|
||||
FILE* fp = _wfopen(path, mode);
|
||||
static inline FILE* Fopen(const char* path, const char* mode, FileLockType lock = FileLockType::None) {
|
||||
#if _MSC_VER
|
||||
const nowide::wstackstring wpath(path);
|
||||
const nowide::wshort_stackstring wmode(mode);
|
||||
FILE* fp = _wfopen(wpath.get(), wmode.get());
|
||||
if (!fp)
|
||||
return nullptr;
|
||||
#else
|
||||
|
@ -394,20 +227,22 @@ static inline int64_t FTell(FILE* fp) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz) {
|
||||
static inline bool CheckFreeSpace(const char* path, size_t reqSz) {
|
||||
#if _WIN32
|
||||
ULARGE_INTEGER freeBytes;
|
||||
wchar_t buf[1024];
|
||||
wchar_t* end;
|
||||
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
|
||||
if (!ret || ret > 1024) {
|
||||
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("GetFullPathNameW {}")), path);
|
||||
const nowide::wstackstring wpath(path);
|
||||
std::array<wchar_t, 1024> buf{};
|
||||
wchar_t* end = nullptr;
|
||||
DWORD ret = GetFullPathNameW(wpath.get(), 1024, buf.data(), &end);
|
||||
if (ret == 0 || ret > 1024) {
|
||||
LogModule.report(logvisor::Error, FMT_STRING("GetFullPathNameA {}"), path);
|
||||
return false;
|
||||
}
|
||||
if (end)
|
||||
if (end != nullptr) {
|
||||
end[0] = L'\0';
|
||||
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr)) {
|
||||
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("GetDiskFreeSpaceExW {}: {}")), path, GetLastError());
|
||||
}
|
||||
if (!GetDiskFreeSpaceExW(buf.data(), &freeBytes, nullptr, nullptr)) {
|
||||
LogModule.report(logvisor::Error, FMT_STRING("GetDiskFreeSpaceExA {}: {}"), path, GetLastError());
|
||||
return false;
|
||||
}
|
||||
return reqSz < freeBytes.QuadPart;
|
||||
|
|
|
@ -12,10 +12,10 @@ class DiscBase;
|
|||
|
||||
struct ExtractionContext final {
|
||||
bool force : 1;
|
||||
std::function<void(nod::SystemStringView, float)> progressCB;
|
||||
std::function<void(std::string_view, float)> progressCB;
|
||||
};
|
||||
|
||||
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path);
|
||||
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii, Codepage_t codepage = CP_US_ASCII);
|
||||
std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path);
|
||||
std::unique_ptr<DiscBase> OpenDiscFromImage(std::string_view path, bool& isWii);
|
||||
|
||||
} // namespace nod
|
||||
|
|
|
@ -10,6 +10,7 @@ add_library(nod
|
|||
DiscIOWBFS.cpp
|
||||
DiscWii.cpp
|
||||
nod.cpp
|
||||
OSUTF.c
|
||||
|
||||
../include/nod/aes.hpp
|
||||
../include/nod/DirectoryEnumerator.hpp
|
||||
|
@ -21,13 +22,14 @@ add_library(nod
|
|||
../include/nod/nod.hpp
|
||||
../include/nod/sha1.h
|
||||
../include/nod/Util.hpp
|
||||
../include/nod/OSUTF.h
|
||||
)
|
||||
|
||||
target_include_directories(nod PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
|
||||
)
|
||||
|
||||
target_link_libraries(nod PUBLIC logvisor)
|
||||
target_link_libraries(nod PUBLIC $<BUILD_INTERFACE:logvisor>)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(nod PRIVATE FileIOWin32.cpp)
|
||||
|
|
|
@ -9,127 +9,157 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
|
||||
namespace nod {
|
||||
|
||||
DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, bool sizeSort, bool reverse, bool noHidden) {
|
||||
DirectoryEnumerator::DirectoryEnumerator(std::string_view path, Mode mode, bool sizeSort, bool reverse, bool noHidden) {
|
||||
Sstat theStat;
|
||||
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
SystemString wc(path);
|
||||
wc += _SYS_STR("/*");
|
||||
std::wstring wc = nowide::widen(path);
|
||||
wc += L"/*";
|
||||
WIN32_FIND_DATAW d;
|
||||
HANDLE dir = FindFirstFileW(wc.c_str(), &d);
|
||||
if (dir == INVALID_HANDLE_VALUE)
|
||||
if (dir == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case Mode::Native:
|
||||
do {
|
||||
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
||||
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L"..")) {
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
}
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)) {
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _SYS_STR('/');
|
||||
fp += d.cFileName;
|
||||
}
|
||||
std::string fileName = nowide::narrow(d.cFileName);
|
||||
std::string fp(path);
|
||||
fp += '/';
|
||||
fp += fileName;
|
||||
Sstat st;
|
||||
if (Stat(fp.c_str(), &st))
|
||||
continue;
|
||||
|
||||
size_t sz = 0;
|
||||
bool isDir = false;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
isDir = true;
|
||||
else if (S_ISREG(st.st_mode))
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
sz = st.st_size;
|
||||
else
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_entries.push_back(Entry(fp, d.cFileName, sz, isDir));
|
||||
m_entries.emplace_back(fp, fileName, sz, isDir);
|
||||
} while (FindNextFileW(dir, &d));
|
||||
break;
|
||||
case Mode::DirsThenFilesSorted:
|
||||
case Mode::DirsSorted: {
|
||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort; |