Files
nt-lite/code/win32/kernel32.c
T

1970 lines
72 KiB
C

// kernel32.c - Win32 personality layer
//
// Thin wrappers around syscall.dll stubs that expose a familiar Win32 API
// surface to user-mode code originally written for Windows.
//
// Design notes:
// - HANDLE is void*; we store the uint32_t kernel handle as a pointer value
// so Win32 code can pass it around opaquely without knowing its size.
// HANDLE_TO_KH / KH_TO_HANDLE do that conversion in one place.
// - INVALID_HANDLE_VALUE maps to kernel handle 0 (the always-invalid slot).
// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
// always use the single kernel user-heap. This is fine: we don't expose
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - GetLastError / SetLastError are per-thread on Windows. We keep a single
// global for now; enough for Bink and similar single-threaded guests.
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
// SysCloseFile).
// - WriteFile / CreateFileA in write-mode / SetEndOfFile are stubs because
// the VFS has no write path yet.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
#include <syscall.h>
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef short SHORT;
typedef long LONG;
typedef struct { LONG x; LONG y; } POINT;
#define MAX_PATH 260
#define TRUE 1
#define FALSE 0
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
#define MEM_COMMIT 0x00001000UL
#define MEM_RESERVE 0x00002000UL
#define MEM_RELEASE 0x00008000UL
#define MEM_FREE 0x00010000UL
#define MEM_PRIVATE 0x00020000UL
#define PAGE_NOACCESS 0x01UL
#define PAGE_READONLY 0x02UL
#define PAGE_READWRITE 0x04UL
#define PAGE_WRITECOPY 0x08UL
#define PAGE_EXECUTE 0x10UL
#define PAGE_EXECUTE_READ 0x20UL
#define PAGE_EXECUTE_READWRITE 0x40UL
typedef struct _MEMORY_BASIC_INFORMATION {
void* BaseAddress;
void* AllocationBase;
DWORD AllocationProtect;
size_t RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION;
// File-find structures
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
typedef struct _WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
char cFileName[260]; // MAX_PATH
char cAlternateFileName[14];
} WIN32_FIND_DATAA;
// CreateFileA disposition / access flags (subset used by Bink and similar)
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define OPEN_EXISTING 3
#define OPEN_ALWAYS 4
#define CREATE_ALWAYS 2
#define CREATE_NEW 1
#define TRUNCATE_EXISTING 5
#define FILE_BEGIN 0
#define FILE_CURRENT 1
#define FILE_END 2
// ---------------------------------------------------------------------------
// Handle conversion helpers
// uint32_t kernel handle <-> opaque Win32 HANDLE
// Kernel handle 0 == invalid; we map that to INVALID_HANDLE_VALUE (NULL).
// ---------------------------------------------------------------------------
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; }
static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; }
// Thread IDs are not kernel handles. Keep Win32 thread pseudo-handles out of
// the low numeric handle namespace so CloseHandle/WaitForSingleObject cannot
// accidentally operate on an unrelated file/process handle with the same value.
#define WIN32_THREAD_HANDLE_TAG ((uintptr_t)1ULL << 63)
#define WIN32_MUTEX_SENTINEL ((uintptr_t)0x4D555458UL) // 'MUTX'
#define WIN32_SEMA_SENTINEL ((uintptr_t)0x53454D41UL) // 'SEMA'
static inline HANDLE TID_TO_THREAD_HANDLE(uint32_t tid) {
return (HANDLE)(WIN32_THREAD_HANDLE_TAG | (uintptr_t)tid);
}
static inline BOOL IS_THREAD_HANDLE(HANDLE h) {
return h && h != INVALID_HANDLE_VALUE && (((uintptr_t)h & WIN32_THREAD_HANDLE_TAG) != 0);
}
static inline uint32_t THREAD_HANDLE_TO_TID(HANDLE h) {
return (uint32_t)((uintptr_t)h & 0xFFFFFFFFu);
}
static inline BOOL IS_NOOP_SYNC_HANDLE(HANDLE h) {
uintptr_t v = (uintptr_t)h;
return v == WIN32_MUTEX_SENTINEL || v == WIN32_SEMA_SENTINEL;
}
// Path translator
static void TranslatePath(const char* input, char* output) {
if (!output) return;
if (!input) { output[0] = '\0'; return; }
if (input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
// Translate "C:\" -> "0:/". Native nt-lite paths such as
// "0:/system/foo.dll" are already drive-indexed and pass through.
char drive = input[0];
if (drive >= 'a' && drive <= 'z') drive -= ('a' - 'A'); // To uppercase
output[0] = (drive >= '0' && drive <= '7') ? drive : (char)('0' + (drive - 'C'));
output[1] = ':';
output[2] = '/';
// Copy the rest, swapping backslashes to forward slashes
const char* src = input + 3;
char* dst = output + 3;
while (*src) {
*dst = (*src == '\\') ? '/' : *src;
src++; dst++;
}
*dst = '\0';
} else {
// Fallback for relative paths: just swap slashes
while (*input) {
*output = (*input == '\\') ? '/' : *input;
input++; output++;
}
*output = '\0';
}
}
static DWORD gLastError = 0;
static BOOL KiFileExistsA(const char* path) {
if (!path || !*path) return FALSE;
char native[MAX_PATH];
TranslatePath(path, native);
uint32_t h = 0;
if (SysOpenFile(native, &h) != 0) return FALSE;
SysCloseHandle(h);
return TRUE;
}
static DWORD KiCopySearchPathResult(char* out, DWORD outChars, const char* text, char** filePart) {
DWORD len = (DWORD)strlen(text);
if (out && outChars) {
DWORD copy = (len < outChars - 1) ? len : outChars - 1;
memcpy(out, text, copy);
out[copy] = '\0';
if (filePart) {
char* slash1 = strrchr(out, '/');
char* slash2 = strrchr(out, '\\');
char* slash = slash1 > slash2 ? slash1 : slash2;
*filePart = slash ? slash + 1 : out;
}
}
return len;
}
__declspec(dllexport) DWORD SearchPathA(const char* lpPath, const char* lpFileName,
const char* lpExtension, DWORD nBufferLength,
char* lpBuffer, char** lpFilePart) {
if (!lpFileName || !*lpFileName) { gLastError = 87; return 0; }
char candidate[MAX_PATH];
size_t nameLen = strlen(lpFileName);
bool hasExt = (strrchr(lpFileName, '.') != NULL);
bool rooted = (lpFileName[0] == '/' || lpFileName[0] == '\\' ||
(lpFileName[0] && lpFileName[1] == ':'));
if (rooted || strchr(lpFileName, '/') || strchr(lpFileName, '\\')) {
strncpy(candidate, lpFileName, sizeof(candidate) - 1);
candidate[sizeof(candidate) - 1] = '\0';
if (!hasExt && lpExtension && nameLen + strlen(lpExtension) + 1 < sizeof(candidate))
strcat(candidate, lpExtension);
if (KiFileExistsA(candidate))
return KiCopySearchPathResult(lpBuffer, nBufferLength, candidate, lpFilePart);
}
const char* paths = lpPath && *lpPath ? lpPath : ".";
const char* p = paths;
while (*p) {
char dir[MAX_PATH];
size_t di = 0;
while (*p && *p != ';' && di + 1 < sizeof(dir)) dir[di++] = *p++;
while (*p == ';') p++;
dir[di] = '\0';
size_t pos = 0;
if (strcmp(dir, ".") != 0) {
while (dir[pos] && pos + 1 < sizeof(candidate)) { candidate[pos] = dir[pos]; pos++; }
if (pos && candidate[pos - 1] != '/' && candidate[pos - 1] != '\\' && pos + 1 < sizeof(candidate))
candidate[pos++] = '/';
}
for (size_t i = 0; lpFileName[i] && pos + 1 < sizeof(candidate); i++) candidate[pos++] = lpFileName[i];
if (!hasExt && lpExtension) {
for (size_t i = 0; lpExtension[i] && pos + 1 < sizeof(candidate); i++) candidate[pos++] = lpExtension[i];
}
candidate[pos] = '\0';
if (KiFileExistsA(candidate))
return KiCopySearchPathResult(lpBuffer, nBufferLength, candidate, lpFilePart);
}
gLastError = 2; // ERROR_FILE_NOT_FOUND
if (lpBuffer && nBufferLength) lpBuffer[0] = '\0';
if (lpFilePart) *lpFilePart = NULL;
return 0;
}
// ---------------------------------------------------------------------------
// GetLastError / SetLastError (single-threaded stub)
// ---------------------------------------------------------------------------
static SYS_PEB* gProcessPeb = NULL;
static SYS_PEB* Win32Peb(void) {
if (!gProcessPeb) gProcessPeb = SysGetPeb();
return gProcessPeb;
}
__declspec(dllexport) DWORD GetLastError(void) {
return gLastError;
}
__declspec(dllexport) void SetLastError(DWORD err) {
gLastError = err;
}
// ---------------------------------------------------------------------------
// Process termination
// ---------------------------------------------------------------------------
__declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
SysExitProcess((int)code);
while (true) ; // unreachable
}
// ---------------------------------------------------------------------------
// Debug output
// ---------------------------------------------------------------------------
__declspec(dllexport) void OutputDebugStringA(const char* str) {
if (str) SysDebugPrint(str);
}
// ---------------------------------------------------------------------------
// Timer
// ---------------------------------------------------------------------------
__declspec(dllexport) DWORD GetTickCount(void) {
return (DWORD)(SysGetTick() / 1000);
}
__declspec(dllexport) uint64_t GetTickCount64(void) {
return SysGetTick() / 1000;
}
// ---------------------------------------------------------------------------
// Memory (heap argument is ignored - single kernel user-heap)
// ---------------------------------------------------------------------------
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap;
if (!size) return NULL;
void* ptr = SysAlloc(size);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return ptr;
}
__declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
(void)heap; (void)flags;
// Win32's HeapFree doesn't need the size; we pass 0 and the kernel's
// allocator handles it (the block header carries the size).
if (ptr) SysFree(ptr);
return TRUE;
}
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
void* ptr, size_t size) {
(void)heap; (void)flags;
void* newptr = SysRealloc(ptr, size);
if (!newptr && size) gLastError = 8;
return newptr;
}
// GetProcessHeap - return a sentinel non-NULL value so callers can pass it
// back into HeapAlloc/HeapFree without crashing (we ignore it anyway).
__declspec(dllexport) void* GetProcessHeap(void) {
return (void*)1;
}
// ---------------------------------------------------------------------------
// Virtual memory compatibility
// ---------------------------------------------------------------------------
__declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize,
DWORD flAllocationType, DWORD flProtect) {
(void)lpAddress; (void)flAllocationType; (void)flProtect;
if (!dwSize) return NULL;
void* ptr = SysAlloc(dwSize);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return ptr;
}
__declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwFreeType) {
(void)dwSize; (void)dwFreeType;
if (lpAddress) SysFree(lpAddress);
return TRUE;
}
__declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize,
DWORD flNewProtect, DWORD* lpflOldProtect) {
(void)lpAddress; (void)dwSize; (void)flNewProtect;
if (lpflOldProtect) *lpflOldProtect = PAGE_EXECUTE_READWRITE;
return TRUE;
}
__declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
MEMORY_BASIC_INFORMATION* lpBuffer,
size_t dwLength) {
if (!lpAddress || !lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION)) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return 0;
}
uintptr_t base = (uintptr_t)lpAddress & ~(uintptr_t)0xFFF;
lpBuffer->BaseAddress = (void*)base;
lpBuffer->AllocationBase = (void*)base;
lpBuffer->AllocationProtect = PAGE_EXECUTE_READWRITE;
lpBuffer->RegionSize = 0x1000;
lpBuffer->State = MEM_COMMIT;
lpBuffer->Protect = PAGE_EXECUTE_READWRITE;
lpBuffer->Type = MEM_PRIVATE;
return sizeof(MEMORY_BASIC_INFORMATION);
}
// --- Win32 API ---
// Updated GetAsyncKeyState implementation
__declspec(dllexport) SHORT GetAsyncKeyState(int vKey) {
static const struct { int vk; uint8_t sc; bool ext; } vk_to_sc[] = {
// Letters
{ 0x41, 0x1E, false }, { 0x42, 0x30, false }, { 0x43, 0x2E, false },
{ 0x44, 0x20, false }, { 0x45, 0x12, false }, { 0x46, 0x21, false },
{ 0x47, 0x22, false }, { 0x48, 0x23, false }, { 0x49, 0x17, false },
{ 0x4A, 0x24, false }, { 0x4B, 0x25, false }, { 0x4C, 0x26, false },
{ 0x4D, 0x32, false }, { 0x4E, 0x31, false }, { 0x4F, 0x18, false },
{ 0x50, 0x19, false }, { 0x51, 0x10, false }, { 0x52, 0x13, false },
{ 0x53, 0x1F, false }, { 0x54, 0x14, false }, { 0x55, 0x16, false },
{ 0x56, 0x2F, false }, { 0x57, 0x11, false }, { 0x58, 0x2D, false },
{ 0x59, 0x15, false }, { 0x5A, 0x2C, false },
// Numbers
{ 0x31, 0x02, false }, { 0x32, 0x03, false }, { 0x33, 0x04, false },
{ 0x34, 0x05, false }, { 0x35, 0x06, false }, { 0x36, 0x07, false },
{ 0x37, 0x08, false }, { 0x38, 0x09, false }, { 0x39, 0x0A, false },
{ 0x30, 0x0B, false },
// Arrows (Extended)
{ 0x26, 0x48, true }, // VK_UP
{ 0x28, 0x50, true }, // VK_DOWN
{ 0x25, 0x4B, true }, // VK_LEFT
{ 0x27, 0x4D, true }, // VK_RIGHT
// Controls
{ 0x1B, 0x01, false }, // ESC
{ 0x0D, 0x1C, false }, // ENTER
{ 0x20, 0x39, false }, // SPACE
{ 0x11, 0x1D, false }, // CTRL (Generic)
{ 0x10, 0x2A, false }, // SHIFT (Generic)
{ 0x12, 0x38, false }, // ALT (Generic)
{ 0x08, 0x0E, false }, // BACKSPACE
{ 0x09, 0x0F, false }, // TAB
// Function Keys
{ 0x70, 0x3B, false }, { 0x71, 0x3C, false }, { 0x72, 0x3D, false },
{ 0x73, 0x3E, false }, { 0x74, 0x3F, false }, { 0x75, 0x40, false },
{ 0x76, 0x41, false }, { 0x77, 0x42, false }, { 0x78, 0x43, false },
{ 0x79, 0x44, false }, { 0x7A, 0x57, false }, { 0x7B, 0x58, false },
// Symbols
{ 0xBD, 0x0C, false }, // MINUS '-'
{ 0xBB, 0x0D, false }, // EQUALS '='
{ 0xBC, 0x33, false }, // COMMA ','
{ 0xBE, 0x34, false }, // PERIOD '.'
};
for (size_t i = 0; i < sizeof(vk_to_sc)/sizeof(vk_to_sc[0]); ++i) {
if (vk_to_sc[i].vk == vKey) {
// Check the hardware state array via our HAL helper
if (SysKbIsKeyDown(vk_to_sc[i].sc, vk_to_sc[i].ext)) {
return (SHORT)0x8000;
}
break;
}
}
return 0;
}
// FlushConsoleInputBuffer: clear the keyboard input buffer
__declspec(dllexport) BOOL FlushConsoleInputBuffer(HANDLE hConsoleInput) {
(void)hConsoleInput;
// Drain the keyboard buffer by reading until empty
while (SysKbPoll()) {
(void)SysKbReadChar();
}
return TRUE;
}
// !Made-up! clear console api.
__declspec(dllexport) BOOL ClearConsoleScreenBuffer(HANDLE hConsole) {
SysScreenClear(0x00000000);
return TRUE;
}
// ---------------------------------------------------------------------------
// Locale / code-page stubs
// ---------------------------------------------------------------------------
__declspec(dllexport) int MultiByteToWideChar(unsigned int cp, DWORD flags,
const char* src, int srcLen,
unsigned short* dst, int dstLen) {
(void)cp; (void)flags;
// Trivial ASCII -> UTF-16LE expansion, good enough for English strings.
if (!src || !dstLen) return 0;
if (srcLen < 0) srcLen = (int)strlen(src) + 1;
int out = 0;
while (out < dstLen && out < srcLen) {
if (dst) dst[out] = (unsigned short)(unsigned char)src[out];
out++;
}
return out;
}
__declspec(dllexport) int WideCharToMultiByte(unsigned int cp, DWORD flags,
const unsigned short* src, int srcLen,
char* dst, int dstLen,
const char* defChar, int* usedDefault) {
(void)cp; (void)flags; (void)defChar;
if (usedDefault) *usedDefault = FALSE;
if (!src || !dstLen) return 0;
if (srcLen < 0) {
srcLen = 0;
while (src[srcLen]) srcLen++;
srcLen++;
}
int out = 0;
while (out < dstLen && out < srcLen) {
if (dst) dst[out] = (char)(src[out] < 128 ? src[out] : '?');
out++;
}
return out;
}
__declspec(dllexport) DWORD GetACP(void) { return 1252; } // Windows-1252
__declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US
__declspec(dllexport) BOOL IsDBCSLeadByteEx(unsigned int CodePage, BYTE TestChar) {
(void)CodePage; (void)TestChar;
return FALSE;
}
// ---------------------------------------------------------------------------
// File I/O
// ---------------------------------------------------------------------------
// CreateFileA - opens or creates a file.
// Supported modes:
// OPEN_EXISTING + GENERIC_READ -> open existing, read-only
// OPEN_EXISTING + GENERIC_WRITE -> open existing, read-write
// OPEN_EXISTING + GENERIC_READ|GENERIC_WRITE -> open existing, read-write
// CREATE_ALWAYS + GENERIC_WRITE -> create/truncate, write
// OPEN_ALWAYS + GENERIC_WRITE -> create if absent, write
// TRUNCATE_EXISTING + GENERIC_WRITE -> open & truncate to zero
__declspec(dllexport) HANDLE CreateFileA(const char* name,
DWORD access, DWORD share,
void* sa, DWORD disposition,
DWORD flags, void* templ) {
(void)share; (void)sa; (void)flags; (void)templ;
// Translate the path from Windows format to custom 0:/ format
char translatedName[260];
TranslatePath(name, translatedName);
int wantWrite = (access & GENERIC_WRITE) != 0;
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
// Open the file via the read path (it must already exist).
uint32_t kh = 0;
uint32_t s = SysOpenFile(translatedName, &kh);
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
if (disposition == TRUNCATE_EXISTING && wantWrite) {
// Immediately truncate to zero.
uint32_t ts = SysTruncateFile(kh, 0);
if (ts != 0) {
SysCloseHandle(kh);
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
if (disposition == CREATE_ALWAYS && wantWrite) {
// Create-or-replace: try to create; if the file already exists,
// open it and truncate it to zero instead.
uint32_t kh = 0;
uint32_t s = SysCreateFile(translatedName, &kh);
if (s != 0) {
// File may already exist - open and truncate.
s = SysOpenFile(translatedName, &kh);
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
SysTruncateFile(kh, 0);
}
return KH_TO_HANDLE(kh);
}
if (disposition == OPEN_ALWAYS && wantWrite) {
// Open if exists, create if not.
uint32_t kh = 0;
if (SysOpenFile(translatedName, &kh) != 0) {
if (SysCreateFile(translatedName, &kh) != 0) {
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
if (disposition == CREATE_NEW && wantWrite) {
// Fail if the file already exists (we can't check atomically,
// but SysCreateFile returns NAME_COLLISION for existing files).
uint32_t kh = 0;
uint32_t s = SysCreateFile(translatedName, &kh);
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
return KH_TO_HANDLE(kh);
}
// Any other combination (e.g. read-only CREATE_ALWAYS) is not supported.
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
}
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
}
// ReadFile - sequential read at the file's current position.
__declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
if (bytesRead) *bytesRead = 0;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) {
gLastError = 6;
return FALSE;
}
size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
if (bytesRead) *bytesRead = (DWORD)got;
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but consuming stale stack/heap bytes after them makes
// the failure mode look like random game-data corruption.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
if (s != 0) {
gLastError = 1;
return FALSE;
}
return TRUE;
}
// WriteFile - sequential write at the file's current position.
// Extends the file if writing past EOF. Overlapped I/O is not supported.
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)overlapped;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
if (bytesWritten) *bytesWritten = (DWORD)written;
if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT
return TRUE;
}
// SetFilePointer - seek to an absolute, relative, or end-relative position.
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint32_t kh = HANDLE_TO_KH(hFile);
int64_t delta;
if (distHigh) {
uint64_t combined = ((uint64_t)(uint32_t)(*distHigh) << 32) | (uint32_t)dist;
delta = (int64_t)combined;
} else {
delta = (int64_t)dist;
}
int64_t target = 0;
if (method == FILE_BEGIN) {
target = delta;
} else if (method == FILE_CURRENT) {
uint64_t cur = SysGetFilePos(kh);
if (cur == (uint64_t)-1 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)cur + delta;
} else if (method == FILE_END) {
uint64_t sz = SysGetFileSize(kh);
if (sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)sz + delta;
} else {
gLastError = 87; return 0xFFFFFFFFUL;
}
if (target < 0) {
gLastError = 131; // ERROR_NEGATIVE_SEEK
return 0xFFFFFFFFUL;
}
uint64_t offset = (uint64_t)target;
uint32_t s = SysSeekFile(kh, offset);
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
if (distHigh) *distHigh = (long)(int32_t)(offset >> 32);
return (DWORD)(offset & 0xFFFFFFFFULL);
}
// SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
uint32_t s = SysTruncateFile(kh, pos);
if (s != 0) { gLastError = 1; return FALSE; }
return TRUE;
}
// DeleteFileA - delete a file by name; no open handle required.
__declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
if (!lpFileName) { gLastError = 87; return FALSE; }
char translatedPath[MAX_PATH];
TranslatePath(lpFileName, translatedPath);
uint32_t s = SysDeleteFile(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
// Our thread/sentinel sync handles are not entries in the kernel handle table.
// There is nothing to close; the thread object is reaped by the scheduler.
if (IS_THREAD_HANDLE(hObject) || IS_NOOP_SYNC_HANDLE(hObject)) return TRUE;
SysCloseHandle(HANDLE_TO_KH(hObject));
return TRUE;
}
// ===========================================================================
// Extended shims - funcs.txt additions
// All functions here return plausible values; no real implementation exists
// unless a matching syscall stub is available.
// ===========================================================================
// ---------------------------------------------------------------------------
// Additional Heap APIs
// We have one kernel heap; the HANDLE is always ignored.
// ---------------------------------------------------------------------------
typedef int HEAP_INFORMATION_CLASS; // opaque
__declspec(dllexport) HANDLE HeapCreate(DWORD flOptions,
size_t dwInitialSize,
size_t dwMaximumSize) {
(void)flOptions; (void)dwInitialSize; (void)dwMaximumSize;
return (HANDLE)(uintptr_t)1; // non-NULL sentinel
}
__declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
(void)hHeap;
return TRUE;
}
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
(void)heap; (void)flags; (void)ptr;
return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported
}
__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle,
HEAP_INFORMATION_CLASS cls,
void* info, size_t infoLen) {
(void)HeapHandle; (void)cls; (void)info; (void)infoLen;
return TRUE;
}
// ---------------------------------------------------------------------------
// Process & Module pseudo-handles / queries
// ---------------------------------------------------------------------------
// Pseudo-handles matching Windows convention: -1 = process, -2 = thread.
__declspec(dllexport) HANDLE GetCurrentProcess(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)0);
}
__declspec(dllexport) HANDLE GetCurrentThread(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)1);
}
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
// Tiny TLS table; enough for MSVCRT/TCC compatibility paths that probe TLS.
#define WIN32_TLS_SLOTS 64
static void* gTlsSlots[WIN32_TLS_SLOTS];
static uint64_t gTlsBitmap = 1; // reserve slot 0
__declspec(dllexport) DWORD TlsAlloc(void) {
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
uint64_t bit = 1ULL << i;
if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; gTlsSlots[i] = NULL; return i; }
}
gLastError = 8;
return 0xFFFFFFFFUL;
}
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
gTlsSlots[dwTlsIndex] = NULL;
gTlsBitmap &= ~(1ULL << dwTlsIndex);
return TRUE;
}
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
gLastError = 0;
return gTlsSlots[dwTlsIndex];
}
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
gTlsSlots[dwTlsIndex] = lpTlsValue;
return TRUE;
}
// Pseudo HMODULEs for callers that probe already-linked personality DLLs.
#define WIN32_HMODULE_EXE ((HANDLE)(uintptr_t)0x00400000UL)
#define WIN32_HMODULE_WIN32 ((HANDLE)(uintptr_t)0x57333232UL) // W322
static int KiStricmpA(const char* a, const char* b) {
while (*a && (((unsigned char)*a | 0x20) == ((unsigned char)*b | 0x20))) { a++; b++; }
return ((unsigned char)*a | 0x20) - ((unsigned char)*b | 0x20);
}
static BOOL KiIsWin32DllNameA(const char* name) {
return name && (
!KiStricmpA(name, "kernel32.dll") || !KiStricmpA(name, "user32.dll") ||
!KiStricmpA(name, "gdi32.dll") || !KiStricmpA(name, "ole32.dll") ||
!KiStricmpA(name, "winmm.dll") || !KiStricmpA(name, "win32.dll"));
}
static void KiDbgWriteA(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
}
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
if (!lpModuleName) return WIN32_HMODULE_EXE;
if (KiIsWin32DllNameA(lpModuleName)) return WIN32_HMODULE_WIN32;
gLastError = 126; // ERROR_MOD_NOT_FOUND
return NULL;
}
__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) {
return lpModuleName ? NULL : WIN32_HMODULE_EXE;
}
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
if (KiIsWin32DllNameA(lpLibFileName)) return WIN32_HMODULE_WIN32;
gLastError = 2; // ERROR_FILE_NOT_FOUND
return NULL;
}
// Minimal GetProcAddress for code that probes kernel32 functions already present
// in this win32.dll image. Full dynamic DLL loading remains a kernel-loader job.
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule);
__declspec(dllexport) void Sleep(DWORD dwMilliseconds);
__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) {
if (hModule != WIN32_HMODULE_WIN32 || !lpProcName) { gLastError = 127; return NULL; }
// Ordinal probes are represented as small integer pointers. Do not treat
// them as strings while debugging misses.
if ((uintptr_t)lpProcName < 0x10000) {
KiDbgWriteA("GetProcAddress ordinal miss\n");
gLastError = 127;
return NULL;
}
#define GP(name) if (strcmp(lpProcName, #name) == 0) return (void*)&name
GP(GetLastError); GP(SetLastError); GP(LoadLibraryA); GP(GetProcAddress);
GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect);
GP(VirtualQuery); GP(GetTickCount); GP(GetTickCount64); GP(Sleep);
GP(TlsAlloc); GP(TlsFree); GP(TlsGetValue); GP(TlsSetValue);
#undef GP
KiDbgWriteA("GetProcAddress miss: ");
KiDbgWriteA(lpProcName);
KiDbgWriteA("\n");
gLastError = 127; // ERROR_PROC_NOT_FOUND
return NULL;
}
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
(void)hLibModule;
return TRUE;
}
__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
char* lpFilename, DWORD nSize) {
(void)hModule;
if (!lpFilename || !nSize) return 0;
SYS_PEB* peb = Win32Peb();
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath)
? peb->ImagePath
: "";
DWORD len = (DWORD)strlen(src);
DWORD copy = (len < nSize - 1) ? len : nSize - 1;
memcpy(lpFilename, src, copy);
lpFilename[copy] = '\0';
if (copy < len) gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return copy;
}
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
(void)hProcess;
SysExitProcess((int)uExitCode);
while (true) ;
}
// STARTUPINFOA - only cb matters for our stub.
typedef struct {
DWORD cb;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
DWORD dwX, dwY, dwXSize, dwYSize;
DWORD dwXCountChars, dwYCountChars;
DWORD dwFillAttribute, dwFlags;
unsigned short wShowWindow, cbReserved2;
unsigned char* lpReserved2;
HANDLE hStdInput, hStdOutput, hStdError;
} STARTUPINFOA;
__declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
if (!lpStartupInfo) return;
// Zero every field, then set the mandatory cb.
unsigned char* p = (unsigned char*)lpStartupInfo;
for (size_t i = 0; i < sizeof(*lpStartupInfo); i++) p[i] = 0;
lpStartupInfo->cb = (DWORD)sizeof(*lpStartupInfo);
}
__declspec(dllexport) char* GetCommandLineA(void) {
SYS_PEB* peb = Win32Peb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return (char*)peb->CommandLine;
}
// ---------------------------------------------------------------------------
// Threading
// ---------------------------------------------------------------------------
#define WAIT_OBJECT_0 0x00000000UL
#define WAIT_TIMEOUT 0x00000102UL
#define INFINITE 0xFFFFFFFFUL
__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
size_t dwStackSize,
void* lpStartAddress,
void* lpParameter,
DWORD dwCreationFlags,
DWORD* lpThreadId) {
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress,
(uint64_t)(uintptr_t)lpParameter,
0 /* normal priority */, &tid);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
return TID_TO_THREAD_HANDLE(tid);
}
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
// thread and other handle types are not yet waitable so return immediately.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
(void)dwMilliseconds;
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
if (IS_THREAD_HANDLE(hHandle)) {
uint32_t tid = THREAD_HANDLE_TO_TID(hHandle);
uint32_t s = SysWaitThread(tid);
if (s != 0 && s != 0x0022) { gLastError = 6; return 0xFFFFFFFFUL; }
return WAIT_OBJECT_0;
}
if (IS_NOOP_SYNC_HANDLE(hHandle)) return WAIT_OBJECT_0;
uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) {
uint32_t s = SysWaitForProcess(kh);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
}
return WAIT_OBJECT_0;
}
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
if (!commandLine || !out || outSize == 0) return FALSE;
while (*commandLine == ' ' || *commandLine == '\t') commandLine++;
if (*commandLine == '\0') return FALSE;
size_t pos = 0;
if (*commandLine == '"') {
commandLine++;
while (*commandLine && *commandLine != '"' && pos + 1 < outSize)
out[pos++] = *commandLine++;
} else {
while (*commandLine && *commandLine != ' ' && *commandLine != '\t' && pos + 1 < outSize)
out[pos++] = *commandLine++;
}
out[pos] = '\0';
return pos != 0;
}
// ---------------------------------------------------------------------------
// Process creation
// ---------------------------------------------------------------------------
typedef struct {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
#define STILL_ACTIVE 259UL
__declspec(dllexport) BOOL CreateProcessA(
const char* lpApplicationName,
char* lpCommandLine,
void* lpProcessAttributes,
void* lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
void* lpEnvironment,
const char* lpCurrentDirectory,
STARTUPINFOA* lpStartupInfo,
PROCESS_INFORMATION* lpProcessInformation) {
(void)lpProcessAttributes; (void)lpThreadAttributes;
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpStartupInfo;
// lpProcessInformation is mandatory
if (!lpProcessInformation) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return FALSE;
}
char appPath[MAX_PATH];
const char* targetPath = lpApplicationName;
if (!targetPath) {
if (!ExtractApplicationPathA(lpCommandLine, appPath, sizeof(appPath))) {
gLastError = 87;
return FALSE;
}
targetPath = appPath;
}
const char* cmdline = (lpCommandLine && lpCommandLine[0]) ? lpCommandLine : targetPath;
char translatedPath[MAX_PATH];
TranslatePath(targetPath, translatedPath);
char savedCwd[SYS_CWD_MAX];
bool changedCwd = false;
savedCwd[0] = '\0';
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
char translatedCwd[MAX_PATH];
uint32_t gs = SysGetCwd(savedCwd, sizeof(savedCwd));
if (gs != 0) { gLastError = 3; return FALSE; }
TranslatePath(lpCurrentDirectory, translatedCwd);
gs = SysSetCwd(translatedCwd);
if (gs != 0) { gLastError = 3; return FALSE; }
changedCwd = true;
}
uint32_t kh = 0;
// Call the syscall.dll stub. The kernel snapshots the current CWD into
// the child process, so the temporary lpCurrentDirectory switch can be
// restored immediately after creation.
uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline);
if (changedCwd) {
if (savedCwd[0]) {
char restoreCwd[SYS_CWD_MAX + 3];
restoreCwd[0] = '0'; restoreCwd[1] = ':'; restoreCwd[2] = '/';
size_t rlen = strlen(savedCwd);
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
memcpy(restoreCwd + 3, savedCwd, rlen);
restoreCwd[3 + rlen] = '\0';
SysSetCwd(restoreCwd);
} else {
SysSetCwd("0:/");
}
}
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
return TRUE;
}
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
*lpExitCode = (DWORD)code;
return TRUE;
}
__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) {
(void)hThread;
return 0; // previous suspend count
}
#define THREAD_PRIORITY_NORMAL 0
__declspec(dllexport) BOOL SetThreadPriority(HANDLE hThread, int nPriority) {
(void)hThread; (void)nPriority;
return TRUE;
}
__declspec(dllexport) int GetThreadPriority(HANDLE hThread) {
(void)hThread;
return THREAD_PRIORITY_NORMAL;
}
__declspec(dllexport) uintptr_t SetThreadAffinityMask(HANDLE hThread,
uintptr_t dwThreadAffinityMask) {
(void)hThread; (void)dwThreadAffinityMask;
return 1; // previous mask: CPU 0 only
}
__declspec(dllexport) void Sleep(DWORD dwMilliseconds) {
SysSleep(dwMilliseconds);
}
// ---------------------------------------------------------------------------
// Console I/O
// ---------------------------------------------------------------------------
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
// We have no real console handles; return a sentinel the other APIs recognise.
__declspec(dllexport) HANDLE GetStdHandle(DWORD nStdHandle) {
return (HANDLE)(uintptr_t)nStdHandle;
}
// ReadConsoleA: blocking read of up to nNumberOfCharsToRead characters.
// Each character is read individually via SysKbReadChar so the caller can
// do its own line-editing (echo, backspace, etc.) one char at a time.
__declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
void* lpBuffer,
DWORD nNumberOfCharsToRead,
DWORD* lpNumberOfCharsRead,
void* pInputControl) {
(void)hConsoleInput; (void)pInputControl;
if (!lpBuffer || nNumberOfCharsToRead == 0) return FALSE;
char* buf = (char*)lpBuffer;
DWORD i = 0; // Notice we switched to a while loop
while (i < nNumberOfCharsToRead) {
char c = SysKbReadChar();
// Ignore unmapped keys (Shift, Ctrl, etc.)
if (c == 0) continue;
// Deliver the raw character; the caller handles echo and line editing.
buf[i] = c;
i++;
// Stop reading when the user presses Enter
if (c == '\n' || c == '\r') {
// If your shell expects strings to be \n terminated, leave this.
// If your shell expects \0 termination without a newline,
// you might want to replace buf[i-1] with \0 here!
break;
}
}
if (lpNumberOfCharsRead) *lpNumberOfCharsRead = i;
return TRUE;
}
// WriteConsoleA: write nNumberOfCharsToWrite characters from lpBuffer to the
// console as white-on-black text via SYS_WRITE_CONSOLE.
__declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput,
const void* lpBuffer,
DWORD nNumberOfCharsToWrite,
DWORD* lpNumberOfCharsWritten,
void* lpReserved) {
(void)hConsoleOutput; (void)lpReserved;
if (!lpBuffer || nNumberOfCharsToWrite == 0) {
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
return TRUE;
}
SysWriteConsole((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;
}
// ---------------------------------------------------------------------------
// Directory enumeration (FindFirstFileA / FindNextFileA / FindClose)
// ---------------------------------------------------------------------------
// KDIR_ENTRY layout must exactly match the definition in io_file.h.
#define KDIR_NAME_MAX 260
typedef struct {
char Name[KDIR_NAME_MAX];
uint8_t Attr;
uint32_t FileSize;
} KDIR_ENTRY;
// FIND_HANDLE is a user-side wrapper: it stores the kernel dir handle and
// the wildcard pattern so FindNextFileA can filter subsequent entries.
// We return a pointer to this struct cast to HANDLE; it is NOT a kernel
// handle, so FindClose must NOT go through CloseHandle/SysCloseHandle.
typedef struct {
uint32_t kh; // kernel DIR_OBJECT handle (raw pointer)
char pattern[MAX_PATH]; // wildcard pattern, e.g. "*" or "*.txt"
} FIND_HANDLE;
// Minimal wildcard match: supports '*' (any sequence) and '?' (any char).
// Case-insensitive to match FAT32/Windows behaviour.
static int WildMatch(const char* pat, const char* name) {
while (*pat && *name) {
if (*pat == '*') {
// Skip consecutive stars
while (*pat == '*') pat++;
if (!*pat) return 1; // trailing * matches everything
// Try matching the rest of the pattern at every position
while (*name) {
if (WildMatch(pat, name)) return 1;
name++;
}
return 0;
}
char p = (*pat >= 'A' && *pat <= 'Z') ? *pat + 32 : *pat;
char n = (*name >= 'A' && *name <= 'Z') ? *name + 32 : *name;
if (*pat != '?' && p != n) return 0;
pat++; name++;
}
// Consume trailing stars in the pattern
while (*pat == '*') pat++;
return (*pat == '\0' && *name == '\0');
}
// Split a FindFirstFileA-style search path into directory and pattern parts.
// "apps\\*" -> dir="apps" pat="*"
// "*.txt" -> dir="" pat="*.txt"
// "*" -> dir="" pat="*"
// "apps\\" -> dir="apps" pat="*"
static void SplitFindPath(const char* searchPath,
char* dirOut, size_t dirSize,
char* patOut, size_t patSize) {
// Find last backslash or forward-slash
const char* last = NULL;
for (const char* p = searchPath; *p; p++)
if (*p == '\\' || *p == '/') last = p;
if (!last) {
// No separator: whole string is the pattern, dir is CWD
if (dirOut && dirSize) dirOut[0] = '\0';
if (patOut && patSize) {
size_t len = strlen(searchPath);
if (len + 1 > patSize) len = patSize - 1;
memcpy(patOut, searchPath, len);
patOut[len] = '\0';
}
} else {
// Everything before the last separator is the directory
if (dirOut) {
size_t len = (size_t)(last - searchPath);
if (len + 1 > dirSize) len = dirSize - 1;
memcpy(dirOut, searchPath, len);
dirOut[len] = '\0';
}
// Everything after the last separator is the pattern
if (patOut) {
const char* p = last + 1;
size_t len = strlen(p);
if (len + 1 > patSize) len = patSize - 1;
memcpy(patOut, p, len);
patOut[len] = '\0';
}
}
// Empty pattern defaults to "*"
if (patOut && patOut[0] == '\0') {
patOut[0] = '*'; patOut[1] = '\0';
}
}
// Fill a WIN32_FIND_DATAA from a KDIR_ENTRY.
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010UL
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020UL
#define FILE_ATTRIBUTE_NORMAL 0x00000080UL
static void KdirToFindData(const KDIR_ENTRY* ke, WIN32_FIND_DATAA* fd) {
// Zero the entire struct (covers all FILETIME and reserved fields)
unsigned char* p = (unsigned char*)fd;
for (size_t i = 0; i < sizeof(*fd); i++) p[i] = 0;
fd->dwFileAttributes = ke->Attr;
if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(fd->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
fd->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
fd->nFileSizeLow = ke->FileSize;
fd->nFileSizeHigh = 0;
size_t nameLen = strlen(ke->Name);
if (nameLen >= MAX_PATH) nameLen = MAX_PATH - 1;
memcpy(fd->cFileName, ke->Name, nameLen);
fd->cFileName[nameLen] = '\0';
}
__declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
WIN32_FIND_DATAA* lpFindFileData) {
if (!lpFileName || !lpFindFileData) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
}
// Translate the path
char translatedInput[MAX_PATH];
TranslatePath(lpFileName, translatedInput);
// Split path into directory and wildcard pattern
char dir[MAX_PATH];
char pat[MAX_PATH];
SplitFindPath(translatedInput, dir, sizeof(dir), pat, sizeof(pat));
// Open the kernel directory iterator
uint32_t kh = 0;
uint32_t s = SysOpenDir(dir[0] ? dir : NULL, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE;
}
// Allocate our user-side FIND_HANDLE wrapper
FIND_HANDLE* fh = (FIND_HANDLE*)HeapAlloc(NULL, 0, sizeof(FIND_HANDLE));
if (!fh) {
SysCloseHandle(kh);
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return INVALID_HANDLE_VALUE;
}
fh->kh = kh;
size_t patLen = strlen(pat);
if (patLen >= MAX_PATH) patLen = MAX_PATH - 1;
memcpy(fh->pattern, pat, patLen);
fh->pattern[patLen] = '\0';
// Read the first entry that matches the pattern
KDIR_ENTRY ke;
for (;;) {
uint32_t rs = SysReadDir(kh, &ke);
if (rs != 0) {
// Directory exhausted or error
SysCloseHandle(kh);
HeapFree(NULL, 0, fh);
gLastError = 18; // ERROR_NO_MORE_FILES
return INVALID_HANDLE_VALUE;
}
if (WildMatch(fh->pattern, ke.Name)) {
KdirToFindData(&ke, lpFindFileData);
return (HANDLE)fh;
}
}
}
__declspec(dllexport) BOOL FindNextFileA(HANDLE hFindFile,
WIN32_FIND_DATAA* lpFindFileData) {
if (hFindFile == INVALID_HANDLE_VALUE || !lpFindFileData) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
KDIR_ENTRY ke;
for (;;) {
uint32_t rs = SysReadDir(fh->kh, &ke);
if (rs != 0) {
gLastError = 18; // ERROR_NO_MORE_FILES
return FALSE;
}
if (WildMatch(fh->pattern, ke.Name)) {
KdirToFindData(&ke, lpFindFileData);
return TRUE;
}
}
}
__declspec(dllexport) BOOL FindClose(HANDLE hFindFile) {
if (hFindFile == INVALID_HANDLE_VALUE) {
gLastError = 6;
return FALSE;
}
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
SysCloseHandle(fh->kh);
HeapFree(NULL, 0, fh);
return TRUE;
}
// ---------------------------------------------------------------------------
// Current Working Directory
// ---------------------------------------------------------------------------
__declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
if (!lpPathName) { gLastError = 87; return FALSE; }
char translatedPath[MAX_PATH];
TranslatePath(lpPathName, translatedPath);
uint32_t s = SysSetCwd(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
__declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) {
if (!lpBuffer || nBufferLength == 0) {
// Return required size (including NUL). Max CWD is 512 bytes + "C:\\".
return 515;
}
// Kernel returns native "N:/path". Present it as a Win32 drive letter.
char raw[512];
uint32_t s = SysGetCwd(raw, sizeof(raw));
if (s != 0) { gLastError = 1; return 0; }
char full[515];
char drive = 'C';
const char* rel = raw;
if (raw[0] >= '0' && raw[0] <= '7' && raw[1] == ':' &&
(raw[2] == '\\' || raw[2] == '/')) {
drive = (char)('C' + (raw[0] - '0'));
rel = raw + 3;
}
full[0] = drive;
full[1] = ':';
full[2] = '\\';
size_t rawLen = strlen(rel);
if (rawLen + 4 > sizeof(full)) rawLen = sizeof(full) - 4;
memcpy(full + 3, rel, rawLen);
full[3 + rawLen] = '\0';
for (char* p = full + 3; *p; p++) if (*p == '/') *p = '\\';
size_t needed = strlen(full) + 1;
if ((DWORD)needed > nBufferLength) {
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return (DWORD)needed;
}
memcpy(lpBuffer, full, needed);
return (DWORD)(needed - 1); // length without NUL, matching Win32
}
// ---------------------------------------------------------------------------
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)WIN32_MUTEX_SENTINEL;
}
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
(void)hMutex;
return TRUE;
}
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
long lInitialCount, long lMaximumCount,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName;
return (HANDLE)WIN32_SEMA_SENTINEL;
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
long* lpPreviousCount) {
(void)hSemaphore; (void)lReleaseCount;
if (lpPreviousCount) *lpPreviousCount = 0;
return TRUE;
}
// ---------------------------------------------------------------------------
// Critical Sections & Condition Variables
// Single-threaded: all locks are no-ops.
// ---------------------------------------------------------------------------
// Minimal opaque types; callers allocate these from their Windows headers -
// we just need the pointer to be accepted by our stubs.
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS;
}
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
DWORD dwSpinCount) {
(void)lpCS; (void)dwSpinCount;
return TRUE;
}
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS; return TRUE;
}
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv,
CRITICAL_SECTION* cs,
DWORD dwMilliseconds) {
(void)cv; (void)cs; (void)dwMilliseconds;
return TRUE;
}
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
// ---------------------------------------------------------------------------
// Timing & Performance Counters
// ---------------------------------------------------------------------------
typedef union {
struct { DWORD LowPart; long HighPart; } u;
long long QuadPart;
} LARGE_INTEGER;
static volatile long long gTscFreq = 0;
// Attempt to read TSC frequency from CPUID leaf 0x15 (Modern Intel/AMD).
static long long TscFreqFromCpuid(void) {
int info[4];
__cpuid(info, 0);
if ((unsigned)info[0] < 0x15) return 0;
__cpuid(info, 0x15);
unsigned denom = (unsigned)info[0];
unsigned numer = (unsigned)info[1];
unsigned crystal_hz = (unsigned)info[2];
if (!denom || !numer || !crystal_hz) return 0;
return (long long)crystal_hz * numer / denom;
}
// Calibrate TSC against SysGetTick (1 unit = 1 microsecond).
static long long TscFreqCalibrate(void) {
// We'll wait for 100ms (100,000 microseconds) to get a stable sample.
const uint64_t CALIBRATION_US = 100000;
uint64_t t0 = SysGetTick();
uint64_t tsc0 = __rdtsc();
// Simple busy wait for the duration
while ((SysGetTick() - t0) < CALIBRATION_US) {
_mm_pause();
}
uint64_t t1 = SysGetTick();
uint64_t tsc1 = __rdtsc();
uint64_t delta_tsc = tsc1 - tsc0;
uint64_t delta_us = t1 - t0;
// Hz = (Delta TSC / Delta Microseconds) * 1,000,000
return (long long)(delta_tsc * 1000000ULL / delta_us);
}
static long long TscFreqGet(void) {
if (gTscFreq) return gTscFreq;
long long f = TscFreqFromCpuid();
if (!f) f = TscFreqCalibrate();
gTscFreq = f;
return f;
}
__declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) {
if (lpFrequency) lpFrequency->QuadPart = TscFreqGet();
return TRUE;
}
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
// Ensure calibration happens before the first read.
TscFreqGet();
if (lpPerformanceCount)
lpPerformanceCount->QuadPart = (long long)__rdtsc();
return TRUE;
}
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
if (!lpSystemTimeAsFileTime) return;
// Baseline: 2024-01-01 00:00:00 UTC in 100-ns intervals
const unsigned long long BASELINE_FT = 133485408000000000ULL;
// SysGetTick() is in microseconds.
// 1 microsecond = 10 units of 100-nanoseconds.
unsigned long long now_us = SysGetTick();
unsigned long long ft = BASELINE_FT + (now_us * 10ULL);
// Copy the 64-bit value into the output buffer.
// We use memcpy to safely handle potential alignment issues with the void pointer.
memcpy(lpSystemTimeAsFileTime, &ft, sizeof(ft));
}
// ---------------------------------------------------------------------------
// System Information
// ---------------------------------------------------------------------------
typedef struct {
union {
DWORD dwOemId;
struct { unsigned short wProcessorArchitecture;
unsigned short wReserved; };
};
DWORD dwPageSize;
void* lpMinimumApplicationAddress;
void* lpMaximumApplicationAddress;
uintptr_t dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
unsigned short wProcessorLevel;
unsigned short wProcessorRevision;
} SYSTEM_INFO;
__declspec(dllexport) void GetSystemInfo(SYSTEM_INFO* lpSystemInfo) {
if (!lpSystemInfo) return;
unsigned char* p = (unsigned char*)lpSystemInfo;
for (size_t i = 0; i < sizeof(*lpSystemInfo); i++) p[i] = 0;
lpSystemInfo->wProcessorArchitecture = 9; // PROCESSOR_ARCHITECTURE_AMD64
lpSystemInfo->dwPageSize = 4096;
lpSystemInfo->lpMinimumApplicationAddress = (void*)0x10000;
lpSystemInfo->lpMaximumApplicationAddress = (void*)0x7FFFFFFEFFFFull;
lpSystemInfo->dwActiveProcessorMask = 1;
lpSystemInfo->dwNumberOfProcessors = 1;
lpSystemInfo->dwProcessorType = 8664; // PROCESSOR_AMD_X8664
lpSystemInfo->dwAllocationGranularity= 65536;
lpSystemInfo->wProcessorLevel = 6;
lpSystemInfo->wProcessorRevision = 0;
}
typedef struct {
DWORD dwLength;
DWORD dwMemoryLoad;
unsigned long long ullTotalPhys;
unsigned long long ullAvailPhys;
unsigned long long ullTotalPageFile;
unsigned long long ullAvailPageFile;
unsigned long long ullTotalVirtual;
unsigned long long ullAvailVirtual;
unsigned long long ullAvailExtendedVirtual;
} MEMORYSTATUSEX;
__declspec(dllexport) BOOL GetLogicalProcessorInformation(void* Buffer,
DWORD* ReturnedLength) {
(void)Buffer;
// Signal that the buffer is too small so callers know to handle failure.
if (ReturnedLength) *ReturnedLength = 0;
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return FALSE;
}
#define MAX_PATH 260
__declspec(dllexport) unsigned int GetSystemDirectoryA(char* lpBuffer, unsigned int uSize) {
static const char sysdir[] = "C:\\Windows\\System32";
unsigned int needed = sizeof(sysdir); // includes NUL
if (lpBuffer && uSize >= needed) {
for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = sysdir[i];
}
return needed - 1; // return length without NUL, matching Win32
}
__declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned int uSize) {
static const char windir[] = "C:\\Windows";
unsigned int needed = sizeof(windir);
if (lpBuffer && uSize >= needed) {
for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = windir[i];
}
return needed - 1;
}
static const char* LookupStaticEnvA(const char* name) {
if (!name) return NULL;
if (strcmp(name, "HOME") == 0) return ".";
if (strcmp(name, "USERPROFILE") == 0) return ".";
if (strcmp(name, "HOMEDRIVE") == 0) return "C:";
if (strcmp(name, "HOMEPATH") == 0) return "\\";
return NULL;
}
__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer,
DWORD nSize) {
const char* value = LookupStaticEnvA(lpName);
if (!value) {
if (lpBuffer && nSize) lpBuffer[0] = '\0';
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND
return 0;
}
DWORD len = (DWORD)strlen(value);
if (!lpBuffer || nSize == 0) return len;
if (len + 1 > nSize) {
if (nSize) lpBuffer[0] = '\0';
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return len + 1;
}
memcpy(lpBuffer, value, len + 1);
return len;
}
__declspec(dllexport) char* GetEnvironmentStrings(void) {
// Double-NUL-terminated environment block.
static char env[] = "HOME=.\0USERPROFILE=.\0HOMEDRIVE=C:\0HOMEPATH=\\\0\0";
return env;
}
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
(void)penv; return TRUE;
}
__declspec(dllexport) unsigned short* GetEnvironmentStringsW(void) {
static unsigned short emptyEnvW[2] = { 0, 0 };
return emptyEnvW;
}
__declspec(dllexport) BOOL FreeEnvironmentStringsW(unsigned short* penv) {
(void)penv; return TRUE;
}
// ---------------------------------------------------------------------------
// Handle / Error helpers
// ---------------------------------------------------------------------------
__declspec(dllexport) unsigned int SetErrorMode(unsigned int uMode) {
(void)uMode;
return 0; // previous mode
}
__declspec(dllexport) unsigned int SetHandleCount(unsigned int uNumber) {
return uNumber < 256 ? 256 : uNumber;
}
// ---------------------------------------------------------------------------
// File helpers
// ---------------------------------------------------------------------------
#define FILE_TYPE_UNKNOWN 0x0000
#define FILE_TYPE_DISK 0x0001
#define FILE_TYPE_CHAR 0x0002
#define FILE_TYPE_PIPE 0x0003
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
__declspec(dllexport) DWORD GetFileType(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) return FILE_TYPE_UNKNOWN;
return FILE_TYPE_DISK;
}
// ---------------------------------------------------------------------------
// Exception Handling & Debugging
// ---------------------------------------------------------------------------
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
(void)First; (void)Handler;
// Return a non-NULL sentinel so callers can pass it to RemoveVectoredExceptionHandler.
return (void*)(uintptr_t)0x56455843UL; // 'VEXC'
}
__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) {
(void)Handle;
return 1; // success
}
__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
const uintptr_t* lpArguments) {
(void)dwExceptionCode; (void)dwExceptionFlags;
(void)nNumberOfArguments; (void)lpArguments;
// Best-effort: nothing to unwind; just halt.
while (true) ;
}
typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS;
__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
(void)ExceptionInfo;
return EXCEPTION_CONTINUE_SEARCH;
}
typedef long (*LPTOP_LEVEL_EXCEPTION_FILTER)(EXCEPTION_POINTERS*);
__declspec(dllexport) LPTOP_LEVEL_EXCEPTION_FILTER
SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) {
(void)lpFilter;
return NULL; // previous filter: none
}
__declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// ---------------------------------------------------------------------------
// RTL Exception / Unwind (SEH infrastructure - all stubs)
// ---------------------------------------------------------------------------
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
unsigned int EndAddress;
unsigned int UnwindInfoAddress;
} RUNTIME_FUNCTION;
__declspec(dllexport) BOOL RtlAddFunctionTable(RUNTIME_FUNCTION* FunctionTable,
DWORD EntryCount,
unsigned long long BaseAddress) {
(void)FunctionTable; (void)EntryCount; (void)BaseAddress;
return TRUE;
}
__declspec(dllexport) BOOL RtlDeleteFunctionTable(RUNTIME_FUNCTION* FunctionTable) {
(void)FunctionTable;
return TRUE;
}
// We have no runtime function table, so RtlLookupFunctionEntry always fails.
__declspec(dllexport) RUNTIME_FUNCTION*
RtlLookupFunctionEntry(unsigned long long ControlPc,
unsigned long long* ImageBase,
void* HistoryTable) {
(void)ControlPc; (void)HistoryTable;
if (ImageBase) *ImageBase = 0;
return NULL;
}
__declspec(dllexport) void* RtlVirtualUnwind(unsigned long HandlerType,
unsigned long long ImageBase,
unsigned long long ControlPc,
RUNTIME_FUNCTION* FunctionEntry,
void* ContextRecord,
void** HandlerData,
unsigned long long* EstablisherFrame,
void* ContextPointers) {
(void)HandlerType; (void)ImageBase; (void)ControlPc; (void)FunctionEntry;
(void)ContextRecord; (void)ContextPointers;
if (HandlerData) *HandlerData = NULL;
if (EstablisherFrame)*EstablisherFrame = 0;
return NULL;
}
__declspec(dllexport) void RtlCaptureContext(void* ContextRecord) {
// Zero out the context record so callers don't read garbage.
if (!ContextRecord) return;
// CONTEXT is ~1232 bytes on x64; zero a conservative upper bound.
unsigned char* p = (unsigned char*)ContextRecord;
for (int i = 0; i < 1232; i++) p[i] = 0;
}
__declspec(dllexport) void RtlUnwindEx(void* TargetFrame, void* TargetIp,
void* ExceptionRecord, void* ReturnValue,
void* ContextRecord, void* HistoryTable) {
(void)TargetFrame; (void)TargetIp; (void)ExceptionRecord;
(void)ReturnValue; (void)ContextRecord; (void)HistoryTable;
// No real unwind possible; halt rather than corrupt the stack silently.
while (true) ;
}
// ---------------------------------------------------------------------------
// Code Pages & Locale helpers
// ---------------------------------------------------------------------------
typedef struct { unsigned int MaxCharSize; unsigned char DefaultChar[2]; unsigned char LeadByte[12]; } CPINFO;
__declspec(dllexport) BOOL GetCPInfo(unsigned int CodePage, CPINFO* lpCPInfo) {
(void)CodePage;
if (lpCPInfo) {
lpCPInfo->MaxCharSize = 1;
lpCPInfo->DefaultChar[0] = '?';
lpCPInfo->DefaultChar[1] = 0;
for (int i = 0; i < 12; i++) lpCPInfo->LeadByte[i] = 0;
}
return TRUE;
}
__declspec(dllexport) BOOL IsValidCodePage(unsigned int CodePage) {
// Acknowledge the two code pages we claim to support.
return (CodePage == 1252 || CodePage == 437) ? TRUE : FALSE;
}
__declspec(dllexport) int LCMapStringA(unsigned int Locale, DWORD dwMapFlags,
const char* lpSrcStr, int cchSrc,
char* lpDestStr, int cchDest) {
(void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc;
(void)lpDestStr; (void)cchDest;
return 0;
}
__declspec(dllexport) int LCMapStringW(unsigned int Locale, DWORD dwMapFlags,
const unsigned short* lpSrcStr, int cchSrc,
unsigned short* lpDestStr, int cchDest) {
(void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc;
(void)lpDestStr; (void)cchDest;
return 0;
}
__declspec(dllexport) BOOL GetStringTypeA(unsigned int Locale, DWORD dwInfoType,
const char* lpSrcStr, int cchSrc,
unsigned short* lpCharType) {
(void)Locale; (void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType;
return FALSE;
}
__declspec(dllexport) BOOL GetStringTypeW(DWORD dwInfoType,
const unsigned short* lpSrcStr, int cchSrc,
unsigned short* lpCharType) {
(void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType;
return FALSE;
}
__declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
char* lpLCData, int cchData) {
(void)Locale; (void)LCType; (void)lpLCData; (void)cchData;
return 0;
}
// ---------------------------------------------------------------------------
// Fiber Local Storage (FLS)
// We implement a minimal 64-slot static table so callers that use FLS for
// thread-local-ish state don't immediately fail.
// ---------------------------------------------------------------------------
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
static void* gFlsSlots[FLS_MAX_SLOTS];
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
__declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
(void)lpCallback;
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
if (!gFlsUsed[i]) {
gFlsUsed[i] = 1;
gFlsSlots[i] = NULL;
return (DWORD)i;
}
}
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return (DWORD)FLS_OUT_OF_INDEXES;
}
__declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return NULL;
}
return gFlsSlots[dwFlsIndex];
}
__declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsSlots[dwFlsIndex] = lpFlsData;
return TRUE;
}
__declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsUsed[dwFlsIndex] = 0;
gFlsSlots[dwFlsIndex] = NULL;
return TRUE;
}
// ---------------------------------------------------------------------------
// Pointer encoding (security cookie obfuscation)
// We have no security cookie; return the pointer unchanged.
// ---------------------------------------------------------------------------
__declspec(dllexport) void* EncodePointer(void* Ptr) { return Ptr; }
__declspec(dllexport) void* DecodePointer(void* Ptr) { return Ptr; }