From 8d24ebdad8c3d73918dfeea477b9769bf10d6bd5 Mon Sep 17 00:00:00 2001 From: ella love Date: Mon, 2 Mar 2026 23:17:28 -0500 Subject: [PATCH 1/3] feat: add stubs for Windows system time functions --- Minecraft.Client/Linux/LinuxStubs.h | 129 ++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/Minecraft.Client/Linux/LinuxStubs.h b/Minecraft.Client/Linux/LinuxStubs.h index 2e16db67d..0b3d2445c 100644 --- a/Minecraft.Client/Linux/LinuxStubs.h +++ b/Minecraft.Client/Linux/LinuxStubs.h @@ -18,6 +18,7 @@ #include #include #include +#include #define TRUE true #define FALSE false @@ -160,6 +161,18 @@ typedef struct _LINUXSTUBS_FIND_HANDLE { char pattern[MAX_PATH]; } _LINUXSTUBS_FIND_HANDLE; +// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime +typedef struct _SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; + #define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) typedef pthread_mutex_t RTL_CRITICAL_SECTION; @@ -520,4 +533,120 @@ static inline BOOL FindClose(HANDLE hFindFile) return TRUE; } +// internal helper: convert FILETIME (100ns since 1601) to time_t (seconds since 1970) +static inline time_t _FileTimeToTimeT(const FILETIME& ft) +{ + ULONGLONG val = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + const ULONGLONG EPOCH_DIFF = 116444736000000000ULL; // 100ns intervals between 1601-01-01 and 1970-01-01 + return (time_t)((val - EPOCH_DIFF) / 10000000ULL); +} + +// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime +static inline VOID GetSystemTime(LPSYSTEMTIME lpSystemTime) +{ + struct timespec ts; +#ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#endif + struct tm tm; + gmtime_r(&ts.tv_sec, &tm); // UTC + + lpSystemTime->wYear = tm.tm_year + 1900; + lpSystemTime->wMonth = tm.tm_mon + 1; + lpSystemTime->wDayOfWeek = tm.tm_wday; // 0 = Sunday + lpSystemTime->wDay = tm.tm_mday; + lpSystemTime->wHour = tm.tm_hour; + lpSystemTime->wMinute = tm.tm_min; + lpSystemTime->wSecond = tm.tm_sec; + lpSystemTime->wMilliseconds = ts.tv_nsec / 1000000; // ns to ms +} + +// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlocaltime +static inline VOID GetLocalTime(LPSYSTEMTIME lpSystemTime) +{ + struct timespec ts; +#ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; +#endif + struct tm tm; + localtime_r(&ts.tv_sec, &tm); // local time + + lpSystemTime->wYear = tm.tm_year + 1900; + lpSystemTime->wMonth = tm.tm_mon + 1; + lpSystemTime->wDayOfWeek = tm.tm_wday; + lpSystemTime->wDay = tm.tm_mday; + lpSystemTime->wHour = tm.tm_hour; + lpSystemTime->wMinute = tm.tm_min; + lpSystemTime->wSecond = tm.tm_sec; + lpSystemTime->wMilliseconds = ts.tv_nsec / 1000000; +} + +// https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime +static inline BOOL SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime) +{ + struct tm tm = {0}; + tm.tm_year = lpSystemTime->wYear - 1900; + tm.tm_mon = lpSystemTime->wMonth - 1; + tm.tm_mday = lpSystemTime->wDay; + tm.tm_hour = lpSystemTime->wHour; + tm.tm_min = lpSystemTime->wMinute; + tm.tm_sec = lpSystemTime->wSecond; + tm.tm_isdst = -1; // unknown + + // Convert to time_t assuming UTC + time_t t; + // Portable UTC mktime: temporarily set TZ to UTC + char *tz_old = getenv("TZ"); + setenv("TZ", "UTC", 1); + tzset(); + t = mktime(&tm); + if (tz_old) + setenv("TZ", tz_old, 1); + else + unsetenv("TZ"); + tzset(); + + if (t == (time_t)-1) + return FALSE; + + // Add milliseconds + ULONGLONG ft = ((ULONGLONG)t + 11644473600ULL) * 10000000ULL; + ft += lpSystemTime->wMilliseconds * 10000ULL; // 1ms = 10000 * 100ns + + lpFileTime->dwLowDateTime = (DWORD)(ft & 0xFFFFFFFF); + lpFileTime->dwHighDateTime = (DWORD)(ft >> 32); + return TRUE; +} + +// https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime +static inline BOOL FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime) +{ + time_t t = _FileTimeToTimeT(*lpFileTime); + ULONGLONG ft = ((ULONGLONG)lpFileTime->dwHighDateTime << 32) | lpFileTime->dwLowDateTime; + ULONGLONG remainder100ns = ft % 10000000ULL; // 1 second = 10^7 * 100ns + + struct tm tm; + gmtime_r(&t, &tm); // UTC + + lpSystemTime->wYear = tm.tm_year + 1900; + lpSystemTime->wMonth = tm.tm_mon + 1; + lpSystemTime->wDayOfWeek = tm.tm_wday; + lpSystemTime->wDay = tm.tm_mday; + lpSystemTime->wHour = tm.tm_hour; + lpSystemTime->wMinute = tm.tm_min; + lpSystemTime->wSecond = tm.tm_sec; + lpSystemTime->wMilliseconds = (WORD)(remainder100ns / 10000); // 1ms = 10000 * 100ns + return TRUE; +} + #endif // LINUXSTUBS_H \ No newline at end of file From 5dd67bd334cba344d16a6eb750196a64e69891b5 Mon Sep 17 00:00:00 2001 From: ella love Date: Tue, 3 Mar 2026 02:14:24 -0500 Subject: [PATCH 2/3] feat: Stubs for Debug Outputs --- Minecraft.Client/Linux/LinuxStubs.h | 134 +++++++++++++--------------- 1 file changed, 64 insertions(+), 70 deletions(-) diff --git a/Minecraft.Client/Linux/LinuxStubs.h b/Minecraft.Client/Linux/LinuxStubs.h index eb751d246..16abe7d60 100644 --- a/Minecraft.Client/Linux/LinuxStubs.h +++ b/Minecraft.Client/Linux/LinuxStubs.h @@ -549,89 +549,65 @@ static inline time_t _FileTimeToTimeT(const FILETIME& ft) return (time_t)((val - EPOCH_DIFF) / 10000000ULL); } -// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime -static inline VOID GetSystemTime(LPSYSTEMTIME lpSystemTime) +// internal helper: read the current wall clock into a timespec +static inline void _CurrentTimeSpec(struct timespec *ts) { - struct timespec ts; #ifdef CLOCK_REALTIME - clock_gettime(CLOCK_REALTIME, &ts); + clock_gettime(CLOCK_REALTIME, ts); #else struct timeval tv; gettimeofday(&tv, NULL); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; #endif - struct tm tm; - gmtime_r(&ts.tv_sec, &tm); // UTC +} - lpSystemTime->wYear = tm.tm_year + 1900; - lpSystemTime->wMonth = tm.tm_mon + 1; - lpSystemTime->wDayOfWeek = tm.tm_wday; // 0 = Sunday - lpSystemTime->wDay = tm.tm_mday; - lpSystemTime->wHour = tm.tm_hour; - lpSystemTime->wMinute = tm.tm_min; - lpSystemTime->wSecond = tm.tm_sec; - lpSystemTime->wMilliseconds = ts.tv_nsec / 1000000; // ns to ms +// internal helper: fill SYSTEMTIME from a broken-down tm + nanosecond remainder +static inline void _FillSystemTime(const struct tm *tm, long tv_nsec, LPSYSTEMTIME lpSystemTime) +{ + lpSystemTime->wYear = tm->tm_year + 1900; + lpSystemTime->wMonth = tm->tm_mon + 1; + lpSystemTime->wDayOfWeek = tm->tm_wday; // 0 = Sunday + lpSystemTime->wDay = tm->tm_mday; + lpSystemTime->wHour = tm->tm_hour; + lpSystemTime->wMinute = tm->tm_min; + lpSystemTime->wSecond = tm->tm_sec; + lpSystemTime->wMilliseconds = (WORD)(tv_nsec / 1000000); // ns to ms +} + +// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime +static inline VOID GetSystemTime(LPSYSTEMTIME lpSystemTime) +{ + struct timespec ts; _CurrentTimeSpec(&ts); + struct tm tm; gmtime_r(&ts.tv_sec, &tm); // UTC + _FillSystemTime(&tm, ts.tv_nsec, lpSystemTime); } // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlocaltime static inline VOID GetLocalTime(LPSYSTEMTIME lpSystemTime) { - struct timespec ts; -#ifdef CLOCK_REALTIME - clock_gettime(CLOCK_REALTIME, &ts); -#else - struct timeval tv; - gettimeofday(&tv, NULL); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; -#endif - struct tm tm; - localtime_r(&ts.tv_sec, &tm); // local time - - lpSystemTime->wYear = tm.tm_year + 1900; - lpSystemTime->wMonth = tm.tm_mon + 1; - lpSystemTime->wDayOfWeek = tm.tm_wday; - lpSystemTime->wDay = tm.tm_mday; - lpSystemTime->wHour = tm.tm_hour; - lpSystemTime->wMinute = tm.tm_min; - lpSystemTime->wSecond = tm.tm_sec; - lpSystemTime->wMilliseconds = ts.tv_nsec / 1000000; + struct timespec ts; _CurrentTimeSpec(&ts); + struct tm tm; localtime_r(&ts.tv_sec, &tm); // local time + _FillSystemTime(&tm, ts.tv_nsec, lpSystemTime); } // https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime static inline BOOL SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime) { - struct tm tm = {0}; + struct tm tm = {}; tm.tm_year = lpSystemTime->wYear - 1900; tm.tm_mon = lpSystemTime->wMonth - 1; tm.tm_mday = lpSystemTime->wDay; tm.tm_hour = lpSystemTime->wHour; tm.tm_min = lpSystemTime->wMinute; tm.tm_sec = lpSystemTime->wSecond; - tm.tm_isdst = -1; // unknown - // Convert to time_t assuming UTC - time_t t; - // Portable UTC mktime: temporarily set TZ to UTC - char *tz_old = getenv("TZ"); - setenv("TZ", "UTC", 1); - tzset(); - t = mktime(&tm); - if (tz_old) - setenv("TZ", tz_old, 1); - else - unsetenv("TZ"); - tzset(); + time_t t = timegm(&tm); + if (t == (time_t)-1) return FALSE; - if (t == (time_t)-1) - return FALSE; - - // Add milliseconds ULONGLONG ft = ((ULONGLONG)t + 11644473600ULL) * 10000000ULL; - ft += lpSystemTime->wMilliseconds * 10000ULL; // 1ms = 10000 * 100ns - - lpFileTime->dwLowDateTime = (DWORD)(ft & 0xFFFFFFFF); + ft += lpSystemTime->wMilliseconds * 10000ULL; + lpFileTime->dwLowDateTime = (DWORD)(ft & 0xFFFFFFFF); lpFileTime->dwHighDateTime = (DWORD)(ft >> 32); return TRUE; } @@ -639,21 +615,12 @@ static inline BOOL SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETI // https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime static inline BOOL FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime) { - time_t t = _FileTimeToTimeT(*lpFileTime); ULONGLONG ft = ((ULONGLONG)lpFileTime->dwHighDateTime << 32) | lpFileTime->dwLowDateTime; - ULONGLONG remainder100ns = ft % 10000000ULL; // 1 second = 10^7 * 100ns + time_t t = _FileTimeToTimeT(*lpFileTime); + long remainder_ns = (long)((ft % 10000000ULL) * 100); - struct tm tm; - gmtime_r(&t, &tm); // UTC - - lpSystemTime->wYear = tm.tm_year + 1900; - lpSystemTime->wMonth = tm.tm_mon + 1; - lpSystemTime->wDayOfWeek = tm.tm_wday; - lpSystemTime->wDay = tm.tm_mday; - lpSystemTime->wHour = tm.tm_hour; - lpSystemTime->wMinute = tm.tm_min; - lpSystemTime->wSecond = tm.tm_sec; - lpSystemTime->wMilliseconds = (WORD)(remainder100ns / 10000); // 1ms = 10000 * 100ns + struct tm tm; gmtime_r(&t, &tm); // UTC + _FillSystemTime(&tm, remainder_ns, lpSystemTime); return TRUE; } static inline DWORD GetTickCount() @@ -701,4 +668,31 @@ VOID OutputDebugStringA(LPCSTR lpOutputString) } #endif // _CONTENT_PACKAGE +// https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-outputdebugstringa +static inline VOID OutputDebugStringA(LPCSTR lpOutputString) +{ + if (!lpOutputString) return; + fputs(lpOutputString, stderr); +} + +// https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-outputdebugstringw +static inline VOID OutputDebugStringW(LPCWSTR lpOutputString) +{ + if (!lpOutputString) return; + // wchar_t* -> char* via wcstombs, respecting the current locale. + // Passing NULL as dst to wcstombs queries the required buffer length. + size_t len = wcstombs(NULL, lpOutputString, 0); + if (len == (size_t)-1) return; // unconvertible sequence + char *buf = (char *)malloc(len + 1); + if (!buf) return; + wcstombs(buf, lpOutputString, len + 1); + fputs(buf, stderr); + free(buf); +} + +static inline VOID OutputDebugString(LPCSTR lpOutputString) +{ + return OutputDebugStringA(lpOutputString); +} + #endif // LINUXSTUBS_H \ No newline at end of file From 5d5cd69a5e0a4842886b2e587b25702f2fc75616 Mon Sep 17 00:00:00 2001 From: ella love Date: Tue, 3 Mar 2026 02:35:41 -0500 Subject: [PATCH 3/3] update: fwprintf for wchar_t handling. Cleaned up old debug impl --- Minecraft.Client/Linux/LinuxStubs.h | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/Minecraft.Client/Linux/LinuxStubs.h b/Minecraft.Client/Linux/LinuxStubs.h index 16abe7d60..e64e3c0a7 100644 --- a/Minecraft.Client/Linux/LinuxStubs.h +++ b/Minecraft.Client/Linux/LinuxStubs.h @@ -651,23 +651,6 @@ static inline BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) return true; } -#ifndef _FINAL_BUILD -VOID OutputDebugStringW(LPCWSTR lpOutputString) -{ - fwprintf(stderr, lpOutputString); -} - -VOID OutputDebugString(LPCSTR lpOutputString) -{ - fprintf(stderr, lpOutputString); -} - -VOID OutputDebugStringA(LPCSTR lpOutputString) -{ - fprintf(stderr, lpOutputString); -} -#endif // _CONTENT_PACKAGE - // https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-outputdebugstringa static inline VOID OutputDebugStringA(LPCSTR lpOutputString) { @@ -679,15 +662,7 @@ static inline VOID OutputDebugStringA(LPCSTR lpOutputString) static inline VOID OutputDebugStringW(LPCWSTR lpOutputString) { if (!lpOutputString) return; - // wchar_t* -> char* via wcstombs, respecting the current locale. - // Passing NULL as dst to wcstombs queries the required buffer length. - size_t len = wcstombs(NULL, lpOutputString, 0); - if (len == (size_t)-1) return; // unconvertible sequence - char *buf = (char *)malloc(len + 1); - if (!buf) return; - wcstombs(buf, lpOutputString, len + 1); - fputs(buf, stderr); - free(buf); + fprintf(stderr, "%ls", lpOutputString); } static inline VOID OutputDebugString(LPCSTR lpOutputString)