mirror of
https://github.com/AxioDL/nod.git
synced 2025-12-08 13:14:59 +00:00
Compare commits
15 Commits
acdadaf963
...
hsh
| Author | SHA1 | Date | |
|---|---|---|---|
| bad0f06254 | |||
| 9a10e58f8d | |||
|
|
1d61a211f2 | ||
|
|
2783337c36 | ||
|
|
dffcac50c5 | ||
|
|
f147e12356 | ||
|
|
48a2981a93 | ||
|
|
19604b2a3b | ||
|
|
c1a1d1abc8 | ||
|
|
6bf4f07129 | ||
|
|
75fc574f81 | ||
|
|
11a0351d1c | ||
|
|
4ec6c6697b | ||
| ba0c2b7843 | |||
|
|
221bc7c7f2 |
@@ -1,8 +1,10 @@
|
|||||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
||||||
project(nod VERSION 0.1)
|
project(nod VERSION 0.1)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
if (NOT MSVC)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include (CMakePackageConfigHelpers)
|
include (CMakePackageConfigHelpers)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <nod/nod.hpp>
|
#include <nod/nod.hpp>
|
||||||
|
|
||||||
static void printHelp() {
|
static void printHelp() {
|
||||||
fmt::print(stderr, fmt(
|
fmt::print(stderr, FMT_STRING(
|
||||||
"Usage:\n"
|
"Usage:\n"
|
||||||
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
||||||
" nodtool makegcn <fsroot-in> [<image-out>]\n"
|
" nodtool makegcn <fsroot-in> [<image-out>]\n"
|
||||||
@@ -47,7 +47,7 @@ int main(int argc, char* argv[])
|
|||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) {
|
nod::ExtractionContext ctx = {true, [&](std::string_view str, float c) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fmt::print(stderr, fmt("Current node: {}, Extraction {:g}% Complete\n"), str,
|
fmt::print(stderr, FMT_STRING("Current node: {}, Extraction {:g}% Complete\n"), str,
|
||||||
c * 100.f);
|
c * 100.f);
|
||||||
}};
|
}};
|
||||||
const nod::SystemChar* inDir = nullptr;
|
const nod::SystemChar* inDir = nullptr;
|
||||||
@@ -66,11 +66,11 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) {
|
auto progFunc = [&](float prog, nod::SystemStringView name, size_t bytes) {
|
||||||
fmt::print(fmt(_SYS_STR("\r ")));
|
fmt::print(FMT_STRING(_SYS_STR("\r ")));
|
||||||
if (bytes != SIZE_MAX)
|
if (bytes != SIZE_MAX)
|
||||||
fmt::print(fmt(_SYS_STR("\r{:g}% {} {} B")), prog * 100.f, name, bytes);
|
fmt::print(FMT_STRING(_SYS_STR("\r{:g}% {} {} B")), prog * 100.f, name, bytes);
|
||||||
else
|
else
|
||||||
fmt::print(fmt(_SYS_STR("\r{:g}% {}")), prog * 100.f, name);
|
fmt::print(FMT_STRING(_SYS_STR("\r{:g}% {}")), prog * 100.f, name);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ int main(int argc, char* argv[])
|
|||||||
/* Pre-validate path */
|
/* Pre-validate path */
|
||||||
nod::Sstat theStat;
|
nod::Sstat theStat;
|
||||||
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {} as directory")), argv[2]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[2]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +111,14 @@ int main(int argc, char* argv[])
|
|||||||
ret = b.buildFromDirectory(argv[2]);
|
ret = b.buildFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(fmt("\n"));
|
fmt::print(FMT_STRING("\n"));
|
||||||
if (ret != nod::EBuildResult::Success)
|
if (ret != nod::EBuildResult::Success)
|
||||||
return 1;
|
return 1;
|
||||||
} else if (!strcasecmp(argv[1], _SYS_STR("makewii"))) {
|
} else if (!strcasecmp(argv[1], _SYS_STR("makewii"))) {
|
||||||
/* Pre-validate path */
|
/* Pre-validate path */
|
||||||
nod::Sstat theStat;
|
nod::Sstat theStat;
|
||||||
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {} as directory")), argv[4]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[4]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,29 +138,29 @@ int main(int argc, char* argv[])
|
|||||||
ret = b.buildFromDirectory(argv[2]);
|
ret = b.buildFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(fmt("\n"));
|
fmt::print(FMT_STRING("\n"));
|
||||||
if (ret != nod::EBuildResult::Success)
|
if (ret != nod::EBuildResult::Success)
|
||||||
return 1;
|
return 1;
|
||||||
} else if (!strcasecmp(argv[1], _SYS_STR("mergegcn"))) {
|
} else if (!strcasecmp(argv[1], _SYS_STR("mergegcn"))) {
|
||||||
/* Pre-validate paths */
|
/* Pre-validate paths */
|
||||||
nod::Sstat theStat;
|
nod::Sstat theStat;
|
||||||
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {} as directory")), argv[2]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[2]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) {
|
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {} as file")), argv[3]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), argv[3]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWii;
|
bool isWii;
|
||||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
|
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
|
||||||
if (!disc) {
|
if (!disc) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open image {}")), argv[3]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), argv[3]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (isWii) {
|
if (isWii) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("Wii images should be merged with 'mergewii'")));
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Wii images should be merged with 'mergewii'")));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,29 +179,29 @@ int main(int argc, char* argv[])
|
|||||||
ret = b.mergeFromDirectory(argv[2]);
|
ret = b.mergeFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(fmt("\n"));
|
fmt::print(FMT_STRING("\n"));
|
||||||
if (ret != nod::EBuildResult::Success)
|
if (ret != nod::EBuildResult::Success)
|
||||||
return 1;
|
return 1;
|
||||||
} else if (!strcasecmp(argv[1], _SYS_STR("mergewii"))) {
|
} else if (!strcasecmp(argv[1], _SYS_STR("mergewii"))) {
|
||||||
/* Pre-validate paths */
|
/* Pre-validate paths */
|
||||||
nod::Sstat theStat;
|
nod::Sstat theStat;
|
||||||
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode)) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {} as directory")), argv[2]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as directory")), argv[2]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) {
|
if (nod::Stat(argv[3], &theStat) || !S_ISREG(theStat.st_mode)) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {} as file")), argv[3]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {} as file")), argv[3]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWii;
|
bool isWii;
|
||||||
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
|
std::unique_ptr<nod::DiscBase> disc = nod::OpenDiscFromImage(argv[3], isWii);
|
||||||
if (!disc) {
|
if (!disc) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open image {}")), argv[3]);
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open image {}")), argv[3]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!isWii) {
|
if (!isWii) {
|
||||||
nod::LogModule.report(logvisor::Error, fmt(_SYS_STR("GameCube images should be merged with 'mergegcn'")));
|
nod::LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("GameCube images should be merged with 'mergegcn'")));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ int main(int argc, char* argv[])
|
|||||||
ret = b.mergeFromDirectory(argv[2]);
|
ret = b.mergeFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(fmt("\n"));
|
fmt::print(FMT_STRING("\n"));
|
||||||
if (ret != nod::EBuildResult::Success)
|
if (ret != nod::EBuildResult::Success)
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -229,6 +229,6 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nod::LogModule.report(logvisor::Info, fmt(_SYS_STR("Success!")));
|
nod::LogModule.report(logvisor::Info, FMT_STRING(_SYS_STR("Success!")));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ public:
|
|||||||
if (m_kind == Kind::Directory) {
|
if (m_kind == Kind::Directory) {
|
||||||
DirectoryIterator it = begin();
|
DirectoryIterator it = begin();
|
||||||
for (; it != end(); ++it) {
|
for (; it != end(); ++it) {
|
||||||
if (!it->getName().compare(name))
|
if (it->getName() == name)
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
return it;
|
return it;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public:
|
|||||||
virtual ~IDiscIO() = default;
|
virtual ~IDiscIO() = default;
|
||||||
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset = 0) const = 0;
|
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset = 0) const = 0;
|
||||||
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset = 0) const = 0;
|
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset = 0) const = 0;
|
||||||
|
virtual bool hasWiiCrypto() const { return true; } /* NFS overrides this to false */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IPartReadStream : IReadStream {
|
struct IPartReadStream : IReadStream {
|
||||||
@@ -49,10 +50,11 @@ public:
|
|||||||
AthenaPartReadStream(std::unique_ptr<IPartReadStream>&& rs) : m_rs(std::move(rs)) {}
|
AthenaPartReadStream(std::unique_ptr<IPartReadStream>&& rs) : m_rs(std::move(rs)) {}
|
||||||
|
|
||||||
void seek(atInt64 off, athena::SeekOrigin origin) override {
|
void seek(atInt64 off, athena::SeekOrigin origin) override {
|
||||||
if (origin == athena::Begin)
|
if (origin == athena::SeekOrigin::Begin) {
|
||||||
m_rs->seek(off, SEEK_SET);
|
m_rs->seek(off, SEEK_SET);
|
||||||
else if (origin == athena::Current)
|
} else if (origin == athena::SeekOrigin::Current) {
|
||||||
m_rs->seek(off, SEEK_CUR);
|
m_rs->seek(off, SEEK_CUR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
atUint64 position() const override { return m_rs->position(); }
|
atUint64 position() const override { return m_rs->position(); }
|
||||||
atUint64 length() const override { return 0; }
|
atUint64 length() const override { return 0; }
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ public:
|
|||||||
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
||||||
uint64_t readSz = discio.read(buf, thisSz);
|
uint64_t readSz = discio.read(buf, thisSz);
|
||||||
if (thisSz != readSz) {
|
if (thisSz != readSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to read enough from disc"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from disc"));
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
if (write(buf, readSz) != readSz) {
|
if (write(buf, readSz) != readSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to write in file"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to write in file"));
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
@@ -45,11 +45,11 @@ public:
|
|||||||
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
||||||
uint64_t readSz = discio.read(buf, thisSz);
|
uint64_t readSz = discio.read(buf, thisSz);
|
||||||
if (thisSz != readSz) {
|
if (thisSz != readSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to read enough from disc"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from disc"));
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
if (write(buf, readSz) != readSz) {
|
if (write(buf, readSz) != readSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to write in file"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to write in file"));
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
|
|||||||
@@ -54,14 +54,21 @@
|
|||||||
namespace nod {
|
namespace nod {
|
||||||
/* define our own min/max to avoid MSVC BS */
|
/* define our own min/max to avoid MSVC BS */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T min(T a, T b) {
|
constexpr T min(T a, T b) {
|
||||||
return a < b ? a : b;
|
return a < b ? a : b;
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T max(T a, T b) {
|
constexpr T max(T a, T b) {
|
||||||
return a > b ? a : b;
|
return a > b ? a : 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; };
|
||||||
|
return DivTp{a / b, a % b};
|
||||||
|
}
|
||||||
|
|
||||||
/* Log Module */
|
/* Log Module */
|
||||||
extern logvisor::Module LogModule;
|
extern logvisor::Module LogModule;
|
||||||
|
|
||||||
@@ -258,9 +265,9 @@ static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLo
|
|||||||
OVERLAPPED ov = {};
|
OVERLAPPED ov = {};
|
||||||
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
|
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
|
||||||
&ov);
|
&ov);
|
||||||
#else
|
#elif !defined(__SWITCH__)
|
||||||
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
|
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
|
||||||
LogModule.report(logvisor::Error, fmt("flock {}: {}"), path, strerror(errno));
|
LogModule.report(logvisor::Error, FMT_STRING("flock {}: {}"), path, strerror(errno));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +277,7 @@ static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLo
|
|||||||
static inline int FSeek(FILE* fp, int64_t offset, int whence) {
|
static inline int FSeek(FILE* fp, int64_t offset, int whence) {
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return _fseeki64(fp, offset, whence);
|
return _fseeki64(fp, offset, whence);
|
||||||
#elif __APPLE__ || __FreeBSD__
|
#elif __APPLE__ || __FreeBSD__ || __SWITCH__
|
||||||
return fseeko(fp, offset, whence);
|
return fseeko(fp, offset, whence);
|
||||||
#else
|
#else
|
||||||
return fseeko64(fp, offset, whence);
|
return fseeko64(fp, offset, whence);
|
||||||
@@ -280,7 +287,7 @@ static inline int FSeek(FILE* fp, int64_t offset, int whence) {
|
|||||||
static inline int64_t FTell(FILE* fp) {
|
static inline int64_t FTell(FILE* fp) {
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return _ftelli64(fp);
|
return _ftelli64(fp);
|
||||||
#elif __APPLE__ || __FreeBSD__
|
#elif __APPLE__ || __FreeBSD__ || __SWITCH__
|
||||||
return ftello(fp);
|
return ftello(fp);
|
||||||
#else
|
#else
|
||||||
return ftello64(fp);
|
return ftello64(fp);
|
||||||
@@ -294,20 +301,20 @@ static inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz) {
|
|||||||
wchar_t* end;
|
wchar_t* end;
|
||||||
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
|
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
|
||||||
if (!ret || ret > 1024) {
|
if (!ret || ret > 1024) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("GetFullPathNameW {}")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("GetFullPathNameW {}")), path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (end)
|
if (end)
|
||||||
end[0] = L'\0';
|
end[0] = L'\0';
|
||||||
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr)) {
|
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("GetDiskFreeSpaceExW {}: {}")), path, GetLastError());
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("GetDiskFreeSpaceExW {}: {}")), path, GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return reqSz < freeBytes.QuadPart;
|
return reqSz < freeBytes.QuadPart;
|
||||||
#else
|
#else
|
||||||
struct statvfs svfs;
|
struct statvfs svfs;
|
||||||
if (statvfs(path, &svfs)) {
|
if (statvfs(path, &svfs)) {
|
||||||
LogModule.report(logvisor::Error, fmt("statvfs {}: {}"), path, strerror(errno));
|
LogModule.report(logvisor::Error, FMT_STRING("statvfs {}: {}"), path, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return reqSz < svfs.f_frsize * svfs.f_bavail;
|
return reqSz < svfs.f_frsize * svfs.f_bavail;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ add_library(nod
|
|||||||
DiscBase.cpp
|
DiscBase.cpp
|
||||||
DiscGCN.cpp
|
DiscGCN.cpp
|
||||||
DiscIOISO.cpp
|
DiscIOISO.cpp
|
||||||
|
DiscIONFS.cpp
|
||||||
DiscIOWBFS.cpp
|
DiscIOWBFS.cpp
|
||||||
DiscWii.cpp
|
DiscWii.cpp
|
||||||
nod.cpp
|
nod.cpp
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ Node::Node(const IPartition& parent, const FSTNode& node, std::string_view name)
|
|||||||
|
|
||||||
std::unique_ptr<IPartReadStream> Node::beginReadStream(uint64_t offset) const {
|
std::unique_ptr<IPartReadStream> Node::beginReadStream(uint64_t offset) const {
|
||||||
if (m_kind != Kind::File) {
|
if (m_kind != Kind::File) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to stream a non-file {}"), m_name);
|
LogModule.report(logvisor::Error, FMT_STRING("unable to stream a non-file {}"), m_name);
|
||||||
return std::unique_ptr<IPartReadStream>();
|
return std::unique_ptr<IPartReadStream>();
|
||||||
}
|
}
|
||||||
return m_parent.beginReadStream(m_discOffset + offset);
|
return m_parent.beginReadStream(m_discOffset + offset);
|
||||||
@@ -117,7 +117,7 @@ std::unique_ptr<IPartReadStream> Node::beginReadStream(uint64_t offset) const {
|
|||||||
|
|
||||||
std::unique_ptr<uint8_t[]> Node::getBuf() const {
|
std::unique_ptr<uint8_t[]> Node::getBuf() const {
|
||||||
if (m_kind != Kind::File) {
|
if (m_kind != Kind::File) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to buffer a non-file {}"), m_name);
|
LogModule.report(logvisor::Error, FMT_STRING("unable to buffer a non-file {}"), m_name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext
|
|||||||
if (ctx.progressCB && !getName().empty())
|
if (ctx.progressCB && !getName().empty())
|
||||||
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
||||||
if (Mkdir(path.c_str(), 0755) && errno != EEXIST) {
|
if (Mkdir(path.c_str(), 0755) && errno != EEXIST) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to mkdir '{}'")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Node& subnode : *this)
|
for (Node& subnode : *this)
|
||||||
@@ -164,14 +164,14 @@ bool Node::extractToDirectory(SystemStringView basePath, const ExtractionContext
|
|||||||
bool IPartition::extractToDirectory(SystemStringView path, const ExtractionContext& ctx) {
|
bool IPartition::extractToDirectory(SystemStringView path, const ExtractionContext& ctx) {
|
||||||
m_curNodeIdx = 0;
|
m_curNodeIdx = 0;
|
||||||
if (Mkdir(path.data(), 0755) && errno != EEXIST) {
|
if (Mkdir(path.data(), 0755) && errno != EEXIST) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to mkdir '{}'")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemString basePath = m_isWii ? SystemString(path) + _SYS_STR("/") + getKindString(m_kind) : SystemString(path);
|
SystemString basePath = m_isWii ? SystemString(path) + _SYS_STR("/") + getKindString(m_kind) : SystemString(path);
|
||||||
if (m_isWii) {
|
if (m_isWii) {
|
||||||
if (Mkdir(basePath.c_str(), 0755) && errno != EEXIST) {
|
if (Mkdir(basePath.c_str(), 0755) && errno != EEXIST) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to mkdir '{}'")), basePath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), basePath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ bool IPartition::extractToDirectory(SystemStringView path, const ExtractionConte
|
|||||||
/* Extract Filesystem */
|
/* Extract Filesystem */
|
||||||
SystemString fsPath = basePath + _SYS_STR("/files");
|
SystemString fsPath = basePath + _SYS_STR("/files");
|
||||||
if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST) {
|
if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to mkdir '{}'")), fsPath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}'")), fsPath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ bool IPartition::extractToDirectory(SystemStringView path, const ExtractionConte
|
|||||||
bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionContext& ctx) const {
|
bool IPartition::extractSysFiles(SystemStringView basePath, const ExtractionContext& ctx) const {
|
||||||
SystemString basePathStr(basePath);
|
SystemString basePathStr(basePath);
|
||||||
if (Mkdir((basePathStr + _SYS_STR("/sys")).c_str(), 0755) && errno != EEXIST) {
|
if (Mkdir((basePathStr + _SYS_STR("/sys")).c_str(), 0755) && errno != EEXIST) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to mkdir '{}/sys'")), basePath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}/sys'")), basePath);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +284,7 @@ static bool IsSystemFile(SystemStringView name, bool& isDol) {
|
|||||||
* This is required for multi-DOL games, but doesn't harm functionality otherwise */
|
* This is required for multi-DOL games, but doesn't harm functionality otherwise */
|
||||||
static void PatchDOL(std::unique_ptr<uint8_t[]>& buf, size_t sz, bool& patched) {
|
static void PatchDOL(std::unique_ptr<uint8_t[]>& buf, size_t sz, bool& patched) {
|
||||||
patched = false;
|
patched = false;
|
||||||
|
#ifndef __SWITCH__
|
||||||
uint8_t* found = static_cast<uint8_t*>(memmem(buf.get(), sz,
|
uint8_t* found = static_cast<uint8_t*>(memmem(buf.get(), sz,
|
||||||
"\x3C\x03\xF8\x00\x28\x00\x00\x00\x40\x82\x00\x0C"
|
"\x3C\x03\xF8\x00\x28\x00\x00\x00\x40\x82\x00\x0C"
|
||||||
"\x38\x60\x00\x01\x48\x00\x02\x44\x38\x61\x00\x18\x48",
|
"\x38\x60\x00\x01\x48\x00\x02\x44\x38\x61\x00\x18\x48",
|
||||||
@@ -292,6 +293,7 @@ static void PatchDOL(std::unique_ptr<uint8_t[]>& buf, size_t sz, bool& patched)
|
|||||||
found[11] = '\x04';
|
found[11] = '\x04';
|
||||||
patched = true;
|
patched = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t sz, bool& patched) {
|
static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t sz, bool& patched) {
|
||||||
@@ -712,7 +714,7 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(uint64_t
|
|||||||
|
|
||||||
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn) {
|
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws, SystemStringView dirIn) {
|
||||||
if (dirIn.empty()) {
|
if (dirIn.empty()) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("all arguments must be supplied to buildFromDirectory()")));
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("all arguments must be supplied to buildFromDirectory()")));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -737,7 +739,7 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
|||||||
{
|
{
|
||||||
Sstat dolStat;
|
Sstat dolStat;
|
||||||
if (Stat(dolIn.c_str(), &dolStat)) {
|
if (Stat(dolIn.c_str(), &dolStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), dolIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), dolIn);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
||||||
@@ -778,7 +780,7 @@ std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSiz
|
|||||||
|
|
||||||
Sstat dolStat;
|
Sstat dolStat;
|
||||||
if (Stat(dolIn.c_str(), &dolStat)) {
|
if (Stat(dolIn.c_str(), &dolStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), dolIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), dolIn);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
|
uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
|
||||||
@@ -790,7 +792,7 @@ std::optional<uint64_t> DiscBuilderBase::PartitionBuilderBase::CalculateTotalSiz
|
|||||||
bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn,
|
bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream& ws, const IPartition* partIn,
|
||||||
SystemStringView dirIn) {
|
SystemStringView dirIn) {
|
||||||
if (dirIn.empty()) {
|
if (dirIn.empty()) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("all arguments must be supplied to mergeFromDirectory()")));
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("all arguments must be supplied to mergeFromDirectory()")));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public:
|
|||||||
m_curUser -= reqSz;
|
m_curUser -= reqSz;
|
||||||
m_curUser &= 0xfffffffffffffff0;
|
m_curUser &= 0xfffffffffffffff0;
|
||||||
if (m_curUser < 0x30000) {
|
if (m_curUser < 0x30000) {
|
||||||
LogModule.report(logvisor::Error, fmt("user area low mark reached"));
|
LogModule.report(logvisor::Error, FMT_STRING("user area low mark reached"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
static_cast<PartWriteStream&>(ws).seek(m_curUser);
|
static_cast<PartWriteStream&>(ws).seek(m_curUser);
|
||||||
@@ -211,7 +211,7 @@ public:
|
|||||||
fstSz = ROUND_UP_32(fstSz);
|
fstSz = ROUND_UP_32(fstSz);
|
||||||
|
|
||||||
if (fstOff + fstSz >= m_curUser) {
|
if (fstOff + fstSz >= m_curUser) {
|
||||||
LogModule.report(logvisor::Error, fmt("FST flows into user area (one or the other is too big)"));
|
LogModule.report(logvisor::Error, FMT_STRING("FST flows into user area (one or the other is too big)"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ public:
|
|||||||
SystemString apploaderIn = dirStr + _SYS_STR("/sys/apploader.img");
|
SystemString apploaderIn = dirStr + _SYS_STR("/sys/apploader.img");
|
||||||
Sstat apploaderStat;
|
Sstat apploaderStat;
|
||||||
if (Stat(apploaderIn.c_str(), &apploaderStat)) {
|
if (Stat(apploaderIn.c_str(), &apploaderStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), apploaderIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), apploaderIn);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ public:
|
|||||||
SystemString bootIn = dirStr + _SYS_STR("/sys/boot.bin");
|
SystemString bootIn = dirStr + _SYS_STR("/sys/boot.bin");
|
||||||
Sstat bootStat;
|
Sstat bootStat;
|
||||||
if (Stat(bootIn.c_str(), &bootStat)) {
|
if (Stat(bootIn.c_str(), &bootStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), bootIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), bootIn);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ public:
|
|||||||
SystemString bi2In = dirStr + _SYS_STR("/sys/bi2.bin");
|
SystemString bi2In = dirStr + _SYS_STR("/sys/bi2.bin");
|
||||||
Sstat bi2Stat;
|
Sstat bi2Stat;
|
||||||
if (Stat(bi2In.c_str(), &bi2Stat)) {
|
if (Stat(bi2In.c_str(), &bi2Stat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), bi2In);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), bi2In);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ public:
|
|||||||
ws.write(buf, rdSz);
|
ws.write(buf, rdSz);
|
||||||
xferSz += rdSz;
|
xferSz += rdSz;
|
||||||
if (0x2440 + xferSz >= m_curUser) {
|
if (0x2440 + xferSz >= m_curUser) {
|
||||||
LogModule.report(logvisor::Error, fmt("apploader flows into user area (one or the other is too big)"));
|
LogModule.report(logvisor::Error, FMT_STRING("apploader flows into user area (one or the other is too big)"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
||||||
@@ -340,7 +340,7 @@ public:
|
|||||||
ws.write(apploaderBuf.get(), apploaderSz);
|
ws.write(apploaderBuf.get(), apploaderSz);
|
||||||
xferSz += apploaderSz;
|
xferSz += apploaderSz;
|
||||||
if (0x2440 + xferSz >= m_curUser) {
|
if (0x2440 + xferSz >= m_curUser) {
|
||||||
LogModule.report(logvisor::Error, fmt("apploader flows into user area (one or the other is too big)"));
|
LogModule.report(logvisor::Error, FMT_STRING("apploader flows into user area (one or the other is too big)"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
||||||
@@ -354,7 +354,7 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(SystemStringView dirIn) {
|
|||||||
if (!m_fileIO->beginWriteStream())
|
if (!m_fileIO->beginWriteStream())
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) {
|
if (!CheckFreeSpace(m_outPath.c_str(), 0x57058000)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("not enough free disk space for {}")), m_outPath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("not enough free disk space for {}")), m_outPath);
|
||||||
return EBuildResult::DiskFull;
|
return EBuildResult::DiskFull;
|
||||||
}
|
}
|
||||||
m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
||||||
@@ -378,7 +378,7 @@ std::optional<uint64_t> DiscBuilderGCN::CalculateTotalSizeRequired(SystemStringV
|
|||||||
return sz;
|
return sz;
|
||||||
*sz += 0x30000;
|
*sz += 0x30000;
|
||||||
if (sz > 0x57058000) {
|
if (sz > 0x57058000) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x57058000);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x57058000);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return sz;
|
return sz;
|
||||||
@@ -396,7 +396,7 @@ EBuildResult DiscMergerGCN::mergeFromDirectory(SystemStringView dirIn) {
|
|||||||
if (!m_builder.getFileIO().beginWriteStream())
|
if (!m_builder.getFileIO().beginWriteStream())
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) {
|
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), 0x57058000)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("not enough free disk space for {}")), m_builder.m_outPath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("not enough free disk space for {}")), m_builder.m_outPath);
|
||||||
return EBuildResult::DiskFull;
|
return EBuildResult::DiskFull;
|
||||||
}
|
}
|
||||||
m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
||||||
@@ -422,7 +422,7 @@ std::optional<uint64_t> DiscMergerGCN::CalculateTotalSizeRequired(DiscGCN& sourc
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
*sz += 0x30000;
|
*sz += 0x30000;
|
||||||
if (sz > 0x57058000) {
|
if (sz > 0x57058000) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x57058000);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x57058000);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return sz;
|
return sz;
|
||||||
|
|||||||
@@ -28,9 +28,8 @@ public:
|
|||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_fio->beginReadStream(offset), err));
|
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_fio->beginReadStream(offset), err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -51,9 +50,8 @@ public:
|
|||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_fio->beginWriteStream(offset), err));
|
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_fio->beginWriteStream(offset), err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
271
lib/DiscIONFS.cpp
Normal file
271
lib/DiscIONFS.cpp
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
#include "nod/IDiscIO.hpp"
|
||||||
|
#include "nod/IFileIO.hpp"
|
||||||
|
#include "nod/Util.hpp"
|
||||||
|
#include "nod/aes.hpp"
|
||||||
|
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
|
|
||||||
|
namespace nod {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NFS is the image format used to distribute Wii VC games for the Wii U.
|
||||||
|
* It is an LBA format similar to WBFS but adds its own encryption layer.
|
||||||
|
* It logically stores a standard Wii disc image with partitions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DiscIONFS : public IDiscIO {
|
||||||
|
std::vector<std::unique_ptr<IFileIO>> files;
|
||||||
|
|
||||||
|
struct NFSHead {
|
||||||
|
uint32_t magic; // EGGS
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t unknown[2]; // Signature, UUID?
|
||||||
|
uint32_t lbaRangeCount;
|
||||||
|
struct {
|
||||||
|
uint32_t startBlock;
|
||||||
|
uint32_t numBlocks;
|
||||||
|
} lbaRanges[61];
|
||||||
|
uint32_t endMagic; // SGGE
|
||||||
|
} nfsHead;
|
||||||
|
|
||||||
|
uint8_t key[16];
|
||||||
|
|
||||||
|
uint32_t calculateNumFiles() const {
|
||||||
|
uint32_t totalBlockCount = 0;
|
||||||
|
for (uint32_t i = 0; i < nfsHead.lbaRangeCount; ++i)
|
||||||
|
totalBlockCount += nfsHead.lbaRanges[i].numBlocks;
|
||||||
|
return (uint64_t(totalBlockCount) * uint64_t(0x8000) +
|
||||||
|
(uint64_t(0x200) + uint64_t(0xF9FFFFF))) / uint64_t(0xFA00000);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FBO {
|
||||||
|
uint32_t file, block, lblock, offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
FBO logicalToFBO(uint64_t offset) const {
|
||||||
|
auto blockAndRemBytes = nod::div(offset, uint64_t(0x8000)); /* 32768 bytes per block */
|
||||||
|
uint32_t block = UINT32_MAX;
|
||||||
|
for (uint32_t i = 0, physicalBlock = 0; i < nfsHead.lbaRangeCount; ++i) {
|
||||||
|
const auto& range = nfsHead.lbaRanges[i];
|
||||||
|
if (blockAndRemBytes.quot >= range.startBlock && blockAndRemBytes.quot - range.startBlock < range.numBlocks) {
|
||||||
|
block = physicalBlock + (blockAndRemBytes.quot - range.startBlock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
physicalBlock += range.numBlocks;
|
||||||
|
}
|
||||||
|
/* This offset has no physical mapping, read zeroes */
|
||||||
|
if (block == UINT32_MAX)
|
||||||
|
return {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
|
||||||
|
auto fileAndRemBlocks = nod::div(block, uint32_t(8000)); /* 8000 blocks per file */
|
||||||
|
return {uint32_t(fileAndRemBlocks.quot), uint32_t(fileAndRemBlocks.rem),
|
||||||
|
uint32_t(blockAndRemBytes.quot), uint32_t(blockAndRemBytes.rem)};
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
DiscIONFS(SystemStringView fpin, bool& err) {
|
||||||
|
/* Validate file path format */
|
||||||
|
using SignedSize = std::make_signed<SystemString::size_type>::type;
|
||||||
|
const auto dotPos = SignedSize(fpin.rfind(_SYS_STR('.')));
|
||||||
|
const auto slashPos = SignedSize(fpin.find_last_of(_SYS_STR("/\\")));
|
||||||
|
if (fpin.size() <= 4 || dotPos == -1 || dotPos <= slashPos ||
|
||||||
|
fpin.compare(slashPos + 1, 4, _SYS_STR("hif_")) ||
|
||||||
|
fpin.compare(dotPos, fpin.size() - dotPos, _SYS_STR(".nfs"))) {
|
||||||
|
LogModule.report(logvisor::Error,
|
||||||
|
FMT_STRING(_SYS_STR("'{}' must begin with 'hif_' and end with '.nfs' to be accepted as an NFS image")), fpin);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load key file */
|
||||||
|
const SystemString dir(fpin.begin(), fpin.begin() + slashPos + 1);
|
||||||
|
auto keyFile = NewFileIO(dir + _SYS_STR("../code/htk.bin"))->beginReadStream();
|
||||||
|
if (!keyFile)
|
||||||
|
keyFile = NewFileIO(dir + _SYS_STR("htk.bin"))->beginReadStream();
|
||||||
|
if (!keyFile) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}../code/htk.bin' or '{}htk.bin'")), dir, dir);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keyFile->read(key, 16) != 16) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to read from '{}../code/htk.bin' or '{}htk.bin'")), dir, dir);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load header from first file */
|
||||||
|
const SystemString firstPath = fmt::format(FMT_STRING(_SYS_STR("{}hif_{:06}.nfs")), dir, 0);
|
||||||
|
files.push_back(NewFileIO(firstPath));
|
||||||
|
auto rs = files.back()->beginReadStream();
|
||||||
|
if (!rs) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("'{}' does not exist")), firstPath);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rs->read(&nfsHead, 0x200) != 0x200) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to read header from '{}'")), firstPath);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (std::memcmp(&nfsHead.magic, "EGGS", 4)) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Invalid magic in '{}'")), firstPath);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nfsHead.lbaRangeCount = SBig(nfsHead.lbaRangeCount);
|
||||||
|
for (uint32_t i = 0; i < nfsHead.lbaRangeCount; ++i) {
|
||||||
|
auto& range = nfsHead.lbaRanges[i];
|
||||||
|
range.startBlock = SBig(range.startBlock);
|
||||||
|
range.numBlocks = SBig(range.numBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure remaining files exist */
|
||||||
|
const uint32_t numFiles = calculateNumFiles();
|
||||||
|
files.reserve(numFiles);
|
||||||
|
for (uint32_t i = 1; i < numFiles; ++i) {
|
||||||
|
SystemString path = fmt::format(FMT_STRING(_SYS_STR("{}hif_{:06}.nfs")), dir, i);
|
||||||
|
files.push_back(NewFileIO(path));
|
||||||
|
if (!files.back()->exists()) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("'{}' does not exist")), path);
|
||||||
|
err = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReadStream : public IReadStream {
|
||||||
|
friend class DiscIONFS;
|
||||||
|
const DiscIONFS& m_parent;
|
||||||
|
std::unique_ptr<IReadStream> m_rs;
|
||||||
|
std::unique_ptr<IAES> m_aes;
|
||||||
|
|
||||||
|
/* Physical address - all UINT32_MAX indicates logical zero block */
|
||||||
|
DiscIONFS::FBO m_physAddr;
|
||||||
|
|
||||||
|
/* Logical address */
|
||||||
|
uint64_t m_offset;
|
||||||
|
|
||||||
|
/* Active file stream and its offset as set in the system.
|
||||||
|
* Block is typically one ahead of the presently decrypted block. */
|
||||||
|
uint32_t m_curFile = UINT32_MAX;
|
||||||
|
uint32_t m_curBlock = UINT32_MAX;
|
||||||
|
|
||||||
|
ReadStream(const DiscIONFS& parent, uint64_t offset, bool& err)
|
||||||
|
: m_parent(parent), m_aes(NewAES()),
|
||||||
|
m_physAddr({UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX}), m_offset(offset) {
|
||||||
|
m_aes->setKey(m_parent.key);
|
||||||
|
setLogicalAddr(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t m_encBuf[0x8000] = {};
|
||||||
|
uint8_t m_decBuf[0x8000] = {};
|
||||||
|
|
||||||
|
void setCurFile(uint32_t curFile) {
|
||||||
|
if (curFile >= m_parent.files.size()) {
|
||||||
|
LogModule.report(logvisor::Error, FMT_STRING("Out of bounds NFS file access"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_curFile = curFile;
|
||||||
|
m_curBlock = UINT32_MAX;
|
||||||
|
m_rs = m_parent.files[m_curFile]->beginReadStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCurBlock(uint32_t curBlock) {
|
||||||
|
m_curBlock = curBlock;
|
||||||
|
m_rs->seek(m_curBlock * 0x8000 + 0x200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPhysicalAddr(DiscIONFS::FBO physAddr) {
|
||||||
|
/* If we're just changing the offset, nothing else needs to be done */
|
||||||
|
if (m_physAddr.file == physAddr.file && m_physAddr.block == physAddr.block) {
|
||||||
|
m_physAddr.offset = physAddr.offset;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_physAddr = physAddr;
|
||||||
|
|
||||||
|
/* Set logical zero block */
|
||||||
|
if (m_physAddr.file == UINT32_MAX) {
|
||||||
|
memset(m_decBuf, 0, 0x8000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make necessary file and block current with system */
|
||||||
|
if (m_physAddr.file != m_curFile)
|
||||||
|
setCurFile(m_physAddr.file);
|
||||||
|
if (m_physAddr.block != m_curBlock)
|
||||||
|
setCurBlock(m_physAddr.block);
|
||||||
|
|
||||||
|
/* Read block, handling 0x200 overlap case */
|
||||||
|
if (m_physAddr.block == 7999) {
|
||||||
|
m_rs->read(m_encBuf, 0x7E00);
|
||||||
|
setCurFile(m_curFile + 1);
|
||||||
|
m_rs->read(m_encBuf + 0x7E00, 0x200);
|
||||||
|
m_curBlock = 0;
|
||||||
|
} else {
|
||||||
|
m_rs->read(m_encBuf, 0x8000);
|
||||||
|
++m_curBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrypt */
|
||||||
|
const uint32_t ivBuf[] = {0, 0, 0, SBig(m_physAddr.lblock)};
|
||||||
|
m_aes->decrypt((const uint8_t*)ivBuf, m_encBuf, m_decBuf, 0x8000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLogicalAddr(uint64_t addr) { setPhysicalAddr(m_parent.logicalToFBO(m_offset)); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint64_t read(void* buf, uint64_t length) override {
|
||||||
|
uint64_t rem = length;
|
||||||
|
uint8_t* dst = (uint8_t*)buf;
|
||||||
|
|
||||||
|
/* Perform reads on block boundaries */
|
||||||
|
while (rem) {
|
||||||
|
uint64_t readSize = rem;
|
||||||
|
uint32_t blockOffset = (m_physAddr.offset == UINT32_MAX) ? 0 : m_physAddr.offset;
|
||||||
|
if (readSize + blockOffset > 0x8000)
|
||||||
|
readSize = 0x8000 - blockOffset;
|
||||||
|
|
||||||
|
memmove(dst, m_decBuf + blockOffset, readSize);
|
||||||
|
dst += readSize;
|
||||||
|
rem -= readSize;
|
||||||
|
m_offset += readSize;
|
||||||
|
setLogicalAddr(m_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst - (uint8_t*)buf;
|
||||||
|
}
|
||||||
|
uint64_t position() const override { return m_offset; }
|
||||||
|
void seek(int64_t offset, int whence) override {
|
||||||
|
if (whence == SEEK_SET)
|
||||||
|
m_offset = offset;
|
||||||
|
else if (whence == SEEK_CUR)
|
||||||
|
m_offset += offset;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
setLogicalAddr(m_offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
|
||||||
|
bool err = false;
|
||||||
|
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, offset, err));
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override { return {}; }
|
||||||
|
|
||||||
|
bool hasWiiCrypto() const override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IDiscIO> NewDiscIONFS(SystemStringView path) {
|
||||||
|
bool err = false;
|
||||||
|
auto ret = std::make_unique<DiscIONFS>(path, err);
|
||||||
|
if (err)
|
||||||
|
return {};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ static uint8_t size_to_shift(uint32_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DiscIOWBFS : public IDiscIO {
|
class DiscIOWBFS : public IDiscIO {
|
||||||
SystemString filepath;
|
std::unique_ptr<IFileIO> m_fio;
|
||||||
|
|
||||||
struct WBFSHead {
|
struct WBFSHead {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -76,24 +76,23 @@ class DiscIOWBFS : public IDiscIO {
|
|||||||
off *= 512ULL;
|
off *= 512ULL;
|
||||||
rs.seek(off, SEEK_SET);
|
rs.seek(off, SEEK_SET);
|
||||||
if (rs.read(buf, count * 512ULL) != count * 512ULL) {
|
if (rs.read(buf, count * 512ULL) != count * 512ULL) {
|
||||||
LogModule.report(logvisor::Error, fmt("error reading disc"));
|
LogModule.report(logvisor::Error, FMT_STRING("error reading disc"));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DiscIOWBFS(SystemStringView fpin) : filepath(fpin) {
|
DiscIOWBFS(SystemStringView fpin) : m_fio(NewFileIO(fpin)) {
|
||||||
/* Temporary file handle to read LBA table */
|
/* Temporary file handle to read LBA table */
|
||||||
std::unique_ptr<IFileIO> fio = NewFileIO(filepath);
|
std::unique_ptr<IFileIO::IReadStream> rs = m_fio->beginReadStream();
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
|
||||||
if (!rs)
|
if (!rs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WBFS* p = &wbfs;
|
WBFS* p = &wbfs;
|
||||||
WBFSHead tmpHead;
|
WBFSHead tmpHead;
|
||||||
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) {
|
if (rs->read(&tmpHead, sizeof(tmpHead)) != sizeof(tmpHead)) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to read WBFS head"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to read WBFS head"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
|
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
|
||||||
@@ -103,7 +102,7 @@ public:
|
|||||||
WBFSHead* head = (WBFSHead*)wbfsHead.get();
|
WBFSHead* head = (WBFSHead*)wbfsHead.get();
|
||||||
rs->seek(0, SEEK_SET);
|
rs->seek(0, SEEK_SET);
|
||||||
if (rs->read(head, hd_sector_size) != hd_sector_size) {
|
if (rs->read(head, hd_sector_size) != hd_sector_size) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to read WBFS head"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to read WBFS head"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,11 +115,11 @@ public:
|
|||||||
if (_wbfsReadSector(*rs, p->part_lba, 1, head))
|
if (_wbfsReadSector(*rs, p->part_lba, 1, head))
|
||||||
return;
|
return;
|
||||||
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) {
|
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) {
|
||||||
LogModule.report(logvisor::Error, fmt("hd sector size doesn't match"));
|
LogModule.report(logvisor::Error, FMT_STRING("hd sector size doesn't match"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
|
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
|
||||||
LogModule.report(logvisor::Error, fmt("hd num sector doesn't match"));
|
LogModule.report(logvisor::Error, FMT_STRING("hd num sector doesn't match"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
p->hd_sec_sz = 1 << head->hd_sec_sz_s;
|
p->hd_sec_sz = 1 << head->hd_sec_sz_s;
|
||||||
@@ -148,7 +147,7 @@ public:
|
|||||||
if (head->disc_table[0]) {
|
if (head->disc_table[0]) {
|
||||||
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
|
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
|
||||||
if (!wbfsDiscInfo) {
|
if (!wbfsDiscInfo) {
|
||||||
LogModule.report(logvisor::Error, fmt("allocating memory"));
|
LogModule.report(logvisor::Error, FMT_STRING("allocating memory"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_wbfsReadSector(*rs, p->part_lba + 1, disc_info_sz_lba, wbfsDiscInfo.get()))
|
if (_wbfsReadSector(*rs, p->part_lba + 1, disc_info_sz_lba, wbfsDiscInfo.get()))
|
||||||
@@ -265,16 +264,15 @@ public:
|
|||||||
|
|
||||||
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
|
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const override {
|
||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, NewFileIO(filepath)->beginReadStream(), offset, err));
|
auto ret = std::unique_ptr<IReadStream>(new ReadStream(*this, m_fio->beginReadStream(), offset, err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override { return nullptr; }
|
std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const override { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<IDiscIO> NewDiscIOWBFS(SystemStringView path) { return std::make_unique<DiscIOWBFS>(path); }
|
std::unique_ptr<IDiscIO> NewDiscIOWBFS(SystemStringView path) { return std::make_unique<DiscIOWBFS>(path); }
|
||||||
|
|||||||
@@ -332,14 +332,22 @@ public:
|
|||||||
uint8_t m_decBuf[0x7c00];
|
uint8_t m_decBuf[0x7c00];
|
||||||
|
|
||||||
void decryptBlock() {
|
void decryptBlock() {
|
||||||
m_dio->read(m_encBuf, 0x8000);
|
if (m_aes) {
|
||||||
m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00);
|
m_dio->read(m_encBuf, 0x8000);
|
||||||
|
m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00);
|
||||||
|
} else {
|
||||||
|
m_dio->seek(0x400, SEEK_CUR);
|
||||||
|
m_dio->read(m_decBuf, 0x7c00);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset, bool& err)
|
PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset, bool& err)
|
||||||
: m_aes(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) {
|
: m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) {
|
||||||
m_aes->setKey(parent.m_decKey);
|
if (m_parent.m_parent.getDiscIO().hasWiiCrypto()) {
|
||||||
|
m_aes = NewAES();
|
||||||
|
m_aes->setKey(parent.m_decKey);
|
||||||
|
}
|
||||||
size_t block = m_offset / 0x7c00;
|
size_t block = m_offset / 0x7c00;
|
||||||
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
|
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
|
||||||
if (!m_dio) {
|
if (!m_dio) {
|
||||||
@@ -365,27 +373,25 @@ public:
|
|||||||
}
|
}
|
||||||
uint64_t position() const override { return m_offset; }
|
uint64_t position() const override { return m_offset; }
|
||||||
uint64_t read(void* buf, uint64_t length) override {
|
uint64_t read(void* buf, uint64_t length) override {
|
||||||
size_t block = m_offset / 0x7c00;
|
auto blockAndRemOff = nod::div(m_offset, uint64_t(0x7c00));
|
||||||
size_t cacheOffset = m_offset % 0x7c00;
|
|
||||||
uint64_t cacheSize;
|
|
||||||
uint64_t rem = length;
|
uint64_t rem = length;
|
||||||
uint8_t* dst = (uint8_t*)buf;
|
uint8_t* dst = (uint8_t*)buf;
|
||||||
|
|
||||||
while (rem) {
|
while (rem) {
|
||||||
if (block != m_curBlock) {
|
if (blockAndRemOff.quot != m_curBlock) {
|
||||||
decryptBlock();
|
decryptBlock();
|
||||||
m_curBlock = block;
|
m_curBlock = blockAndRemOff.quot;
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheSize = rem;
|
uint64_t cacheSize = rem;
|
||||||
if (cacheSize + cacheOffset > 0x7c00)
|
if (cacheSize + blockAndRemOff.rem > 0x7c00)
|
||||||
cacheSize = 0x7c00 - cacheOffset;
|
cacheSize = 0x7c00 - blockAndRemOff.rem;
|
||||||
|
|
||||||
memmove(dst, m_decBuf + cacheOffset, cacheSize);
|
memmove(dst, m_decBuf + blockAndRemOff.rem, cacheSize);
|
||||||
dst += cacheSize;
|
dst += cacheSize;
|
||||||
rem -= cacheSize;
|
rem -= cacheSize;
|
||||||
cacheOffset = 0;
|
blockAndRemOff.rem = 0;
|
||||||
++block;
|
++blockAndRemOff.quot;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_offset += length;
|
m_offset += length;
|
||||||
@@ -415,7 +421,7 @@ public:
|
|||||||
|
|
||||||
uint32_t h3;
|
uint32_t h3;
|
||||||
if (rs->read(&h3, 4) != 4) {
|
if (rs->read(&h3, 4) != 4) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to read H3 offset apploader")));
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to read H3 offset apploader")));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
h3 = SBig(h3);
|
h3 = SBig(h3);
|
||||||
@@ -533,7 +539,7 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err) : DiscBase(std::move
|
|||||||
kind = part.partType;
|
kind = part.partType;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LogModule.report(logvisor::Error, fmt("invalid partition type {}"), part.partType);
|
LogModule.report(logvisor::Error, FMT_STRING("invalid partition type {}"), part.partType);
|
||||||
err = true;
|
err = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -551,7 +557,7 @@ bool DiscWii::extractDiscHeaderFiles(SystemStringView basePath, const Extraction
|
|||||||
SystemString basePathStr(basePath);
|
SystemString basePathStr(basePath);
|
||||||
|
|
||||||
if (Mkdir((basePathStr + _SYS_STR("/disc")).c_str(), 0755) && errno != EEXIST) {
|
if (Mkdir((basePathStr + _SYS_STR("/disc")).c_str(), 0755) && errno != EEXIST) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to mkdir '{}/disc'")), basePathStr);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to mkdir '{}/disc'")), basePathStr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,7 +675,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_fio->write(m_buf, 0x200000) != 0x200000) {
|
if (m_fio->write(m_buf, 0x200000) != 0x200000) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to write full disc group"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to write full disc group"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -678,7 +684,7 @@ public:
|
|||||||
PartWriteStream(PartitionBuilderWii& parent, uint64_t baseOffset, uint64_t offset, bool& err)
|
PartWriteStream(PartitionBuilderWii& parent, uint64_t baseOffset, uint64_t offset, bool& err)
|
||||||
: m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) {
|
: m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) {
|
||||||
if (offset % 0x1F0000) {
|
if (offset % 0x1F0000) {
|
||||||
LogModule.report(logvisor::Error, fmt("partition write stream MUST begin on 0x1F0000-aligned boundary"));
|
LogModule.report(logvisor::Error, FMT_STRING("partition write stream MUST begin on 0x1F0000-aligned boundary"));
|
||||||
err = true;
|
err = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -748,13 +754,13 @@ public:
|
|||||||
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override {
|
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws) override {
|
||||||
reqSz = ROUND_UP_32(reqSz);
|
reqSz = ROUND_UP_32(reqSz);
|
||||||
if (m_curUser + reqSz >= 0x1FB450000) {
|
if (m_curUser + reqSz >= 0x1FB450000) {
|
||||||
LogModule.report(logvisor::Error, fmt("partition exceeds maximum single-partition capacity"));
|
LogModule.report(logvisor::Error, FMT_STRING("partition exceeds maximum single-partition capacity"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
uint64_t ret = m_curUser;
|
uint64_t ret = m_curUser;
|
||||||
PartWriteStream& cws = static_cast<PartWriteStream&>(ws);
|
PartWriteStream& cws = static_cast<PartWriteStream&>(ws);
|
||||||
if (cws.m_offset > ret) {
|
if (cws.m_offset > ret) {
|
||||||
LogModule.report(logvisor::Error, fmt("partition overwrite error"));
|
LogModule.report(logvisor::Error, FMT_STRING("partition overwrite error"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (cws.m_offset < ret)
|
while (cws.m_offset < ret)
|
||||||
@@ -832,7 +838,7 @@ public:
|
|||||||
fstSz = ROUND_UP_32(fstSz);
|
fstSz = ROUND_UP_32(fstSz);
|
||||||
|
|
||||||
if (fstOff + fstSz >= 0x1F0000) {
|
if (fstOff + fstSz >= 0x1F0000) {
|
||||||
LogModule.report(logvisor::Error, fmt("FST flows into user area (one or the other is too big)"));
|
LogModule.report(logvisor::Error, FMT_STRING("FST flows into user area (one or the other is too big)"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,7 +854,7 @@ public:
|
|||||||
|
|
||||||
size_t fstOffRel = fstOff - 0x2440;
|
size_t fstOffRel = fstOff - 0x2440;
|
||||||
if (xferSz > fstOffRel) {
|
if (xferSz > fstOffRel) {
|
||||||
LogModule.report(logvisor::Error, fmt("apploader unexpectedly flows into FST"));
|
LogModule.report(logvisor::Error, FMT_STRING("apploader unexpectedly flows into FST"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < fstOffRel - xferSz; ++i)
|
for (size_t i = 0; i < fstOffRel - xferSz; ++i)
|
||||||
@@ -932,7 +938,7 @@ public:
|
|||||||
SystemString ticketIn = basePath + _SYS_STR("/ticket.bin");
|
SystemString ticketIn = basePath + _SYS_STR("/ticket.bin");
|
||||||
Sstat theStat;
|
Sstat theStat;
|
||||||
if (Stat(ticketIn.c_str(), &theStat)) {
|
if (Stat(ticketIn.c_str(), &theStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), ticketIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), ticketIn);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -940,7 +946,7 @@ public:
|
|||||||
SystemString tmdIn = basePath + _SYS_STR("/tmd.bin");
|
SystemString tmdIn = basePath + _SYS_STR("/tmd.bin");
|
||||||
Sstat tmdStat;
|
Sstat tmdStat;
|
||||||
if (Stat(tmdIn.c_str(), &tmdStat)) {
|
if (Stat(tmdIn.c_str(), &tmdStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), tmdIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), tmdIn);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -948,7 +954,7 @@ public:
|
|||||||
SystemString certIn = basePath + _SYS_STR("/cert.bin");
|
SystemString certIn = basePath + _SYS_STR("/cert.bin");
|
||||||
Sstat certStat;
|
Sstat certStat;
|
||||||
if (Stat(certIn.c_str(), &certStat)) {
|
if (Stat(certIn.c_str(), &certStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), certIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), certIn);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -956,7 +962,7 @@ public:
|
|||||||
SystemString apploaderIn = basePath + _SYS_STR("/sys/apploader.img");
|
SystemString apploaderIn = basePath + _SYS_STR("/sys/apploader.img");
|
||||||
Sstat apploaderStat;
|
Sstat apploaderStat;
|
||||||
if (Stat(apploaderIn.c_str(), &apploaderStat)) {
|
if (Stat(apploaderIn.c_str(), &apploaderStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), apploaderIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), apploaderIn);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -964,7 +970,7 @@ public:
|
|||||||
SystemString bootIn = basePath + _SYS_STR("/sys/boot.bin");
|
SystemString bootIn = basePath + _SYS_STR("/sys/boot.bin");
|
||||||
Sstat bootStat;
|
Sstat bootStat;
|
||||||
if (Stat(bootIn.c_str(), &bootStat)) {
|
if (Stat(bootIn.c_str(), &bootStat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), bootIn);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), bootIn);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,7 +978,7 @@ public:
|
|||||||
SystemString bi2In = basePath + _SYS_STR("/sys/bi2.bin");
|
SystemString bi2In = basePath + _SYS_STR("/sys/bi2.bin");
|
||||||
Sstat bi2Stat;
|
Sstat bi2Stat;
|
||||||
if (Stat(bi2In.c_str(), &bi2Stat)) {
|
if (Stat(bi2In.c_str(), &bi2Stat)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to stat {}")), bi2In);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to stat {}")), bi2In);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1065,7 +1071,7 @@ public:
|
|||||||
cws.write(buf, rdSz);
|
cws.write(buf, rdSz);
|
||||||
xferSz += rdSz;
|
xferSz += rdSz;
|
||||||
if (0x2440 + xferSz >= 0x1F0000) {
|
if (0x2440 + xferSz >= 0x1F0000) {
|
||||||
LogModule.report(logvisor::Error, fmt("apploader flows into user area (one or the other is too big)"));
|
LogModule.report(logvisor::Error, FMT_STRING("apploader flows into user area (one or the other is too big)"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
||||||
@@ -1123,7 +1129,7 @@ public:
|
|||||||
cws.write(apploaderBuf.get(), apploaderSz);
|
cws.write(apploaderBuf.get(), apploaderSz);
|
||||||
xferSz += apploaderSz;
|
xferSz += apploaderSz;
|
||||||
if (0x2440 + xferSz >= 0x1F0000) {
|
if (0x2440 + xferSz >= 0x1F0000) {
|
||||||
LogModule.report(logvisor::Error, fmt("apploader flows into user area (one or the other is too big)"));
|
LogModule.report(logvisor::Error, FMT_STRING("apploader flows into user area (one or the other is too big)"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
||||||
@@ -1147,7 +1153,7 @@ EBuildResult DiscBuilderWii::buildFromDirectory(SystemStringView dirIn) {
|
|||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
|
|
||||||
if (!CheckFreeSpace(m_outPath.c_str(), m_discCapacity)) {
|
if (!CheckFreeSpace(m_outPath.c_str(), m_discCapacity)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("not enough free disk space for {}")), m_outPath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("not enough free disk space for {}")), m_outPath);
|
||||||
return EBuildResult::DiskFull;
|
return EBuildResult::DiskFull;
|
||||||
}
|
}
|
||||||
m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
m_progressCB(getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
||||||
@@ -1166,7 +1172,7 @@ EBuildResult DiscBuilderWii::buildFromDirectory(SystemStringView dirIn) {
|
|||||||
if (filledSz == UINT64_MAX)
|
if (filledSz == UINT64_MAX)
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
else if (filledSz >= uint64_t(m_discCapacity)) {
|
else if (filledSz >= uint64_t(m_discCapacity)) {
|
||||||
LogModule.report(logvisor::Error, fmt("data partition exceeds disc capacity"));
|
LogModule.report(logvisor::Error, FMT_STRING("data partition exceeds disc capacity"));
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1233,14 +1239,14 @@ std::optional<uint64_t> DiscBuilderWii::CalculateTotalSizeRequired(SystemStringV
|
|||||||
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true);
|
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn, PartitionKind::Data, true);
|
||||||
if (!sz)
|
if (!sz)
|
||||||
return sz;
|
return sz;
|
||||||
auto szDiv = std::lldiv(*sz, 0x1F0000);
|
auto szDiv = nod::div(*sz, uint64_t(0x1F0000));
|
||||||
if (szDiv.rem)
|
if (szDiv.rem)
|
||||||
++szDiv.quot;
|
++szDiv.quot;
|
||||||
sz = szDiv.quot * 0x200000;
|
sz = szDiv.quot * 0x200000;
|
||||||
*sz += 0x200000;
|
*sz += 0x200000;
|
||||||
dualLayer = (sz > 0x118240000);
|
dualLayer = (sz > 0x118240000);
|
||||||
if (sz > 0x1FB4E0000) {
|
if (sz > 0x1FB4E0000) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x1FB4E0000);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x1FB4E0000);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return sz;
|
return sz;
|
||||||
@@ -1261,7 +1267,7 @@ EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) {
|
|||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
|
|
||||||
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), m_builder.m_discCapacity)) {
|
if (!CheckFreeSpace(m_builder.m_outPath.c_str(), m_builder.m_discCapacity)) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("not enough free disk space for {}")), m_builder.m_outPath);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("not enough free disk space for {}")), m_builder.m_outPath);
|
||||||
return EBuildResult::DiskFull;
|
return EBuildResult::DiskFull;
|
||||||
}
|
}
|
||||||
m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
m_builder.m_progressCB(m_builder.getProgressFactor(), _SYS_STR("Preallocating image"), -1);
|
||||||
@@ -1280,7 +1286,7 @@ EBuildResult DiscMergerWii::mergeFromDirectory(SystemStringView dirIn) {
|
|||||||
if (filledSz == UINT64_MAX)
|
if (filledSz == UINT64_MAX)
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
else if (filledSz >= uint64_t(m_builder.m_discCapacity)) {
|
else if (filledSz >= uint64_t(m_builder.m_discCapacity)) {
|
||||||
LogModule.report(logvisor::Error, fmt("data partition exceeds disc capacity"));
|
LogModule.report(logvisor::Error, FMT_STRING("data partition exceeds disc capacity"));
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1340,14 +1346,14 @@ std::optional<uint64_t> DiscMergerWii::CalculateTotalSizeRequired(DiscWii& sourc
|
|||||||
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn);
|
std::optional<uint64_t> sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(sourceDisc.getDataPartition(), dirIn);
|
||||||
if (!sz)
|
if (!sz)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
auto szDiv = std::lldiv(*sz, 0x1F0000);
|
auto szDiv = nod::div(*sz, uint64_t(0x1F0000));
|
||||||
if (szDiv.rem)
|
if (szDiv.rem)
|
||||||
++szDiv.quot;
|
++szDiv.quot;
|
||||||
sz = szDiv.quot * 0x200000;
|
sz = szDiv.quot * 0x200000;
|
||||||
*sz += 0x200000;
|
*sz += 0x200000;
|
||||||
dualLayer = (sz > 0x118240000);
|
dualLayer = (sz > 0x118240000);
|
||||||
if (sz > 0x1FB4E0000) {
|
if (sz > 0x1FB4E0000) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x1FB4E0000);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("disc capacity exceeded [{} / {}]")), *sz, 0x1FB4E0000);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return sz;
|
return sz;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
WriteStream(SystemStringView path, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) {
|
WriteStream(SystemStringView path, int64_t maxWriteSize, bool& err) : m_maxWriteSize(maxWriteSize) {
|
||||||
fp = Fopen(path.data(), _SYS_STR("wb"));
|
fp = Fopen(path.data(), _SYS_STR("wb"));
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open '{}' for writing")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open '{}' for writing")), path);
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,14 +57,14 @@ public:
|
|||||||
FSeek(fp, offset, SEEK_SET);
|
FSeek(fp, offset, SEEK_SET);
|
||||||
return;
|
return;
|
||||||
FailLoc:
|
FailLoc:
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open '{}' for writing")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open '{}' for writing")), path);
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
~WriteStream() override { fclose(fp); }
|
~WriteStream() override { fclose(fp); }
|
||||||
uint64_t write(const void* buf, uint64_t length) override {
|
uint64_t write(const void* buf, uint64_t length) override {
|
||||||
if (m_maxWriteSize >= 0) {
|
if (m_maxWriteSize >= 0) {
|
||||||
if (FTell(fp) + length > m_maxWriteSize) {
|
if (FTell(fp) + length > m_maxWriteSize) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("write operation exceeds file's {}-byte limit")),
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("write operation exceeds file's {}-byte limit")),
|
||||||
m_maxWriteSize);
|
m_maxWriteSize);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -77,9 +77,8 @@ public:
|
|||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, err));
|
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, m_maxWriteSize, err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -88,9 +87,8 @@ public:
|
|||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, err));
|
auto ret = std::unique_ptr<IWriteStream>(new WriteStream(m_path, offset, m_maxWriteSize, err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -101,7 +99,7 @@ public:
|
|||||||
fp = Fopen(path.data(), _SYS_STR("rb"));
|
fp = Fopen(path.data(), _SYS_STR("rb"));
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
err = true;
|
err = true;
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open '{}' for reading")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open '{}' for reading")), path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReadStream(SystemStringView path, uint64_t offset, bool& err) : ReadStream(path, err) {
|
ReadStream(SystemStringView path, uint64_t offset, bool& err) : ReadStream(path, err) {
|
||||||
@@ -119,11 +117,11 @@ public:
|
|||||||
while (length) {
|
while (length) {
|
||||||
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
||||||
if (read(buf, thisSz) != thisSz) {
|
if (read(buf, thisSz) != thisSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to read enough from file"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from file"));
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
if (discio.write(buf, thisSz) != thisSz) {
|
if (discio.write(buf, thisSz) != thisSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to write enough to disc"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to write enough to disc"));
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
@@ -137,9 +135,8 @@ public:
|
|||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, err));
|
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -148,9 +145,8 @@ public:
|
|||||||
bool err = false;
|
bool err = false;
|
||||||
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, err));
|
auto ret = std::unique_ptr<IReadStream>(new ReadStream(m_path, offset, err));
|
||||||
|
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr);
|
fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr);
|
||||||
#endif
|
#endif
|
||||||
if (fp == INVALID_HANDLE_VALUE) {
|
if (fp == INVALID_HANDLE_VALUE) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open '{}' for writing")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open '{}' for writing")), path);
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, OPEN_ALWAYS, nullptr);
|
fp = CreateFile2(path.data(), GENERIC_WRITE, FILE_SHARE_WRITE, OPEN_ALWAYS, nullptr);
|
||||||
#endif
|
#endif
|
||||||
if (fp == INVALID_HANDLE_VALUE) {
|
if (fp == INVALID_HANDLE_VALUE) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open '{}' for writing")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open '{}' for writing")), path);
|
||||||
err = true;
|
err = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ public:
|
|||||||
LARGE_INTEGER res;
|
LARGE_INTEGER res;
|
||||||
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
|
SetFilePointerEx(fp, li, &res, FILE_CURRENT);
|
||||||
if (res.QuadPart + int64_t(length) > m_maxWriteSize) {
|
if (res.QuadPart + int64_t(length) > m_maxWriteSize) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("write operation exceeds file's {}-byte limit")),
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("write operation exceeds file's {}-byte limit")),
|
||||||
m_maxWriteSize);
|
m_maxWriteSize);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
if (fp == INVALID_HANDLE_VALUE) {
|
if (fp == INVALID_HANDLE_VALUE) {
|
||||||
err = true;
|
err = true;
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("unable to open '{}' for reading")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to open '{}' for reading")), path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReadStream(SystemStringView path, uint64_t offset, bool& err) : ReadStream(path, err) {
|
ReadStream(SystemStringView path, uint64_t offset, bool& err) : ReadStream(path, err) {
|
||||||
@@ -163,11 +163,11 @@ public:
|
|||||||
while (length) {
|
while (length) {
|
||||||
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
uint64_t thisSz = nod::min(uint64_t(0x7c00), length);
|
||||||
if (read(buf, thisSz) != thisSz) {
|
if (read(buf, thisSz) != thisSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to read enough from file"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to read enough from file"));
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
if (discio.write(buf, thisSz) != thisSz) {
|
if (discio.write(buf, thisSz) != thisSz) {
|
||||||
LogModule.report(logvisor::Error, fmt("unable to write enough to disc"));
|
LogModule.report(logvisor::Error, FMT_STRING("unable to write enough to disc"));
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
length -= thisSz;
|
length -= thisSz;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#else
|
#elif __x86_64__
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
25
lib/nod.cpp
25
lib/nod.cpp
@@ -12,12 +12,13 @@ logvisor::Module LogModule("nod");
|
|||||||
|
|
||||||
std::unique_ptr<IDiscIO> NewDiscIOISO(SystemStringView path);
|
std::unique_ptr<IDiscIO> NewDiscIOISO(SystemStringView path);
|
||||||
std::unique_ptr<IDiscIO> NewDiscIOWBFS(SystemStringView path);
|
std::unique_ptr<IDiscIO> NewDiscIOWBFS(SystemStringView path);
|
||||||
|
std::unique_ptr<IDiscIO> NewDiscIONFS(SystemStringView path);
|
||||||
|
|
||||||
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii) {
|
std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii) {
|
||||||
/* Temporary file handle to determine image type */
|
/* Temporary file handle to determine image type */
|
||||||
std::unique_ptr<IFileIO> fio = NewFileIO(path);
|
std::unique_ptr<IFileIO> fio = NewFileIO(path);
|
||||||
if (!fio->exists()) {
|
if (!fio->exists()) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("Unable to open '{}'")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to open '{}'")), path);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
std::unique_ptr<IFileIO::IReadStream> rs = fio->beginReadStream();
|
||||||
@@ -28,13 +29,21 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
|
|||||||
std::unique_ptr<IDiscIO> discIO;
|
std::unique_ptr<IDiscIO> discIO;
|
||||||
uint32_t magic = 0;
|
uint32_t magic = 0;
|
||||||
if (rs->read(&magic, 4) != 4) {
|
if (rs->read(&magic, 4) != 4) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("Unable to read magic from '{}'")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to read magic from '{}'")), path);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using SignedSize = std::make_signed<SystemString::size_type>::type;
|
||||||
|
const auto dotPos = SignedSize(path.rfind(_SYS_STR('.')));
|
||||||
|
const auto slashPos = SignedSize(path.find_last_of(_SYS_STR("/\\")));
|
||||||
if (magic == nod::SBig((uint32_t)'WBFS')) {
|
if (magic == nod::SBig((uint32_t)'WBFS')) {
|
||||||
discIO = NewDiscIOWBFS(path);
|
discIO = NewDiscIOWBFS(path);
|
||||||
isWii = true;
|
isWii = true;
|
||||||
|
} else if (path.size() > 4 && dotPos != -1 && dotPos > slashPos &&
|
||||||
|
!path.compare(slashPos + 1, 4, _SYS_STR("hif_")) &&
|
||||||
|
!path.compare(dotPos, path.size() - dotPos, _SYS_STR(".nfs"))) {
|
||||||
|
discIO = NewDiscIONFS(path);
|
||||||
|
isWii = true;
|
||||||
} else {
|
} else {
|
||||||
rs->seek(0x18, SEEK_SET);
|
rs->seek(0x18, SEEK_SET);
|
||||||
rs->read(&magic, 4);
|
rs->read(&magic, 4);
|
||||||
@@ -51,7 +60,7 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!discIO) {
|
if (!discIO) {
|
||||||
LogModule.report(logvisor::Error, fmt(_SYS_STR("'{}' is not a valid image")), path);
|
LogModule.report(logvisor::Error, FMT_STRING(_SYS_STR("'{}' is not a valid image")), path);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,16 +68,14 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(SystemStringView path, bool& isWii)
|
|||||||
std::unique_ptr<DiscBase> ret;
|
std::unique_ptr<DiscBase> ret;
|
||||||
if (isWii) {
|
if (isWii) {
|
||||||
ret = std::make_unique<DiscWii>(std::move(discIO), err);
|
ret = std::make_unique<DiscWii>(std::move(discIO), err);
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = std::make_unique<DiscGCN>(std::move(discIO), err);
|
ret = std::make_unique<DiscGCN>(std::move(discIO), err);
|
||||||
if (err) {
|
if (err)
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# endif
|
# endif
|
||||||
#else // ! defined __LITTLE_ENDIAN__
|
#else // ! defined __LITTLE_ENDIAN__
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
# include <endian.h> // machine/endian.h
|
# include <machine/endian.h>
|
||||||
#endif
|
#endif
|
||||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
# define SHA_BIG_ENDIAN
|
# define SHA_BIG_ENDIAN
|
||||||
|
|||||||
2
logvisor
2
logvisor
Submodule logvisor updated: 8c2e711362...81fb4e4c2d
Reference in New Issue
Block a user