From b7c6512445225981d851287ff2a574bb1f80b5df Mon Sep 17 00:00:00 2001 From: ella love Date: Mon, 2 Mar 2026 22:00:33 -0500 Subject: [PATCH] feat: implement win32 file I/O stubs over POSIX in wlinux.h --- .gitignore | 1 + Minecraft.World/linux/wlinux.h | 386 ++++++++++++++++++++++++++++++--- 2 files changed, 357 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 6c81ca77e..830932518 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # .gitignore build/ .cache/ +.idea/ diff --git a/Minecraft.World/linux/wlinux.h b/Minecraft.World/linux/wlinux.h index 07745d2ce..0550ca375 100644 --- a/Minecraft.World/linux/wlinux.h +++ b/Minecraft.World/linux/wlinux.h @@ -5,15 +5,24 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #define TRUE true #define FALSE false #define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) -#define ZeroMemory RtlZeroMemory +#define ZeroMemory RtlZeroMemory #define WINAPI typedef unsigned int DWORD; @@ -34,7 +43,7 @@ typedef unsigned int* LPDWORD; typedef const void* LPCVOID; typedef char CHAR; typedef void* PVOID; -typedef unsigned long* ULONG_PTR; +typedef uintptr_t ULONG_PTR; typedef long LONG; typedef long LONG64, *PLONG64; typedef void VOID; @@ -42,8 +51,8 @@ typedef ULONGLONG PlayerUID; typedef DWORD WORD; typedef struct { DWORD LowPart; - long long QuadPart; LONG HighPart; + long long QuadPart; } LARGE_INTEGER; typedef long long LONGLONG; typedef size_t SIZE_T; @@ -53,10 +62,45 @@ typedef unsigned char boolean; // java brainrot #define __int32 int typedef unsigned long ULONG; typedef unsigned char byte; +typedef short SHORT; +typedef float FLOAT; #define PAGE_READWRITE 0x04 #define MEM_LARGE_PAGES 0x20000000 -#define MAXULONG_PTR ((ULONG)0xFFFFFFFF) +#define MAXULONG_PTR ((ULONG_PTR)~0UL) +#define MAX_PATH 260 + +#define GENERIC_READ 0x80000000UL +#define GENERIC_WRITE 0x40000000UL +#define GENERIC_EXECUTE 0x20000000UL +#define GENERIC_ALL 0x10000000UL + +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 + +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 + +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#define INVALID_FILE_SIZE ((DWORD)-1) +#define INVALID_SET_FILE_POINTER ((DWORD)-1) + +#define FILE_BEGIN SEEK_SET +#define FILE_CURRENT SEEK_CUR +#define FILE_END SEEK_END + +#define INVALID_HANDLE_VALUE ((HANDLE)(ULONG_PTR)-1) // https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime typedef struct _FILETIME { @@ -75,14 +119,46 @@ typedef struct _MEMORYSTATUS { SIZE_T dwAvailVirtual; } MEMORYSTATUS, *LPMEMORYSTATUS; -#define FILE_BEGIN SEEK_SET +typedef struct _WIN32_FIND_DATAA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + char cFileName[MAX_PATH]; + char cAlternateFileName[14]; +} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA; -typedef short SHORT; +typedef WIN32_FIND_DATAA WIN32_FIND_DATA; +typedef PWIN32_FIND_DATAA PWIN32_FIND_DATA; +typedef LPWIN32_FIND_DATAA LPWIN32_FIND_DATA; + +typedef struct _WIN32_FILE_ATTRIBUTE_DATA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA; + +typedef enum _GET_FILEEX_INFO_LEVELS { + GetFileExInfoStandard, + GetFileExMaxInfoLevel +} GET_FILEEX_INFO_LEVELS; typedef VOID* XMEMCOMPRESSION_CONTEXT; typedef VOID* XMEMDECOMPRESSION_CONTEXT; -typedef float FLOAT; +// internal search state for FindFirstFile/FindNextFile +typedef struct _WLINUX_FIND_HANDLE { + DIR *dir; + char dirpath[MAX_PATH]; + char pattern[MAX_PATH]; +} _WLINUX_FIND_HANDLE; #define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) @@ -93,86 +169,81 @@ typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; typedef PRTL_CRITICAL_SECTION PCRITICAL_SECTION; typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION; -void InitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) +static inline void InitializeCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) { pthread_mutexattr_t attr; - int ret; - pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(CriticalSection, &attr); pthread_mutexattr_destroy(&attr); } -void InitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection, ULONG SpinCount) +static inline void InitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection, ULONG SpinCount) { // no spin count required because we use a recursive mutex InitializeCriticalSection(CriticalSection); } -void DeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) +static inline void DeleteCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) { pthread_mutex_destroy(CriticalSection); } -void EnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) +static inline void EnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) { pthread_mutex_lock(CriticalSection); } -void LeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) +static inline void LeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) { pthread_mutex_unlock(CriticalSection); } -ULONG TryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) +static inline ULONG TryEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection) { return pthread_mutex_trylock(CriticalSection) == 0; } // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsalloc -DWORD TlsAlloc(VOID) { +static inline DWORD TlsAlloc(VOID) +{ pthread_key_t key; - - if (pthread_key_create(&key, NULL) == 0) { + if (pthread_key_create(&key, NULL) == 0) return key; - } else { - return TLS_OUT_OF_INDEXES; - } + return TLS_OUT_OF_INDEXES; } // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsfree -BOOL TlsFree(DWORD dwTlsIndex) +static inline BOOL TlsFree(DWORD dwTlsIndex) { return pthread_key_delete(dwTlsIndex) == 0; } // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsgetvalue -LPVOID TlsGetValue(DWORD dwTlsIndex) +static inline LPVOID TlsGetValue(DWORD dwTlsIndex) { return pthread_getspecific(dwTlsIndex); } // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlssetvalue -BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) +static inline BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) { return pthread_setspecific(dwTlsIndex, lpTlsValue) == 0; } - // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalmemorystatus -VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer) +static inline VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer) { // TODO: Parse /proc/meminfo and set lpBuffer based on that. Probably will also need another // different codepath for macOS too. } -DWORD GetLastError(VOID) +static inline DWORD GetLastError(VOID) { return errno; } -VOID Sleep(DWORD dwMilliseconds) +static inline VOID Sleep(DWORD dwMilliseconds) { struct timespec ts; ts.tv_nsec = (dwMilliseconds * 1000000) % 1000000000; @@ -184,7 +255,7 @@ VOID Sleep(DWORD dwMilliseconds) } while (ret == -1 && errno == EINTR); } -LONG64 InterlockedCompareExchangeRelease64( +static inline LONG64 InterlockedCompareExchangeRelease64( LONG64 volatile *Destination, LONG64 Exchange, LONG64 Comperand) @@ -194,4 +265,259 @@ LONG64 InterlockedCompareExchangeRelease64( return expected; } -#endif // WLINUX_H +// internal helper: convert time_t to FILETIME (100ns intervals since 1601-01-01) +static inline FILETIME _TimeToFileTime(time_t t) +{ + const ULONGLONG EPOCH_DIFF = 11644473600ULL; + ULONGLONG val = ((ULONGLONG)t + EPOCH_DIFF) * 10000000ULL; + FILETIME ft; + ft.dwLowDateTime = (DWORD)(val & 0xFFFFFFFF); + ft.dwHighDateTime = (DWORD)(val >> 32); + return ft; +} + +// internal helper: fill WIN32_FIND_DATAA from stat + name +static inline void _FillFindData(const char *name, const struct stat *st, WIN32_FIND_DATAA *out) +{ + memset(out, 0, sizeof(*out)); + out->dwFileAttributes = S_ISDIR(st->st_mode) ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; + if (!(st->st_mode & S_IWUSR)) out->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + if (name[0] == '.') out->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + out->ftCreationTime = _TimeToFileTime(st->st_mtime); + out->ftLastAccessTime = _TimeToFileTime(st->st_atime); + out->ftLastWriteTime = _TimeToFileTime(st->st_mtime); + out->nFileSizeHigh = (DWORD)((st->st_size >> 32) & 0xFFFFFFFF); + out->nFileSizeLow = (DWORD)(st->st_size & 0xFFFFFFFF); + strncpy(out->cFileName, name, MAX_PATH - 1); +} + +static inline HANDLE CreateFileA(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + void *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + int flags = 0; + if ((dwDesiredAccess & GENERIC_READ) && (dwDesiredAccess & GENERIC_WRITE)) flags = O_RDWR; + else if (dwDesiredAccess & GENERIC_WRITE) flags = O_WRONLY; + else flags = O_RDONLY; + + switch (dwCreationDisposition) + { + case CREATE_NEW: flags |= O_CREAT | O_EXCL; break; + case CREATE_ALWAYS: flags |= O_CREAT | O_TRUNC; break; + case OPEN_EXISTING: break; + case OPEN_ALWAYS: flags |= O_CREAT; break; + case TRUNCATE_EXISTING: flags |= O_TRUNC; break; + } + + int fd = open(lpFileName, flags, 0644); + return fd == -1 ? INVALID_HANDLE_VALUE : (HANDLE)(intptr_t)fd; +} + +static inline HANDLE CreateFile(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, + void *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); +} + +static inline BOOL CloseHandle(HANDLE hObject) +{ + if (hObject == INVALID_HANDLE_VALUE) return FALSE; + return close((int)(intptr_t)hObject) == 0; +} + +static inline DWORD GetFileSize(HANDLE hFile, DWORD *lpFileSizeHigh) +{ + struct stat st{}; + if (fstat((int)(intptr_t)hFile, &st) != 0) { if (lpFileSizeHigh) *lpFileSizeHigh = 0; return INVALID_FILE_SIZE; } + if (lpFileSizeHigh) *lpFileSizeHigh = (DWORD)((st.st_size >> 32) & 0xFFFFFFFF); + return (DWORD)(st.st_size & 0xFFFFFFFF); +} + +static inline BOOL GetFileSizeEx(HANDLE hFile, LARGE_INTEGER *lpFileSize) +{ + struct stat st{}; + if (fstat((int)(intptr_t)hFile, &st) != 0) return FALSE; + if (lpFileSize) { lpFileSize->QuadPart = st.st_size; lpFileSize->LowPart = (DWORD)(st.st_size & 0xFFFFFFFF); lpFileSize->HighPart = (LONG)(st.st_size >> 32); } + return TRUE; +} + +static inline BOOL ReadFile(HANDLE hFile, void *lpBuffer, DWORD nNumberOfBytesToRead, DWORD *lpNumberOfBytesRead, void *lpOverlapped) +{ + ssize_t n = read((int)(intptr_t)hFile, lpBuffer, nNumberOfBytesToRead); + if (lpNumberOfBytesRead) *lpNumberOfBytesRead = n >= 0 ? (DWORD)n : 0; + return n >= 0; +} + +static inline BOOL WriteFile(HANDLE hFile, const void *lpBuffer, DWORD nNumberOfBytesToWrite, DWORD *lpNumberOfBytesWritten, void *lpOverlapped) +{ + ssize_t n = write((int)(intptr_t)hFile, lpBuffer, nNumberOfBytesToWrite); + if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = n >= 0 ? (DWORD)n : 0; + return n >= 0; +} + +static inline DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, LONG *lpDistanceToMoveHigh, DWORD dwMoveMethod) +{ + off_t offset = lDistanceToMove; + if (lpDistanceToMoveHigh) offset |= ((off_t)*lpDistanceToMoveHigh << 32); + off_t result = lseek((int)(intptr_t)hFile, offset, dwMoveMethod); + if (result == (off_t)-1) { if (lpDistanceToMoveHigh) *lpDistanceToMoveHigh = -1; return INVALID_SET_FILE_POINTER; } + if (lpDistanceToMoveHigh) *lpDistanceToMoveHigh = (LONG)(result >> 32); + return (DWORD)(result & 0xFFFFFFFF); +} + +static inline DWORD GetFileAttributesA(const char *lpFileName) +{ + struct stat st{}; + if (stat(lpFileName, &st) != 0) return INVALID_FILE_ATTRIBUTES; + DWORD attrs = S_ISDIR(st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; + if (!(st.st_mode & S_IWUSR)) attrs |= FILE_ATTRIBUTE_READONLY; + const char *base = strrchr(lpFileName, '/'); base = base ? base + 1 : lpFileName; + if (base[0] == '.') attrs |= FILE_ATTRIBUTE_HIDDEN; + return attrs; +} + +static inline DWORD GetFileAttributes(const char *lpFileName) +{ + return GetFileAttributesA(lpFileName); +} + +static inline BOOL GetFileAttributesExA(const char *lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, void *lpFileInformation) +{ + if (fInfoLevelId != GetFileExInfoStandard || !lpFileInformation) return FALSE; + struct stat st{}; + if (stat(lpFileName, &st) != 0) return FALSE; + WIN32_FILE_ATTRIBUTE_DATA *out = (WIN32_FILE_ATTRIBUTE_DATA *)lpFileInformation; + out->dwFileAttributes = GetFileAttributesA(lpFileName); + out->ftCreationTime = _TimeToFileTime(st.st_mtime); + out->ftLastAccessTime = _TimeToFileTime(st.st_atime); + out->ftLastWriteTime = _TimeToFileTime(st.st_mtime); + out->nFileSizeHigh = (DWORD)((st.st_size >> 32) & 0xFFFFFFFF); + out->nFileSizeLow = (DWORD)(st.st_size & 0xFFFFFFFF); + return TRUE; +} + +static inline BOOL GetFileAttributesEx(const char *lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, void *lpFileInformation) +{ + return GetFileAttributesExA(lpFileName, fInfoLevelId, lpFileInformation); +} + +static inline BOOL CreateDirectoryA(const char *lpPathName, void *lpSecurityAttributes) +{ + return mkdir(lpPathName, 0755) == 0; +} + +static inline BOOL CreateDirectory(const char *lpPathName, void *lpSecurityAttributes) +{ + return CreateDirectoryA(lpPathName, lpSecurityAttributes); +} + +static inline BOOL DeleteFileA(const char *lpFileName) +{ + return unlink(lpFileName) == 0; +} + +static inline BOOL DeleteFile(const char *lpFileName) +{ + return DeleteFileA(lpFileName); +} + +static inline BOOL MoveFileA(const char *lpExistingFileName, const char *lpNewFileName) +{ + return rename(lpExistingFileName, lpNewFileName) == 0; +} + +static inline BOOL MoveFile(const char *lpExistingFileName, const char *lpNewFileName) +{ + return MoveFileA(lpExistingFileName, lpNewFileName); +} + +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilea +static inline HANDLE FindFirstFileA(const char *lpFileName, WIN32_FIND_DATAA *lpFindFileData) +{ + if (!lpFileName || !lpFindFileData) return INVALID_HANDLE_VALUE; + + char dirpath[MAX_PATH], pattern[MAX_PATH]; + const char *sep = strrchr(lpFileName, '/'); + if (sep) + { + size_t len = sep - lpFileName; + if (len >= MAX_PATH) return INVALID_HANDLE_VALUE; + strncpy(dirpath, lpFileName, len); dirpath[len] = '\0'; + strncpy(pattern, sep + 1, MAX_PATH - 1); + } + else + { + strncpy(dirpath, ".", MAX_PATH - 1); + strncpy(pattern, lpFileName, MAX_PATH - 1); + } + + DIR *dir = opendir(dirpath); + if (!dir) return INVALID_HANDLE_VALUE; + + _WLINUX_FIND_HANDLE *fh = (_WLINUX_FIND_HANDLE *)malloc(sizeof(_WLINUX_FIND_HANDLE)); + if (!fh) { closedir(dir); return INVALID_HANDLE_VALUE; } + fh->dir = dir; + strncpy(fh->dirpath, dirpath, MAX_PATH - 1); + strncpy(fh->pattern, pattern, MAX_PATH - 1); + + struct dirent *ent; + while ((ent = readdir(fh->dir)) != NULL) + { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; + if (fnmatch(fh->pattern, ent->d_name, 0) == 0) + { + char fullpath[MAX_PATH * 2]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", fh->dirpath, ent->d_name); + struct stat st{}; + if (stat(fullpath, &st) == 0) _FillFindData(ent->d_name, &st, lpFindFileData); + else { memset(lpFindFileData, 0, sizeof(*lpFindFileData)); strncpy(lpFindFileData->cFileName, ent->d_name, MAX_PATH - 1); } + return (HANDLE)fh; + } + } + + closedir(fh->dir); free(fh); + return INVALID_HANDLE_VALUE; +} + +static inline HANDLE FindFirstFile(const char *lpFileName, WIN32_FIND_DATAA *lpFindFileData) +{ + return FindFirstFileA(lpFileName, lpFindFileData); +} + +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilea +static inline BOOL FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATAA *lpFindFileData) +{ + if (hFindFile == INVALID_HANDLE_VALUE || !lpFindFileData) return FALSE; + _WLINUX_FIND_HANDLE *fh = (_WLINUX_FIND_HANDLE *)hFindFile; + + struct dirent *ent; + while ((ent = readdir(fh->dir)) != NULL) + { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; + if (fnmatch(fh->pattern, ent->d_name, 0) == 0) + { + char fullpath[MAX_PATH * 2]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", fh->dirpath, ent->d_name); + struct stat st{}; + if (stat(fullpath, &st) == 0) _FillFindData(ent->d_name, &st, lpFindFileData); + else { memset(lpFindFileData, 0, sizeof(*lpFindFileData)); strncpy(lpFindFileData->cFileName, ent->d_name, MAX_PATH - 1); } + return TRUE; + } + } + return FALSE; +} + +static inline BOOL FindNextFile(HANDLE hFindFile, WIN32_FIND_DATAA *lpFindFileData) +{ + return FindNextFileA(hFindFile, lpFindFileData); +} + +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findclose +static inline BOOL FindClose(HANDLE hFindFile) +{ + if (hFindFile == INVALID_HANDLE_VALUE) return FALSE; + _WLINUX_FIND_HANDLE *fh = (_WLINUX_FIND_HANDLE *)hFindFile; + closedir(fh->dir); free(fh); + return TRUE; +} + +#endif // WLINUX_H \ No newline at end of file