2017-01-08 03:08:02 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <sys/time.h>
|
2022-02-21 02:28:07 +00:00
|
|
|
#include <unistd.h>
|
2017-01-08 03:08:02 +00:00
|
|
|
#if __APPLE__
|
|
|
|
#include <mach/mach_time.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2022-02-21 02:28:07 +00:00
|
|
|
#include <algorithm>
|
2017-12-29 08:08:12 +00:00
|
|
|
#include <cstdarg>
|
2022-02-21 02:28:07 +00:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstring>
|
2017-12-29 08:08:12 +00:00
|
|
|
#include <ctime>
|
2022-03-12 19:13:01 +00:00
|
|
|
#ifdef _WIN32
|
2022-02-22 05:53:57 +00:00
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#endif
|
|
|
|
#ifndef NOMINMAX
|
|
|
|
#define NOMINMAX
|
|
|
|
#endif
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <nowide/stackstring.hpp>
|
2022-02-21 02:28:07 +00:00
|
|
|
#ifndef _WIN32_IE
|
|
|
|
#define _WIN32_IE 0x0400
|
|
|
|
#endif
|
2022-02-22 05:53:57 +00:00
|
|
|
#include <ShlObj.h>
|
|
|
|
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
|
|
|
|
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
|
|
|
|
#endif
|
2022-02-21 02:28:07 +00:00
|
|
|
#endif
|
2015-08-17 05:26:58 +00:00
|
|
|
|
2019-12-22 20:04:07 +00:00
|
|
|
#include "Runtime/CBasics.hpp"
|
2022-02-26 16:42:42 +00:00
|
|
|
|
|
|
|
#include "Runtime/CStopwatch.hpp"
|
|
|
|
|
2022-02-21 02:28:07 +00:00
|
|
|
#include <logvisor/logvisor.hpp>
|
2015-08-17 05:26:58 +00:00
|
|
|
|
2017-01-08 03:08:02 +00:00
|
|
|
#if __APPLE__
|
|
|
|
static u64 MachToDolphinNum;
|
|
|
|
static u64 MachToDolphinDenom;
|
|
|
|
#elif _WIN32
|
|
|
|
static LARGE_INTEGER PerfFrequency;
|
|
|
|
#endif
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
namespace metaforce {
|
2022-02-21 02:28:07 +00:00
|
|
|
static logvisor::Module LogModule("metaforce::CBasics");
|
2018-12-08 05:30:43 +00:00
|
|
|
void CBasics::Initialize() {
|
2022-02-26 16:42:42 +00:00
|
|
|
CStopwatch::InitGlobalTimer();
|
2017-01-08 03:08:02 +00:00
|
|
|
#if __APPLE__
|
2018-12-08 05:30:43 +00:00
|
|
|
mach_timebase_info_data_t timebase;
|
|
|
|
mach_timebase_info(&timebase);
|
|
|
|
MachToDolphinNum = GetGCTicksPerSec() * timebase.numer;
|
|
|
|
MachToDolphinDenom = 1000000000ull * timebase.denom;
|
2017-01-08 03:08:02 +00:00
|
|
|
#elif _WIN32
|
2018-12-08 05:30:43 +00:00
|
|
|
QueryPerformanceFrequency(&PerfFrequency);
|
2017-01-08 03:08:02 +00:00
|
|
|
#endif
|
2015-08-17 05:26:58 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u64 CBasics::GetGCTicks() {
|
2017-01-08 03:08:02 +00:00
|
|
|
#if __APPLE__
|
2018-12-08 05:30:43 +00:00
|
|
|
return mach_absolute_time() * MachToDolphinNum / MachToDolphinDenom;
|
2017-01-08 03:08:02 +00:00
|
|
|
#elif __linux__ || __FreeBSD__
|
2018-12-08 05:30:43 +00:00
|
|
|
struct timespec tp;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
2017-01-08 03:08:02 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return u64((tp.tv_sec * 1000000000ull) + tp.tv_nsec) * GetGCTicksPerSec() / 1000000000ull;
|
2017-01-08 03:08:02 +00:00
|
|
|
#elif _WIN32
|
2018-12-08 05:30:43 +00:00
|
|
|
LARGE_INTEGER perf;
|
|
|
|
QueryPerformanceCounter(&perf);
|
|
|
|
perf.QuadPart *= GetGCTicksPerSec();
|
|
|
|
perf.QuadPart /= PerfFrequency.QuadPart;
|
|
|
|
return perf.QuadPart;
|
2017-01-08 03:08:02 +00:00
|
|
|
#else
|
2018-12-08 05:30:43 +00:00
|
|
|
return 0;
|
2017-01-08 03:08:02 +00:00
|
|
|
#endif
|
2017-01-01 06:46:52 +00:00
|
|
|
}
|
|
|
|
|
2016-10-09 21:41:23 +00:00
|
|
|
const u64 CBasics::SECONDS_TO_2000 = 946684800LL;
|
|
|
|
const u64 CBasics::TICKS_PER_SECOND = 60750000LL;
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
static struct tm* localtime_r(const time_t& time, struct tm& timeSt, long& gmtOff) {
|
2019-09-09 11:21:01 +00:00
|
|
|
#ifndef _WIN32
|
2018-12-08 05:30:43 +00:00
|
|
|
auto ret = ::localtime_r(&time, &timeSt);
|
|
|
|
if (!ret)
|
|
|
|
return nullptr;
|
|
|
|
gmtOff = ret->tm_gmtoff;
|
|
|
|
return ret;
|
2016-12-24 00:45:51 +00:00
|
|
|
#else
|
2018-12-08 05:30:43 +00:00
|
|
|
struct tm _gmSt;
|
|
|
|
auto reta = localtime_s(&timeSt, &time);
|
|
|
|
auto retb = gmtime_s(&_gmSt, &time);
|
|
|
|
if (reta || retb)
|
|
|
|
return nullptr;
|
|
|
|
gmtOff = mktime(&timeSt) - mktime(&_gmSt);
|
|
|
|
return &timeSt;
|
2016-12-24 00:45:51 +00:00
|
|
|
#endif
|
2019-09-09 11:21:01 +00:00
|
|
|
}
|
2016-12-24 00:45:51 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
OSTime CBasics::ToWiiTime(std::chrono::system_clock::time_point time) {
|
|
|
|
auto sec = std::chrono::time_point_cast<std::chrono::seconds>(time);
|
|
|
|
auto us = std::chrono::duration_cast<std::chrono::microseconds>((time - sec)).count();
|
|
|
|
time_t sysTime = std::chrono::system_clock::to_time_t(sec);
|
|
|
|
|
|
|
|
struct tm _timeSt;
|
|
|
|
long gmtOff;
|
|
|
|
struct tm* timeSt = localtime_r(sysTime, _timeSt, gmtOff);
|
|
|
|
if (!timeSt)
|
|
|
|
return 0;
|
2016-10-09 21:41:23 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
/* Returning local */
|
|
|
|
return OSTime(TICKS_PER_SECOND * ((sysTime + gmtOff) - SECONDS_TO_2000) + us * TICKS_PER_SECOND / 1000000);
|
2016-10-09 21:41:23 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
std::chrono::system_clock::time_point CBasics::FromWiiTime(OSTime wiiTime) {
|
|
|
|
auto div = std::lldiv(SECONDS_TO_2000 + wiiTime, TICKS_PER_SECOND);
|
|
|
|
time_t time = time_t(div.quot);
|
|
|
|
|
|
|
|
time_t sysTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
|
|
struct tm _timeSt;
|
|
|
|
long gmtOff;
|
|
|
|
struct tm* timeSt = localtime_r(sysTime, _timeSt, gmtOff);
|
|
|
|
if (!timeSt)
|
|
|
|
return std::chrono::system_clock::from_time_t(0);
|
|
|
|
|
|
|
|
/* Returning GMT */
|
|
|
|
return std::chrono::system_clock::from_time_t(time - gmtOff) +
|
|
|
|
std::chrono::microseconds(div.rem * 1000000 / TICKS_PER_SECOND);
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
OSCalendarTime CBasics::ToCalendarTime(std::chrono::system_clock::time_point time) {
|
|
|
|
OSCalendarTime ret;
|
|
|
|
|
|
|
|
auto sec = std::chrono::time_point_cast<std::chrono::seconds>(time);
|
|
|
|
auto us = std::chrono::duration_cast<std::chrono::microseconds>((time - sec)).count();
|
|
|
|
time_t sysTime = std::chrono::system_clock::to_time_t(sec);
|
|
|
|
struct tm _timeSt;
|
|
|
|
long gmtOff;
|
|
|
|
struct tm* timeSt = localtime_r(sysTime, _timeSt, gmtOff);
|
|
|
|
if (!timeSt)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
ret.x0_sec = timeSt->tm_sec;
|
|
|
|
ret.x4_min = timeSt->tm_min;
|
|
|
|
ret.x8_hour = timeSt->tm_hour;
|
|
|
|
ret.xc_mday = timeSt->tm_mday;
|
|
|
|
ret.x10_mon = timeSt->tm_mon;
|
|
|
|
ret.x14_year = timeSt->tm_year + 1900;
|
|
|
|
ret.x18_wday = timeSt->tm_wday;
|
|
|
|
ret.x1c_yday = timeSt->tm_yday;
|
|
|
|
|
|
|
|
auto div = std::ldiv(us, 1000);
|
|
|
|
ret.x20_msec = div.quot;
|
|
|
|
ret.x24_usec = div.rem;
|
|
|
|
|
|
|
|
return ret;
|
2015-08-17 22:05:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
u16 CBasics::SwapBytes(u16 v) {
|
|
|
|
Swap2Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
2022-02-25 07:45:25 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
u32 CBasics::SwapBytes(u32 v) {
|
|
|
|
Swap4Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 CBasics::SwapBytes(u64 v) {
|
|
|
|
Swap8Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
2022-02-25 07:45:25 +00:00
|
|
|
|
|
|
|
s16 CBasics::SwapBytes(s16 v) {
|
|
|
|
Swap2Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 CBasics::SwapBytes(s32 v) {
|
|
|
|
Swap4Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
s64 CBasics::SwapBytes(s64 v) {
|
|
|
|
Swap8Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
float CBasics::SwapBytes(float v) {
|
|
|
|
Swap4Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
2022-02-25 07:45:25 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
double CBasics::SwapBytes(double v) {
|
|
|
|
Swap8Bytes(reinterpret_cast<u8*>(&v));
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBasics::Swap2Bytes(u8* v) {
|
|
|
|
u16* val = reinterpret_cast<u16*>(v);
|
|
|
|
#if __GNUC__
|
|
|
|
*val = __builtin_bswap16(*val);
|
|
|
|
#elif _WIN32
|
|
|
|
*val = _byteswap_ushort(*val);
|
|
|
|
#else
|
|
|
|
*val = (*val << 8) | ((*val >> 8) & 0xFF);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBasics::Swap4Bytes(u8* v) {
|
|
|
|
u32* val = reinterpret_cast<u32*>(v);
|
|
|
|
#if __GNUC__
|
|
|
|
*val = __builtin_bswap32(*val);
|
|
|
|
#elif _WIN32
|
|
|
|
*val = _byteswap_ulong(*val);
|
|
|
|
#else
|
|
|
|
*val = ((*val & 0x0000FFFF) << 16) | ((*val & 0xFFFF0000) >> 16) | ((*val & 0x00FF00FF) << 8) |
|
|
|
|
((*val & 0xFF00FF00) >> 8);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBasics::Swap8Bytes(u8* v) {
|
|
|
|
u64* val = reinterpret_cast<u64*>(v);
|
|
|
|
#if __GNUC__
|
|
|
|
*val = __builtin_bswap64(*val);
|
|
|
|
#elif _WIN32
|
2022-02-22 05:53:57 +00:00
|
|
|
*val = _byteswap_uint64(*val);
|
2022-02-18 07:37:54 +00:00
|
|
|
#else
|
|
|
|
*val = ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
|
|
|
|
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
|
|
|
|
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
|
|
|
|
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-02-21 02:28:07 +00:00
|
|
|
int CBasics::Stat(const char* path, Sstat* statOut) {
|
|
|
|
#if _WIN32
|
|
|
|
size_t pos;
|
|
|
|
const nowide::wstackstring wpath(path);
|
|
|
|
const wchar_t* wpathP = wpath.get();
|
|
|
|
for (pos = 0; pos < 3 && wpathP[pos] != L'\0'; ++pos) {}
|
|
|
|
if (pos == 2 && wpathP[1] == L':') {
|
|
|
|
wchar_t fixPath[4] = {wpathP[0], L':', L'/', L'\0'};
|
|
|
|
return _wstat64(fixPath, statOut);
|
|
|
|
}
|
|
|
|
return _wstat64(wpath.get(), statOut);
|
|
|
|
#else
|
|
|
|
return stat(path, statOut);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* recursive mkdir */
|
|
|
|
int CBasics::RecursiveMakeDir(const char* dir) {
|
|
|
|
#if _WIN32
|
|
|
|
char tmp[1024];
|
|
|
|
|
|
|
|
/* copy path */
|
|
|
|
std::strncpy(tmp, dir, std::size(tmp));
|
|
|
|
const size_t len = std::strlen(tmp);
|
|
|
|
if (len >= std::size(tmp)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove trailing slash */
|
|
|
|
if (tmp[len - 1] == '/' || tmp[len - 1] == '\\') {
|
|
|
|
tmp[len - 1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* recursive mkdir */
|
|
|
|
char* p = nullptr;
|
|
|
|
Sstat sb;
|
|
|
|
for (p = tmp + 1; *p; p++) {
|
|
|
|
if (*p == '/' || *p == '\\') {
|
|
|
|
*p = 0;
|
|
|
|
/* test path */
|
|
|
|
if (Stat(tmp, &sb) != 0) {
|
|
|
|
/* path does not exist - create directory */
|
|
|
|
const nowide::wstackstring wtmp(tmp);
|
|
|
|
if (!CreateDirectoryW(wtmp.get(), nullptr)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
/* not a directory */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*p = '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* test path */
|
|
|
|
if (Stat(tmp, &sb) != 0) {
|
|
|
|
/* path does not exist - create directory */
|
|
|
|
const nowide::wstackstring wtmp(tmp);
|
|
|
|
if (!CreateDirectoryW(wtmp.get(), nullptr)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
/* not a directory */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
char tmp[1024];
|
|
|
|
|
|
|
|
/* copy path */
|
|
|
|
std::memset(tmp, 0, std::size(tmp));
|
|
|
|
std::strncpy(tmp, dir, std::size(tmp) - 1);
|
|
|
|
const size_t len = std::strlen(tmp);
|
|
|
|
if (len >= std::size(tmp)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove trailing slash */
|
|
|
|
if (tmp[len - 1] == '/') {
|
|
|
|
tmp[len - 1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* recursive mkdir */
|
|
|
|
char* p = nullptr;
|
|
|
|
Sstat sb;
|
|
|
|
for (p = tmp + 1; *p; p++) {
|
|
|
|
if (*p == '/') {
|
|
|
|
*p = 0;
|
|
|
|
/* test path */
|
|
|
|
if (Stat(tmp, &sb) != 0) {
|
|
|
|
/* path does not exist - create directory */
|
|
|
|
if (mkdir(tmp, 0755) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
/* not a directory */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*p = '/';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* test path */
|
|
|
|
if (Stat(tmp, &sb) != 0) {
|
|
|
|
/* path does not exist - create directory */
|
|
|
|
if (mkdir(tmp, 0755) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
/* not a directory */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void CBasics::MakeDir(const char* dir) {
|
|
|
|
#if _WIN32
|
|
|
|
HRESULT err;
|
|
|
|
const nowide::wstackstring wdir(dir);
|
|
|
|
if (!CreateDirectoryW(wdir.get(), NULL))
|
|
|
|
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
|
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("MakeDir({})"), dir);
|
|
|
|
#else
|
|
|
|
if (mkdir(dir, 0755))
|
|
|
|
if (errno != EEXIST)
|
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("MakeDir({}): {}"), dir, strerror(errno));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-06-21 20:22:49 +00:00
|
|
|
bool CBasics::IsDir(const char* path) {
|
|
|
|
Sstat theStat;
|
|
|
|
Stat(path, &theStat);
|
|
|
|
|
|
|
|
return S_ISDIR(theStat.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBasics::IsFile(const char* path) {
|
|
|
|
Sstat theStat;
|
|
|
|
Stat(path, &theStat);
|
|
|
|
|
|
|
|
return S_ISREG(theStat.st_mode);
|
|
|
|
}
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
} // namespace metaforce
|