Finally solved the Symbolic Link corruption bug using the Dxbx implementation of EmuFile

This commit is contained in:
Luke Usher 2016-08-26 08:37:07 +01:00
parent 0a51fdf535
commit 0aae6c5f67
3 changed files with 258 additions and 404 deletions

View File

@ -81,19 +81,6 @@ std::string DeviceHarddisk0Partition18 = "\\Device\\Harddisk0\\partition18";
std::string DeviceHarddisk0Partition19 = "\\Device\\Harddisk0\\partition19";
std::string DeviceHarddisk0Partition20 = "\\Device\\Harddisk0\\partition20"; // 20 = Largest possible partition number
char CxbxDefaultXbeVolumeLetter = 'C';
// Array of EmuHandles in the system
EmuHandle EmuHandle::Handles[EMU_MAX_HANDLES];
// Pointer to first free handle in array, or NULL if none
volatile EmuHandle *EmuHandle::FirstFree;
// Pointer to last free handle in array, or NULL if none
volatile EmuHandle *EmuHandle::LastFree;
// Lock on the handle system
CRITICAL_SECTION EmuHandle::HandleLock;
EmuNtSymbolicLinkObject* NtSymbolicLinkObjects[26];
struct XboxDevice {
@ -103,6 +90,60 @@ struct XboxDevice {
};
std::vector<XboxDevice> Devices;
EmuHandle::EmuHandle(EmuNtObject* ntObject)
{
NtObject = ntObject;
}
NTSTATUS EmuHandle::NtClose()
{
return NtObject->NtClose();
}
NTSTATUS EmuHandle::NtDuplicateObject(PHANDLE TargetHandle, DWORD Options)
{
*TargetHandle = NtObject->NtDuplicateObject(Options)->NewHandle();
return STATUS_SUCCESS;
}
EmuNtObject::EmuNtObject()
{
RefCount = 1;
}
HANDLE EmuNtObject::NewHandle()
{
RefCount++;
return EmuHandleToHandle(new EmuHandle(this));
}
NTSTATUS EmuNtObject::NtClose()
{
RefCount--;
if (RefCount <= 0) {
delete this;
}
return STATUS_SUCCESS;
}
EmuNtObject* EmuNtObject::NtDuplicateObject(DWORD Options)
{
RefCount++;
return this;
}
bool IsEmuHandle(HANDLE Handle)
{
return (uint32_t)Handle > 0x80000000;
}
EmuHandle* HandleToEmuHandle(HANDLE Handle) { return (EmuHandle*)((uint32_t)Handle & 0x7FFFFFFF); } HANDLE EmuHandleToHandle(EmuHandle* emuHandle)
{
return (HANDLE)((uint32_t)emuHandle | 0x80000000);
}
bool CxbxRegisterDeviceNativePath(std::string XboxFullPath, std::string NativePath, bool IsFile)
{
bool result;
@ -147,82 +188,6 @@ NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPa
return result;
}
// ******************************************************************
// * Initialize the handle database
// ******************************************************************
bool EmuHandle::Initialize()
{
size_t x;
// Initialize the critical section
InitializeCriticalSection(&HandleLock);
// Mark all handles as free. We also set up the linked list of
// free handles here.
for (x = 0; x < EMU_MAX_HANDLES; x++)
{
Handles[x].m_Type = EMUHANDLE_TYPE_EMPTY;
Handles[x].m_NextFree = &Handles[x + 1];
}
// The last entry should have a NULL next entry
Handles[EMU_MAX_HANDLES - 1].m_NextFree = NULL;
// Set up the head and tail pointers
FirstFree = &Handles[0];
LastFree = &Handles[EMU_MAX_HANDLES];
return true;
}
// ******************************************************************
// * func: EmuHandle::Lock
// * Locks the handle database
// ******************************************************************
inline void EmuHandle::Lock(void)
{
EnterCriticalSection(&HandleLock);
}
// ******************************************************************
// * func: EmuHandle::Unlock
// * Unlocks the handle database
// ******************************************************************
inline void EmuHandle::Unlock(void)
{
LeaveCriticalSection(&HandleLock);
}
// ******************************************************************
// * func: EmuHandle::Allocate
// * Allocates a new handle
// ******************************************************************
EmuHandle volatile *EmuHandle::Allocate(void)
{
volatile EmuHandle *Handle;
// Lock the database
Lock();
// Get the first free entry
Handle = FirstFree;
// Remove it from the list
FirstFree = Handle->m_NextFree;
// If it was the last handle, clear LastFree
if (!Handle->m_NextFree)
LastFree = NULL;
// Initialize the handle's fields
Handle->m_Type = EMUHANDLE_TYPE_ALLOCATED;
Handle->m_Object = NULL;
// Unlock the database
Unlock();
return Handle;
}
NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::string aFullPath)
{
@ -295,7 +260,7 @@ NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::strin
return result;
}
void EmuNtSymbolicLinkObject::Free()
EmuNtSymbolicLinkObject::~EmuNtSymbolicLinkObject()
{
if (DriveLetter >= 'A' && DriveLetter <= 'Z') {
NtSymbolicLinkObjects[DriveLetter - 'A'] = NULL;
@ -388,21 +353,3 @@ EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(const HANDLE Handl
return NULL;
}
NTSTATUS EmuNtObject::NtClose()
{
NTSTATUS result = 0;
RefCount--;
if (RefCount <= 0)
Free();
result = STATUS_SUCCESS;
return result;
}
EmuNtObject* EmuNtObject::NtDuplicateObject(DWORD Options)
{
EmuNtObject* result = NULL;
RefCount++;
result = this;
return result;
}

View File

@ -1,274 +1,165 @@
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->Win32->CxbxKrnl->EmuFile.h
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef EMUFILE_H
#define EMUFILE_H
// ******************************************************************
// * prevent name collisions
// ******************************************************************
namespace xboxkrnl
{
#include <xboxkrnl/xboxkrnl.h>
};
#include <cstdio>
#include <string>
// ******************************************************************
// * prevent name collisions
// ******************************************************************
namespace NtDll
{
#include "EmuNtDll.h"
};
#include "Emu.h"
extern std::string DriveSerial;
extern std::string DriveCdRom0;
extern std::string DriveMbfs;
extern std::string DriveMbcom;
extern std::string DriveMbrom;
extern std::string DriveC;
extern std::string DriveD;
extern std::string DriveE;
extern std::string DriveF;
extern std::string DriveT;
extern std::string DriveU;
extern std::string DriveV;
extern std::string DriveW;
extern std::string DriveX;
extern std::string DriveY;
extern std::string DriveZ;
extern std::string DeviceCdrom0;
extern std::string DeviceHarddisk0;
extern std::string DeviceHarddisk0Partition0;
extern std::string DeviceHarddisk0Partition1;
extern std::string DeviceHarddisk0Partition2;
extern std::string DeviceHarddisk0Partition3;
extern std::string DeviceHarddisk0Partition4;
extern std::string DeviceHarddisk0Partition5;
extern std::string DeviceHarddisk0Partition6;
extern std::string DeviceHarddisk0Partition7;
extern std::string DeviceHarddisk0Partition8;
extern std::string DeviceHarddisk0Partition9;
extern std::string DeviceHarddisk0Partition10;
extern std::string DeviceHarddisk0Partition11;
extern std::string DeviceHarddisk0Partition12;
extern std::string DeviceHarddisk0Partition13;
extern std::string DeviceHarddisk0Partition14;
extern std::string DeviceHarddisk0Partition15;
extern std::string DeviceHarddisk0Partition16;
extern std::string DeviceHarddisk0Partition17;
extern std::string DeviceHarddisk0Partition18;
extern std::string DeviceHarddisk0Partition19;
extern std::string DeviceHarddisk0Partition20;
extern char CxbxDefaultXbeVolumeLetter;
extern std::string CxbxBasePath;
extern HANDLE CxbxBasePathHandle;
// ******************************************************************
// * Maximum number of open handles in the system
// ******************************************************************
#define EMU_MAX_HANDLES 1024
// ******************************************************************
// * Wrapper of a handle object
// ******************************************************************
class EmuHandle
{
public:
// Type of handle
volatile enum _EmuHandleType m_Type;
// To keep the size 8 bytes, these 2 items are in a union
union
{
// Pointer to actual object (when handle is valid)
volatile class EmuNtObject *m_Object;
// Pointer to next free handle
volatile EmuHandle *m_NextFree;
};
// Close this handle
NtDll::NTSTATUS Close(void);
// Initialize the EmuHandle system
static bool Initialize();
// Close all open handles
static void CloseAll(void);
// Allocate an empty handle
static volatile EmuHandle *Allocate(void);
private:
// Array of EmuHandles in the system
static EmuHandle Handles[EMU_MAX_HANDLES];
// Pointer to first free handle in array, or NULL if none
volatile static EmuHandle *FirstFree;
// Pointer to last free handle in array, or NULL if none
volatile static EmuHandle *LastFree;
// Lock on the handle system
static CRITICAL_SECTION HandleLock;
// Quick functions to lock/unlock
inline static void Lock(void);
inline static void Unlock(void);
};
// ******************************************************************
// * Various "special" handle types
// ******************************************************************
typedef enum _EmuHandleType
{
// Unallocated handle
EMUHANDLE_TYPE_EMPTY = 0,
// Allocated but so far unused handle
EMUHANDLE_TYPE_ALLOCATED,
// File handle with no really special features
EMUHANDLE_TYPE_FILE,
// Fake file/directory/directory object/partition handle
EMUHANDLE_TYPE_OBJECT
}
EmuHandleType;
// ******************************************************************
// * An NT fake object
// ******************************************************************
class EmuNtObject
{
public:
// Decrements the reference count of this object (never override)
NTSTATUS NtClose(void);
// These functions mimic the Nt* calls
// Increments the reference count of this object
// For file handles, a whole new EmuFile structure is returned.
// For other objects (the default implementation), "this" is returned.
EmuNtObject *NtDuplicateObject(DWORD options);
protected:
// Object name (Unicode, because we handle after-conversion strings)
const WCHAR *Name;
ULONG NameLength;
// Permanent status
bool PermanentFlag;
// Called by close() when the reference count reaches zero
virtual void Free(void) = 0;
private:
// Reference count
LONG RefCount;
};
// ******************************************************************
// * Emulated file handle
// ******************************************************************
class EmuNtFile : public EmuNtObject
{
public:
// We need to override NtDuplicateObject in this case
private:
// The Windows file handle
HANDLE File;
// Pointer to the volume from which this came
//EmuNtVolume *Volume;
};
// ******************************************************************
// * Emulated symbolic link handle
// ******************************************************************
class EmuNtSymbolicLinkObject : public EmuNtObject {
public:
char DriveLetter;
std::string SymbolicLinkName;
std::string XboxFullPath;
std::string NativePath;
HANDLE RootDirectoryHandle;
NTSTATUS Init(std::string aSymbolicLinkName, std::string aFullPath);
void Free();
};
// ******************************************************************
// * is hFile a 'special' emulated handle?
// ******************************************************************
static inline bool IsEmuHandle(xboxkrnl::HANDLE hFile)
{
bool ret = ((uint32)hFile > 0x80000000) && ((uint32)hFile != -1);
return ret;
}
// ******************************************************************
// * convert from 'special' emulated handle to a pointer
// ******************************************************************
static inline EmuHandle *EmuHandleToPtr(xboxkrnl::HANDLE hFile)
{
return (EmuHandle*)((uint32)hFile - 0x80000000);
}
// ******************************************************************
// * convert from 'special' emulated handle to a pointer
// ******************************************************************
static inline HANDLE PtrToEmuHandle(EmuHandle *pEmuHandle)
{
return (HANDLE)((uint32)pEmuHandle + 0x80000000);
}
char SymbolicLinkToDriveLetter(std::string aSymbolicLinkName);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByVolumeLetter(const char VolumeLetter);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDevice(std::string DeviceName);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle);
void CleanupSymbolicLinks();
bool CxbxRegisterDeviceNativePath(std::string XboxFullPath, std::string NativePath, bool IsFile = false);
HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath);
NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath);
bool CxbxMountUtilityDrive(bool formatClean);
#endif
// ******************************************************************
// *
// * .,-::::: .,:: .::::::::. .,:: .:
// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;;
// * [[[ '[[,,[[' [[[__[[\. '[[,,[['
// * $$$ Y$$$P $$""""Y$$ Y$$$P
// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo,
// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm,
// *
// * Cxbx->Win32->CxbxKrnl->EmuFile.h
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef EMUFILE_H
#define EMUFILE_H
// ******************************************************************
// * prevent name collisions
// ******************************************************************
namespace xboxkrnl
{
#include <xboxkrnl/xboxkrnl.h>
};
#include <cstdio>
#include <string>
// ******************************************************************
// * prevent name collisions
// ******************************************************************
namespace NtDll
{
#include "EmuNtDll.h"
};
#include "Emu.h"
extern std::string DriveSerial;
extern std::string DriveCdRom0;
extern std::string DriveMbfs;
extern std::string DriveMbcom;
extern std::string DriveMbrom;
extern std::string DriveC;
extern std::string DriveD;
extern std::string DriveE;
extern std::string DriveF;
extern std::string DriveT;
extern std::string DriveU;
extern std::string DriveV;
extern std::string DriveW;
extern std::string DriveX;
extern std::string DriveY;
extern std::string DriveZ;
extern std::string DeviceCdrom0;
extern std::string DeviceHarddisk0;
extern std::string DeviceHarddisk0Partition0;
extern std::string DeviceHarddisk0Partition1;
extern std::string DeviceHarddisk0Partition2;
extern std::string DeviceHarddisk0Partition3;
extern std::string DeviceHarddisk0Partition4;
extern std::string DeviceHarddisk0Partition5;
extern std::string DeviceHarddisk0Partition6;
extern std::string DeviceHarddisk0Partition7;
extern std::string DeviceHarddisk0Partition8;
extern std::string DeviceHarddisk0Partition9;
extern std::string DeviceHarddisk0Partition10;
extern std::string DeviceHarddisk0Partition11;
extern std::string DeviceHarddisk0Partition12;
extern std::string DeviceHarddisk0Partition13;
extern std::string DeviceHarddisk0Partition14;
extern std::string DeviceHarddisk0Partition15;
extern std::string DeviceHarddisk0Partition16;
extern std::string DeviceHarddisk0Partition17;
extern std::string DeviceHarddisk0Partition18;
extern std::string DeviceHarddisk0Partition19;
extern std::string DeviceHarddisk0Partition20;
extern char CxbxDefaultXbeVolumeLetter;
extern std::string CxbxBasePath;
extern HANDLE CxbxBasePathHandle;
class EmuNtObject;
// ******************************************************************
// * Wrapper of a handle object
// ******************************************************************
class EmuHandle
{
public:
EmuHandle(EmuNtObject* ntObject);
NTSTATUS NtClose();
NTSTATUS NtDuplicateObject(PHANDLE TargetHandle, DWORD Options);
EmuNtObject* NtObject;
};
// ******************************************************************
// * An NT fake object
// ******************************************************************
class EmuNtObject
{
public:
EmuNtObject();
HANDLE NewHandle();
NTSTATUS NtClose();
EmuNtObject* NtDuplicateObject(DWORD Options);
protected:
virtual ~EmuNtObject() {};
private:
ULONG RefCount;
};
// ******************************************************************
// * Emulated symbolic link handle
// ******************************************************************
class EmuNtSymbolicLinkObject : public EmuNtObject {
public:
char DriveLetter;
std::string SymbolicLinkName;
std::string XboxFullPath;
std::string NativePath;
HANDLE RootDirectoryHandle;
NTSTATUS Init(std::string aSymbolicLinkName, std::string aFullPath);
~EmuNtSymbolicLinkObject();
};
// ******************************************************************
// * is hFile a 'special' emulated handle?
// ******************************************************************
bool IsEmuHandle(HANDLE Handle);
EmuHandle* HandleToEmuHandle(HANDLE Handle); HANDLE EmuHandleToHandle(EmuHandle* emuHandle);
char SymbolicLinkToDriveLetter(std::string aSymbolicLinkName);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByVolumeLetter(const char VolumeLetter);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDevice(std::string DeviceName);
EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle);
void CleanupSymbolicLinks();
bool CxbxRegisterDeviceNativePath(std::string XboxFullPath, std::string NativePath, bool IsFile = false);
HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath);
NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath);
bool CxbxMountUtilityDrive(bool formatClean);
#endif

