nod/include/nod/Util.hpp

260 lines
7.0 KiB
C++
Raw Normal View History

2018-10-06 20:39:24 -07:00
#pragma once
2015-06-27 22:43:53 -07:00
2015-07-13 17:38:16 -07:00
#if _WIN32 && UNICODE
#include <cwctype>
2015-07-13 17:38:16 -07:00
#include <direct.h>
2015-08-31 14:25:55 -07:00
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
2015-09-26 21:35:00 -07:00
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <nowide/stackstring.hpp>
2015-09-26 21:35:00 -07:00
#include <windows.h>
2017-12-05 19:23:58 -08:00
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
#define WINDOWS_STORE 1
#else
#define WINDOWS_STORE 0
#endif
2015-07-13 17:38:16 -07:00
#else
#include <cctype>
2017-12-28 23:57:54 -08:00
#include <cerrno>
#include <sys/file.h>
2016-01-24 18:00:02 -08:00
#include <sys/param.h>
#include <sys/statvfs.h>
#include <unistd.h>
2015-07-13 17:38:16 -07:00
#endif
#include <sys/stat.h>
#include <algorithm>
#include <cstring>
2015-07-02 11:33:55 -07:00
#include <string>
2017-11-12 22:18:53 -08:00
#include <string_view>
#include <logvisor/logvisor.hpp>
2016-01-23 21:06:54 -08:00
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#include <sys/stat.h>
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
2018-12-07 21:21:47 -08:00
#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
2016-01-23 21:06:54 -08:00
#endif
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
2018-12-07 21:21:47 -08:00
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
2016-01-23 21:06:54 -08:00
#endif
#if !defined(S_ISLNK)
#define S_ISLNK(m) 0
#endif
#endif
#undef min
#undef max
2015-07-02 11:33:55 -07:00
2018-12-07 21:21:47 -08:00
namespace nod {
2016-01-23 21:06:54 -08:00
/* define our own min/max to avoid MSVC BS */
2018-12-07 21:21:47 -08:00
template <typename T>
2019-11-24 15:47:48 -08:00
constexpr T min(T a, T b) {
2018-12-07 21:21:47 -08:00
return a < b ? a : b;
}
template <typename T>
2019-11-24 15:47:48 -08:00
constexpr T max(T a, T b) {
2018-12-07 21:21:47 -08:00
return a > b ? a : b;
}
2015-06-27 22:43:53 -07:00
2019-11-24 15:47:48 -08:00
/* 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;
};
2019-11-24 15:47:48 -08:00
return DivTp{a / b, a % b};
}
/* Log Module */
2016-03-04 15:04:30 -08:00
extern logvisor::Module LogModule;
2015-07-02 13:37:07 -07:00
/* filesystem char type */
#if _WIN32
static inline int Mkdir(const char* path, int) {
const nowide::wstackstring str(path);
return _wmkdir(str.get());
The Encoding Update While Nintendo's own documents claim GameCube and Wii disc file symbol tables only support 7-bit ASCII, this is far from the truth. Indeed, even some first-party Nintendo games shipped with Shift-JIS encoded file symbol tables. My guess? The locale of whatever Windows machine mastered a GameCube or Wii disc influenced how wide character strings (UCS-2) were converted to narrow character strings. To account for all possibilites, this update adds extensible multi-byte character set options to NOD-Tool. A rundown of notable changes: - "-c XXXXX" option added to set the encoding of the GameCube / Wii ISO(s) being processed. - "SystemStringConv" renamed to "DiscLocToSystemConv" - "SystemUTF8Conv" renamed to "SystemToDiscLocConv" - Help message updated with new info. - Bugfix: AddBuildName had a logic error wherein the length of the SystemString was being used instead of length of the disc locale string. This would corrupt the File Symbol Table if the disc locale string's length was greater than the SystemString's length. - Bugfix: recursiveMergeFST was not keeping track of parent indexes at all, meaning nested folders and their contents would be corrupted. I simply copied the way recursiveBuildFST did things to fix this. - Bugfix (Windows): On Windows, for some reason, Sstat was a typedef for _stat (32-bit) instead of _stat64 (64-bit). This is confounding, because untrimmed Wii ISOs will always be larger than the unsigned 32-bit integer limit (4,699,979,776 bytes vs 4,294,967,295 bytes), meaning the MergeWii errand has never worked for untrimmed ISOs on Windows. Was this never tested?? - Bugfix (Windows): Did you know Windows Command Prompt fully supports Unicode? Stdio streams are now in _O_U16TEXT mode for Windows only. Previously, attempting to print any character that could not be narrowed to your locale's encoding would either silently fail (std functions), or throw an exception (fmt functions). As a minor drawback, narrow character print functions can no longer be used when stdio is in _O_U16TEXT mode, necessitating my PR for Logvisor here: (AxioDL/logvisor#7) - ExtractionContext::progressCB now uses SystemStringView because widechar printing works correctly on Windows now. - progFunc lambda no longer throws exceptions when printing unicode because widechar printing works correctly on Windows now. - Top-level constructors and functions with a Codepage_t parameter have also signatures that default to the US-ASCII codepage. - DiscGCN constructor - DiscBuilderGCN constructor - DiscBuilderGCN::CalculateTotalSizeRequired - DiscMergerGCN constructor - DiscMergerGCN::CalculateTotalSizeRequired - DiscWii constructor - DiscBuilderWii constructor - DiscBuilderWii::CalculateTotalSizeRequired - DiscMergerWii constructor - DiscMergerWii::CalculateTotalSizeRequired - OpenDiscFromImage - Conversion between system encoding and disc locale encoding has checks in place to warn the user if string conversion goes awry.
2021-06-27 01:26:20 -07:00
}
2015-07-02 11:33:55 -07:00
using Sstat = struct ::_stat64;
static inline int Stat(const char* path, Sstat* statout) {
const nowide::wstackstring wpath(path);
return _wstat64(wpath.get(), statout);
2016-01-22 15:45:58 -08:00
}
#else
static inline int Mkdir(const char* path, mode_t mode) { return CreateDirectoryA(path, mode); }
typedef struct stat Sstat;
static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); }
#endif
static inline int StrCaseCmp(const char* str1, const char* str2) {
#ifdef _MSC_VER
return _stricmp(str1, str2);
#else
2018-12-07 21:21:47 -08:00
return strcasecmp(str1, str2);
#endif
}
2015-09-12 20:41:19 -07:00
#undef bswap16
#undef bswap32
#undef bswap64
2015-06-27 22:43:53 -07:00
/* Type-sensitive byte swappers */
template <typename T>
2018-12-07 21:21:47 -08:00
static inline T bswap16(T val) {
2015-06-27 22:43:53 -07:00
#if __GNUC__
2018-12-07 21:21:47 -08:00
return __builtin_bswap16(val);
2015-06-27 22:43:53 -07:00
#elif _WIN32
2018-12-07 21:21:47 -08:00
return _byteswap_ushort(val);
2015-06-27 22:43:53 -07:00
#else
2018-12-07 21:21:47 -08:00
return (val = (val << 8) | ((val >> 8) & 0xFF));
2015-06-27 22:43:53 -07:00
#endif
}
template <typename T>
2018-12-07 21:21:47 -08:00
static inline T bswap32(T val) {
2015-06-27 22:43:53 -07:00
#if __GNUC__
2018-12-07 21:21:47 -08:00
return __builtin_bswap32(val);
2015-06-27 22:43:53 -07:00
#elif _WIN32
2018-12-07 21:21:47 -08:00
return _byteswap_ulong(val);
2015-06-27 22:43:53 -07:00
#else
2018-12-07 21:21:47 -08:00
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val;
2015-06-27 22:43:53 -07:00
#endif
}
template <typename T>
2018-12-07 21:21:47 -08:00
static inline T bswap64(T val) {
2015-06-27 22:43:53 -07:00
#if __GNUC__
2018-12-07 21:21:47 -08:00
return __builtin_bswap64(val);
2015-06-27 22:43:53 -07:00
#elif _WIN32
2018-12-07 21:21:47 -08:00
return _byteswap_uint64(val);
2015-06-27 22:43:53 -07:00
#else
2018-12-07 21:21:47 -08:00
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
2015-06-27 22:43:53 -07:00
#endif
}
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
2018-12-07 21:21:47 -08:00
static inline int16_t SBig(int16_t val) { return bswap16(val); }
static inline uint16_t SBig(uint16_t val) { return bswap16(val); }
static inline int32_t SBig(int32_t val) { return bswap32(val); }
static inline uint32_t SBig(uint32_t val) { return bswap32(val); }
static inline int64_t SBig(int64_t val) { return bswap64(val); }
static inline uint64_t SBig(uint64_t val) { return bswap64(val); }
static inline int16_t SLittle(int16_t val) { return val; }
static inline uint16_t SLittle(uint16_t val) { return val; }
static inline int32_t SLittle(int32_t val) { return val; }
static inline uint32_t SLittle(uint32_t val) { return val; }
static inline int64_t SLittle(int64_t val) { return val; }
static inline uint64_t SLittle(uint64_t val) { return val; }
2015-06-27 22:43:53 -07:00
#else
2018-12-07 21:21:47 -08:00
static inline int16_t SLittle(int16_t val) { return bswap16(val); }
static inline uint16_t SLittle(uint16_t val) { return bswap16(val); }
static inline int32_t SLittle(int32_t val) { return bswap32(val); }
static inline uint32_t SLittle(uint32_t val) { return bswap32(val); }
static inline int64_t SLittle(int64_t val) { return bswap64(val); }
static inline uint64_t SLittle(uint64_t val) { return bswap64(val); }
static inline int16_t SBig(int16_t val) { return val; }
static inline uint16_t SBig(uint16_t val) { return val; }
static inline int32_t SBig(int32_t val) { return val; }
static inline uint32_t SBig(uint32_t val) { return val; }
static inline int64_t SBig(int64_t val) { return val; }
static inline uint64_t SBig(uint64_t val) { return val; }
2015-06-27 22:43:53 -07:00
#endif
2016-01-20 22:30:37 -08:00
#ifndef ROUND_UP_32
#define ROUND_UP_32(val) (((val) + 31) & ~31)
#define ROUND_UP_16(val) (((val) + 15) & ~15)
#endif
2018-12-07 21:21:47 -08:00
enum class FileLockType { None = 0, Read, Write };
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());
2018-12-07 21:21:47 -08:00
if (!fp)
return nullptr;
2016-01-20 22:30:37 -08:00
#else
2018-12-07 21:21:47 -08:00
FILE* fp = fopen(path, mode);
if (!fp)
return nullptr;
2016-01-20 22:30:37 -08:00
#endif
2018-12-07 21:21:47 -08:00
if (lock != FileLockType::None) {
2016-01-20 22:30:37 -08:00
#if _WIN32
2018-12-07 21:21:47 -08:00
OVERLAPPED ov = {};
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
&ov);
2016-01-20 22:30:37 -08:00
#else
2018-12-07 21:21:47 -08:00
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
2020-04-11 15:45:06 -07:00
LogModule.report(logvisor::Error, FMT_STRING("flock {}: {}"), path, strerror(errno));
2016-01-20 22:30:37 -08:00
#endif
2018-12-07 21:21:47 -08:00
}
2016-01-20 22:30:37 -08:00
2018-12-07 21:21:47 -08:00
return fp;
2016-01-20 22:30:37 -08:00
}
2018-12-07 21:21:47 -08:00
static inline int FSeek(FILE* fp, int64_t offset, int whence) {
2016-01-24 18:13:09 -08:00
#if _WIN32
2018-12-07 21:21:47 -08:00
return _fseeki64(fp, offset, whence);
2016-01-23 15:36:58 -08:00
#elif __APPLE__ || __FreeBSD__
2018-12-07 21:21:47 -08:00
return fseeko(fp, offset, whence);
2016-01-23 15:36:58 -08:00
#else
2018-12-07 21:21:47 -08:00
return fseeko64(fp, offset, whence);
2016-01-23 15:36:58 -08:00
#endif
}
2018-12-07 21:21:47 -08:00
static inline int64_t FTell(FILE* fp) {
2016-01-25 18:01:55 -08:00
#if _WIN32
2018-12-07 21:21:47 -08:00
return _ftelli64(fp);
2016-01-25 18:01:55 -08:00
#elif __APPLE__ || __FreeBSD__
2018-12-07 21:21:47 -08:00
return ftello(fp);
2016-01-25 18:01:55 -08:00
#else
2018-12-07 21:21:47 -08:00
return ftello64(fp);
2016-01-25 18:01:55 -08:00
#endif
}
static inline bool CheckFreeSpace(const char* path, size_t reqSz) {
2016-01-24 18:00:02 -08:00
#if _WIN32
2018-12-07 21:21:47 -08:00
ULARGE_INTEGER freeBytes;
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);
2018-12-07 21:21:47 -08:00
return false;
}
if (end != nullptr) {
2018-12-07 21:21:47 -08:00
end[0] = L'\0';
}
if (!GetDiskFreeSpaceExW(buf.data(), &freeBytes, nullptr, nullptr)) {
LogModule.report(logvisor::Error, FMT_STRING("GetDiskFreeSpaceExA {}: {}"), path, GetLastError());
2018-12-07 21:21:47 -08:00
return false;
}
return reqSz < freeBytes.QuadPart;
2016-01-24 18:00:02 -08:00
#else
2018-12-07 21:21:47 -08:00
struct statvfs svfs;
if (statvfs(path, &svfs)) {
2020-04-11 15:45:06 -07:00
LogModule.report(logvisor::Error, FMT_STRING("statvfs {}: {}"), path, strerror(errno));
2018-12-07 21:21:47 -08:00
return false;
}
return reqSz < svfs.f_frsize * svfs.f_bavail;
2016-01-24 18:00:02 -08:00
#endif
}
2018-12-07 21:21:47 -08:00
} // namespace nod