add a realtime scheduler class and priority-inheritance mutexes with timeouts and abandonment, add an HDA audio driver and a SYS_AUDIO_* syscall family, flesh out kernel32 with Win32 threads, events, mutexes, critical sections, and condition variables, add SYS_RESUME_THREAD for suspended thread creation, add KeInitializeMemoryTypes to reprogram MTRRs and install a canonical PAT for WB/WC/UC control, rework the display pipeline with a pitched present path and console output flush, bring back disk.vhd with mtools since vvfat fucking sucks, harden AHCI with serialized cache flush, USB mass storage with SCSI sense-data decoding, and xHCI with cleaner DMA teardown, fix the last stray for(;;) in HalStopProcessor to while(true), pile on smoke tests for audio, win32 sync primitives, and thread suspend, implement AHCI timeout, kill readahead since it was desperetaly trying to murder me, fix doom sound x2.
This commit is contained in:
1 parent
8b254f84f4
commit
4add6789ba
177 files changed
+22565
-3070
No files matched your search
@@ -39,6 +39,54 @@ typedef long LONG;
|
||||
typedef struct { LONG x; LONG y; } POINT;
|
||||
typedef struct { SHORT X; SHORT Y; } COORD;
|
||||
typedef struct { SHORT Left; SHORT Top; SHORT Right; SHORT Bottom; } SMALL_RECT;
|
||||
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
|
||||
typedef struct { void* _ptr; } CONDITION_VARIABLE;
|
||||
|
||||
// Forward declarations used by the process-local GetProcAddress surface.
|
||||
__declspec(dllexport) HANDLE CreateEventA(void*, BOOL, BOOL, const char*);
|
||||
__declspec(dllexport) HANDLE CreateEventW(void*, BOOL, BOOL, const unsigned short*);
|
||||
__declspec(dllexport) BOOL SetEvent(HANDLE);
|
||||
__declspec(dllexport) BOOL ResetEvent(HANDLE);
|
||||
__declspec(dllexport) _Noreturn void ExitThread(DWORD);
|
||||
__declspec(dllexport) HANDLE CreateThread(void*, size_t, void*, void*, DWORD, DWORD*);
|
||||
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE, DWORD);
|
||||
__declspec(dllexport) HANDLE CreateMutexA(void*, BOOL, const char*);
|
||||
__declspec(dllexport) HANDLE CreateMutexW(void*, BOOL, const unsigned short*);
|
||||
__declspec(dllexport) BOOL ReleaseMutex(HANDLE);
|
||||
__declspec(dllexport) BOOL CloseHandle(HANDLE);
|
||||
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION*);
|
||||
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION*, DWORD);
|
||||
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION*);
|
||||
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION*);
|
||||
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION*);
|
||||
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION*);
|
||||
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE*);
|
||||
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE*, CRITICAL_SECTION*, DWORD);
|
||||
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE*);
|
||||
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE*);
|
||||
__declspec(dllexport) BOOL IsDebuggerPresent(void);
|
||||
extern void waveOutGetNumDevs(void);
|
||||
extern void waveOutOpen(void);
|
||||
extern void waveOutClose(void);
|
||||
extern void waveOutPrepareHeader(void);
|
||||
extern void waveOutUnprepareHeader(void);
|
||||
extern void waveOutWrite(void);
|
||||
extern void waveOutPause(void);
|
||||
extern void waveOutRestart(void);
|
||||
extern void waveOutReset(void);
|
||||
extern void waveOutSetVolume(void);
|
||||
extern void waveOutGetVolume(void);
|
||||
extern void waveOutGetPosition(void);
|
||||
extern void waveOutGetID(void);
|
||||
extern void waveOutGetDevCapsA(void);
|
||||
extern void waveOutGetDevCapsW(void);
|
||||
extern void waveOutGetErrorTextA(void);
|
||||
extern void waveOutGetErrorTextW(void);
|
||||
extern void waveOutBreakLoop(void);
|
||||
extern void waveOutMessage(void);
|
||||
extern void timeGetTime(void);
|
||||
extern void timeBeginPeriod(void);
|
||||
extern void timeEndPeriod(void);
|
||||
|
||||
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
|
||||
COORD dwSize;
|
||||
@@ -318,6 +366,9 @@ static DWORD* K32LastErrorSlot(void) {
|
||||
|
||||
#define gLastError (*K32LastErrorSlot())
|
||||
|
||||
// Returns a stable nonzero identifier for the calling thread.
|
||||
static DWORD K32CurrentTidOrOne(void);
|
||||
|
||||
// Implements the file Exists A operation.
|
||||
static BOOL KiFileExistsA(const char* path) {
|
||||
if (!path || !*path) return FALSE;
|
||||
@@ -976,11 +1027,189 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// CloseHandle - unified close for files, threads, mutexes, semaphores, events, queues, and processes.
|
||||
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
|
||||
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
|
||||
#define K32_MUTEX_MAGIC 0x58544D4Bu
|
||||
|
||||
SysCloseHandle(HANDLE_TO_KH(hObject));
|
||||
typedef struct K32_MUTEX_HANDLE {
|
||||
struct K32_MUTEX_HANDLE* Next;
|
||||
uint32_t Magic;
|
||||
volatile long StateLock;
|
||||
volatile long ApiUsers;
|
||||
uint32_t NativeHandle;
|
||||
DWORD OwnerTid;
|
||||
uint32_t Recursion;
|
||||
uint32_t Waiters;
|
||||
volatile long Closing;
|
||||
} K32_MUTEX_HANDLE;
|
||||
|
||||
static K32_MUTEX_HANDLE* gK32MutexRegistry;
|
||||
static volatile long gK32MutexRegistryLock;
|
||||
|
||||
// Acquires one short process-local mutex registry lock.
|
||||
static void K32MutexRegistryLock(void) {
|
||||
while (_InterlockedCompareExchange(&gK32MutexRegistryLock, 1, 0) != 0)
|
||||
(void)SysYield();
|
||||
}
|
||||
|
||||
// Releases the process-local mutex registry lock.
|
||||
static void K32MutexRegistryUnlock(void) {
|
||||
_InterlockedExchange(&gK32MutexRegistryLock, 0);
|
||||
}
|
||||
|
||||
// Acquires one short mutex-wrapper state lock.
|
||||
static void K32MutexStateLock(K32_MUTEX_HANDLE* mutex) {
|
||||
while (_InterlockedCompareExchange(&mutex->StateLock, 1, 0) != 0)
|
||||
(void)SysYield();
|
||||
}
|
||||
|
||||
// Releases one mutex-wrapper state lock.
|
||||
static void K32MutexStateUnlock(K32_MUTEX_HANDLE* mutex) {
|
||||
_InterlockedExchange(&mutex->StateLock, 0);
|
||||
}
|
||||
|
||||
// Registers one process-local Win32 mutex wrapper.
|
||||
static void K32RegisterMutex(K32_MUTEX_HANDLE* mutex) {
|
||||
K32MutexRegistryLock();
|
||||
mutex->Next = gK32MutexRegistry;
|
||||
gK32MutexRegistry = mutex;
|
||||
K32MutexRegistryUnlock();
|
||||
}
|
||||
|
||||
// References one registered Win32 mutex without dereferencing unknown handles.
|
||||
static K32_MUTEX_HANDLE* K32ReferenceMutex(HANDLE handle) {
|
||||
K32_MUTEX_HANDLE* found = NULL;
|
||||
K32MutexRegistryLock();
|
||||
for (K32_MUTEX_HANDLE* current = gK32MutexRegistry;
|
||||
current;
|
||||
current = current->Next) {
|
||||
if ((HANDLE)current == handle &&
|
||||
current->Magic == K32_MUTEX_MAGIC &&
|
||||
_InterlockedCompareExchange(¤t->Closing, 0, 0) == 0) {
|
||||
_InterlockedIncrement(¤t->ApiUsers);
|
||||
found = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
K32MutexRegistryUnlock();
|
||||
return found;
|
||||
}
|
||||
|
||||
// Releases one mutex-wrapper API reference.
|
||||
static void K32DereferenceMutex(K32_MUTEX_HANDLE* mutex) {
|
||||
if (mutex)
|
||||
_InterlockedDecrement(&mutex->ApiUsers);
|
||||
}
|
||||
|
||||
// Removes one mutex wrapper so no new API references can be acquired.
|
||||
static BOOL K32RemoveMutex(K32_MUTEX_HANDLE* mutex) {
|
||||
BOOL removed = FALSE;
|
||||
K32MutexRegistryLock();
|
||||
K32_MUTEX_HANDLE** link = &gK32MutexRegistry;
|
||||
while (*link) {
|
||||
if (*link == mutex) {
|
||||
*link = mutex->Next;
|
||||
mutex->Next = NULL;
|
||||
removed = TRUE;
|
||||
break;
|
||||
}
|
||||
link = &(*link)->Next;
|
||||
}
|
||||
K32MutexRegistryUnlock();
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Waits on one recursive Win32 mutex wrapper.
|
||||
static DWORD K32WaitMutex(K32_MUTEX_HANDLE* mutex, DWORD milliseconds) {
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
K32MutexStateLock(mutex);
|
||||
if (_InterlockedCompareExchange(&mutex->Closing, 0, 0) != 0) {
|
||||
K32MutexStateUnlock(mutex);
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
if (mutex->OwnerTid == tid) {
|
||||
if (mutex->Recursion == UINT32_MAX) {
|
||||
K32MutexStateUnlock(mutex);
|
||||
gLastError = 8;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
mutex->Recursion++;
|
||||
K32MutexStateUnlock(mutex);
|
||||
return 0;
|
||||
}
|
||||
mutex->Waiters++;
|
||||
K32MutexStateUnlock(mutex);
|
||||
|
||||
uint64_t timeout = milliseconds == 0xFFFFFFFFUL
|
||||
? SYS_WAIT_TIMEOUT_INFINITE
|
||||
: (uint64_t)milliseconds;
|
||||
uint32_t status = SysWaitSingle(mutex->NativeHandle, SYS_WAIT_FLAG_NONE, timeout);
|
||||
|
||||
K32MutexStateLock(mutex);
|
||||
if (mutex->Waiters)
|
||||
mutex->Waiters--;
|
||||
if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_ABANDONED) {
|
||||
mutex->OwnerTid = tid;
|
||||
mutex->Recursion = 1;
|
||||
}
|
||||
K32MutexStateUnlock(mutex);
|
||||
|
||||
if (status == NTLSTATUS_SUCCESS)
|
||||
return 0;
|
||||
if (status == NTLSTATUS_ABANDONED)
|
||||
return 0x00000080UL;
|
||||
if (status == NTLSTATUS_TIMEOUT)
|
||||
return 0x00000102UL;
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
// Closes one idle Win32 mutex wrapper.
|
||||
static BOOL K32CloseMutex(K32_MUTEX_HANDLE* mutex) {
|
||||
K32MutexStateLock(mutex);
|
||||
BOOL busy = mutex->OwnerTid != 0 || mutex->Waiters != 0;
|
||||
if (!busy && _InterlockedCompareExchange(&mutex->Closing, 1, 0) != 0)
|
||||
busy = TRUE;
|
||||
K32MutexStateUnlock(mutex);
|
||||
if (busy) {
|
||||
gLastError = 170;
|
||||
K32DereferenceMutex(mutex);
|
||||
return FALSE;
|
||||
}
|
||||
if (!K32RemoveMutex(mutex)) {
|
||||
gLastError = 6;
|
||||
K32DereferenceMutex(mutex);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (mutex->ApiUsers > 1)
|
||||
(void)SysYield();
|
||||
uint32_t status = SysCloseHandle(mutex->NativeHandle);
|
||||
mutex->Magic = 0;
|
||||
K32DereferenceMutex(mutex);
|
||||
free(mutex);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
gLastError = 6;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Closes one Win32 or native kernel handle.
|
||||
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
|
||||
if (!hObject || hObject == INVALID_HANDLE_VALUE) {
|
||||
gLastError = 6;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hObject);
|
||||
if (mutex)
|
||||
return K32CloseMutex(mutex);
|
||||
|
||||
uint32_t status = SysCloseHandle(HANDLE_TO_KH(hObject));
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
gLastError = 6;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1087,12 +1316,32 @@ static int KiStricmpA(const char* a, const char* b) {
|
||||
return ((unsigned char)*a | 0x20) - ((unsigned char)*b | 0x20);
|
||||
}
|
||||
|
||||
// Checks whether win32 Dll Name A.
|
||||
// Matches one Win32 module basename with its optional DLL suffix.
|
||||
static BOOL KiMatchesWin32DllNameA(const char* name, const char* baseName) {
|
||||
if (!name || !baseName)
|
||||
return FALSE;
|
||||
|
||||
while (*name && *baseName &&
|
||||
(((unsigned char)*name | 0x20u) == ((unsigned char)*baseName | 0x20u))) {
|
||||
name++;
|
||||
baseName++;
|
||||
}
|
||||
|
||||
if (*baseName)
|
||||
return FALSE;
|
||||
if (!*name)
|
||||
return TRUE;
|
||||
return KiStricmpA(name, ".dll") == 0;
|
||||
}
|
||||
|
||||
// Checks whether one name identifies a process-local Win32 personality module.
|
||||
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"));
|
||||
return KiMatchesWin32DllNameA(name, "kernel32") ||
|
||||
KiMatchesWin32DllNameA(name, "user32") ||
|
||||
KiMatchesWin32DllNameA(name, "gdi32") ||
|
||||
KiMatchesWin32DllNameA(name, "ole32") ||
|
||||
KiMatchesWin32DllNameA(name, "winmm") ||
|
||||
KiMatchesWin32DllNameA(name, "win32");
|
||||
}
|
||||
|
||||
// Implements the dbg Write A operation.
|
||||
@@ -1100,11 +1349,13 @@ static void KiDbgWriteA(const char* s) {
|
||||
if (s) K32WriteConsoleSpan(s, strlen(s));
|
||||
}
|
||||
|
||||
// Returns module Handle A.
|
||||
// Returns the process-local module handle for one ANSI module name.
|
||||
__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
|
||||
if (!lpModuleName)
|
||||
return WIN32_HMODULE_EXE;
|
||||
if (KiIsWin32DllNameA(lpModuleName))
|
||||
return WIN32_HMODULE_WIN32;
|
||||
gLastError = 126; // ERROR_MOD_NOT_FOUND.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1175,13 +1426,31 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
|
||||
return (void*)address;
|
||||
}
|
||||
|
||||
#define GP(name) if (strcmp(lpProcName, #name) == 0) return (void*)&name
|
||||
#define GP(name) do { \
|
||||
if (strcmp(lpProcName, #name) == 0) return (void*)&name; \
|
||||
} while (0)
|
||||
GP(GetLastError); GP(SetLastError); GP(LoadLibraryA); GP(GetProcAddress);
|
||||
GP(IsDebuggerPresent);
|
||||
GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect);
|
||||
GP(VirtualQuery); GP(GetTickCount); GP(GetTickCount64); GP(Sleep);
|
||||
GP(CreateEventA); GP(CreateEventW); GP(SetEvent); GP(ResetEvent);
|
||||
GP(CreateThread); GP(ExitThread); GP(WaitForSingleObject);
|
||||
GP(CreateMutexA); GP(CreateMutexW); GP(ReleaseMutex); GP(CloseHandle);
|
||||
GP(InitializeCriticalSection); GP(InitializeCriticalSectionAndSpinCount);
|
||||
GP(DeleteCriticalSection); GP(EnterCriticalSection); GP(LeaveCriticalSection);
|
||||
GP(TryEnterCriticalSection); GP(InitializeConditionVariable);
|
||||
GP(SleepConditionVariableCS); GP(WakeConditionVariable); GP(WakeAllConditionVariable);
|
||||
GP(TlsAlloc); GP(TlsFree); GP(TlsGetValue); GP(TlsSetValue);
|
||||
GP(GetConsoleScreenBufferInfo); GP(FillConsoleOutputCharacterA);
|
||||
GP(FillConsoleOutputAttribute); GP(SetConsoleCursorPosition);
|
||||
GP(waveOutGetNumDevs); GP(waveOutOpen); GP(waveOutClose);
|
||||
GP(waveOutPrepareHeader); GP(waveOutUnprepareHeader); GP(waveOutWrite);
|
||||
GP(waveOutPause); GP(waveOutRestart); GP(waveOutReset);
|
||||
GP(waveOutSetVolume); GP(waveOutGetVolume); GP(waveOutGetPosition); GP(waveOutGetID);
|
||||
GP(waveOutGetDevCapsA); GP(waveOutGetDevCapsW);
|
||||
GP(waveOutGetErrorTextA); GP(waveOutGetErrorTextW);
|
||||
GP(waveOutBreakLoop); GP(waveOutMessage);
|
||||
GP(timeGetTime); GP(timeBeginPeriod); GP(timeEndPeriod);
|
||||
#undef GP
|
||||
KiDbgWriteA("GetProcAddress miss: ");
|
||||
KiDbgWriteA(lpProcName);
|
||||
@@ -1275,53 +1544,163 @@ __declspec(dllexport) char* GetCommandLineA(void) {
|
||||
#define WAIT_OBJECT_0 0x00000000UL
|
||||
#define WAIT_TIMEOUT 0x00000102UL
|
||||
#define INFINITE 0xFFFFFFFFUL
|
||||
#define CREATE_SUSPENDED 0x00000004UL
|
||||
|
||||
typedef DWORD (__stdcall *K32_THREAD_START_ROUTINE)(void* parameter);
|
||||
|
||||
typedef struct K32_THREAD_START_CONTEXT {
|
||||
K32_THREAD_START_ROUTINE StartRoutine;
|
||||
void* Parameter;
|
||||
} K32_THREAD_START_CONTEXT;
|
||||
|
||||
// Invokes one Win32 thread procedure and terminates the native thread on return.
|
||||
static void K32ThreadStart(uintptr_t argument) {
|
||||
K32_THREAD_START_CONTEXT* context = (K32_THREAD_START_CONTEXT*)argument;
|
||||
K32_THREAD_START_ROUTINE startRoutine = context->StartRoutine;
|
||||
void* parameter = context->Parameter;
|
||||
free(context);
|
||||
|
||||
DWORD exitCode = startRoutine(parameter);
|
||||
SysExitThread((int32_t)exitCode);
|
||||
while (true) {
|
||||
(void)SysYield();
|
||||
}
|
||||
}
|
||||
|
||||
// Creates one Win32 thread with return-to-ExitThread semantics.
|
||||
__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
|
||||
size_t dwStackSize,
|
||||
void* lpStartAddress,
|
||||
void* lpParameter,
|
||||
DWORD dwCreationFlags,
|
||||
DWORD* lpThreadId) {
|
||||
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
|
||||
(void)lpThreadAttributes;
|
||||
(void)dwStackSize;
|
||||
if (!lpStartAddress || (dwCreationFlags & ~CREATE_SUSPENDED)) {
|
||||
gLastError = 87;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
K32_THREAD_START_CONTEXT* context =
|
||||
(K32_THREAD_START_CONTEXT*)malloc(sizeof(*context));
|
||||
if (!context) {
|
||||
gLastError = 8;
|
||||
return NULL;
|
||||
}
|
||||
context->StartRoutine = (K32_THREAD_START_ROUTINE)lpStartAddress;
|
||||
context->Parameter = lpParameter;
|
||||
|
||||
uint32_t tid = 0;
|
||||
uint32_t kh = 0;
|
||||
SYS_CREATE_THREAD_REQUEST req = {
|
||||
(uintptr_t)lpStartAddress,
|
||||
(uintptr_t)lpParameter,
|
||||
0,
|
||||
(uintptr_t)K32ThreadStart,
|
||||
(uintptr_t)context,
|
||||
0,
|
||||
(dwCreationFlags & CREATE_SUSPENDED)
|
||||
? SYS_CREATE_THREAD_FLAG_SUSPENDED
|
||||
: SYS_CREATE_THREAD_FLAG_NONE,
|
||||
&kh,
|
||||
&tid
|
||||
};
|
||||
uint32_t s = SysCreateThread(&req);
|
||||
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
|
||||
if (lpThreadId) *lpThreadId = (DWORD)tid;
|
||||
uint32_t status = SysCreateThread(&req);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
free(context);
|
||||
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 11;
|
||||
return NULL;
|
||||
}
|
||||
if (lpThreadId)
|
||||
*lpThreadId = (DWORD)tid;
|
||||
return KH_TO_HANDLE(kh);
|
||||
}
|
||||
|
||||
// WaitForSingleObject - blocks on native waitable handles. A zero timeout is
|
||||
// only a probe for mutexes/semaphores until native timeout waits exist.
|
||||
// Terminates the calling Win32 thread with the supplied exit code.
|
||||
__declspec(dllexport) _Noreturn void ExitThread(DWORD exitCode) {
|
||||
SysExitThread((int32_t)exitCode);
|
||||
while (true) {
|
||||
(void)SysYield();
|
||||
}
|
||||
}
|
||||
|
||||
// Waits for one Win32 mutex wrapper or native waitable handle.
|
||||
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
|
||||
if (!IS_VALID_KERNEL_HANDLE(hHandle)) { gLastError = 6; return 0xFFFFFFFFUL; }
|
||||
|
||||
uint32_t kh = HANDLE_TO_KH(hHandle);
|
||||
if (dwMilliseconds == 0) {
|
||||
uint32_t s = SysTryAcquireMutex(kh);
|
||||
if (s == 0) return WAIT_OBJECT_0;
|
||||
|
||||
s = SysTryWaitSem(kh);
|
||||
if (s == 0) return WAIT_OBJECT_0;
|
||||
|
||||
return WAIT_TIMEOUT;
|
||||
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) {
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
uint32_t s = SysWait(kh, SYS_WAIT_FLAG_NONE);
|
||||
if (s == 0) return WAIT_OBJECT_0;
|
||||
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hHandle);
|
||||
if (mutex) {
|
||||
DWORD result = K32WaitMutex(mutex, dwMilliseconds);
|
||||
K32DereferenceMutex(mutex);
|
||||
return result;
|
||||
}
|
||||
if (!IS_VALID_KERNEL_HANDLE(hHandle)) {
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
uint64_t timeout = dwMilliseconds == INFINITE
|
||||
? SYS_WAIT_TIMEOUT_INFINITE
|
||||
: (uint64_t)dwMilliseconds;
|
||||
uint32_t s = SysWaitSingle(HANDLE_TO_KH(hHandle), SYS_WAIT_FLAG_NONE, timeout);
|
||||
if (s == NTLSTATUS_SUCCESS) return WAIT_OBJECT_0;
|
||||
if (s == NTLSTATUS_ABANDONED) return 0x00000080UL;
|
||||
if (s == NTLSTATUS_TIMEOUT) return WAIT_TIMEOUT;
|
||||
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
// Creates one unnamed native event with Win32 reset semantics.
|
||||
__declspec(dllexport) HANDLE CreateEventA(void* lpEventAttributes,
|
||||
BOOL bManualReset,
|
||||
BOOL bInitialState,
|
||||
const char* lpName) {
|
||||
(void)lpEventAttributes;
|
||||
if (lpName && lpName[0]) {
|
||||
gLastError = 120;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t handle = 0;
|
||||
uint32_t status = SysCreateEvent(bManualReset ? 1u : 0u,
|
||||
bInitialState ? 1u : 0u,
|
||||
&handle);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 87;
|
||||
return NULL;
|
||||
}
|
||||
return KH_TO_HANDLE(handle);
|
||||
}
|
||||
|
||||
// Creates one unnamed native event from the wide Win32 entry point.
|
||||
__declspec(dllexport) HANDLE CreateEventW(void* lpEventAttributes,
|
||||
BOOL bManualReset,
|
||||
BOOL bInitialState,
|
||||
const unsigned short* lpName) {
|
||||
if (lpName && lpName[0]) {
|
||||
gLastError = 120;
|
||||
return NULL;
|
||||
}
|
||||
return CreateEventA(lpEventAttributes, bManualReset, bInitialState, NULL);
|
||||
}
|
||||
|
||||
// Signals one native event.
|
||||
__declspec(dllexport) BOOL SetEvent(HANDLE hEvent) {
|
||||
if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; }
|
||||
uint32_t status = SysSignalEvent(HANDLE_TO_KH(hEvent));
|
||||
if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; }
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Clears one native event.
|
||||
__declspec(dllexport) BOOL ResetEvent(HANDLE hEvent) {
|
||||
if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; }
|
||||
uint32_t status = SysClearEvent(HANDLE_TO_KH(hEvent));
|
||||
if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; }
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the extract Application Path A operation.
|
||||
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
|
||||
if (!commandLine || !out || outSize == 0) return FALSE;
|
||||
@@ -1449,10 +1828,22 @@ __declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements the resume Thread operation.
|
||||
// Decrements a thread's suspend count and makes it runnable at zero.
|
||||
__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) {
|
||||
(void)hThread;
|
||||
return 0; // previous suspend count
|
||||
if (!hThread || hThread == INVALID_HANDLE_VALUE) {
|
||||
gLastError = 6;
|
||||
return 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
uint32_t previousSuspendCount = 0;
|
||||
uint32_t status = SysResumeThread(HANDLE_TO_KH(hThread),
|
||||
&previousSuspendCount);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
gLastError = status == NTLSTATUS_ACCESS_DENIED ? 5 : 6;
|
||||
return 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
return previousSuspendCount;
|
||||
}
|
||||
|
||||
#define THREAD_PRIORITY_NORMAL 0
|
||||
@@ -1876,7 +2267,7 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
|
||||
|
||||
// Read the first entry that matches the pattern.
|
||||
KDIR_ENTRY ke;
|
||||
for (;;) {
|
||||
while (true) {
|
||||
uint32_t rs = SysReadDir(kh, &ke);
|
||||
if (rs != 0) {
|
||||
// Directory exhausted or error.
|
||||
@@ -1902,7 +2293,7 @@ __declspec(dllexport) BOOL FindNextFileA(HANDLE hFindFile,
|
||||
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
|
||||
KDIR_ENTRY ke;
|
||||
|
||||
for (;;) {
|
||||
while (true) {
|
||||
uint32_t rs = SysReadDir(fh->kh, &ke);
|
||||
if (rs != 0) {
|
||||
gLastError = 18; // ERROR_NO_MORE_FILES
|
||||
@@ -1979,28 +2370,88 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
|
||||
return (DWORD)(needed - 1); // length without NUL, matching Win32
|
||||
}
|
||||
|
||||
// Creates one unnamed recursive Win32 mutex over a native kernel mutex.
|
||||
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
|
||||
const char* lpName) {
|
||||
(void)lpMutexAttributes; (void)lpName;
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysCreateMutex(&kh);
|
||||
if (s != 0 || !kh) { gLastError = 8; return NULL; }
|
||||
(void)lpMutexAttributes;
|
||||
if (lpName && lpName[0]) {
|
||||
gLastError = 120;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
K32_MUTEX_HANDLE* mutex = (K32_MUTEX_HANDLE*)calloc(1, sizeof(*mutex));
|
||||
if (!mutex) {
|
||||
gLastError = 8;
|
||||
return NULL;
|
||||
}
|
||||
uint32_t status = SysCreateMutex(&mutex->NativeHandle);
|
||||
if (status != NTLSTATUS_SUCCESS || !mutex->NativeHandle) {
|
||||
free(mutex);
|
||||
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 1;
|
||||
return NULL;
|
||||
}
|
||||
mutex->Magic = K32_MUTEX_MAGIC;
|
||||
if (bInitialOwner) {
|
||||
s = SysWait(kh, SYS_WAIT_FLAG_NONE);
|
||||
if (s != 0) {
|
||||
SysCloseHandle(kh);
|
||||
status = SysWait(mutex->NativeHandle, SYS_WAIT_FLAG_NONE);
|
||||
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_ABANDONED) {
|
||||
(void)SysCloseHandle(mutex->NativeHandle);
|
||||
free(mutex);
|
||||
gLastError = 1;
|
||||
return NULL;
|
||||
}
|
||||
mutex->OwnerTid = K32CurrentTidOrOne();
|
||||
mutex->Recursion = 1;
|
||||
}
|
||||
return KH_TO_HANDLE(kh);
|
||||
K32RegisterMutex(mutex);
|
||||
return (HANDLE)mutex;
|
||||
}
|
||||
|
||||
// Releases mutex.
|
||||
// Creates one unnamed recursive Win32 mutex from a wide-name request.
|
||||
__declspec(dllexport) HANDLE CreateMutexW(void* lpMutexAttributes, BOOL bInitialOwner,
|
||||
const unsigned short* lpName) {
|
||||
if (lpName && lpName[0]) {
|
||||
gLastError = 120;
|
||||
return NULL;
|
||||
}
|
||||
return CreateMutexA(lpMutexAttributes, bInitialOwner, NULL);
|
||||
}
|
||||
|
||||
// Releases one recursion level from a Win32 mutex wrapper.
|
||||
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
|
||||
if (!IS_VALID_KERNEL_HANDLE(hMutex)) { gLastError = 6; return FALSE; }
|
||||
uint32_t s = SysReleaseMutex(HANDLE_TO_KH(hMutex));
|
||||
if (s != 0) { gLastError = 6; return FALSE; }
|
||||
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hMutex);
|
||||
if (!mutex) {
|
||||
gLastError = 6;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
K32MutexStateLock(mutex);
|
||||
if (mutex->OwnerTid != tid || mutex->Recursion == 0) {
|
||||
K32MutexStateUnlock(mutex);
|
||||
K32DereferenceMutex(mutex);
|
||||
gLastError = 288;
|
||||
return FALSE;
|
||||
}
|
||||
mutex->Recursion--;
|
||||
if (mutex->Recursion != 0) {
|
||||
K32MutexStateUnlock(mutex);
|
||||
K32DereferenceMutex(mutex);
|
||||
return TRUE;
|
||||
}
|
||||
mutex->OwnerTid = 0;
|
||||
K32MutexStateUnlock(mutex);
|
||||
|
||||
uint32_t status = SysReleaseMutex(mutex->NativeHandle);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
K32MutexStateLock(mutex);
|
||||
mutex->OwnerTid = tid;
|
||||
mutex->Recursion = 1;
|
||||
K32MutexStateUnlock(mutex);
|
||||
K32DereferenceMutex(mutex);
|
||||
gLastError = 288;
|
||||
return FALSE;
|
||||
}
|
||||
K32DereferenceMutex(mutex);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -2030,8 +2481,19 @@ __declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCoun
|
||||
// using the storage as a small recursive spin lock. Bink uses these from its
|
||||
// worker/copy paths, so the old no-op implementation allowed internal state
|
||||
// races as soon as CreateThread started working.
|
||||
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
|
||||
typedef struct { void* _ptr; } CONDITION_VARIABLE;
|
||||
typedef struct K32_CONDITION_WAITER K32_CONDITION_WAITER;
|
||||
typedef struct K32_CONDITION_STATE {
|
||||
volatile long Lock;
|
||||
K32_CONDITION_WAITER* Head;
|
||||
K32_CONDITION_WAITER* Tail;
|
||||
} K32_CONDITION_STATE;
|
||||
|
||||
struct K32_CONDITION_WAITER {
|
||||
K32_CONDITION_WAITER* Previous;
|
||||
K32_CONDITION_WAITER* Next;
|
||||
uint32_t Event;
|
||||
BOOL Queued;
|
||||
};
|
||||
|
||||
#define K32_CS_LOCK 0
|
||||
#define K32_CS_OWNER 1
|
||||
@@ -2067,8 +2529,12 @@ __declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
lpCS->_opaque[K32_CS_RECURSION]++;
|
||||
return;
|
||||
}
|
||||
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
|
||||
uint32_t spins = 0;
|
||||
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0) {
|
||||
_mm_pause();
|
||||
if ((++spins & 0xFFFu) == 0)
|
||||
(void)SysYield();
|
||||
}
|
||||
lpCS->_opaque[K32_CS_OWNER] = tid;
|
||||
lpCS->_opaque[K32_CS_RECURSION] = 1;
|
||||
}
|
||||
@@ -2100,19 +2566,183 @@ __declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Initializes condition Variable.
|
||||
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
|
||||
// Acquires one condition-variable queue lock.
|
||||
static void K32ConditionLock(K32_CONDITION_STATE* state) {
|
||||
uint32_t spins = 0;
|
||||
while (_InterlockedCompareExchange(&state->Lock, 1, 0) != 0) {
|
||||
_mm_pause();
|
||||
if ((++spins & 0xFFFu) == 0)
|
||||
(void)SysYield();
|
||||
}
|
||||
}
|
||||
|
||||
// Releases one condition-variable queue lock.
|
||||
static void K32ConditionUnlock(K32_CONDITION_STATE* state) {
|
||||
_ReadWriteBarrier();
|
||||
_InterlockedExchange(&state->Lock, 0);
|
||||
}
|
||||
|
||||
// Returns the lazily allocated process-local condition-variable state.
|
||||
static K32_CONDITION_STATE* K32ConditionState(CONDITION_VARIABLE* cv, BOOL create) {
|
||||
if (!cv)
|
||||
return NULL;
|
||||
|
||||
K32_CONDITION_STATE* state = (K32_CONDITION_STATE*)cv->_ptr;
|
||||
if (state || !create)
|
||||
return state;
|
||||
|
||||
K32_CONDITION_STATE* allocated = (K32_CONDITION_STATE*)calloc(1, sizeof(*allocated));
|
||||
if (!allocated)
|
||||
return NULL;
|
||||
|
||||
void* previous = _InterlockedCompareExchangePointer((void* volatile*)&cv->_ptr,
|
||||
allocated,
|
||||
NULL);
|
||||
if (previous) {
|
||||
free(allocated);
|
||||
state = (K32_CONDITION_STATE*)previous;
|
||||
} else {
|
||||
state = allocated;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
// Removes one waiter while the condition-variable queue lock is held.
|
||||
static void K32ConditionRemoveWaiter(K32_CONDITION_STATE* state,
|
||||
K32_CONDITION_WAITER* waiter) {
|
||||
if (!waiter->Queued)
|
||||
return;
|
||||
|
||||
if (waiter->Previous)
|
||||
waiter->Previous->Next = waiter->Next;
|
||||
else
|
||||
state->Head = waiter->Next;
|
||||
|
||||
if (waiter->Next)
|
||||
waiter->Next->Previous = waiter->Previous;
|
||||
else
|
||||
state->Tail = waiter->Previous;
|
||||
|
||||
waiter->Previous = NULL;
|
||||
waiter->Next = NULL;
|
||||
waiter->Queued = FALSE;
|
||||
}
|
||||
|
||||
// Initializes one process-local condition variable.
|
||||
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) {
|
||||
if (cv)
|
||||
cv->_ptr = NULL;
|
||||
}
|
||||
|
||||
// Atomically releases a critical section and waits for one condition wake.
|
||||
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv,
|
||||
CRITICAL_SECTION* cs,
|
||||
DWORD dwMilliseconds) {
|
||||
(void)cv; (void)cs; (void)dwMilliseconds;
|
||||
return TRUE;
|
||||
if (!cv || !cs) {
|
||||
gLastError = 87;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD tid = K32CurrentTidOrOne();
|
||||
uintptr_t recursion = cs->_opaque[K32_CS_RECURSION];
|
||||
if ((DWORD)cs->_opaque[K32_CS_OWNER] != tid || recursion == 0) {
|
||||
gLastError = 87;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
K32_CONDITION_STATE* state = K32ConditionState(cv, TRUE);
|
||||
if (!state) {
|
||||
gLastError = 8;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
uint32_t event = 0;
|
||||
KSTATUS status = SysCreateEvent(0, 0, &event);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 87;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// The waiter is heap-backed because process termination can stop a user
|
||||
// thread at syscall return before it can remove a stack-backed queue node.
|
||||
K32_CONDITION_WAITER* waiter = (K32_CONDITION_WAITER*)calloc(1, sizeof(*waiter));
|
||||
if (!waiter) {
|
||||
(void)SysCloseHandle(event);
|
||||
gLastError = 8;
|
||||
return FALSE;
|
||||
}
|
||||
waiter->Event = event;
|
||||
waiter->Queued = TRUE;
|
||||
|
||||
K32ConditionLock(state);
|
||||
waiter->Previous = state->Tail;
|
||||
if (state->Tail)
|
||||
state->Tail->Next = waiter;
|
||||
else
|
||||
state->Head = waiter;
|
||||
state->Tail = waiter;
|
||||
K32ConditionUnlock(state);
|
||||
|
||||
// The waiter is visible before the lock is released, so a wake that races
|
||||
// this transition is retained by the private auto-reset event.
|
||||
cs->_opaque[K32_CS_RECURSION] = 0;
|
||||
cs->_opaque[K32_CS_OWNER] = 0;
|
||||
_ReadWriteBarrier();
|
||||
_InterlockedExchange(K32CsLockWord(cs), 0);
|
||||
|
||||
uint64_t timeout = dwMilliseconds == INFINITE
|
||||
? SYS_WAIT_TIMEOUT_INFINITE
|
||||
: (uint64_t)dwMilliseconds;
|
||||
status = SysWaitSingle(event, SYS_WAIT_FLAG_NONE, timeout);
|
||||
|
||||
K32ConditionLock(state);
|
||||
BOOL wakeWon = waiter->Queued ? FALSE : TRUE;
|
||||
K32ConditionRemoveWaiter(state, waiter);
|
||||
K32ConditionUnlock(state);
|
||||
|
||||
EnterCriticalSection(cs);
|
||||
cs->_opaque[K32_CS_RECURSION] = recursion;
|
||||
(void)SysCloseHandle(event);
|
||||
free(waiter);
|
||||
|
||||
if (status == NTLSTATUS_SUCCESS || wakeWon)
|
||||
return TRUE;
|
||||
if (status == NTLSTATUS_TIMEOUT)
|
||||
gLastError = 1460;
|
||||
else
|
||||
gLastError = 6;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Wakes the oldest waiter on one condition variable.
|
||||
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) {
|
||||
K32_CONDITION_STATE* state = K32ConditionState(cv, FALSE);
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
K32ConditionLock(state);
|
||||
K32_CONDITION_WAITER* waiter = state->Head;
|
||||
if (waiter) {
|
||||
K32ConditionRemoveWaiter(state, waiter);
|
||||
(void)SysSignalEvent(waiter->Event);
|
||||
}
|
||||
K32ConditionUnlock(state);
|
||||
}
|
||||
|
||||
// Wakes every waiter currently queued on one condition variable.
|
||||
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) {
|
||||
K32_CONDITION_STATE* state = K32ConditionState(cv, FALSE);
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
K32ConditionLock(state);
|
||||
while (state->Head) {
|
||||
K32_CONDITION_WAITER* waiter = state->Head;
|
||||
K32ConditionRemoveWaiter(state, waiter);
|
||||
(void)SysSignalEvent(waiter->Event);
|
||||
}
|
||||
K32ConditionUnlock(state);
|
||||
}
|
||||
// Implements the wake Condition Variable operation.
|
||||
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
|
||||
// Implements the wake All Condition Variable operation.
|
||||
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
|
||||
|
||||
// Timing & Performance Counters.
|
||||
|
||||
@@ -2961,7 +3591,7 @@ SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) {
|
||||
return NULL; // previous filter: none
|
||||
}
|
||||
|
||||
// Checks whether debugger Present.
|
||||
// Reports that no user-mode debugger is attached.
|
||||
__declspec(dllexport) BOOL IsDebuggerPresent(void) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -1,126 +1,28 @@
|
||||
// Win32 multimedia compatibility stubs. This file provides minimal timing and sound API entry points.
|
||||
// Win32 multimedia timing entry points.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
// Types.
|
||||
typedef unsigned int MMRESULT;
|
||||
typedef void* HWAVEOUT;
|
||||
typedef unsigned int MMRESULT;
|
||||
|
||||
#define MMSYSERR_NOERROR 0
|
||||
#define MMSYSERR_NOTSUPPORTED 8
|
||||
#define WAVERR_STILLPLAYING 33
|
||||
|
||||
// WAVEFORMATEX - standard PCM audio format descriptor.
|
||||
typedef struct {
|
||||
unsigned short wFormatTag;
|
||||
unsigned short nChannels;
|
||||
unsigned int nSamplesPerSec;
|
||||
unsigned int nAvgBytesPerSec;
|
||||
unsigned short nBlockAlign;
|
||||
unsigned short wBitsPerSample;
|
||||
unsigned short cbSize;
|
||||
} WAVEFORMATEX;
|
||||
|
||||
// WAVEHDR - buffer header submitted to waveOutWrite.
|
||||
typedef struct _WAVEHDR {
|
||||
char* lpData;
|
||||
unsigned int dwBufferLength;
|
||||
unsigned int dwBytesRecorded;
|
||||
uintptr_t dwUser;
|
||||
unsigned int dwFlags;
|
||||
unsigned int dwLoops;
|
||||
struct _WAVEHDR* lpNext;
|
||||
uintptr_t reserved;
|
||||
} WAVEHDR;
|
||||
|
||||
// MMTIME - used by waveOutGetPosition.
|
||||
typedef struct {
|
||||
unsigned int wType;
|
||||
union {
|
||||
unsigned int ms;
|
||||
unsigned int sample;
|
||||
unsigned int cb;
|
||||
unsigned int ticks;
|
||||
} u;
|
||||
} MMTIME;
|
||||
|
||||
// Timer.
|
||||
#define MMSYSERR_NOERROR 0u
|
||||
|
||||
// Returns monotonic milliseconds since boot.
|
||||
__declspec(dllexport) unsigned long timeGetTime(void) {
|
||||
uint64_t frequency = 0;
|
||||
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS || !frequency) return 0;
|
||||
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS || !frequency)
|
||||
return 0;
|
||||
return (unsigned long)SysTicksToMilliseconds(SysGetTick(), frequency);
|
||||
}
|
||||
|
||||
// Waveform audio (stubs - no audio hardware at this stage).
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutOpen(HWAVEOUT* phwo, unsigned int deviceId,
|
||||
const WAVEFORMATEX* fmt, uintptr_t callback,
|
||||
uintptr_t callbackInstance, unsigned long flags) {
|
||||
(void)deviceId; (void)fmt; (void)callback; (void)callbackInstance; (void)flags;
|
||||
if (phwo) *phwo = (void*)0;
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
// Implements the wave Out Close operation.
|
||||
__declspec(dllexport) MMRESULT waveOutClose(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// Implements the wave Out Write operation.
|
||||
__declspec(dllexport) MMRESULT waveOutWrite(HWAVEOUT hwo, WAVEHDR* hdr, unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
// Implements the wave Out Pause operation.
|
||||
__declspec(dllexport) MMRESULT waveOutPause(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// Implements the wave Out Restart operation.
|
||||
__declspec(dllexport) MMRESULT waveOutRestart(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// Implements the wave Out Reset operation.
|
||||
__declspec(dllexport) MMRESULT waveOutReset(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// Implements the wave Out Set Volume operation.
|
||||
__declspec(dllexport) MMRESULT waveOutSetVolume(HWAVEOUT hwo, unsigned long volume) {
|
||||
(void)hwo; (void)volume; return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutGetPosition(HWAVEOUT hwo, MMTIME* mmt,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)size;
|
||||
if (mmt) { mmt->wType = 0; mmt->u.ms = 0; }
|
||||
// Accepts timer-resolution requests without reprogramming the native timer.
|
||||
__declspec(dllexport) MMRESULT timeBeginPeriod(unsigned int periodMilliseconds) {
|
||||
(void)periodMilliseconds;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// Timer resolution control
|
||||
// No hardware timer reprogramming at this stage; pretend success so callers
|
||||
// that wrap their audio/game loop in timeBeginPeriod(1) don't bail out.
|
||||
__declspec(dllexport) MMRESULT timeBeginPeriod(unsigned int uPeriod) {
|
||||
(void)uPeriod;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// Implements the time End Period operation.
|
||||
__declspec(dllexport) MMRESULT timeEndPeriod(unsigned int uPeriod) {
|
||||
(void)uPeriod;
|
||||
// Releases one timer-resolution request without changing native timer policy.
|
||||
__declspec(dllexport) MMRESULT timeEndPeriod(unsigned int periodMilliseconds) {
|
||||
(void)periodMilliseconds;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user