mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2026-01-04 02:31:10 -06:00
Linux support. (#54)
* Initial Linux attempt. * Add clang toolchain & make tools compile. * vcpkg as submodule. * First implementation of IO rewrite. (#31) * Fix directory iteration resolving symlinks. * Refactor kernel objects to be lock-free. * Implement guest critical sections using std::atomic. * Make D3D12 support optional. (#33) * Make D3D12 support optional. * Update ShaderRecomp, fix macros. * Replace QueryPerformanceCounter. (#35) * Add Linux home path for GetUserPath(). (#36) * Cross-platform Sleep. (#37) * Add mmap implementations for virtual allocation. (#38) * Cross-platform TLS. (#34) * Cross-platform TLS. * Fix front() to back(), use Mutex. * Fix global variable namings. --------- Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> * Unicode support. (#39) * Replace CreateDirectoryA with Unicode version. * Cross platform thread implementation. (#41) * Cross-platform thread implementation. * Put set thread name calls behind a Win32 macro. * Cross-platform semaphore implementation. (#43) * xam: use SDL for keyboard input * Cross-platform atomic operations. (#44) * Cross-platform spin lock implementation. * Cross-platform reference counting. * Cross-platform event implementation. (#47) * Compiling and running on Linux. (#49) * Current work trying to get it to compile. * Update vcpkg.json baseline. * vcpkg, memory mapped file. * Bitscan forward. * Fix localtime_s. * FPS patches high res clock. * Rename Window to GameWindow. Fix guest pointers. * GetCurrentThreadID gone. * Code cache pointers, RenderWindow type. * Add Linux stubs. * Refactor Config. * Fix paths. * Add linux-release config. * FS fixes. * Fix Windows compilation errors & unicode converter crash. * Rename physical memory allocation functions to not clash with X11. * Fix NULL character being added on RtlMultiByteToUnicodeN. * Use std::exit. * Add protection to memory on Linux. * Convert majority of dependencies to submodules. (#48) * Convert majority of dependencies to submodules. * Don't compile header-only libraries. * Fix a few incorrect data types. * Fix config directory. * Unicode fixes & sizeof asserts. * Change the exit function to not call static destructors. * Fix files picker. * Add RelWithDebInfo preset for Linux. * Implement OS Restart on Linux. (#50) --------- Co-authored-by: Dario <dariosamo@gmail.com> * Update PowerRecomp. * Add Env Var detection for VCPKG_ROOT, add DLC detection. * Use error code version on DLC directory iterator. * Set D3D12MA::ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED flag. * Linux flatpak. (#51) * Add flatpak support. * Add game install directory override for flatpak. * Flatpak'ing. * Flatpak it some more. * We flat it, we pak it. * Flatpak'd. * The Marvelous Misadventures of Flatpak. * Attempt to change logic of NFD and show error. * Flattenpakken. * Use game install directory instead of current path. * Attempt to fix line endings. * Update io.github.hedge_dev.unleashedrecomp.json * Fix system time query implementation. * Add Present Wait to Vulkan to improve frame pacing and reduce latency. (#53) * Add present wait support to Vulkan. * Default to triple buffering if presentWait is supported. * Bracey fellas. * Update paths.h * SDL2 audio (again). (#52) * Implement SDL2 audio (again). * Call timeBeginPeriod/timeEndPeriod. * Replace miniaudio with SDL mixer. * Queue audio samples in a separate thread. * Enable CMake option override policy & fix compilation error. * Fix compilation error on Linux. * Fix but also trim shared strings. * Wayland support. (#55) * Make channel index a global variable in embedded player. * Fix SDL Audio selection for OGG on Flatpak. * Minor installer wizard fixes. * Fix compilation error. * Yield in model consumer and pipeline compiler threads. * Special case Sleep(0) to yield on Linux. * Add App Id hint. * Correct implementation for auto reset events. (#57) --------- Co-authored-by: Dario <dariosamo@gmail.com> Co-authored-by: Hyper <34012267+hyperbx@users.noreply.github.com>
This commit is contained in:
@@ -39,7 +39,7 @@ std::enable_if_t<(I < sizeof...(TArgs)), void> _tuple_for(std::tuple<TArgs...>&
|
||||
|
||||
struct ArgTranslator
|
||||
{
|
||||
FORCEINLINE constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
{
|
||||
if (arg <= 7)
|
||||
{
|
||||
@@ -57,10 +57,10 @@ struct ArgTranslator
|
||||
}
|
||||
}
|
||||
|
||||
return *reinterpret_cast<XLPDWORD>(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8));
|
||||
return *reinterpret_cast<be<uint32_t>*>(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8));
|
||||
}
|
||||
|
||||
FORCEINLINE static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
@@ -84,7 +84,7 @@ struct ArgTranslator
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCEINLINE constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept
|
||||
constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept
|
||||
{
|
||||
if (arg <= 7)
|
||||
{
|
||||
@@ -105,7 +105,7 @@ struct ArgTranslator
|
||||
assert(arg < 7 && "Pushing to stack memory is not yet supported.");
|
||||
}
|
||||
|
||||
FORCEINLINE static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept
|
||||
static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
@@ -129,7 +129,7 @@ struct ArgTranslator
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<!std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
constexpr static std::enable_if_t<!std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
{
|
||||
if constexpr (is_precise_v<T>)
|
||||
{
|
||||
@@ -142,7 +142,7 @@ struct ArgTranslator
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
constexpr static std::enable_if_t<std::is_pointer_v<T>, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept
|
||||
{
|
||||
const auto v = GetIntegerArgumentValue(ctx, base, idx);
|
||||
if (!v)
|
||||
@@ -154,7 +154,7 @@ struct ArgTranslator
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<!std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
constexpr static std::enable_if_t<!std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
{
|
||||
if constexpr (is_precise_v<T>)
|
||||
{
|
||||
@@ -175,7 +175,7 @@ struct ArgTranslator
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE constexpr static std::enable_if_t<std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
constexpr static std::enable_if_t<std::is_pointer_v<T>, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept
|
||||
{
|
||||
const auto v = g_memory.MapVirtual((void*)value);
|
||||
if (!v)
|
||||
@@ -240,13 +240,13 @@ struct arg_ordinal_t
|
||||
};
|
||||
|
||||
template<auto Func, int I = 0, typename ...TArgs>
|
||||
FORCEINLINE void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
requires (I >= sizeof...(TArgs))
|
||||
{
|
||||
}
|
||||
|
||||
template <auto Func, int I = 0, typename ...TArgs>
|
||||
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
{
|
||||
using T = std::tuple_element_t<I, std::remove_reference_t<decltype(tpl)>>;
|
||||
std::get<I>(tpl) = ArgTranslator::GetValue<T>(ctx, base, arg_ordinal_t<Func, I>::value);
|
||||
@@ -255,13 +255,13 @@ FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_ho
|
||||
}
|
||||
|
||||
template<int I = 0, typename ...TArgs>
|
||||
FORCEINLINE void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
|
||||
requires (I >= sizeof...(TArgs))
|
||||
{
|
||||
}
|
||||
|
||||
template <int I = 0, typename ...TArgs>
|
||||
FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>& tpl) noexcept
|
||||
{
|
||||
using T = std::tuple_element_t<I, std::remove_reference_t<decltype(tpl)>>;
|
||||
ArgTranslator::SetValue<T>(ctx, base, GatherFunctionArguments(std::tuple<TArgs...>{})[I].ordinal, std::get<I>(tpl));
|
||||
@@ -270,7 +270,7 @@ FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_gu
|
||||
}
|
||||
|
||||
template<auto Func>
|
||||
FORCEINLINE PPC_FUNC(HostToGuestFunction)
|
||||
PPC_FUNC(HostToGuestFunction)
|
||||
{
|
||||
using ret_t = decltype(std::apply(Func, function_args(Func)));
|
||||
|
||||
@@ -293,7 +293,7 @@ FORCEINLINE PPC_FUNC(HostToGuestFunction)
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.r3.u64 = NULL;
|
||||
ctx.r3.u64 = 0;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_precise_v<ret_t>)
|
||||
@@ -308,7 +308,7 @@ FORCEINLINE PPC_FUNC(HostToGuestFunction)
|
||||
}
|
||||
|
||||
template<typename T, typename TFunction, typename... TArgs>
|
||||
FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
||||
T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
||||
{
|
||||
auto args = std::make_tuple(std::forward<TArgs>(argv)...);
|
||||
auto& currentCtx = *GetPPCContext();
|
||||
@@ -331,11 +331,7 @@ FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
||||
currentCtx.fpscr = newCtx.fpscr;
|
||||
SetPPCContext(currentCtx);
|
||||
|
||||
if constexpr (std::is_void_v<T>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if constexpr (std::is_pointer_v<T>)
|
||||
if constexpr (std::is_pointer_v<T>)
|
||||
{
|
||||
return reinterpret_cast<T>((uint64_t)g_memory.Translate(newCtx.r3.u32));
|
||||
}
|
||||
@@ -349,7 +345,7 @@ FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs&&... argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(false, "Unsupported return type.");
|
||||
static_assert(std::is_void_v<T>, "Unsupported return type.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ uint32_t RtlSizeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XAlloc(uint32_t size, uint32_t flags)
|
||||
SWA_API uint32_t XAllocMem(uint32_t size, uint32_t flags)
|
||||
{
|
||||
void* ptr = (flags & 0x80000000) != 0 ?
|
||||
g_userHeap.AllocPhysical(size, (1ull << ((flags >> 24) & 0xF))) :
|
||||
@@ -116,7 +116,7 @@ SWA_API uint32_t XAlloc(uint32_t size, uint32_t flags)
|
||||
return g_memory.MapVirtual(ptr);
|
||||
}
|
||||
|
||||
SWA_API void XFree(uint32_t baseAddress, uint32_t flags)
|
||||
SWA_API void XFreeMem(uint32_t baseAddress, uint32_t flags)
|
||||
{
|
||||
if (baseAddress != NULL)
|
||||
g_userHeap.Free(g_memory.Translate(baseAddress));
|
||||
@@ -130,6 +130,5 @@ GUEST_FUNCTION_HOOK(sub_82BD8600, RtlFreeHeap);
|
||||
GUEST_FUNCTION_HOOK(sub_82BD88F0, RtlReAllocateHeap);
|
||||
GUEST_FUNCTION_HOOK(sub_82BD6FD0, RtlSizeHeap);
|
||||
|
||||
// Seems like these handle allocation of virtual and physical pages
|
||||
GUEST_FUNCTION_HOOK(sub_831CC9C8, XAlloc);
|
||||
GUEST_FUNCTION_HOOK(sub_831CCA60, XFree);
|
||||
GUEST_FUNCTION_HOOK(sub_831CC9C8, XAllocMem);
|
||||
GUEST_FUNCTION_HOOK(sub_831CCA60, XFreeMem);
|
||||
|
||||
@@ -19,7 +19,7 @@ struct Heap
|
||||
size_t Size(void* ptr);
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* Alloc(Args... args)
|
||||
T* Alloc(Args&&... args)
|
||||
{
|
||||
T* obj = (T*)Alloc(sizeof(T));
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
@@ -27,7 +27,7 @@ struct Heap
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* AllocPhysical(Args... args)
|
||||
T* AllocPhysical(Args&&... args)
|
||||
{
|
||||
T* obj = (T*)AllocPhysical(sizeof(T), alignof(T));
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
|
||||
@@ -10,11 +10,129 @@
|
||||
#include <memory>
|
||||
#include "xam.h"
|
||||
#include "xdm.h"
|
||||
#include <timeapi.h>
|
||||
#include <user/config.h>
|
||||
#include <os/logger.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ntstatus.h>
|
||||
#endif
|
||||
|
||||
struct Event final : KernelObject, HostObject<XKEVENT>
|
||||
{
|
||||
bool manualReset;
|
||||
std::atomic<bool> signaled;
|
||||
|
||||
Event(XKEVENT* header)
|
||||
: manualReset(!header->Type), signaled(!!header->SignalState)
|
||||
{
|
||||
}
|
||||
|
||||
Event(bool manualReset, bool initialState)
|
||||
: manualReset(manualReset), signaled(initialState)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t Wait(uint32_t timeout) override
|
||||
{
|
||||
if (timeout == 0)
|
||||
{
|
||||
if (manualReset)
|
||||
{
|
||||
if (!signaled)
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool expected = true;
|
||||
if (!signaled.compare_exchange_strong(expected, false))
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
else if (timeout == INFINITE)
|
||||
{
|
||||
if (manualReset)
|
||||
{
|
||||
signaled.wait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
bool expected = true;
|
||||
if (signaled.compare_exchange_weak(expected, false))
|
||||
break;
|
||||
|
||||
signaled.wait(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false && "Unhandled timeout value.");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
bool Set()
|
||||
{
|
||||
signaled = true;
|
||||
|
||||
if (manualReset)
|
||||
signaled.notify_all();
|
||||
else
|
||||
signaled.notify_one();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool Reset()
|
||||
{
|
||||
signaled = false;
|
||||
return TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
static std::atomic<uint32_t> g_keSetEventGeneration;
|
||||
|
||||
struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
|
||||
{
|
||||
std::counting_semaphore<> semaphore;
|
||||
uint32_t maximumCount;
|
||||
|
||||
Semaphore(XKSEMAPHORE* semaphore)
|
||||
: semaphore(semaphore->Header.SignalState), maximumCount(semaphore->Limit)
|
||||
{
|
||||
}
|
||||
|
||||
Semaphore(uint32_t count, uint32_t maximumCount)
|
||||
: semaphore(count), maximumCount(maximumCount)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t Wait(uint32_t timeout) override
|
||||
{
|
||||
if (timeout == 0)
|
||||
{
|
||||
return semaphore.try_acquire() ? STATUS_SUCCESS : STATUS_TIMEOUT;
|
||||
}
|
||||
else if (timeout == INFINITE)
|
||||
{
|
||||
semaphore.acquire();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false && "Unhandled timeout value.");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
void Release(uint32_t releaseCount, uint32_t* previousCount)
|
||||
{
|
||||
semaphore.release(releaseCount);
|
||||
}
|
||||
};
|
||||
|
||||
inline void CloseKernelObject(XDISPATCHER_HEADER& header)
|
||||
{
|
||||
@@ -23,10 +141,10 @@ inline void CloseKernelObject(XDISPATCHER_HEADER& header)
|
||||
return;
|
||||
}
|
||||
|
||||
ObCloseHandle(header.WaitListHead.Blink);
|
||||
DestroyKernelObject(header.WaitListHead.Blink);
|
||||
}
|
||||
|
||||
DWORD GuestTimeoutToMilliseconds(XLPQWORD timeout)
|
||||
uint32_t GuestTimeoutToMilliseconds(be<int64_t>* timeout)
|
||||
{
|
||||
return timeout ? (*timeout * -1) / 10000 : INFINITE;
|
||||
}
|
||||
@@ -84,7 +202,7 @@ uint32_t XGetGameRegion()
|
||||
return 0x03FF;
|
||||
}
|
||||
|
||||
uint32_t XMsgStartIORequest(DWORD App, DWORD Message, XXOVERLAPPED* lpOverlapped, void* Buffer, DWORD szBuffer)
|
||||
uint32_t XMsgStartIORequest(uint32_t App, uint32_t Message, XXOVERLAPPED* lpOverlapped, void* Buffer, uint32_t szBuffer)
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -104,8 +222,7 @@ void XamContentDelete()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
|
||||
uint32_t XamContentGetCreator(DWORD userIndex, const XCONTENT_DATA* contentData, PBOOL isCreator, XLPQWORD xuid, XXOVERLAPPED* overlapped)
|
||||
uint32_t XamContentGetCreator(uint32_t userIndex, const XCONTENT_DATA* contentData, be<uint32_t>* isCreator, be<uint64_t>* xuid, XXOVERLAPPED* overlapped)
|
||||
{
|
||||
if (isCreator)
|
||||
*isCreator = true;
|
||||
@@ -146,7 +263,7 @@ uint32_t XamShowDeviceSelectorUI
|
||||
uint32_t contentType,
|
||||
uint32_t contentFlags,
|
||||
uint64_t totalRequested,
|
||||
XDWORD* deviceId,
|
||||
be<uint32_t>* deviceId,
|
||||
XXOVERLAPPED* overlapped
|
||||
)
|
||||
{
|
||||
@@ -214,10 +331,10 @@ void RtlInitAnsiString(XANSI_STRING* destination, char* source)
|
||||
destination->Buffer = source;
|
||||
}
|
||||
|
||||
DWORD NtCreateFile
|
||||
uint32_t NtCreateFile
|
||||
(
|
||||
XLPDWORD FileHandle,
|
||||
DWORD DesiredAccess,
|
||||
be<uint32_t>* FileHandle,
|
||||
uint32_t DesiredAccess,
|
||||
XOBJECT_ATTRIBUTES* Attributes,
|
||||
XIO_STATUS_BLOCK* IoStatusBlock,
|
||||
uint64_t* AllocationSize,
|
||||
@@ -232,16 +349,19 @@ DWORD NtCreateFile
|
||||
|
||||
uint32_t NtClose(uint32_t handle)
|
||||
{
|
||||
if (handle == (uint32_t)INVALID_HANDLE_VALUE)
|
||||
if (handle == GUEST_INVALID_HANDLE_VALUE)
|
||||
return 0xFFFFFFFF;
|
||||
|
||||
if (CHECK_GUEST_HANDLE(handle))
|
||||
if (IsKernelObject(handle))
|
||||
{
|
||||
ObCloseHandle(HOST_HANDLE(handle));
|
||||
DestroyKernelObject(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return CloseHandle((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
else
|
||||
{
|
||||
assert(false && "Unrecognized kernel object.");
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
void NtSetInformationFile()
|
||||
@@ -254,20 +374,21 @@ uint32_t FscSetCacheElementCount()
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD NtWaitForSingleObjectEx(DWORD Handle, DWORD WaitMode, DWORD Alertable, XLPQWORD Timeout)
|
||||
uint32_t NtWaitForSingleObjectEx(uint32_t Handle, uint32_t WaitMode, uint32_t Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
const auto status = WaitForSingleObjectEx((HANDLE)Handle, GuestTimeoutToMilliseconds(Timeout), Alertable);
|
||||
uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == 0 || timeout == INFINITE);
|
||||
|
||||
if (status == WAIT_IO_COMPLETION)
|
||||
if (IsKernelObject(Handle))
|
||||
{
|
||||
return STATUS_USER_APC;
|
||||
return GetKernelObject(Handle)->Wait(timeout);
|
||||
}
|
||||
else if (status)
|
||||
else
|
||||
{
|
||||
return STATUS_ALERTED;
|
||||
assert(false && "Unrecognized handle value.");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
void NtWriteFile()
|
||||
@@ -280,7 +401,7 @@ void vsprintf_x()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t ExGetXConfigSetting(uint16_t Category, uint16_t Setting, void* Buffer, uint16_t SizeOfBuffer, XLPDWORD RequiredSize)
|
||||
uint32_t ExGetXConfigSetting(uint16_t Category, uint16_t Setting, void* Buffer, uint16_t SizeOfBuffer, be<uint32_t>* RequiredSize)
|
||||
{
|
||||
uint32_t data[4]{};
|
||||
|
||||
@@ -357,9 +478,9 @@ void MmQueryStatistics()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtCreateEvent(uint32_t* handle, void* objAttributes, uint32_t eventType, uint32_t initialState)
|
||||
uint32_t NtCreateEvent(be<uint32_t>* handle, void* objAttributes, uint32_t eventType, uint32_t initialState)
|
||||
{
|
||||
*handle = ByteSwap((uint32_t)CreateEventA(nullptr, !eventType, !!initialState, nullptr));
|
||||
*handle = GetKernelHandle(CreateKernelObject<Event>(!eventType, !!initialState));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -393,9 +514,9 @@ void XexGetModuleSection()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
NTSTATUS RtlUnicodeToMultiByteN(PCHAR MultiByteString, DWORD MaxBytesInMultiByteString, XLPDWORD BytesInMultiByteString, PCWCH UnicodeString, ULONG BytesInUnicodeString)
|
||||
uint32_t RtlUnicodeToMultiByteN(char* MultiByteString, uint32_t MaxBytesInMultiByteString, be<uint32_t>* BytesInMultiByteString, const be<uint16_t>* UnicodeString, uint32_t BytesInUnicodeString)
|
||||
{
|
||||
const auto reqSize = BytesInUnicodeString / sizeof(wchar_t);
|
||||
const auto reqSize = BytesInUnicodeString / sizeof(uint16_t);
|
||||
|
||||
if (BytesInMultiByteString)
|
||||
*BytesInMultiByteString = reqSize;
|
||||
@@ -405,7 +526,7 @@ NTSTATUS RtlUnicodeToMultiByteN(PCHAR MultiByteString, DWORD MaxBytesInMultiByte
|
||||
|
||||
for (size_t i = 0; i < reqSize; i++)
|
||||
{
|
||||
const auto c = ByteSwap(UnicodeString[i]);
|
||||
const auto c = UnicodeString[i].get();
|
||||
|
||||
MultiByteString[i] = c < 256 ? c : '?';
|
||||
}
|
||||
@@ -413,24 +534,22 @@ NTSTATUS RtlUnicodeToMultiByteN(PCHAR MultiByteString, DWORD MaxBytesInMultiByte
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
DWORD KeDelayExecutionThread(DWORD WaitMode, bool Alertable, XLPQWORD Timeout)
|
||||
uint32_t KeDelayExecutionThread(uint32_t WaitMode, bool Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
// We don't do async file reads.
|
||||
if (Alertable)
|
||||
return STATUS_USER_APC;
|
||||
|
||||
timeBeginPeriod(1);
|
||||
const auto status = SleepEx(GuestTimeoutToMilliseconds(Timeout), Alertable);
|
||||
timeEndPeriod(1);
|
||||
uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
|
||||
if (status == WAIT_IO_COMPLETION)
|
||||
{
|
||||
return STATUS_USER_APC;
|
||||
}
|
||||
else if (status)
|
||||
{
|
||||
return STATUS_ALERTED;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
Sleep(timeout);
|
||||
#else
|
||||
if (timeout == 0)
|
||||
std::this_thread::yield();
|
||||
else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
|
||||
#endif
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -485,8 +604,9 @@ void ObDereferenceObject()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
void KeSetBasePriorityThread(uint32_t thread, int priority)
|
||||
void KeSetBasePriorityThread(GuestThreadHandle* hThread, int priority)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (priority == 16)
|
||||
{
|
||||
priority = 15;
|
||||
@@ -496,10 +616,11 @@ void KeSetBasePriorityThread(uint32_t thread, int priority)
|
||||
priority = -15;
|
||||
}
|
||||
|
||||
SetThreadPriority((HANDLE)thread, priority);
|
||||
SetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), priority);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t ObReferenceObjectByHandle(uint32_t handle, uint32_t objectType, XLPDWORD object)
|
||||
uint32_t ObReferenceObjectByHandle(uint32_t handle, uint32_t objectType, be<uint32_t>* object)
|
||||
{
|
||||
*object = handle;
|
||||
return 0;
|
||||
@@ -510,20 +631,17 @@ void KeQueryBasePriorityThread()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtSuspendThread(uint32_t hThread, uint32_t* suspendCount)
|
||||
uint32_t NtSuspendThread(GuestThreadHandle* hThread, uint32_t* suspendCount)
|
||||
{
|
||||
DWORD count = SuspendThread((HANDLE)hThread);
|
||||
assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE) && hThread->thread.get_id() == std::this_thread::get_id());
|
||||
|
||||
if (count == (DWORD)-1)
|
||||
return E_FAIL;
|
||||
|
||||
if (suspendCount != nullptr)
|
||||
*suspendCount = ByteSwap(count);
|
||||
hThread->suspended = true;
|
||||
hThread->suspended.wait(true);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uint32_t KeSetAffinityThread(DWORD Thread, DWORD Affinity, XLPDWORD lpPreviousAffinity)
|
||||
uint32_t KeSetAffinityThread(uint32_t Thread, uint32_t Affinity, be<uint32_t>* lpPreviousAffinity)
|
||||
{
|
||||
if (lpPreviousAffinity)
|
||||
*lpPreviousAffinity = 2;
|
||||
@@ -531,38 +649,6 @@ uint32_t KeSetAffinityThread(DWORD Thread, DWORD Affinity, XLPDWORD lpPreviousAf
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct Event : HostObject<XKEVENT>
|
||||
{
|
||||
HANDLE handle;
|
||||
|
||||
Event(XKEVENT* header)
|
||||
{
|
||||
handle = CreateEventA(nullptr, !header->Type, !!header->SignalState, nullptr);
|
||||
}
|
||||
|
||||
bool Set()
|
||||
{
|
||||
return SetEvent(handle);
|
||||
}
|
||||
|
||||
bool Reset()
|
||||
{
|
||||
return ResetEvent(handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct Semaphore : HostObject<XKSEMAPHORE>
|
||||
{
|
||||
HANDLE handle;
|
||||
|
||||
Semaphore(XKSEMAPHORE* semaphore)
|
||||
{
|
||||
handle = CreateSemaphoreA(nullptr, semaphore->Header.SignalState, semaphore->Limit, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
// https://devblogs.microsoft.com/oldnewthing/20160825-00/?p=94165
|
||||
|
||||
void RtlLeaveCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
{
|
||||
cs->RecursionCount--;
|
||||
@@ -570,25 +656,29 @@ void RtlLeaveCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
if (cs->RecursionCount != 0)
|
||||
return;
|
||||
|
||||
InterlockedExchange(&cs->OwningThread, 0);
|
||||
WakeByAddressSingle(&cs->OwningThread);
|
||||
std::atomic_ref owningThread(cs->OwningThread);
|
||||
owningThread.store(0);
|
||||
owningThread.notify_one();
|
||||
}
|
||||
|
||||
void RtlEnterCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
{
|
||||
DWORD thisThread = GetCurrentThreadId();
|
||||
uint32_t thisThread = g_ppcContext->r13.u32;
|
||||
assert(thisThread != NULL);
|
||||
|
||||
std::atomic_ref owningThread(cs->OwningThread);
|
||||
|
||||
while (true)
|
||||
{
|
||||
DWORD previousOwner = InterlockedCompareExchangeAcquire(&cs->OwningThread, thisThread, 0);
|
||||
uint32_t previousOwner = 0;
|
||||
|
||||
if (previousOwner == 0 || previousOwner == thisThread)
|
||||
if (owningThread.compare_exchange_weak(previousOwner, thisThread) || previousOwner == thisThread)
|
||||
{
|
||||
cs->RecursionCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
WaitOnAddress(&cs->OwningThread, &previousOwner, sizeof(previousOwner), INFINITE);
|
||||
owningThread.wait(previousOwner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +699,7 @@ void RtlFillMemoryUlong()
|
||||
|
||||
void KeBugCheckEx()
|
||||
{
|
||||
__debugbreak();
|
||||
__builtin_debugtrap();
|
||||
}
|
||||
|
||||
uint32_t KeGetCurrentProcessType()
|
||||
@@ -639,15 +729,22 @@ void RtlRaiseException_x()
|
||||
|
||||
void KfReleaseSpinLock(uint32_t* spinLock)
|
||||
{
|
||||
InterlockedExchange((volatile long*)spinLock, 0);
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
spinLockRef = 0;
|
||||
}
|
||||
|
||||
void KfAcquireSpinLock(uint32_t* spinLock)
|
||||
{
|
||||
const auto ctx = GetPPCContext();
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
|
||||
while (InterlockedCompareExchange((volatile long*)spinLock, ByteSwap(*(uint32_t*)(g_memory.Translate(ctx->r13.u32 + 0x110))), 0) != 0)
|
||||
Sleep(0);
|
||||
while (true)
|
||||
{
|
||||
uint32_t expected = 0;
|
||||
if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32))
|
||||
break;
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t KeQueryPerformanceFrequency()
|
||||
@@ -679,15 +776,22 @@ void VdGetSystemCommandBuffer()
|
||||
|
||||
void KeReleaseSpinLockFromRaisedIrql(uint32_t* spinLock)
|
||||
{
|
||||
InterlockedExchange((volatile long*)spinLock, 0);
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
spinLockRef = 0;
|
||||
}
|
||||
|
||||
void KeAcquireSpinLockAtRaisedIrql(uint32_t* spinLock)
|
||||
{
|
||||
const auto ctx = GetPPCContext();
|
||||
std::atomic_ref spinLockRef(*spinLock);
|
||||
|
||||
while (InterlockedCompareExchange((volatile long*)spinLock, ByteSwap(*(uint32_t*)(g_memory.Translate(ctx->r13.u32 + 0x110))), 0) != 0)
|
||||
Sleep(0);
|
||||
while (true)
|
||||
{
|
||||
uint32_t expected = 0;
|
||||
if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32))
|
||||
break;
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t KiApcNormalRoutineNop()
|
||||
@@ -851,7 +955,7 @@ void VdEnableDisableClockGating()
|
||||
|
||||
void KeBugCheck()
|
||||
{
|
||||
__debugbreak();
|
||||
__builtin_debugtrap();
|
||||
}
|
||||
|
||||
void KeLockL2()
|
||||
@@ -864,62 +968,95 @@ void KeUnlockL2()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
bool KeSetEvent(XKEVENT* pEvent, DWORD Increment, bool Wait)
|
||||
bool KeSetEvent(XKEVENT* pEvent, uint32_t Increment, bool Wait)
|
||||
{
|
||||
return ObQueryObject<Event>(*pEvent)->Set();
|
||||
bool result = QueryKernelObject<Event>(*pEvent)->Set();
|
||||
|
||||
++g_keSetEventGeneration;
|
||||
g_keSetEventGeneration.notify_all();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool KeResetEvent(XKEVENT* pEvent)
|
||||
{
|
||||
return ObQueryObject<Event>(*pEvent)->Reset();
|
||||
return QueryKernelObject<Event>(*pEvent)->Reset();
|
||||
}
|
||||
|
||||
DWORD KeWaitForSingleObject(XDISPATCHER_HEADER* Object, DWORD WaitReason, DWORD WaitMode, bool Alertable, XLPQWORD Timeout)
|
||||
uint32_t KeWaitForSingleObject(XDISPATCHER_HEADER* Object, uint32_t WaitReason, uint32_t WaitMode, bool Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
const uint64_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
|
||||
HANDLE handle = nullptr;
|
||||
const uint32_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == INFINITE);
|
||||
|
||||
switch (Object->Type)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
handle = ObQueryObject<Event>(*Object)->handle;
|
||||
QueryKernelObject<Event>(*Object)->Wait(timeout);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
handle = ObQueryObject<Semaphore>(*Object)->handle;
|
||||
QueryKernelObject<Semaphore>(*Object)->Wait(timeout);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
assert(false && "Unrecognized kernel object type.");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
return WaitForSingleObjectEx(handle, timeout, Alertable);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t KeTlsGetValue(DWORD dwTlsIndex)
|
||||
static std::vector<size_t> g_tlsFreeIndices;
|
||||
static size_t g_tlsNextIndex = 0;
|
||||
static Mutex g_tlsAllocationMutex;
|
||||
|
||||
static uint32_t& KeTlsGetValueRef(size_t index)
|
||||
{
|
||||
return (uint32_t)TlsGetValue(dwTlsIndex);
|
||||
// Having this a global thread_local variable
|
||||
// for some reason crashes on boot in debug builds.
|
||||
thread_local std::vector<uint32_t> s_tlsValues;
|
||||
|
||||
if (s_tlsValues.size() <= index)
|
||||
{
|
||||
s_tlsValues.resize(index + 1, 0);
|
||||
}
|
||||
|
||||
return s_tlsValues[index];
|
||||
}
|
||||
|
||||
BOOL KeTlsSetValue(DWORD dwTlsIndex, DWORD lpTlsValue)
|
||||
uint32_t KeTlsGetValue(uint32_t dwTlsIndex)
|
||||
{
|
||||
return TlsSetValue(dwTlsIndex, (LPVOID)lpTlsValue);
|
||||
return KeTlsGetValueRef(dwTlsIndex);
|
||||
}
|
||||
|
||||
DWORD KeTlsAlloc()
|
||||
uint32_t KeTlsSetValue(uint32_t dwTlsIndex, uint32_t lpTlsValue)
|
||||
{
|
||||
return TlsAlloc();
|
||||
KeTlsGetValueRef(dwTlsIndex) = lpTlsValue;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL KeTlsFree(DWORD dwTlsIndex)
|
||||
uint32_t KeTlsAlloc()
|
||||
{
|
||||
return TlsFree(dwTlsIndex);
|
||||
std::lock_guard<Mutex> lock(g_tlsAllocationMutex);
|
||||
if (!g_tlsFreeIndices.empty())
|
||||
{
|
||||
size_t index = g_tlsFreeIndices.back();
|
||||
g_tlsFreeIndices.pop_back();
|
||||
return index;
|
||||
}
|
||||
|
||||
return g_tlsNextIndex++;
|
||||
}
|
||||
|
||||
DWORD XMsgInProcessCall(uint32_t app, uint32_t message, XDWORD* param1, XDWORD* param2)
|
||||
uint32_t KeTlsFree(uint32_t dwTlsIndex)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(g_tlsAllocationMutex);
|
||||
g_tlsFreeIndices.push_back(dwTlsIndex);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uint32_t XMsgInProcessCall(uint32_t app, uint32_t message, be<uint32_t>* param1, be<uint32_t>* param2)
|
||||
{
|
||||
if (message == 0x7001B)
|
||||
{
|
||||
@@ -939,7 +1076,7 @@ void XamUserReadProfileSettings
|
||||
uint64_t* xuids,
|
||||
uint32_t settingCount,
|
||||
uint32_t* settingIds,
|
||||
XDWORD* bufferSize,
|
||||
be<uint32_t>* bufferSize,
|
||||
void* buffer,
|
||||
void* overlapped
|
||||
)
|
||||
@@ -1036,10 +1173,14 @@ void XexGetModuleHandle()
|
||||
|
||||
bool RtlTryEnterCriticalSection(XRTL_CRITICAL_SECTION* cs)
|
||||
{
|
||||
DWORD thisThread = GetCurrentThreadId();
|
||||
DWORD previousOwner = InterlockedCompareExchangeAcquire(&cs->OwningThread, thisThread, 0);
|
||||
uint32_t thisThread = g_ppcContext->r13.u32;
|
||||
assert(thisThread != NULL);
|
||||
|
||||
if (previousOwner == 0 || previousOwner == thisThread)
|
||||
std::atomic_ref owningThread(cs->OwningThread);
|
||||
|
||||
uint32_t previousOwner = 0;
|
||||
|
||||
if (owningThread.compare_exchange_weak(previousOwner, thisThread) || previousOwner == thisThread)
|
||||
{
|
||||
cs->RecursionCount++;
|
||||
return true;
|
||||
@@ -1116,19 +1257,15 @@ void NtQueryFullAttributesFile()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
NTSTATUS RtlMultiByteToUnicodeN(PWCH UnicodeString, ULONG MaxBytesInUnicodeString, XLPDWORD BytesInUnicodeString, const CHAR* MultiByteString, ULONG BytesInMultiByteString)
|
||||
uint32_t RtlMultiByteToUnicodeN(be<uint16_t>* UnicodeString, uint32_t MaxBytesInUnicodeString, be<uint32_t>* BytesInUnicodeString, const char* MultiByteString, uint32_t BytesInMultiByteString)
|
||||
{
|
||||
// i am lazy
|
||||
const auto n = MultiByteToWideChar(CP_UTF8, 0, MultiByteString, BytesInMultiByteString, UnicodeString, MaxBytesInUnicodeString);
|
||||
uint32_t length = std::min(MaxBytesInUnicodeString / 2, BytesInMultiByteString);
|
||||
|
||||
if (BytesInUnicodeString)
|
||||
*BytesInUnicodeString = n * sizeof(wchar_t);
|
||||
for (size_t i = 0; i < length; i++)
|
||||
UnicodeString[i] = MultiByteString[i];
|
||||
|
||||
if (n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
UnicodeString[i] = ByteSwap(UnicodeString[i]);
|
||||
}
|
||||
if (BytesInUnicodeString != nullptr)
|
||||
*BytesInUnicodeString = length * 2;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1143,41 +1280,41 @@ void MmQueryAllocationSize()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t NtClearEvent(uint32_t handle, uint32_t* previousState)
|
||||
uint32_t NtClearEvent(Event* handle, uint32_t* previousState)
|
||||
{
|
||||
return ResetEvent((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
handle->Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t NtResumeThread(uint32_t hThread, uint32_t* suspendCount)
|
||||
uint32_t NtResumeThread(GuestThreadHandle* hThread, uint32_t* suspendCount)
|
||||
{
|
||||
DWORD count = ResumeThread((HANDLE)hThread);
|
||||
assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE));
|
||||
|
||||
if (count == (DWORD)-1)
|
||||
return E_FAIL;
|
||||
|
||||
if (suspendCount != nullptr)
|
||||
*suspendCount = ByteSwap(count);
|
||||
hThread->suspended = false;
|
||||
hThread->suspended.notify_all();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uint32_t NtSetEvent(uint32_t handle, uint32_t* previousState)
|
||||
uint32_t NtSetEvent(Event* handle, uint32_t* previousState)
|
||||
{
|
||||
return SetEvent((HANDLE)handle) ? 0 : 0xFFFFFFFF;
|
||||
handle->Set();
|
||||
return 0;
|
||||
}
|
||||
|
||||
NTSTATUS NtCreateSemaphore(XLPDWORD Handle, XOBJECT_ATTRIBUTES* ObjectAttributes, DWORD InitialCount, DWORD MaximumCount)
|
||||
uint32_t NtCreateSemaphore(be<uint32_t>* Handle, XOBJECT_ATTRIBUTES* ObjectAttributes, uint32_t InitialCount, uint32_t MaximumCount)
|
||||
{
|
||||
*Handle = (uint32_t)CreateSemaphoreA(nullptr, InitialCount, MaximumCount, nullptr);
|
||||
*Handle = GetKernelHandle(CreateKernelObject<Semaphore>(InitialCount, MaximumCount));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS NtReleaseSemaphore(uint32_t Handle, DWORD ReleaseCount, LONG* PreviousCount)
|
||||
uint32_t NtReleaseSemaphore(Semaphore* Handle, uint32_t ReleaseCount, int32_t* PreviousCount)
|
||||
{
|
||||
ReleaseSemaphore((HANDLE)Handle, ReleaseCount, PreviousCount);
|
||||
uint32_t previousCount;
|
||||
Handle->Release(ReleaseCount, &previousCount);
|
||||
|
||||
if (PreviousCount)
|
||||
*PreviousCount = ByteSwap(*PreviousCount);
|
||||
if (PreviousCount != nullptr)
|
||||
*PreviousCount = ByteSwap(previousCount);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1212,11 +1349,17 @@ void NtFlushBuffersFile()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
void KeQuerySystemTime(uint64_t* time)
|
||||
void KeQuerySystemTime(be<uint64_t>* time)
|
||||
{
|
||||
FILETIME t;
|
||||
GetSystemTimeAsFileTime(&t);
|
||||
*time = ByteSwap((uint64_t(t.dwHighDateTime) << 32) | t.dwLowDateTime);
|
||||
constexpr int64_t FILETIME_EPOCH_DIFFERENCE = 116444736000000000LL;
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto timeSinceEpoch = now.time_since_epoch();
|
||||
|
||||
int64_t currentTime100ns = std::chrono::duration_cast<std::chrono::duration<int64_t, std::ratio<1, 10000000>>>(timeSinceEpoch).count();
|
||||
currentTime100ns += FILETIME_EPOCH_DIFFERENCE;
|
||||
|
||||
*time = currentTime100ns;
|
||||
}
|
||||
|
||||
void RtlTimeToTimeFields()
|
||||
@@ -1244,14 +1387,14 @@ void ExTerminateThread()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
uint32_t ExCreateThread(XLPDWORD handle, uint32_t stackSize, XLPDWORD threadId, uint32_t xApiThreadStartup, uint32_t startAddress, uint32_t startContext, uint32_t creationFlags)
|
||||
uint32_t ExCreateThread(be<uint32_t>* handle, uint32_t stackSize, be<uint32_t>* threadId, uint32_t xApiThreadStartup, uint32_t startAddress, uint32_t startContext, uint32_t creationFlags)
|
||||
{
|
||||
LOGF_UTILITY("0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}",
|
||||
(intptr_t)handle, stackSize, (intptr_t)threadId, xApiThreadStartup, startAddress, startContext, creationFlags);
|
||||
|
||||
DWORD hostThreadId;
|
||||
uint32_t hostThreadId;
|
||||
|
||||
*handle = (uint32_t)GuestThread::Start(startAddress, startContext, creationFlags, &hostThreadId);
|
||||
*handle = GetKernelHandle(GuestThread::Start({ startAddress, startContext, creationFlags }, &hostThreadId));
|
||||
|
||||
if (threadId != nullptr)
|
||||
*threadId = hostThreadId;
|
||||
@@ -1329,21 +1472,43 @@ void NetDll_XNetGetTitleXnAddr()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
DWORD KeWaitForMultipleObjects(DWORD Count, xpointer<XDISPATCHER_HEADER>* Objects, DWORD WaitType, DWORD WaitReason, DWORD WaitMode, DWORD Alertable, XLPQWORD Timeout)
|
||||
uint32_t KeWaitForMultipleObjects(uint32_t Count, xpointer<XDISPATCHER_HEADER>* Objects, uint32_t WaitType, uint32_t WaitReason, uint32_t WaitMode, uint32_t Alertable, be<int64_t>* Timeout)
|
||||
{
|
||||
// TODO: create actual objects by type.
|
||||
// FIXME: This function is only accounting for events.
|
||||
|
||||
const uint64_t timeout = GuestTimeoutToMilliseconds(Timeout);
|
||||
assert(timeout == INFINITE);
|
||||
|
||||
thread_local std::vector<HANDLE> events;
|
||||
events.resize(Count);
|
||||
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
if (WaitType == 0) // Wait all
|
||||
{
|
||||
assert(Objects[i]->Type <= 1);
|
||||
events[i] = ObQueryObject<Event>(*Objects[i].get())->handle;
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
QueryKernelObject<Event>(*Objects[i])->Wait(timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_local std::vector<Event*> s_events;
|
||||
s_events.resize(Count);
|
||||
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
s_events[i] = QueryKernelObject<Event>(*Objects[i]);
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint32_t generation = g_keSetEventGeneration.load();
|
||||
|
||||
for (size_t i = 0; i < Count; i++)
|
||||
{
|
||||
if (s_events[i]->Wait(0) == STATUS_SUCCESS)
|
||||
{
|
||||
return STATUS_WAIT_0 + i;
|
||||
}
|
||||
}
|
||||
|
||||
g_keSetEventGeneration.wait(generation);
|
||||
}
|
||||
}
|
||||
|
||||
return WaitForMultipleObjectsEx(Count, events.data(), WaitType == 0, timeout, Alertable);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t KeRaiseIrqlToDpcLevel()
|
||||
@@ -1355,8 +1520,9 @@ void KfLowerIrql() { }
|
||||
|
||||
uint32_t KeReleaseSemaphore(XKSEMAPHORE* semaphore, uint32_t increment, uint32_t adjustment, uint32_t wait)
|
||||
{
|
||||
auto* object = ObQueryObject<Semaphore>(semaphore->Header);
|
||||
return ReleaseSemaphore(object->handle, adjustment, nullptr) ? 0 : 0xFFFFFFFF;
|
||||
auto* object = QueryKernelObject<Semaphore>(semaphore->Header);
|
||||
object->Release(adjustment, nullptr);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void XAudioGetVoiceCategoryVolume()
|
||||
@@ -1364,16 +1530,19 @@ void XAudioGetVoiceCategoryVolume()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
DWORD XAudioGetVoiceCategoryVolumeChangeMask(DWORD Driver, XLPDWORD Mask)
|
||||
uint32_t XAudioGetVoiceCategoryVolumeChangeMask(uint32_t Driver, be<uint32_t>* Mask)
|
||||
{
|
||||
*Mask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t KeResumeThread(uint32_t object)
|
||||
uint32_t KeResumeThread(GuestThreadHandle* object)
|
||||
{
|
||||
LOGF_UTILITY("0x{:x}", object);
|
||||
return ResumeThread((HANDLE)object);
|
||||
assert(object != GetKernelObject(CURRENT_THREAD_HANDLE));
|
||||
|
||||
object->suspended = false;
|
||||
object->suspended.notify_all();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KeInitializeSemaphore(XKSEMAPHORE* semaphore, uint32_t count, uint32_t limit)
|
||||
@@ -1382,7 +1551,7 @@ void KeInitializeSemaphore(XKSEMAPHORE* semaphore, uint32_t count, uint32_t limi
|
||||
semaphore->Header.SignalState = count;
|
||||
semaphore->Limit = limit;
|
||||
|
||||
auto* object = ObQueryObject<Semaphore>(semaphore->Header);
|
||||
auto* object = QueryKernelObject<Semaphore>(semaphore->Header);
|
||||
}
|
||||
|
||||
void XMAReleaseContext()
|
||||
@@ -1395,7 +1564,7 @@ void XMACreateContext()
|
||||
LOG_UTILITY("!!! STUB !!!");
|
||||
}
|
||||
|
||||
// uint32_t XAudioRegisterRenderDriverClient(XLPDWORD callback, XLPDWORD driver)
|
||||
// uint32_t XAudioRegisterRenderDriverClient(be<uint32_t>* callback, be<uint32_t>* driver)
|
||||
// {
|
||||
// //printf("XAudioRegisterRenderDriverClient(): %x %x\n");
|
||||
//
|
||||
|
||||
@@ -6,78 +6,140 @@
|
||||
#include <cpu/guest_thread.h>
|
||||
#include <os/logger.h>
|
||||
|
||||
bool FindHandleCloser(void* handle)
|
||||
struct FileHandle : KernelObject
|
||||
{
|
||||
FindClose(handle);
|
||||
return false;
|
||||
}
|
||||
std::fstream stream;
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
SWA_API uint32_t XCreateFileA
|
||||
struct FindHandle : KernelObject
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::path searchPath;
|
||||
std::filesystem::directory_iterator iterator;
|
||||
|
||||
void fillFindData(WIN32_FIND_DATAA* lpFindFileData)
|
||||
{
|
||||
if (iterator->is_directory())
|
||||
lpFindFileData->dwFileAttributes = ByteSwap(FILE_ATTRIBUTE_DIRECTORY);
|
||||
else if (iterator->is_regular_file())
|
||||
lpFindFileData->dwFileAttributes = ByteSwap(FILE_ATTRIBUTE_NORMAL);
|
||||
|
||||
std::u8string pathU8Str = iterator->path().lexically_relative(searchPath).u8string();
|
||||
uint64_t fileSize = iterator->file_size(ec);
|
||||
strncpy(lpFindFileData->cFileName, (const char *)(pathU8Str.c_str()), sizeof(lpFindFileData->cFileName));
|
||||
lpFindFileData->nFileSizeLow = ByteSwap(uint32_t(fileSize >> 32U));
|
||||
lpFindFileData->nFileSizeHigh = ByteSwap(uint32_t(fileSize));
|
||||
lpFindFileData->ftCreationTime = {};
|
||||
lpFindFileData->ftLastAccessTime = {};
|
||||
lpFindFileData->ftLastWriteTime = {};
|
||||
}
|
||||
};
|
||||
|
||||
SWA_API FileHandle* XCreateFileA
|
||||
(
|
||||
LPCSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes
|
||||
const char* lpFileName,
|
||||
uint32_t dwDesiredAccess,
|
||||
uint32_t dwShareMode,
|
||||
void* lpSecurityAttributes,
|
||||
uint32_t dwCreationDisposition,
|
||||
uint32_t dwFlagsAndAttributes
|
||||
)
|
||||
{
|
||||
const auto handle = (uint32_t)CreateFileA(
|
||||
FileSystem::TransformPath(lpFileName),
|
||||
dwDesiredAccess,
|
||||
dwShareMode,
|
||||
nullptr,
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes & ~(FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED),
|
||||
nullptr);
|
||||
assert(((dwDesiredAccess & ~(GENERIC_READ | GENERIC_WRITE | FILE_READ_DATA)) == 0) && "Unknown desired access bits.");
|
||||
assert(((dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE)) == 0) && "Unknown share mode bits.");
|
||||
assert(((dwCreationDisposition & ~(CREATE_NEW | CREATE_ALWAYS)) == 0) && "Unknown creation disposition bits.");
|
||||
|
||||
GuestThread::SetLastError(GetLastError());
|
||||
std::filesystem::path filePath = std::u8string_view((const char8_t*)(FileSystem::TransformPath(lpFileName)));
|
||||
std::fstream fileStream;
|
||||
std::ios::openmode fileOpenMode = std::ios::binary;
|
||||
if (dwDesiredAccess & (GENERIC_READ | FILE_READ_DATA))
|
||||
{
|
||||
fileOpenMode |= std::ios::in;
|
||||
}
|
||||
|
||||
LOGF_UTILITY("\"{}\", 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X} -> 0x{:X}",
|
||||
lpFileName, dwDesiredAccess, dwShareMode, (intptr_t)lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, handle);
|
||||
if (dwDesiredAccess & GENERIC_WRITE)
|
||||
{
|
||||
fileOpenMode |= std::ios::out;
|
||||
}
|
||||
|
||||
return handle;
|
||||
fileStream.open(filePath, fileOpenMode);
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GuestThread::SetLastError(GetLastError());
|
||||
#endif
|
||||
return GetInvalidKernelObject<FileHandle>();
|
||||
}
|
||||
|
||||
FileHandle *fileHandle = CreateKernelObject<FileHandle>();
|
||||
fileHandle->stream = std::move(fileStream);
|
||||
fileHandle->path = std::move(filePath);
|
||||
return fileHandle;
|
||||
}
|
||||
|
||||
static DWORD XGetFileSizeA(uint32_t hFile, LPDWORD lpFileSizeHigh)
|
||||
static uint32_t XGetFileSizeA(FileHandle* hFile, be<uint32_t>* lpFileSizeHigh)
|
||||
{
|
||||
DWORD fileSize = GetFileSize((HANDLE)hFile, lpFileSizeHigh);
|
||||
std::error_code ec;
|
||||
auto fileSize = std::filesystem::file_size(hFile->path, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (lpFileSizeHigh != nullptr)
|
||||
{
|
||||
*lpFileSizeHigh = uint32_t(fileSize >> 32U);
|
||||
}
|
||||
|
||||
return (uint32_t)(fileSize);
|
||||
}
|
||||
|
||||
if (lpFileSizeHigh != nullptr)
|
||||
*lpFileSizeHigh = ByteSwap(*lpFileSizeHigh);
|
||||
|
||||
return fileSize;
|
||||
return INVALID_FILE_SIZE;
|
||||
}
|
||||
|
||||
BOOL XGetFileSizeExA(uint32_t hFile, PLARGE_INTEGER lpFileSize)
|
||||
uint32_t XGetFileSizeExA(FileHandle* hFile, LARGE_INTEGER* lpFileSize)
|
||||
{
|
||||
BOOL result = GetFileSizeEx((HANDLE)hFile, lpFileSize);
|
||||
std::error_code ec;
|
||||
auto fileSize = std::filesystem::file_size(hFile->path, ec);
|
||||
if (!ec)
|
||||
{
|
||||
if (lpFileSize != nullptr)
|
||||
{
|
||||
lpFileSize->QuadPart = ByteSwap(fileSize);
|
||||
}
|
||||
|
||||
if (result)
|
||||
lpFileSize->QuadPart = ByteSwap(lpFileSize->QuadPart);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL XReadFile
|
||||
uint32_t XReadFile
|
||||
(
|
||||
uint32_t hFile,
|
||||
LPVOID lpBuffer,
|
||||
DWORD nNumberOfBytesToRead,
|
||||
XLPDWORD lpNumberOfBytesRead,
|
||||
FileHandle* hFile,
|
||||
void* lpBuffer,
|
||||
uint32_t nNumberOfBytesToRead,
|
||||
be<uint32_t>* lpNumberOfBytesRead,
|
||||
XOVERLAPPED* lpOverlapped
|
||||
)
|
||||
{
|
||||
uint32_t result = FALSE;
|
||||
if (lpOverlapped != nullptr)
|
||||
{
|
||||
LONG distanceToMoveHigh = lpOverlapped->OffsetHigh;
|
||||
|
||||
if (SetFilePointer((HANDLE)hFile, lpOverlapped->Offset, &distanceToMoveHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
||||
std::streamoff streamOffset = lpOverlapped->Offset + (std::streamoff(lpOverlapped->OffsetHigh.get()) << 32U);
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(streamOffset, std::ios::beg);
|
||||
if (hFile->stream.bad())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD numberOfBytesRead;
|
||||
BOOL result = ReadFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToRead, &numberOfBytesRead, nullptr);
|
||||
uint32_t numberOfBytesRead;
|
||||
hFile->stream.read((char *)(lpBuffer), nNumberOfBytesToRead);
|
||||
if (!hFile->stream.bad())
|
||||
{
|
||||
numberOfBytesRead = uint32_t(hFile->stream.gcount());
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
@@ -85,9 +147,6 @@ BOOL XReadFile
|
||||
{
|
||||
lpOverlapped->Internal = 0;
|
||||
lpOverlapped->InternalHigh = numberOfBytesRead;
|
||||
|
||||
if (lpOverlapped->hEvent != NULL)
|
||||
SetEvent((HANDLE)lpOverlapped->hEvent.get());
|
||||
}
|
||||
else if (lpNumberOfBytesRead != nullptr)
|
||||
{
|
||||
@@ -95,115 +154,197 @@ BOOL XReadFile
|
||||
}
|
||||
}
|
||||
|
||||
// printf("ReadFile(): %x %x %x %x %x %x\n", hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD XSetFilePointer(uint32_t hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
|
||||
uint32_t XSetFilePointer(FileHandle* hFile, int32_t lDistanceToMove, be<int32_t>* lpDistanceToMoveHigh, uint32_t dwMoveMethod)
|
||||
{
|
||||
LONG distanceToMoveHigh = lpDistanceToMoveHigh ? ByteSwap(*lpDistanceToMoveHigh) : 0;
|
||||
DWORD result = SetFilePointer((HANDLE)hFile, lDistanceToMove, lpDistanceToMoveHigh ? &distanceToMoveHigh : nullptr, dwMoveMethod);
|
||||
int32_t distanceToMoveHigh = lpDistanceToMoveHigh ? lpDistanceToMoveHigh->get() : 0;
|
||||
std::streamoff streamOffset = lDistanceToMove + (std::streamoff(distanceToMoveHigh) << 32U);
|
||||
std::fstream::seekdir streamSeekDir = {};
|
||||
switch (dwMoveMethod)
|
||||
{
|
||||
case FILE_BEGIN:
|
||||
streamSeekDir = std::ios::beg;
|
||||
break;
|
||||
case FILE_CURRENT:
|
||||
streamSeekDir = std::ios::cur;
|
||||
break;
|
||||
case FILE_END:
|
||||
streamSeekDir = std::ios::end;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown move method.");
|
||||
break;
|
||||
}
|
||||
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(streamOffset, streamSeekDir);
|
||||
if (hFile->stream.bad())
|
||||
{
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
|
||||
std::streampos streamPos = hFile->stream.tellg();
|
||||
if (lpDistanceToMoveHigh != nullptr)
|
||||
*lpDistanceToMoveHigh = ByteSwap(distanceToMoveHigh);
|
||||
*lpDistanceToMoveHigh = int32_t(streamPos >> 32U);
|
||||
|
||||
return result;
|
||||
return uint32_t(streamPos);
|
||||
}
|
||||
|
||||
BOOL XSetFilePointerEx(uint32_t hFile, LONG lDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
|
||||
uint32_t XSetFilePointerEx(FileHandle* hFile, int32_t lDistanceToMove, LARGE_INTEGER* lpNewFilePointer, uint32_t dwMoveMethod)
|
||||
{
|
||||
LARGE_INTEGER distanceToMove;
|
||||
distanceToMove.QuadPart = lDistanceToMove;
|
||||
std::fstream::seekdir streamSeekDir = {};
|
||||
switch (dwMoveMethod)
|
||||
{
|
||||
case FILE_BEGIN:
|
||||
streamSeekDir = std::ios::beg;
|
||||
break;
|
||||
case FILE_CURRENT:
|
||||
streamSeekDir = std::ios::cur;
|
||||
break;
|
||||
case FILE_END:
|
||||
streamSeekDir = std::ios::end;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown move method.");
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD result = SetFilePointerEx((HANDLE)hFile, distanceToMove, lpNewFilePointer, dwMoveMethod);
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(lDistanceToMove, streamSeekDir);
|
||||
if (hFile->stream.bad())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpNewFilePointer != nullptr)
|
||||
lpNewFilePointer->QuadPart = ByteSwap(lpNewFilePointer->QuadPart);
|
||||
{
|
||||
lpNewFilePointer->QuadPart = ByteSwap(int64_t(hFile->stream.tellg()));
|
||||
}
|
||||
|
||||
return result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uint32_t XFindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
|
||||
FindHandle* XFindFirstFileA(const char* lpFileName, WIN32_FIND_DATAA* lpFindFileData)
|
||||
{
|
||||
auto& data = *lpFindFileData;
|
||||
const auto handle = FindFirstFileA(FileSystem::TransformPath(lpFileName), &data);
|
||||
const char *transformedPath = FileSystem::TransformPath(lpFileName);
|
||||
size_t transformedPathLength = strlen(transformedPath);
|
||||
if (transformedPathLength == 0)
|
||||
return (FindHandle*)GUEST_INVALID_HANDLE_VALUE;
|
||||
|
||||
GuestThread::SetLastError(GetLastError());
|
||||
std::filesystem::path dirPath;
|
||||
if (strstr(transformedPath, "\\*") == (&transformedPath[transformedPathLength - 2]) || strstr(transformedPath, "/*") == (&transformedPath[transformedPathLength - 2]))
|
||||
{
|
||||
dirPath = std::u8string_view((const char8_t*)(transformedPath), transformedPathLength - 2);
|
||||
}
|
||||
else if (strstr(transformedPath, "\\*.*") == (&transformedPath[transformedPathLength - 4]) || strstr(transformedPath, "/*.*") == (&transformedPath[transformedPathLength - 4]))
|
||||
{
|
||||
dirPath = std::u8string_view((const char8_t *)(transformedPath), transformedPathLength - 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
dirPath = std::u8string_view((const char8_t *)(transformedPath), transformedPathLength);
|
||||
assert(!dirPath.has_extension() && "Unknown search pattern.");
|
||||
}
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return 0xFFFFFFFF;
|
||||
if (!std::filesystem::is_directory(dirPath))
|
||||
return GetInvalidKernelObject<FindHandle>();
|
||||
|
||||
ByteSwapInplace(data.dwFileAttributes);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftCreationTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastAccessTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastWriteTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.nFileSizeHigh);
|
||||
std::filesystem::directory_iterator dirIterator(dirPath);
|
||||
if (dirIterator == std::filesystem::directory_iterator())
|
||||
return GetInvalidKernelObject<FindHandle>();
|
||||
|
||||
return GUEST_HANDLE(ObInsertObject(handle, FindHandleCloser));
|
||||
FindHandle *findHandle = CreateKernelObject<FindHandle>();
|
||||
findHandle->searchPath = std::move(dirPath);
|
||||
findHandle->iterator = std::move(dirIterator);
|
||||
findHandle->fillFindData(lpFindFileData);
|
||||
return findHandle;
|
||||
}
|
||||
|
||||
uint32_t XFindNextFileA(uint32_t Handle, LPWIN32_FIND_DATAA lpFindFileData)
|
||||
uint32_t XFindNextFileA(FindHandle* Handle, WIN32_FIND_DATAA* lpFindFileData)
|
||||
{
|
||||
auto* handle = ObQueryObject(HOST_HANDLE(Handle));
|
||||
auto& data = *lpFindFileData;
|
||||
const auto result = FindNextFileA(handle, &data);
|
||||
Handle->iterator++;
|
||||
|
||||
ByteSwapInplace(data.dwFileAttributes);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftCreationTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastAccessTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.ftLastWriteTime);
|
||||
ByteSwapInplace(*(uint64_t*)&data.nFileSizeHigh);
|
||||
|
||||
return result;
|
||||
if (Handle->iterator == std::filesystem::directory_iterator())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Handle->fillFindData(lpFindFileData);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL XReadFileEx(uint32_t hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, XOVERLAPPED* lpOverlapped, uint32_t lpCompletionRoutine)
|
||||
uint32_t XReadFileEx(FileHandle* hFile, void* lpBuffer, uint32_t nNumberOfBytesToRead, XOVERLAPPED* lpOverlapped, uint32_t lpCompletionRoutine)
|
||||
{
|
||||
LONG distanceToMoveHigh = lpOverlapped->OffsetHigh;
|
||||
|
||||
if (SetFilePointer((HANDLE)hFile, lpOverlapped->Offset, &distanceToMoveHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
||||
uint32_t result = FALSE;
|
||||
uint32_t numberOfBytesRead;
|
||||
std::streamoff streamOffset = lpOverlapped->Offset + (std::streamoff(lpOverlapped->OffsetHigh.get()) << 32U);
|
||||
hFile->stream.clear();
|
||||
hFile->stream.seekg(streamOffset, std::ios::beg);
|
||||
if (hFile->stream.bad())
|
||||
return FALSE;
|
||||
|
||||
DWORD numberOfBytesRead;
|
||||
BOOL result = ReadFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToRead, &numberOfBytesRead, nullptr);
|
||||
hFile->stream.read((char *)(lpBuffer), nNumberOfBytesToRead);
|
||||
if (!hFile->stream.bad())
|
||||
{
|
||||
numberOfBytesRead = uint32_t(hFile->stream.gcount());
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
lpOverlapped->Internal = 0;
|
||||
lpOverlapped->InternalHigh = numberOfBytesRead;
|
||||
|
||||
if (lpOverlapped->hEvent != NULL)
|
||||
SetEvent((HANDLE)lpOverlapped->hEvent.get());
|
||||
}
|
||||
|
||||
// printf("ReadFileEx(): %x %x %x %x %x %x\n", hFile, lpBuffer, nNumberOfBytesToRead, lpOverlapped, lpCompletionRoutine, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD XGetFileAttributesA(LPCSTR lpFileName)
|
||||
uint32_t XGetFileAttributesA(const char* lpFileName)
|
||||
{
|
||||
return GetFileAttributesA(FileSystem::TransformPath(lpFileName));
|
||||
std::filesystem::path filePath(std::u8string_view((const char8_t*)(FileSystem::TransformPath(lpFileName))));
|
||||
if (std::filesystem::is_directory(filePath))
|
||||
return FILE_ATTRIBUTE_DIRECTORY;
|
||||
else if (std::filesystem::is_regular_file(filePath))
|
||||
return FILE_ATTRIBUTE_NORMAL;
|
||||
else
|
||||
return INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
BOOL XWriteFile(uint32_t hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
|
||||
uint32_t XWriteFile(FileHandle* hFile, const void* lpBuffer, uint32_t nNumberOfBytesToWrite, be<uint32_t>* lpNumberOfBytesWritten, void* lpOverlapped)
|
||||
{
|
||||
assert(lpOverlapped == nullptr);
|
||||
assert(lpOverlapped == nullptr && "Overlapped not implemented.");
|
||||
|
||||
BOOL result = WriteFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, nullptr);
|
||||
hFile->stream.write((const char *)(lpBuffer), nNumberOfBytesToWrite);
|
||||
if (hFile->stream.bad())
|
||||
return FALSE;
|
||||
|
||||
if (result && lpNumberOfBytesWritten != nullptr)
|
||||
ByteSwapInplace(*lpNumberOfBytesWritten);
|
||||
if (lpNumberOfBytesWritten != nullptr)
|
||||
*lpNumberOfBytesWritten = uint32_t(hFile->stream.gcount());
|
||||
|
||||
return result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void fixSlashes(char *path)
|
||||
{
|
||||
while (*path != 0)
|
||||
{
|
||||
if (*path == '\\')
|
||||
{
|
||||
*path = '/';
|
||||
}
|
||||
|
||||
path++;
|
||||
}
|
||||
}
|
||||
|
||||
const char* FileSystem::TransformPath(const char* path)
|
||||
{
|
||||
thread_local char builtPath[2048]{};
|
||||
const char* relativePath = strstr(path, ":\\");
|
||||
|
||||
if (relativePath != nullptr)
|
||||
{
|
||||
// rooted folder, handle direction
|
||||
@@ -215,12 +356,19 @@ const char* FileSystem::TransformPath(const char* path)
|
||||
strncpy(builtPath, newRoot.data(), newRoot.size());
|
||||
builtPath[newRoot.size()] = '\\';
|
||||
strcpy(builtPath + newRoot.size() + 1, relativePath + 2);
|
||||
|
||||
return builtPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(builtPath, relativePath + 2, sizeof(builtPath));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(builtPath, path, sizeof(builtPath));
|
||||
}
|
||||
|
||||
return relativePath != nullptr ? relativePath + 2 : path;
|
||||
fixSlashes(builtPath);
|
||||
return builtPath;
|
||||
}
|
||||
|
||||
SWA_API const char* XExpandFilePathA(const char* path)
|
||||
|
||||
@@ -3,25 +3,46 @@
|
||||
|
||||
Memory::Memory(void* address, size_t size) : size(size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
base = (char*)VirtualAlloc(address, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base == nullptr)
|
||||
base = (char*)VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
#else
|
||||
base = (char*)mmap(address, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (base == (char*)MAP_FAILED)
|
||||
base = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
mprotect(base, 4096, PROT_NONE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* Memory::Alloc(size_t offset, size_t size, uint32_t type)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return VirtualAlloc(base + offset, size, type, PAGE_READWRITE);
|
||||
#else
|
||||
return base + offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* Memory::Commit(size_t offset, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return Alloc(offset, size, MEM_COMMIT);
|
||||
#else
|
||||
return base + offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* Memory::Reserve(size_t offset, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return Alloc(offset, size, MEM_RESERVE);
|
||||
#else
|
||||
return base + offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* MmGetHostAddress(uint32_t ptr)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
#define MEM_COMMIT 0x00001000
|
||||
#define MEM_RESERVE 0x00002000
|
||||
#endif
|
||||
|
||||
class Memory
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -2,16 +2,92 @@
|
||||
#include "xam.h"
|
||||
#include "xdm.h"
|
||||
#include <hid/hid.h>
|
||||
#include <ui/window.h>
|
||||
#include <ui/game_window.h>
|
||||
#include <cpu/guest_thread.h>
|
||||
#include <ranges>
|
||||
#include <unordered_set>
|
||||
#include <CommCtrl.h>
|
||||
#include "xxHashMap.h"
|
||||
#include <user/paths.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <CommCtrl.h>
|
||||
// Needed for commctrl
|
||||
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||
#endif
|
||||
|
||||
struct XamListener : KernelObject
|
||||
{
|
||||
uint32_t id{};
|
||||
uint64_t areas{};
|
||||
std::vector<std::tuple<uint32_t, uint32_t>> notifications;
|
||||
|
||||
XamListener(const XamListener&) = delete;
|
||||
XamListener& operator=(const XamListener&) = delete;
|
||||
|
||||
XamListener();
|
||||
~XamListener();
|
||||
};
|
||||
|
||||
struct XamEnumeratorBase : KernelObject
|
||||
{
|
||||
virtual uint32_t Next(void* buffer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TIterator = std::vector<XHOSTCONTENT_DATA>::iterator>
|
||||
struct XamEnumerator : XamEnumeratorBase
|
||||
{
|
||||
uint32_t fetch;
|
||||
size_t size;
|
||||
TIterator position;
|
||||
TIterator begin;
|
||||
TIterator end;
|
||||
|
||||
XamEnumerator() = default;
|
||||
XamEnumerator(uint32_t fetch, size_t size, TIterator begin, TIterator end) : fetch(fetch), size(size), position(begin), begin(begin), end(end)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint32_t Next(void* buffer) override
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
memcpy(buffer, &*position, size);
|
||||
|
||||
++position;
|
||||
buffer = (void*)((size_t)buffer + size);
|
||||
}
|
||||
|
||||
return fetch;
|
||||
}
|
||||
};
|
||||
|
||||
std::array<xxHashMap<XHOSTCONTENT_DATA>, 3> gContentRegistry{};
|
||||
std::unordered_set<XamListener*> gListeners{};
|
||||
@@ -42,7 +118,7 @@ XamListener::~XamListener()
|
||||
gListeners.erase(this);
|
||||
}
|
||||
|
||||
XCONTENT_DATA XamMakeContent(DWORD type, const std::string_view& name)
|
||||
XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name)
|
||||
{
|
||||
XCONTENT_DATA data{ 1, type };
|
||||
|
||||
@@ -58,7 +134,7 @@ void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root)
|
||||
gContentRegistry[idx].emplace(StringHash(data.szFileName), XHOSTCONTENT_DATA{ data }).first->second.szRoot = root;
|
||||
}
|
||||
|
||||
void XamRegisterContent(DWORD type, const std::string_view name, const std::string_view& root)
|
||||
void XamRegisterContent(uint32_t type, const std::string_view name, const std::string_view& root)
|
||||
{
|
||||
XCONTENT_DATA data{ 1, type, {}, "" };
|
||||
|
||||
@@ -67,17 +143,16 @@ void XamRegisterContent(DWORD type, const std::string_view name, const std::stri
|
||||
XamRegisterContent(data, root);
|
||||
}
|
||||
|
||||
SWA_API DWORD XamNotifyCreateListener(uint64_t qwAreas)
|
||||
SWA_API uint32_t XamNotifyCreateListener(uint64_t qwAreas)
|
||||
{
|
||||
int handle;
|
||||
auto* listener = ObCreateObject<XamListener>(handle);
|
||||
auto* listener = CreateKernelObject<XamListener>();
|
||||
|
||||
listener->areas = qwAreas;
|
||||
|
||||
return GUEST_HANDLE(handle);
|
||||
return GetKernelHandle(listener);
|
||||
}
|
||||
|
||||
SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam)
|
||||
SWA_API void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam)
|
||||
{
|
||||
for (const auto& listener : gListeners)
|
||||
{
|
||||
@@ -88,9 +163,9 @@ SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam)
|
||||
}
|
||||
}
|
||||
|
||||
SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwId, XDWORD* pParam)
|
||||
SWA_API bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be<uint32_t>* pdwId, be<uint32_t>* pParam)
|
||||
{
|
||||
auto& listener = *ObTryQueryObject<XamListener>(HOST_HANDLE(hNotification));
|
||||
auto& listener = *GetKernelObject<XamListener>(hNotification);
|
||||
|
||||
if (dwMsgFilter)
|
||||
{
|
||||
@@ -129,9 +204,12 @@ SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwI
|
||||
return false;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD* wszText, DWORD cButtons,
|
||||
xpointer<XWORD>* pwszButtons, DWORD dwFocusButton, DWORD dwFlags, XLPDWORD pResult, XXOVERLAPPED* pOverlapped)
|
||||
SWA_API uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be<uint16_t>* wszTitle, be<uint16_t>* wszText, uint32_t cButtons,
|
||||
xpointer<be<uint16_t>>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be<uint32_t>* pResult, XXOVERLAPPED* pOverlapped)
|
||||
{
|
||||
int button{};
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<std::wstring> texts{};
|
||||
std::vector<TASKDIALOG_BUTTON> buttons{};
|
||||
|
||||
@@ -154,20 +232,19 @@ SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD*
|
||||
|
||||
TASKDIALOGCONFIG config{};
|
||||
config.cbSize = sizeof(config);
|
||||
// config.hwndParent = Window::s_hWnd;
|
||||
config.pszWindowTitle = texts[0].c_str();
|
||||
config.pszContent = texts[1].c_str();
|
||||
config.cButtons = cButtons;
|
||||
config.pButtons = buttons.data();
|
||||
|
||||
int button{};
|
||||
TaskDialogIndirect(&config, &button, nullptr, nullptr);
|
||||
#endif
|
||||
|
||||
*pResult = button;
|
||||
|
||||
if (pOverlapped)
|
||||
{
|
||||
pOverlapped->dwCompletionContext = GetCurrentThreadId();
|
||||
pOverlapped->dwCompletionContext = GuestThread::GetCurrentThreadId();
|
||||
pOverlapped->Error = 0;
|
||||
pOverlapped->Length = -1;
|
||||
}
|
||||
@@ -177,8 +254,8 @@ SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD*
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, DWORD dwContentType,
|
||||
DWORD dwContentFlags, DWORD cItem, XLPDWORD pcbBuffer, XLPDWORD phEnum)
|
||||
SWA_API uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType,
|
||||
uint32_t dwContentFlags, uint32_t cItem, be<uint32_t>* pcbBuffer, be<uint32_t>* phEnum)
|
||||
{
|
||||
if (dwUserIndex != 0)
|
||||
{
|
||||
@@ -188,19 +265,19 @@ SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, D
|
||||
|
||||
const auto& registry = gContentRegistry[dwContentType - 1];
|
||||
const auto& values = registry | std::views::values;
|
||||
const int handle = ObInsertObject(new XamEnumerator(cItem, sizeof(_XCONTENT_DATA), values.begin(), values.end()));
|
||||
auto* enumerator = CreateKernelObject<XamEnumerator<decltype(values.begin())>>(cItem, sizeof(_XCONTENT_DATA), values.begin(), values.end());
|
||||
|
||||
if (pcbBuffer)
|
||||
*pcbBuffer = sizeof(_XCONTENT_DATA) * cItem;
|
||||
|
||||
*phEnum = GUEST_HANDLE(handle);
|
||||
*phEnum = GetKernelHandle(enumerator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWORD cbBuffer, XLPDWORD pcItemsReturned, XXOVERLAPPED* pOverlapped)
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be<uint32_t>* pcItemsReturned, XXOVERLAPPED* pOverlapped)
|
||||
{
|
||||
auto* enumerator = ObTryQueryObject<XamEnumeratorBase>(HOST_HANDLE(hEnum));
|
||||
auto* enumerator = GetKernelObject<XamEnumeratorBase>(hEnum);
|
||||
const auto count = enumerator->Next(pvBuffer);
|
||||
|
||||
if (count == -1)
|
||||
@@ -212,9 +289,9 @@ SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWO
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const XCONTENT_DATA* pContentData,
|
||||
DWORD dwContentFlags, XLPDWORD pdwDisposition, XLPDWORD pdwLicenseMask,
|
||||
DWORD dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped)
|
||||
SWA_API uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData,
|
||||
uint32_t dwContentFlags, be<uint32_t>* pdwDisposition, be<uint32_t>* pdwLicenseMask,
|
||||
uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped)
|
||||
{
|
||||
const auto& registry = gContentRegistry[pContentData->dwContentType - 1];
|
||||
const auto exists = registry.contains(StringHash(pContentData->szFileName));
|
||||
@@ -231,19 +308,23 @@ SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const
|
||||
|
||||
if (pContentData->dwContentType == XCONTENTTYPE_SAVEDATA)
|
||||
{
|
||||
root = GetSavePath().string();
|
||||
std::u8string savePathU8 = GetSavePath().u8string();
|
||||
root = (const char *)(savePathU8.c_str());
|
||||
}
|
||||
else if (pContentData->dwContentType == XCONTENTTYPE_DLC)
|
||||
{
|
||||
root = ".\\dlc";
|
||||
root = GAME_INSTALL_DIRECTORY "/dlc";
|
||||
}
|
||||
else
|
||||
{
|
||||
root = ".";
|
||||
root = GAME_INSTALL_DIRECTORY;
|
||||
}
|
||||
|
||||
XamRegisterContent(*pContentData, root);
|
||||
CreateDirectoryA(root.c_str(), nullptr);
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directory(std::u8string_view((const char8_t*)(root.c_str())), ec);
|
||||
|
||||
XamRootCreate(szRootName, root);
|
||||
}
|
||||
else
|
||||
@@ -277,13 +358,13 @@ SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentClose(LPCSTR szRootName, XXOVERLAPPED* pOverlapped)
|
||||
SWA_API uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped)
|
||||
{
|
||||
gRootMap.erase(StringHash(szRootName));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamContentGetDeviceData(DWORD DeviceID, XDEVICE_DATA* pDeviceData)
|
||||
SWA_API uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData)
|
||||
{
|
||||
pDeviceData->DeviceID = DeviceID;
|
||||
pDeviceData->DeviceType = XCONTENTDEVICETYPE_HDD;
|
||||
@@ -320,73 +401,63 @@ SWA_API uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint3
|
||||
|
||||
SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state)
|
||||
{
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
uint32_t result = hid::GetState(userIndex, state);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
if (GameWindow::s_isFocused)
|
||||
{
|
||||
ByteSwapInplace(state->dwPacketNumber);
|
||||
ByteSwapInplace(state->Gamepad.wButtons);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLY);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRY);
|
||||
}
|
||||
else if (userIndex == 0)
|
||||
{
|
||||
if (!Window::s_isFocused)
|
||||
return ERROR_SUCCESS;
|
||||
auto keyboardState = SDL_GetKeyboardState(NULL);
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
if (GetAsyncKeyState('W') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_W])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y;
|
||||
if (GetAsyncKeyState('A') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_A])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X;
|
||||
if (GetAsyncKeyState('S') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_S])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A;
|
||||
if (GetAsyncKeyState('D') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_D])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B;
|
||||
if (GetAsyncKeyState('Q') & 0x8000)
|
||||
|
||||
if (keyboardState[SDL_SCANCODE_Q])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER;
|
||||
if (GetAsyncKeyState('E') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_E])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER;
|
||||
if (GetAsyncKeyState('1') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_1])
|
||||
state->Gamepad.bLeftTrigger = 0xFF;
|
||||
if (GetAsyncKeyState('3') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_3])
|
||||
state->Gamepad.bRightTrigger = 0xFF;
|
||||
|
||||
if (GetAsyncKeyState('I') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_I])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP;
|
||||
if (GetAsyncKeyState('J') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_J])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT;
|
||||
if (GetAsyncKeyState('K') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_K])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN;
|
||||
if (GetAsyncKeyState('L') & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_L])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_RIGHT;
|
||||
|
||||
if (GetAsyncKeyState(VK_UP) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_UP])
|
||||
state->Gamepad.sThumbLY = 32767;
|
||||
if (GetAsyncKeyState(VK_LEFT) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_LEFT])
|
||||
state->Gamepad.sThumbLX = -32768;
|
||||
if (GetAsyncKeyState(VK_DOWN) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_DOWN])
|
||||
state->Gamepad.sThumbLY = -32768;
|
||||
if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_RIGHT])
|
||||
state->Gamepad.sThumbLX = 32767;
|
||||
|
||||
if (GetAsyncKeyState(VK_RETURN) & 0x8000)
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START;
|
||||
if (GetAsyncKeyState(VK_BACK) & 0x8000)
|
||||
if (keyboardState[SDL_SCANCODE_RETURN])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START;
|
||||
if (keyboardState[SDL_SCANCODE_BACKSPACE])
|
||||
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_BACK;
|
||||
|
||||
ByteSwapInplace(state->Gamepad.wButtons);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLY);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRY);
|
||||
|
||||
result = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return result;
|
||||
ByteSwapInplace(state->Gamepad.wButtons);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbLY);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRX);
|
||||
ByteSwapInplace(state->Gamepad.sThumbRY);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
SWA_API uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration)
|
||||
|
||||
@@ -1,108 +1,32 @@
|
||||
#pragma once
|
||||
#include <xbox.h>
|
||||
|
||||
#define MSGID(Area, Number) (DWORD)((WORD)(Area) << 16 | (WORD)(Number))
|
||||
#define MSGID(Area, Number) (uint32_t)((uint16_t)(Area) << 16 | (uint16_t)(Number))
|
||||
#define MSG_AREA(msgid) (((msgid) >> 16) & 0xFFFF)
|
||||
#define MSG_NUMBER(msgid) ((msgid) & 0xFFFF)
|
||||
|
||||
struct XamListener
|
||||
{
|
||||
uint32_t id{};
|
||||
uint64_t areas{};
|
||||
std::vector<std::tuple<DWORD, DWORD>> notifications;
|
||||
|
||||
XamListener(const XamListener&) = delete;
|
||||
XamListener& operator=(const XamListener&) = delete;
|
||||
|
||||
XamListener();
|
||||
~XamListener();
|
||||
};
|
||||
|
||||
class XamEnumeratorBase
|
||||
{
|
||||
public:
|
||||
virtual ~XamEnumeratorBase() = default;
|
||||
virtual uint32_t Next(void* buffer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TIterator = std::vector<XHOSTCONTENT_DATA>::iterator>
|
||||
class XamEnumerator : public XamEnumeratorBase
|
||||
{
|
||||
public:
|
||||
uint32_t fetch;
|
||||
size_t size;
|
||||
TIterator position;
|
||||
TIterator begin;
|
||||
TIterator end;
|
||||
|
||||
XamEnumerator() = default;
|
||||
XamEnumerator(uint32_t fetch, size_t size, TIterator begin, TIterator end) : fetch(fetch), size(size), position(begin), begin(begin), end(end)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint32_t Next(void* buffer) override
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
++position;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fetch; i++)
|
||||
{
|
||||
if (position == end)
|
||||
{
|
||||
return i == 0 ? -1 : i;
|
||||
}
|
||||
|
||||
memcpy(buffer, &*position, size);
|
||||
|
||||
++position;
|
||||
buffer = (void*)((size_t)buffer + size);
|
||||
}
|
||||
|
||||
return fetch;
|
||||
}
|
||||
};
|
||||
|
||||
XCONTENT_DATA XamMakeContent(DWORD type, const std::string_view& name);
|
||||
XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name);
|
||||
void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root);
|
||||
|
||||
std::string_view XamGetRootPath(const std::string_view& root);
|
||||
void XamRootCreate(const std::string_view& root, const std::string_view& path);
|
||||
|
||||
SWA_API DWORD XamNotifyCreateListener(uint64_t qwAreas);
|
||||
SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam); // i made it the fuck up
|
||||
SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwId, XDWORD* pParam);
|
||||
SWA_API uint32_t XamNotifyCreateListener(uint64_t qwAreas);
|
||||
SWA_API void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam); // i made it the fuck up
|
||||
SWA_API bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be<uint32_t>* pdwId, be<uint32_t>* pParam);
|
||||
|
||||
SWA_API uint32_t XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD* wszText, DWORD cButtons,
|
||||
xpointer<XWORD>* pwszButtons, DWORD dwFocusButton, DWORD dwFlags, XLPDWORD pResult, XXOVERLAPPED* pOverlapped);
|
||||
SWA_API uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be<uint16_t>* wszTitle, be<uint16_t>* wszText, uint32_t cButtons,
|
||||
xpointer<be<uint16_t>>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be<uint32_t>* pResult, XXOVERLAPPED* pOverlapped);
|
||||
|
||||
SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, DWORD dwContentType,
|
||||
DWORD dwContentFlags, DWORD cItem, XLPDWORD pcbBuffer, XLPDWORD phEnum);
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWORD cbBuffer, XLPDWORD pcItemsReturned, XXOVERLAPPED* pOverlapped);
|
||||
SWA_API uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType,
|
||||
uint32_t dwContentFlags, uint32_t cItem, be<uint32_t>* pcbBuffer, be<uint32_t>* phEnum);
|
||||
SWA_API uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be<uint32_t>* pcItemsReturned, XXOVERLAPPED* pOverlapped);
|
||||
|
||||
SWA_API uint32_t XamContentCreateEx(DWORD dwUserIndex, LPCSTR szRootName, const XCONTENT_DATA* pContentData,
|
||||
DWORD dwContentFlags, XLPDWORD pdwDisposition, XLPDWORD pdwLicenseMask,
|
||||
DWORD dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped);
|
||||
SWA_API uint32_t XamContentGetDeviceData(DWORD DeviceID, XDEVICE_DATA* pDeviceData);
|
||||
SWA_API uint32_t XamContentClose(LPCSTR szRootName, XXOVERLAPPED* pOverlapped);
|
||||
SWA_API uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData,
|
||||
uint32_t dwContentFlags, be<uint32_t>* pdwDisposition, be<uint32_t>* pdwLicenseMask,
|
||||
uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped);
|
||||
SWA_API uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData);
|
||||
SWA_API uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped);
|
||||
|
||||
SWA_API uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint32_t flags, XAMINPUT_CAPABILITIES* caps);
|
||||
SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state);
|
||||
|
||||
@@ -2,47 +2,36 @@
|
||||
#include "xdm.h"
|
||||
#include "freelist.h"
|
||||
|
||||
FreeList<std::tuple<std::unique_ptr<char>, TypeDestructor_t>> gKernelObjects;
|
||||
Mutex gKernelLock;
|
||||
Mutex g_kernelLock;
|
||||
|
||||
void* ObQueryObject(size_t handle)
|
||||
void DestroyKernelObject(KernelObject* obj)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
|
||||
if (handle >= gKernelObjects.items.size())
|
||||
return nullptr;
|
||||
|
||||
return std::get<0>(gKernelObjects[handle]).get();
|
||||
obj->~KernelObject();
|
||||
g_userHeap.Free(obj);
|
||||
}
|
||||
|
||||
uint32_t ObInsertObject(void* object, TypeDestructor_t destructor)
|
||||
uint32_t GetKernelHandle(KernelObject* obj)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
|
||||
const auto handle = gKernelObjects.Alloc();
|
||||
|
||||
auto& holder = gKernelObjects[handle];
|
||||
|
||||
std::get<0>(holder).reset(static_cast<char*>(object));
|
||||
std::get<1>(holder) = destructor;
|
||||
|
||||
return handle;
|
||||
assert(obj != GetInvalidKernelObject());
|
||||
return g_memory.MapVirtual(obj);
|
||||
}
|
||||
|
||||
void ObCloseHandle(uint32_t handle)
|
||||
void DestroyKernelObject(uint32_t handle)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
|
||||
auto& obj = gKernelObjects[handle];
|
||||
|
||||
if (std::get<1>(obj)(std::get<0>(obj).get()))
|
||||
{
|
||||
std::get<0>(obj).reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::get<0>(obj).release();
|
||||
}
|
||||
|
||||
gKernelObjects.Free(handle);
|
||||
DestroyKernelObject(GetKernelObject(handle));
|
||||
}
|
||||
|
||||
bool IsKernelObject(uint32_t handle)
|
||||
{
|
||||
return (handle & 0x80000000) != 0;
|
||||
}
|
||||
|
||||
bool IsKernelObject(void* obj)
|
||||
{
|
||||
return IsKernelObject(g_memory.MapVirtual(obj));
|
||||
}
|
||||
|
||||
bool IsInvalidKernelObject(void* obj)
|
||||
{
|
||||
return obj == GetInvalidKernelObject();
|
||||
}
|
||||
|
||||
@@ -1,56 +1,153 @@
|
||||
#pragma once
|
||||
#define DUMMY_HANDLE (DWORD)('HAND')
|
||||
#define OBJECT_SIGNATURE (DWORD)'XBOX'
|
||||
|
||||
extern Mutex gKernelLock;
|
||||
#include "heap.h"
|
||||
#include "memory.h"
|
||||
|
||||
void* ObQueryObject(size_t handle);
|
||||
uint32_t ObInsertObject(void* object, TypeDestructor_t destructor);
|
||||
void ObCloseHandle(uint32_t handle);
|
||||
#define OBJECT_SIGNATURE (uint32_t)'XBOX'
|
||||
#define GUEST_INVALID_HANDLE_VALUE 0xFFFFFFFF
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#define S_OK 0x00000000
|
||||
#define FALSE 0x00000000
|
||||
#define TRUE 0x00000001
|
||||
#define STATUS_SUCCESS 0x00000000
|
||||
#define STATUS_WAIT_0 0x00000000
|
||||
#define STATUS_USER_APC 0x000000C0
|
||||
#define STATUS_TIMEOUT 0x00000102
|
||||
#define STATUS_FAIL_CHECK 0xC0000229
|
||||
#define INFINITE 0xFFFFFFFF
|
||||
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
||||
#define GENERIC_READ 0x80000000
|
||||
#define GENERIC_WRITE 0x40000000
|
||||
#define FILE_READ_DATA 0x0001
|
||||
#define FILE_SHARE_READ 0x00000001
|
||||
#define FILE_SHARE_WRITE 0x00000002
|
||||
#define CREATE_NEW 1
|
||||
#define CREATE_ALWAYS 2
|
||||
#define OPEN_EXISTING 3
|
||||
#define INVALID_FILE_SIZE 0xFFFFFFFF
|
||||
#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
|
||||
#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
|
||||
#define FILE_BEGIN 0
|
||||
#define FILE_CURRENT 1
|
||||
#define FILE_END 2
|
||||
#define ERROR_NO_MORE_FILES 0x12
|
||||
#define ERROR_NO_SUCH_USER 0x525
|
||||
#define ERROR_SUCCESS 0x0
|
||||
#define ERROR_PATH_NOT_FOUND 0x3
|
||||
#define ERROR_BAD_ARGUMENTS 0xA0
|
||||
#define ERROR_DEVICE_NOT_CONNECTED 0x48F
|
||||
#define PAGE_READWRITE 0x04
|
||||
|
||||
typedef union _LARGE_INTEGER {
|
||||
struct {
|
||||
uint32_t LowPart;
|
||||
int32_t HighPart;
|
||||
};
|
||||
struct {
|
||||
uint32_t LowPart;
|
||||
int32_t HighPart;
|
||||
} u;
|
||||
int64_t QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
|
||||
static_assert(sizeof(LARGE_INTEGER) == 8);
|
||||
|
||||
typedef struct _FILETIME
|
||||
{
|
||||
uint32_t dwLowDateTime;
|
||||
uint32_t dwHighDateTime;
|
||||
} FILETIME;
|
||||
|
||||
static_assert(sizeof(FILETIME) == 8);
|
||||
|
||||
typedef struct _WIN32_FIND_DATAA
|
||||
{
|
||||
uint32_t dwFileAttributes;
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
uint32_t nFileSizeHigh;
|
||||
uint32_t nFileSizeLow;
|
||||
uint32_t dwReserved0;
|
||||
uint32_t dwReserved1;
|
||||
char cFileName[260];
|
||||
char cAlternateFileName[14];
|
||||
} WIN32_FIND_DATAA;
|
||||
|
||||
static_assert(sizeof(WIN32_FIND_DATAA) == 320);
|
||||
|
||||
#endif
|
||||
|
||||
struct KernelObject
|
||||
{
|
||||
virtual ~KernelObject()
|
||||
{
|
||||
}
|
||||
|
||||
virtual uint32_t Wait(uint32_t timeout)
|
||||
{
|
||||
assert(false && "Wait not implemented for this kernel object.");
|
||||
return STATUS_TIMEOUT;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline T* CreateKernelObject(Args&&... args)
|
||||
{
|
||||
static_assert(std::is_base_of_v<KernelObject, T>);
|
||||
return g_userHeap.AllocPhysical<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T = KernelObject>
|
||||
inline T* GetKernelObject(uint32_t handle)
|
||||
{
|
||||
assert(handle != GUEST_INVALID_HANDLE_VALUE);
|
||||
return reinterpret_cast<T*>(g_memory.Translate(handle));
|
||||
}
|
||||
|
||||
uint32_t GetKernelHandle(KernelObject* obj);
|
||||
|
||||
void DestroyKernelObject(KernelObject* obj);
|
||||
void DestroyKernelObject(uint32_t handle);
|
||||
|
||||
bool IsKernelObject(uint32_t handle);
|
||||
bool IsKernelObject(void* obj);
|
||||
|
||||
bool IsInvalidKernelObject(void* obj);
|
||||
|
||||
template<typename T = void>
|
||||
inline T* GetInvalidKernelObject()
|
||||
{
|
||||
return reinterpret_cast<T*>(g_memory.Translate(GUEST_INVALID_HANDLE_VALUE));
|
||||
}
|
||||
|
||||
extern Mutex g_kernelLock;
|
||||
|
||||
template<typename T>
|
||||
T* ObQueryObject(XDISPATCHER_HEADER& header)
|
||||
inline T* QueryKernelObject(XDISPATCHER_HEADER& header)
|
||||
{
|
||||
std::lock_guard guard{ gKernelLock };
|
||||
std::lock_guard guard{ g_kernelLock };
|
||||
if (header.WaitListHead.Flink != OBJECT_SIGNATURE)
|
||||
{
|
||||
header.WaitListHead.Flink = OBJECT_SIGNATURE;
|
||||
auto* obj = new T(reinterpret_cast<typename T::guest_type*>(&header));
|
||||
header.WaitListHead.Blink = ObInsertObject(obj, DestroyObject<T>);
|
||||
auto* obj = CreateKernelObject<T>(reinterpret_cast<typename T::guest_type*>(&header));
|
||||
header.WaitListHead.Blink = g_memory.MapVirtual(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
return static_cast<T*>(ObQueryObject(header.WaitListHead.Blink.get()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t ObInsertObject(T* object)
|
||||
{
|
||||
return ObInsertObject(object, DestroyObject<T>);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* ObCreateObject(int& handle)
|
||||
{
|
||||
auto* obj = new T();
|
||||
handle = ::ObInsertObject(obj, DestroyObject<T>);
|
||||
|
||||
return obj;
|
||||
return static_cast<T*>(g_memory.Translate(header.WaitListHead.Blink.get()));
|
||||
}
|
||||
|
||||
// Get object without initialisation
|
||||
template<typename T>
|
||||
T* ObTryQueryObject(XDISPATCHER_HEADER& header)
|
||||
inline T* TryQueryKernelObject(XDISPATCHER_HEADER& header)
|
||||
{
|
||||
if (header.WaitListHead.Flink != OBJECT_SIGNATURE)
|
||||
return nullptr;
|
||||
|
||||
return static_cast<T*>(ObQueryObject(header.WaitListHead.Blink));
|
||||
return static_cast<T*>(g_memory.Translate(header.WaitListHead.Blink.get()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* ObTryQueryObject(int handle)
|
||||
{
|
||||
return static_cast<T*>(ObQueryObject(handle));
|
||||
}
|
||||
Reference in New Issue
Block a user