View File

@ -2975,15 +2975,8 @@ XBSYSAPI EXPORTNUM(187) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtClose
// delete 'special' handles
if(IsEmuHandle(Handle))
{
EmuHandle *iEmuHandle = EmuHandleToPtr(Handle);
// HACK: Do not delete emuhandle
// There is an issue with EmuHandle/EmuNtObject causing referencing counting to not function
// Consider translating the entirity of EmuFile & EmuKrnl from Dxbx
//delete iEmuHandle;
EmuWarning("EmuKrnl::NtClose : EmuHandle not deleted");
ret = STATUS_SUCCESS;
EmuHandle *iEmuHandle = HandleToEmuHandle(Handle);
ret = iEmuHandle->NtClose();
}
// close normal handles
else
@ -3260,15 +3253,24 @@ XBSYSAPI EXPORTNUM(197) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDuplicateObject
");\n",
GetCurrentThreadId(), SourceHandle, TargetHandle, Options);
// redirect to Win2k/XP
NTSTATUS ret = NtDll::NtDuplicateObject
(
GetCurrentProcess(),
SourceHandle,
GetCurrentProcess(),
TargetHandle,
0, 0, Options
);
NTSTATUS ret;
if (IsEmuHandle(SourceHandle)) {
EmuHandle* iEmuHandle = HandleToEmuHandle(SourceHandle);
ret = iEmuHandle->NtDuplicateObject(TargetHandle, Options);
}
else {
// redirect to Win2k/XP
ret = NtDll::NtDuplicateObject
(
GetCurrentProcess(),
SourceHandle,
GetCurrentProcess(),
TargetHandle,
0, 0, Options
);
}
if(ret != STATUS_SUCCESS)
EmuWarning("Object was not duplicated!");
@ -3393,7 +3395,7 @@ XBSYSAPI EXPORTNUM(203) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenSymbolicLinkObj
if ((symbolicLinkObject != NULL))
{
// Return a new handle
*LinkHandle = EmuHandleToPtr(symbolicLinkObject);
*LinkHandle = symbolicLinkObject->NewHandle();
ret = STATUS_SUCCESS;
}
else
@ -3664,9 +3666,10 @@ XBSYSAPI EXPORTNUM(215) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQuerySymbolicLinkOb
// Check that we actually got an EmuHandle :
result = STATUS_INVALID_HANDLE;
EmuHandle* iEmuHandle = HandleToEmuHandle(LinkHandle);
// Retrieve the NtSymbolicLinkObject and populate the output arguments :
result = STATUS_SUCCESS;
symbolicLinkObject = ((EmuNtSymbolicLinkObject*)PtrToEmuHandle((EmuHandle*)LinkHandle));
symbolicLinkObject = (EmuNtSymbolicLinkObject*)iEmuHandle->NtObject;
if ((LinkTarget != NULL)) {
if (LinkTarget->Length > LinkTarget->MaximumLength) {
result = STATUS_BUFFER_TOO_SMALL;
@ -4157,9 +4160,21 @@ XBSYSAPI EXPORTNUM(234) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForSingleObject
");\n",
GetCurrentThreadId(), Handle, WaitMode, Alertable, Timeout, Timeout == 0 ? 0 : Timeout->QuadPart);
NTSTATUS ret = NtDll::NtWaitForSingleObject(Handle, Alertable, (NtDll::PLARGE_INTEGER)Timeout);
NTSTATUS ret;
DbgPrintf("Finished waiting for 0x%.08X\n", Handle);
if (IsEmuHandle(Handle))
{
ret = WAIT_FAILED;
EmuWarning("WaitFor EmuHandle not supported!");
}
else
{
ret = NtDll::NtWaitForSingleObject(Handle, Alertable, (NtDll::PLARGE_INTEGER)Timeout);
DbgPrintf("Finished waiting for 0x%.08X\n", Handle);
}
if (ret == WAIT_FAILED)
EmuWarning("NtWaitForSingleObjectEx failed! (%s)", NtStatusToString(ret));
EmuSwapFS(); // Xbox FS
@ -4193,6 +4208,7 @@ XBSYSAPI EXPORTNUM(235) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForMultipleObje
GetCurrentThreadId(), Count, Handles, WaitType, WaitMode, Alertable,
Timeout, Timeout == 0 ? 0 : Timeout->QuadPart);
// TODO: Process EmuHandle
NTSTATUS ret = NtDll::NtWaitForMultipleObjects(Count, Handles, (NtDll::OBJECT_WAIT_TYPE)WaitType, Alertable, (NtDll::PLARGE_INTEGER)Timeout);
EmuSwapFS(); // Xbox FS