Initial Commit

This commit is contained in:
Sajid
2024-09-30 12:06:17 +06:00
commit db51236165
42 changed files with 4843 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
project("UnleashedRecomp")
set(TARGET_NAME "SWA")
file(GLOB "*.cpp")
add_compile_definitions(SWA_IMPL)
add_compile_options(
"/D_HAS_EXCEPTIONS=0"
"/fp:strict"
"/GS-"
"/EHa-"
"-march=haswell"
"-fno-strict-aliasing")
file(GLOB SWA_RECOMPILED_SOURCES "ppc/*.cpp")
set(SWA_KERNEL_CXX_SOURCES
"kernel/imports.cpp"
"kernel/xdm.cpp"
"kernel/heap.cpp"
"kernel/memory.cpp"
"kernel/xam.cpp"
"kernel/io/file_system.cpp"
)
set(SWA_CPU_CXX_SOURCES
"cpu/guest_thread.cpp"
"cpu/code_cache.cpp"
)
set(SWA_GPU_CXX_SOURCES
"gpu/window.cpp"
)
set(SWA_HID_CXX_SOURCES
"hid/hid.cpp"
)
set(SWA_CXX_SOURCES
"main.cpp"
${SWA_KERNEL_CXX_SOURCES}
${SWA_CPU_CXX_SOURCES}
${SWA_GPU_CXX_SOURCES}
${SWA_HID_CXX_SOURCES}
)
add_executable(UnleashedRecomp ${SWA_RECOMPILED_SOURCES} ${SWA_CXX_SOURCES})
set_target_properties(UnleashedRecomp PROPERTIES OUTPUT_NAME ${TARGET_NAME})
target_link_libraries(UnleashedRecomp PUBLIC
PowerUtils
o1heap
xxHash::xxhash
unordered_dense::unordered_dense
winmm
ntdll
comctl32
)
target_include_directories(UnleashedRecomp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_precompile_headers(UnleashedRecomp PUBLIC "ppc/ppc_recomp_shared.h")

75
UnleashedRecomp/Config.h Normal file
View File

@@ -0,0 +1,75 @@
#pragma once
#define INI_FILE "SWA.ini"
#define INI_BEGIN_SECTION(section) { std::string CurrentSection = section;
#define INI_END_SECTION() }
#define INI_READ_STRING(var) var = BasicIni::Get(ini, CurrentSection, #var, var)
#define INI_READ_BOOLEAN(var) var = BasicIni::GetBoolean(ini, CurrentSection, #var, var)
#define INI_READ_FLOAT(var) var = BasicIni::GetFloat(ini, CurrentSection, #var, var)
#define INI_READ_INTEGER(var) var = BasicIni::GetInteger(ini, CurrentSection, #var, var)
#define INI_READ_DOUBLE(var) var = BasicIni::GetDouble(ini, CurrentSection, #var, var)
#define INI_READ_ENUM(type, var) var = (type)BasicIni::GetInteger(ini, CurrentSection, #var, var)
enum ELanguage : uint32_t
{
ELanguage_English = 1,
ELanguage_Japanese,
ELanguage_German,
ELanguage_French,
ELanguage_Spanish,
ELanguage_Italian
};
enum EScoreBehaviour : uint32_t
{
EScoreBehaviour_CheckpointReset,
EScoreBehaviour_CheckpointRetain
};
enum EMovieScaleMode : uint32_t
{
EMovieScaleMode_Stretch,
EMovieScaleMode_Fit,
EMovieScaleMode_Fill
};
enum EUIScaleMode : uint32_t
{
EUIScaleMode_Stretch,
EUIScaleMode_Edge,
EUIScaleMode_Centre
};
class Config
{
public:
// System
inline static ELanguage Language = ELanguage_English;
inline static EScoreBehaviour ScoreBehaviour = EScoreBehaviour_CheckpointReset;
inline static bool Hints = true;
inline static bool WerehogHubTransformVideo = true;
inline static bool BootToTitle = false;
// Controls
inline static bool XButtonHoming = true;
inline static bool UnleashCancel = false;
// Audio
inline static bool WerehogBattleMusic = true;
// Video
inline static uint32_t Width = 1280;
inline static uint32_t Height = 720;
inline static int32_t ShadowResolution = 4096;
inline static size_t MSAA = 4;
inline static EMovieScaleMode MovieScaleMode = EMovieScaleMode_Fit;
inline static EUIScaleMode UIScaleMode = EUIScaleMode_Centre;
inline static bool AlphaToCoverage = false;
inline static bool Fullscreen = false;
inline static bool VSync = false;
inline static uint32_t BufferCount = 3;
static void Read();
};

23
UnleashedRecomp/Mutex.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
struct Mutex : CRITICAL_SECTION
{
Mutex()
{
InitializeCriticalSection(this);
}
~Mutex()
{
DeleteCriticalSection(this);
}
void lock()
{
EnterCriticalSection(this);
}
void unlock()
{
LeaveCriticalSection(this);
}
};

View File

@@ -0,0 +1,37 @@
#include <stdafx.h>
#include "code_cache.h"
#include "ppc_context.h"
CodeCache::CodeCache()
{
bucket = (char*)VirtualAlloc(nullptr, 0x200000000, MEM_RESERVE, PAGE_READWRITE);
assert(bucket);
}
CodeCache::~CodeCache()
{
VirtualFree(bucket, 0, MEM_RELEASE);
}
void CodeCache::Init()
{
for (size_t i = 0; PPCFuncMappings[i].guest != 0; i++)
{
if (PPCFuncMappings[i].host != nullptr)
{
VirtualAlloc(bucket + PPCFuncMappings[i].guest * 2, sizeof(void*), MEM_COMMIT, PAGE_READWRITE);
*(void**)(bucket + PPCFuncMappings[i].guest * 2) = PPCFuncMappings[i].host;
}
}
}
void CodeCache::Insert(uint32_t guest, const void* host)
{
VirtualAlloc(bucket + static_cast<uint64_t>(guest) * 2, sizeof(void*), MEM_COMMIT, PAGE_READWRITE);
*reinterpret_cast<const void**>(bucket + static_cast<uint64_t>(guest) * 2) = host;
}
void* CodeCache::Find(uint32_t guest) const
{
return *reinterpret_cast<void**>(bucket + static_cast<uint64_t>(guest) * 2);
}

View File

@@ -0,0 +1,16 @@
#pragma once
struct CodeCache
{
char* bucket{};
CodeCache();
~CodeCache();
void Init();
void Insert(uint32_t guest, const void* host);
void* Find(uint32_t guest) const;
};
extern CodeCache gCodeCache;

View File

@@ -0,0 +1,23 @@
#pragma once
#include "ppc_context.h"
#include "memory.h"
struct GuestCode
{
inline static void Run(void* hostAddress, PPCContext* ctx, void* baseAddress, void* callStack)
{
ctx->fpscr.loadFromHost();
reinterpret_cast<PPCFunc*>(hostAddress)(*ctx, reinterpret_cast<uint8_t*>(baseAddress));
}
inline static void Run(void* hostAddress, PPCContext* ctx)
{
ctx->fpscr.loadFromHost();
reinterpret_cast<PPCFunc*>(hostAddress)(*ctx, reinterpret_cast<uint8_t*>(gMemory.base));
}
inline static void Run(void* hostAddress)
{
Run(hostAddress, GetPPCContext());
}
};

View File

@@ -0,0 +1,143 @@
#include <stdafx.h>
#include "guest_thread.h"
#include <kernel/memory.h>
#include <kernel/heap.h>
#include <kernel/function.h>
#include "code_cache.h"
#include "guest_code.h"
#include "ppc_context.h"
constexpr size_t PCR_SIZE = 0xAB0;
constexpr size_t TLS_SIZE = 0x100;
constexpr size_t TEB_SIZE = 0x2E0;
constexpr size_t STACK_SIZE = 0x40000;
constexpr size_t CALL_STACK_SIZE = 0x8000;
constexpr size_t TOTAL_SIZE = PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE + CALL_STACK_SIZE;
constexpr size_t TEB_OFFSET = PCR_SIZE + TLS_SIZE;
DWORD GuestThread::Start(uint32_t function)
{
const GuestThreadParameter parameter{ function };
return Start(parameter);
}
DWORD GuestThread::Start(const GuestThreadParameter& parameter)
{
auto* thread = (uint8_t*)gUserHeap.Alloc(TOTAL_SIZE);
const auto procMask = (uint8_t)(parameter.flags >> 24);
const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask);
memset(thread, 0, TOTAL_SIZE);
*(uint32_t*)thread = std::byteswap(gMemory.MapVirtual(thread + PCR_SIZE)); // tls pointer
*(uint32_t*)(thread + 0x100) = std::byteswap(gMemory.MapVirtual(thread + PCR_SIZE + TLS_SIZE)); // teb pointer
*(thread + 0x10C) = cpuNumber;
*(uint32_t*)(thread + PCR_SIZE + 0x10) = 0xFFFFFFFF; // that one TLS entry that felt quirky
*(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = std::byteswap(GetCurrentThreadId()); // thread id
PPCContext ppcContext{};
ppcContext.fn = (uint8_t*)gCodeCache.bucket;
ppcContext.r1.u64 = gMemory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer
ppcContext.r3.u64 = parameter.value;
ppcContext.r13.u64 = gMemory.MapVirtual(thread);
SetPPCContext(ppcContext);
GuestCode::Run(gCodeCache.Find(parameter.function), &ppcContext, gMemory.Translate(0), gMemory.Translate(ppcContext.r1.u32));
gUserHeap.Free(thread);
return (DWORD)ppcContext.r3.u64;
}
DWORD HostThreadStart(void* pParameter)
{
auto* parameter = static_cast<GuestThreadParameter*>(pParameter);
const auto result = GuestThread::Start(*parameter);
delete parameter;
return result;
}
HANDLE GuestThread::Start(uint32_t function, uint32_t parameter, uint32_t flags, LPDWORD threadId)
{
const auto hostCreationFlags = (flags & 1) != 0 ? CREATE_SUSPENDED : 0;
//return CreateThread(nullptr, 0, Start, (void*)((uint64_t(parameter) << 32) | function), suspended ? CREATE_SUSPENDED : 0, threadId);
return CreateThread(nullptr, 0, HostThreadStart, new GuestThreadParameter{ function, parameter, flags }, hostCreationFlags, threadId);
}
void GuestThread::SetThreadName(uint32_t id, const char* name)
{
#pragma pack(push,8)
const DWORD MS_VC_EXCEPTION = 0x406D1388;
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = id;
info.dwFlags = 0;
__try
{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}
void GuestThread::SetLastError(DWORD error)
{
auto* thread = (char*)gMemory.Translate(GetPPCContext()->r13.u32);
if (*(DWORD*)(thread + 0x150))
{
// Program doesn't want errors
return;
}
// TEB + 0x160 : Win32LastError
*(DWORD*)(thread + TEB_OFFSET + 0x160) = std::byteswap(error);
}
PPCContext* GuestThread::Invoke(uint32_t address)
{
auto* ctx = GetPPCContext();
GuestCode::Run(gCodeCache.Find(address), ctx);
return ctx;
}
void SetThreadNameImpl(uint32_t a1, uint32_t threadId, uint32_t* name)
{
GuestThread::SetThreadName(threadId, (const char*)gMemory.Translate(std::byteswap(*name)));
}
int GetThreadPriorityImpl(uint32_t hThread)
{
return GetThreadPriority((HANDLE)hThread);
}
DWORD SetThreadIdealProcessorImpl(uint32_t hThread, DWORD dwIdealProcessor)
{
return SetThreadIdealProcessor((HANDLE)hThread, dwIdealProcessor);
}
GUEST_FUNCTION_HOOK(sub_82DFA2E8, SetThreadNameImpl);
GUEST_FUNCTION_HOOK(sub_82BD57A8, GetThreadPriorityImpl);
GUEST_FUNCTION_HOOK(sub_82BD5910, SetThreadIdealProcessorImpl);
void GuestThread::InitHooks()
{
}

View File

@@ -0,0 +1,21 @@
#pragma once
struct PPCContext;
struct GuestThreadParameter
{
uint32_t function;
uint32_t value;
uint32_t flags;
};
struct GuestThread
{
static DWORD Start(uint32_t function);
static DWORD Start(const GuestThreadParameter& parameter);
static HANDLE Start(uint32_t function, uint32_t parameter, uint32_t flags, LPDWORD threadId);
static void SetThreadName(uint32_t id, const char* name);
static void SetLastError(DWORD error);
static PPCContext* Invoke(uint32_t address);
static void InitHooks();
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "ppc/ppc_context.h"
#include "ppc/ppc_recomp_shared.h"
inline thread_local PPCContext* gPPCContext;
inline PPCContext* GetPPCContext()
{
return gPPCContext;
}
inline void SetPPCContext(PPCContext& ctx)
{
gPPCContext = &ctx;
}

View File

@@ -0,0 +1,69 @@
#pragma once
#ifdef _MSVC
#define SWA_DLLEXPORT __declspec(dllexport)
#define SWA_DLLIMPORT __declspec(dllimport)
#else
#define SWA_DLLEXPORT __attribute__((dllexport))
#define SWA_DLLIMPORT __attribute__((dllimport))
#endif
#ifdef SWA_IMPL
#define SWA_API extern "C" SWA_DLLEXPORT
#else
#define SWA_API extern "C" SWA_DLLIMPORT
#endif
template<typename T>
void ByteSwap(T& value)
{
value = std::byteswap(value);
}
template<typename T>
T RoundUp(const T& in_rValue, uint32_t in_round)
{
return (in_rValue + in_round - 1) & ~(in_round - 1);
}
template<typename T>
T RoundDown(const T& in_rValue, uint32_t in_round)
{
return in_rValue & ~(in_round - 1);
}
inline bool FileExists(const char* path)
{
const auto attributes = GetFileAttributesA(path);
return attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
inline bool DirectoryExists(const char* path)
{
const auto attributes = GetFileAttributesA(path);
return attributes != INVALID_FILE_ATTRIBUTES && !!(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
inline size_t StringHash(const std::string_view& str)
{
return XXH3_64bits(str.data(), str.size());
}
template<typename TValue>
constexpr size_t FirstBitLow(TValue value)
{
constexpr size_t nbits = sizeof(TValue) * 8;
constexpr auto zero = TValue{};
constexpr auto one = static_cast<TValue>(1);
for (size_t i = 0; i < nbits; i++)
{
if ((value & (one << i)) != zero)
{
return i;
}
}
return 0;
}

View File

@@ -0,0 +1 @@
#include "Window.h"

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -0,0 +1,22 @@
#include <stdafx.h>
#include "hid.h"
DWORD hid::GetState(DWORD dwUserIndex, XAMINPUT_STATE* pState)
{
return 1;
}
DWORD hid::SetState(DWORD dwUserIndex, XAMINPUT_VIBRATION* pVibration)
{
return 1;
}
DWORD hid::GetCapabilities(DWORD dwUserIndex, XAMINPUT_CAPABILITIES* pCaps)
{
return 1;
}
int hid::OnSDLEvent(void*, SDL_Event* event)
{
return 0;
}

13
UnleashedRecomp/hid/hid.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
union SDL_Event;
namespace hid
{
void Init();
DWORD GetState(DWORD dwUserIndex, XAMINPUT_STATE* pState);
DWORD SetState(DWORD dwUserIndex, XAMINPUT_VIBRATION* pVibration);
DWORD GetCapabilities(DWORD dwUserIndex, XAMINPUT_CAPABILITIES* pCaps);
int OnSDLEvent(void*, SDL_Event* event);
}

View File

@@ -0,0 +1,40 @@
#pragma once
template<typename T>
struct FreeList
{
std::vector<T> items;
std::vector<size_t> freed{};
void Free(T& item)
{
std::destroy_at(&item);
freed.push_back(&item - items.data());
}
void Free(size_t index)
{
std::destroy_at(&items[index]);
freed.push_back(index);
}
size_t Alloc()
{
if (freed.size())
{
auto idx = freed[freed.size() - 1];
freed.pop_back();
std::construct_at(&items[idx]);
return idx;
}
items.emplace_back();
return items.size() - 1;
}
T& operator[](size_t idx)
{
return items[idx];
}
};

View File

@@ -0,0 +1,216 @@
#pragma once
#include <cpu/ppc_context.h>
#include <array>
#include "xbox.h"
template <typename R, typename... T>
constexpr std::tuple<T...> function_args(R(*)(T...)) noexcept
{
return std::tuple<T...>();
}
template<auto V>
static constexpr decltype(V) constant_v = V;
template<typename T>
static constexpr bool is_precise_v = std::is_same_v<T, float> || std::is_same_v<T, double>;
template<auto Func>
struct arg_count_t
{
static constexpr size_t value = std::tuple_size_v<decltype(function_args(Func))>;
};
template<typename TCallable, int I = 0, typename ...TArgs>
std::enable_if_t<(I >= sizeof...(TArgs)), void> _tuple_for(std::tuple<TArgs...>&, const TCallable& callable) noexcept
{
}
template<typename TCallable, int I = 0, typename ...TArgs>
std::enable_if_t<(I < sizeof...(TArgs)), void> _tuple_for(std::tuple<TArgs...>& tpl, const TCallable& callable) noexcept
{
callable(std::get<I>(tpl), I);
_tuple_for<TCallable, I + 1>(tpl, callable);
}
struct ArgTranslator
{
FORCEINLINE constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
{
if (arg <= 7)
{
switch (arg)
{
case 0: return ctx.r3.u32;
case 1: return ctx.r4.u32;
case 2: return ctx.r5.u32;
case 3: return ctx.r6.u32;
case 4: return ctx.r7.u32;
case 5: return ctx.r8.u32;
case 6: return ctx.r9.u32;
case 7: return ctx.r10.u32;
default: break;
}
}
return *reinterpret_cast<XLPDWORD>(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8));
}
FORCEINLINE static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept
{
switch (arg)
{
case 0: return ctx.f1.f64;
case 1: return ctx.f2.f64;
case 2: return ctx.f3.f64;
case 3: return ctx.f4.f64;
case 4: return ctx.f5.f64;
case 5: return ctx.f6.f64;
case 6: return ctx.f7.f64;
case 7: return ctx.f8.f64;
case 8: return ctx.f9.f64;
case 9: return ctx.f10.f64;
case 10: return ctx.f11.f64;
case 11: return ctx.f12.f64;
case 12: return ctx.f13.f64;
[[unlikely]] default: break;
}
// how did you end up here
return 0;
}
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
{
if constexpr (is_precise_v<T>)
{
return static_cast<T>(GetPrecisionArgumentValue(ctx, base, idx));
}
else
{
return static_cast<T>(GetIntegerArgumentValue(ctx, base, idx));
}
}
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
{
const auto v = GetIntegerArgumentValue(ctx, base, idx);
if (!v)
{
return nullptr;
}
return reinterpret_cast<T>(base + static_cast<uint32_t>(v));
}
};
struct Argument
{
int type{};
int ordinal{};
};
template<auto Func>
constexpr std::array<Argument, arg_count_t<Func>::value> GatherFunctionArguments()
{
std::array<Argument, arg_count_t<Func>::value> args{};
int intOrdinal{};
int floatOrdinal{};
size_t i{};
if constexpr (!args.empty())
{
std::apply([&](const auto& first, const auto&... rest)
{
auto append = [&]<typename T>(const T & v)
{
if constexpr (is_precise_v<T>)
{
args[i] = { 1, floatOrdinal++ };
}
else
{
intOrdinal++;
args[i] = { 0, static_cast<int>(i) }; // what the fuck
}
i++;
};
append(first);
(append(rest), ...);
}, function_args(Func));
}
return args;
}
template<auto Func, size_t I>
struct arg_ordinal_t
{
static constexpr size_t value = GatherFunctionArguments<Func>()[I].ordinal;
};
template<auto Func, int I = 0, typename ...TArgs>
void _translate_args(PPCContext& ctx, uint8_t* base, std::tuple<TArgs...>&) noexcept
requires (I >= sizeof...(TArgs))
{
}
template <auto Func, int I = 0, typename ...TArgs>
std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args(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);
_translate_args<Func, I + 1>(ctx, base, tpl);
}
template<auto Func>
PPC_FUNC(GuestFunction)
{
using ret_t = decltype(std::apply(Func, function_args(Func)));
auto args = function_args(Func);
_translate_args<Func>(ctx, base, args);
if constexpr (std::is_same_v<ret_t, void>)
{
std::apply(Func, args);
}
else
{
auto v = std::apply(Func, args);
if constexpr (std::is_pointer<ret_t>())
{
if (v != nullptr)
{
ctx.r3.u64 = static_cast<uint32_t>(reinterpret_cast<size_t>(v) - reinterpret_cast<size_t>(base));
}
else
{
ctx.r3.u64 = NULL;
}
}
else if constexpr (is_precise_v<ret_t>)
{
ctx.f1.f64 = v;
}
else
{
ctx.r3.u64 = (uint64_t)v;
}
}
}
#define GUEST_FUNCTION_HOOK(subroutine, function) \
PPC_FUNC(subroutine) { GuestFunction<function>(ctx, base); }
#define GUEST_FUNCTION_STUB(subroutine) \
PPC_FUNC(subroutine) { }

View File

@@ -0,0 +1,135 @@
#include <stdafx.h>
#include "heap.h"
#include "memory.h"
#include "function.h"
constexpr size_t RESERVED_BEGIN = 0x7FEA0000;
constexpr size_t RESERVED_END = 0xA0000000;
void Heap::Init()
{
gMemory.Alloc(0x20000, RESERVED_BEGIN - 0x20000, MEM_COMMIT);
heap = o1heapInit(gMemory.Translate(0x20000), RESERVED_BEGIN - 0x20000);
gMemory.Alloc(RESERVED_END, 0x100000000 - RESERVED_END, MEM_COMMIT);
physicalHeap = o1heapInit(gMemory.Translate(RESERVED_END), 0x100000000 - RESERVED_END);
}
void* Heap::Alloc(size_t size)
{
std::lock_guard lock(mutex);
return o1heapAllocate(heap, std::max<size_t>(1, size));
}
void* Heap::AllocPhysical(size_t size, size_t alignment)
{
size = std::max<size_t>(1, size);
alignment = alignment == 0 ? 0x1000 : std::max<size_t>(16, alignment);
std::lock_guard lock(physicalMutex);
void* ptr = o1heapAllocate(physicalHeap, size + alignment);
size_t aligned = ((size_t)ptr + alignment) & ~(alignment - 1);
*((void**)aligned - 1) = ptr;
*((size_t*)aligned - 2) = size + O1HEAP_ALIGNMENT;
return (void*)aligned;
}
void Heap::Free(void* ptr)
{
if (ptr >= physicalHeap)
{
std::lock_guard lock(physicalMutex);
o1heapFree(physicalHeap, *((void**)ptr - 1));
}
else
{
std::lock_guard lock(mutex);
o1heapFree(heap, ptr);
}
}
size_t Heap::Size(void* ptr)
{
if (ptr)
return *((size_t*)ptr - 2) - O1HEAP_ALIGNMENT; // relies on fragment header in o1heap.c
return 0;
}
uint32_t RtlAllocateHeap(uint32_t heapHandle, uint32_t flags, uint32_t size)
{
void* ptr = gUserHeap.Alloc(size);
if ((flags & 0x8) != 0)
memset(ptr, 0, size);
assert(ptr);
return gMemory.MapVirtual(ptr);
}
uint32_t RtlReAllocateHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer, uint32_t size)
{
void* ptr = gUserHeap.Alloc(size);
if ((flags & 0x8) != 0)
memset(ptr, 0, size);
if (memoryPointer != 0)
{
void* oldPtr = gMemory.Translate(memoryPointer);
memcpy(ptr, oldPtr, std::min<size_t>(size, gUserHeap.Size(oldPtr)));
gUserHeap.Free(oldPtr);
}
assert(ptr);
return gMemory.MapVirtual(ptr);
}
uint32_t RtlFreeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer)
{
if (memoryPointer != NULL)
gUserHeap.Free(gMemory.Translate(memoryPointer));
return true;
}
uint32_t RtlSizeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer)
{
if (memoryPointer != NULL)
return (uint32_t)gUserHeap.Size(gMemory.Translate(memoryPointer));
return 0;
}
uint32_t XAlloc(uint32_t size, uint32_t flags)
{
void* ptr = (flags & 0x80000000) != 0 ?
gUserHeap.AllocPhysical(size, (1ull << ((flags >> 24) & 0xF))) :
gUserHeap.Alloc(size);
if ((flags & 0x40000000) != 0)
memset(ptr, 0, size);
assert(ptr);
return gMemory.MapVirtual(ptr);
}
void XFree(uint32_t baseAddress, uint32_t flags)
{
if (baseAddress != NULL)
gUserHeap.Free(gMemory.Translate(baseAddress));
}
GUEST_FUNCTION_STUB(sub_82BD7788); // HeapCreate
GUEST_FUNCTION_STUB(sub_82BD9250); // HeapDestroy
GUEST_FUNCTION_HOOK(sub_82BD7D30, RtlAllocateHeap);
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);

View File

@@ -0,0 +1,38 @@
#pragma once
#include "Mutex.h"
#include <o1heap.h>
struct Heap
{
Mutex mutex;
O1HeapInstance* heap;
Mutex physicalMutex;
O1HeapInstance* physicalHeap;
void Init();
void* Alloc(size_t size);
void* AllocPhysical(size_t size, size_t alignment);
void Free(void* ptr);
size_t Size(void* ptr);
template<typename T, typename... Args>
T* Alloc(Args... args)
{
T* obj = (T*)Alloc(sizeof(T));
new (obj) T(std::forward<Args>(args)...);
return obj;
}
template<typename T, typename... Args>
T* AllocPhysical(Args... args)
{
T* obj = (T*)AllocPhysical(sizeof(T), alignof(T));
new (obj) T(std::forward<Args>(args)...);
return obj;
}
};
extern Heap gUserHeap;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
#include <stdafx.h>
#include "file_system.h"
#include <kernel/xam.h>
#include <kernel/xdm.h>
#include <kernel/function.h>
#include <cpu/guest_thread.h>
bool FindHandleCloser(void* handle)
{
FindClose(handle);
return false;
}
static uint32_t CreateFileImpl(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes)
{
const auto handle = (uint32_t)CreateFileA(
FileSystem::TransformPath(lpFileName),
dwDesiredAccess,
dwShareMode,
nullptr,
dwCreationDisposition,
dwFlagsAndAttributes & ~(FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED),
nullptr);
GuestThread::SetLastError(GetLastError());
printf("CreateFileA(%s, %x, %x, %x, %x, %x): %x\n", lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, handle);
return handle;
}
static DWORD GetFileSizeImpl(
uint32_t hFile,
LPDWORD lpFileSizeHigh)
{
DWORD fileSize = GetFileSize((HANDLE)hFile, lpFileSizeHigh);
if (lpFileSizeHigh != nullptr)
*lpFileSizeHigh = std::byteswap(*lpFileSizeHigh);
return fileSize;
}
BOOL GetFileSizeExImpl(
uint32_t hFile,
PLARGE_INTEGER lpFileSize)
{
BOOL result = GetFileSizeEx((HANDLE)hFile, lpFileSize);
if (result)
lpFileSize->QuadPart = std::byteswap(lpFileSize->QuadPart);
return result;
}
BOOL ReadFileImpl(
uint32_t hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
XLPDWORD lpNumberOfBytesRead,
XOVERLAPPED* lpOverlapped)
{
if (lpOverlapped != nullptr)
{
LONG distanceToMoveHigh = lpOverlapped->OffsetHigh;
if (SetFilePointer((HANDLE)hFile, lpOverlapped->Offset, &distanceToMoveHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
return FALSE;
}
DWORD numberOfBytesRead;
BOOL result = ReadFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToRead, &numberOfBytesRead, nullptr);
if (result)
{
if (lpOverlapped != nullptr)
{
lpOverlapped->Internal = 0;
lpOverlapped->InternalHigh = numberOfBytesRead;
if (lpOverlapped->hEvent != NULL)
SetEvent((HANDLE)lpOverlapped->hEvent.get());
}
else if (lpNumberOfBytesRead != nullptr)
{
*lpNumberOfBytesRead = numberOfBytesRead;
}
}
//printf("ReadFile(): %x %x %x %x %x %x\n", hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped, result);
return result;
}
DWORD SetFilePointerImpl(
uint32_t hFile,
LONG lDistanceToMove,
PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod)
{
LONG distanceToMoveHigh = lpDistanceToMoveHigh ? std::byteswap(*lpDistanceToMoveHigh) : 0;
DWORD result = SetFilePointer((HANDLE)hFile, lDistanceToMove, lpDistanceToMoveHigh ? &distanceToMoveHigh : nullptr, dwMoveMethod);
if (lpDistanceToMoveHigh != nullptr)
*lpDistanceToMoveHigh = std::byteswap(distanceToMoveHigh);
return result;
}
BOOL SetFilePointerExImpl(
uint32_t hFile,
LONG lDistanceToMove,
PLARGE_INTEGER lpNewFilePointer,
DWORD dwMoveMethod)
{
LARGE_INTEGER distanceToMove;
distanceToMove.QuadPart = lDistanceToMove;
DWORD result = SetFilePointerEx((HANDLE)hFile, distanceToMove, lpNewFilePointer, dwMoveMethod);
if (lpNewFilePointer != nullptr)
lpNewFilePointer->QuadPart = std::byteswap(lpNewFilePointer->QuadPart);
return result;
}
uint32_t FindFirstFileImpl(
LPCSTR lpFileName,
LPWIN32_FIND_DATAA lpFindFileData)
{
auto& data = *lpFindFileData;
const auto handle = FindFirstFileA(FileSystem::TransformPath(lpFileName), &data);
GuestThread::SetLastError(GetLastError());
if (handle == INVALID_HANDLE_VALUE)
{
return 0xFFFFFFFF;
}
ByteSwap(data.dwFileAttributes);
ByteSwap(*(uint64_t*)&data.ftCreationTime);
ByteSwap(*(uint64_t*)&data.ftLastAccessTime);
ByteSwap(*(uint64_t*)&data.ftLastWriteTime);
ByteSwap(*(uint64_t*)&data.nFileSizeHigh);
return GUEST_HANDLE(ObInsertObject(handle, FindHandleCloser));
}
uint32_t FindNextFileImpl(uint32_t Handle, LPWIN32_FIND_DATAA lpFindFileData)
{
auto* handle = ObQueryObject(HOST_HANDLE(Handle));
auto& data = *lpFindFileData;
const auto result = FindNextFileA(handle, &data);
ByteSwap(data.dwFileAttributes);
ByteSwap(*(uint64_t*)&data.ftCreationTime);
ByteSwap(*(uint64_t*)&data.ftLastAccessTime);
ByteSwap(*(uint64_t*)&data.ftLastWriteTime);
ByteSwap(*(uint64_t*)&data.nFileSizeHigh);
return result;
}
BOOL ReadFileExImpl(
uint32_t hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
XOVERLAPPED* lpOverlapped,
uint32_t lpCompletionRoutine)
{
LONG distanceToMoveHigh = lpOverlapped->OffsetHigh;
if (SetFilePointer((HANDLE)hFile, lpOverlapped->Offset, &distanceToMoveHigh, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
return FALSE;
DWORD numberOfBytesRead;
BOOL result = ReadFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToRead, &numberOfBytesRead, nullptr);
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 GetFileAttributesAImpl(LPCSTR lpFileName)
{
return GetFileAttributesA(FileSystem::TransformPath(lpFileName));
}
BOOL WriteFileImpl(
uint32_t hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped)
{
assert(lpOverlapped == nullptr);
BOOL result = WriteFile((HANDLE)hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, nullptr);
if (result && lpNumberOfBytesWritten != nullptr)
ByteSwap(*lpNumberOfBytesWritten);
return result;
}
const char* FileSystem::TransformPath(const char* path)
{
thread_local char builtPath[2048]{};
const char* relativePath = strstr(path, ":\\");
if (relativePath != nullptr)
{
// rooted folder, handle direction
const std::string_view root = std::string_view{ path, path + (relativePath - path) };
const auto newRoot = XamGetRootPath(root);
if (!newRoot.empty())
{
strncpy(builtPath, newRoot.data(), newRoot.size());
builtPath[newRoot.size()] = '\\';
strcpy(builtPath + newRoot.size() + 1, relativePath + 2);
return builtPath;
}
}
return relativePath != nullptr ? relativePath + 2 : path;
}
GUEST_FUNCTION_HOOK(sub_82BD4668, CreateFileImpl);
GUEST_FUNCTION_HOOK(sub_82BD4600, GetFileSizeImpl);
GUEST_FUNCTION_HOOK(sub_82BD5608, GetFileSizeExImpl);
GUEST_FUNCTION_HOOK(sub_82BD4478, ReadFileImpl);
GUEST_FUNCTION_HOOK(sub_831CD3E8, SetFilePointerImpl);
GUEST_FUNCTION_HOOK(sub_831CE888, SetFilePointerExImpl);
GUEST_FUNCTION_HOOK(sub_831CDC58, FindFirstFileImpl);
GUEST_FUNCTION_HOOK(sub_831CDC00, FindNextFileImpl);
GUEST_FUNCTION_HOOK(sub_831CDF40, ReadFileExImpl);
GUEST_FUNCTION_HOOK(sub_831CD6E8, GetFileAttributesAImpl);
GUEST_FUNCTION_HOOK(sub_831CE3F8, CreateFileImpl);
GUEST_FUNCTION_HOOK(sub_82BD4860, WriteFileImpl);

View File

@@ -0,0 +1,6 @@
#pragma once
struct FileSystem
{
static const char* TransformPath(const char* path);
};

View File

@@ -0,0 +1,37 @@
#include <stdafx.h>
#include "memory.h"
Memory::Memory(void* address, size_t size) : size(size)
{
base = (char*)VirtualAlloc(address, size, MEM_RESERVE, PAGE_READWRITE);
}
void* Memory::Alloc(size_t offset, size_t size, uint32_t type)
{
return VirtualAlloc(base + offset, size, type, PAGE_READWRITE);
}
void* Memory::Commit(size_t offset, size_t size)
{
return Alloc(offset, size, MEM_COMMIT);
}
void* Memory::Reserve(size_t offset, size_t size)
{
return Alloc(offset, size, MEM_RESERVE);
}
void* Memory::Translate(size_t offset) const noexcept
{
return base + offset;
}
uint32_t Memory::MapVirtual(void* host) const noexcept
{
return static_cast<uint32_t>(static_cast<char*>(host) - base);
}
extern "C" void* MmGetHostAddress(uint32_t ptr)
{
return gMemory.Translate(ptr);
}

View File

@@ -0,0 +1,21 @@
#pragma once
class Memory
{
public:
char* base{};
size_t size{};
size_t guestBase{};
Memory(void* address, size_t size);
void* Alloc(size_t offset, size_t size, uint32_t type);
void* Commit(size_t offset, size_t size);
void* Reserve(size_t offset, size_t size);
void* Translate(size_t offset) const noexcept;
uint32_t MapVirtual(void* host) const noexcept;
};
extern Memory gMemory;

View File

@@ -0,0 +1,403 @@
#include <stdafx.h>
#include "xam.h"
#include "xdm.h"
#include <hid/hid.h>
#include <gpu/window.h>
#include <cpu/guest_thread.h>
#include <ranges>
#include <unordered_set>
#include <CommCtrl.h>
#include "xxHashMap.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='*'\"")
std::array<xxHashMap<XHOSTCONTENT_DATA>, 3> gContentRegistry{};
std::unordered_set<XamListener*> gListeners{};
xxHashMap<std::string> gRootMap;
std::string_view XamGetRootPath(const std::string_view& root)
{
const auto result = gRootMap.find(StringHash(root));
if (result == gRootMap.end())
{
return "";
}
return result->second;
}
void XamRootCreate(const std::string_view& root, const std::string_view& path)
{
gRootMap.emplace(StringHash(root), path);
}
XamListener::XamListener()
{
gListeners.insert(this);
}
XamListener::~XamListener()
{
gListeners.erase(this);
}
XCONTENT_DATA XamMakeContent(DWORD type, const std::string_view& name)
{
XCONTENT_DATA data{ 1, type };
strncpy(data.szFileName, name.data(), sizeof(data.szFileName));
return data;
}
void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root)
{
const auto idx = data.dwContentType - 1;
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)
{
XCONTENT_DATA data{ 1, type, {}, "" };
strncpy(data.szFileName, name.data(), sizeof(data.szFileName));
XamRegisterContent(data, root);
}
SWA_API DWORD XamNotifyCreateListener(uint64_t qwAreas)
{
int handle;
auto* listener = ObCreateObject<XamListener>(handle);
listener->areas = qwAreas;
return GUEST_HANDLE(handle);
}
SWA_API void XamNotifyEnqueueEvent(DWORD dwId, DWORD dwParam)
{
for (const auto& listener : gListeners)
{
if (((1 << MSG_AREA(dwId)) & listener->areas) == 0)
{
continue;
}
listener->notifications.emplace_back(dwId, dwParam);
}
}
SWA_API bool XNotifyGetNext(DWORD hNotification, DWORD dwMsgFilter, XDWORD* pdwId, XDWORD* pParam)
{
auto& listener = *ObTryQueryObject<XamListener>(HOST_HANDLE(hNotification));
if (dwMsgFilter)
{
for (size_t i = 0; i < listener.notifications.size(); i++)
{
if (std::get<0>(listener.notifications[i]) == dwMsgFilter)
{
if (pdwId)
{
*pdwId = std::get<0>(listener.notifications[i]);
}
if (pParam)
{
*pParam = std::get<1>(listener.notifications[i]);
}
listener.notifications.erase(listener.notifications.begin() + i);
return true;
}
}
}
else
{
if (listener.notifications.empty())
{
return false;
}
if (pdwId)
{
*pdwId = std::get<0>(listener.notifications[0]);
}
if (pParam)
{
*pParam = std::get<1>(listener.notifications[0]);
}
listener.notifications.erase(listener.notifications.begin());
return true;
}
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)
{
// printf("!!! STUB !!! XamShowMessageBoxUI\n");
std::vector<std::wstring> texts{};
std::vector<TASKDIALOG_BUTTON> buttons{};
texts.emplace_back(reinterpret_cast<wchar_t*>(wszTitle));
texts.emplace_back(reinterpret_cast<wchar_t*>(wszText));
for (size_t i = 0; i < cButtons; i++)
{
texts.emplace_back(reinterpret_cast<wchar_t*>(pwszButtons[i].get()));
}
for (auto& text : texts)
{
for (size_t i = 0; i < text.size(); i++)
{
ByteSwap(text[i]);
}
}
for (size_t i = 0; i < cButtons; i++)
{
buttons.emplace_back(i, texts[2 + i].c_str());
}
XamNotifyEnqueueEvent(9, 1);
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);
*pResult = button;
if (pOverlapped)
{
pOverlapped->dwCompletionContext = GetCurrentThreadId();
pOverlapped->Error = 0;
pOverlapped->Length = -1;
}
XamNotifyEnqueueEvent(9, 0);
return 0;
}
SWA_API uint32_t XamContentCreateEnumerator(DWORD dwUserIndex, DWORD DeviceID, DWORD dwContentType,
DWORD dwContentFlags, DWORD cItem, XLPDWORD pcbBuffer, XLPDWORD phEnum)
{
if (dwUserIndex != 0)
{
GuestThread::SetLastError(ERROR_NO_SUCH_USER);
return 0xFFFFFFFF;
}
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()));
if (pcbBuffer)
{
*pcbBuffer = sizeof(_XCONTENT_DATA) * cItem;
}
*phEnum = GUEST_HANDLE(handle);
return 0;
}
SWA_API uint32_t XamEnumerate(uint32_t hEnum, DWORD dwFlags, PVOID pvBuffer, DWORD cbBuffer, XLPDWORD pcItemsReturned, XXOVERLAPPED* pOverlapped)
{
auto* enumerator = ObTryQueryObject<XamEnumeratorBase>(HOST_HANDLE(hEnum));
const auto count = enumerator->Next(pvBuffer);
if (count == -1)
{
return ERROR_NO_MORE_FILES;
}
if (pcItemsReturned)
{
*pcItemsReturned = count;
}
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)
{
// printf("!!! STUB !!! XamContentCreateEx\n");
const auto& registry = gContentRegistry[pContentData->dwContentType - 1];
const auto exists = registry.contains(StringHash(pContentData->szFileName));
const auto mode = dwContentFlags & 0xF;
if (mode == CREATE_ALWAYS)
{
if (pdwDisposition) *pdwDisposition = XCONTENT_NEW;
if (!exists)
{
const char* root = "";
if (pContentData->dwContentType == XCONTENTTYPE_SAVEDATA)
{
root = ".\\save";
}
else if (pContentData->dwContentType == XCONTENTTYPE_DLC)
{
root = ".\\dlc";
}
else
{
root = ".";
}
XamRegisterContent(*pContentData, root);
CreateDirectoryA(root, nullptr);
XamRootCreate(szRootName, root);
}
else
{
XamRootCreate(szRootName, registry.find(StringHash(pContentData->szFileName))->second.szRoot);
}
return ERROR_SUCCESS;
}
if (mode == OPEN_EXISTING)
{
if (exists)
{
if (pdwDisposition) *pdwDisposition = XCONTENT_EXISTING;
XamRootCreate(szRootName, registry.find(StringHash(pContentData->szFileName))->second.szRoot);
return ERROR_SUCCESS;
}
else
{
if (pdwDisposition) *pdwDisposition = XCONTENT_NEW;
return ERROR_PATH_NOT_FOUND;
}
}
return ERROR_PATH_NOT_FOUND;
}
SWA_API uint32_t XamContentClose(LPCSTR szRootName, XXOVERLAPPED* pOverlapped)
{
// printf("!!! STUB !!! XamContentClose %s\n", szRootName);
gRootMap.erase(StringHash(szRootName));
return 0;
}
SWA_API uint32_t XamContentGetDeviceData(DWORD DeviceID, XDEVICE_DATA* pDeviceData)
{
// printf("!!! STUB !!! XamContentGetDeviceData\n");
pDeviceData->DeviceID = DeviceID;
pDeviceData->DeviceType = XCONTENTDEVICETYPE_HDD;
pDeviceData->ulDeviceBytes = 0x10000000;
pDeviceData->ulDeviceFreeBytes = 0x10000000;
pDeviceData->wszName[0] = 'S';
pDeviceData->wszName[1] = 'o';
pDeviceData->wszName[2] = 'n';
pDeviceData->wszName[3] = 'i';
pDeviceData->wszName[4] = 'c';
pDeviceData->wszName[5] = '\0';
return 0;
}
SWA_API uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint32_t flags, XAMINPUT_CAPABILITIES* caps)
{
//printf("!!! STUB !!! XamInputGetCapabilities\n");
uint32_t result = hid::GetCapabilities(userIndex, caps);
if (result == ERROR_SUCCESS)
{
ByteSwap(caps->Flags);
ByteSwap(caps->Gamepad.wButtons);
ByteSwap(caps->Gamepad.sThumbLX);
ByteSwap(caps->Gamepad.sThumbLY);
ByteSwap(caps->Gamepad.sThumbRX);
ByteSwap(caps->Gamepad.sThumbRY);
ByteSwap(caps->Vibration.wLeftMotorSpeed);
ByteSwap(caps->Vibration.wRightMotorSpeed);
}
return result;
}
SWA_API uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state)
{
//printf("!!! STUB !!! XamInputGetState\n");
uint32_t result = hid::GetState(userIndex, state);
if (result == ERROR_SUCCESS)
{
ByteSwap(state->dwPacketNumber);
ByteSwap(state->Gamepad.wButtons);
ByteSwap(state->Gamepad.sThumbLX);
ByteSwap(state->Gamepad.sThumbLY);
ByteSwap(state->Gamepad.sThumbRX);
ByteSwap(state->Gamepad.sThumbRY);
}
else if (userIndex == 0)
{
memset(state, 0, sizeof(*state));
if (GetAsyncKeyState('W') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y;
if (GetAsyncKeyState('A') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X;
if (GetAsyncKeyState('S') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A;
if (GetAsyncKeyState('D') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B;
if (GetAsyncKeyState('Q') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER;
if (GetAsyncKeyState('E') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER;
if (GetAsyncKeyState('1') & 0x8000)
state->Gamepad.bLeftTrigger = 0xFF;
if (GetAsyncKeyState('3') & 0x8000)
state->Gamepad.bRightTrigger = 0xFF;
if (GetAsyncKeyState('I') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP;
if (GetAsyncKeyState('J') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT;
if (GetAsyncKeyState('K') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN;
if (GetAsyncKeyState('L') & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_RIGHT;
if (GetAsyncKeyState(VK_UP) & 0x8000)
state->Gamepad.sThumbLY = 32767;
if (GetAsyncKeyState(VK_LEFT) & 0x8000)
state->Gamepad.sThumbLX = -32768;
if (GetAsyncKeyState(VK_DOWN) & 0x8000)
state->Gamepad.sThumbLY = -32768;
if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
state->Gamepad.sThumbLX = 32767;
if (GetAsyncKeyState(VK_RETURN) & 0x8000)
state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START;
ByteSwap(state->Gamepad.wButtons);
ByteSwap(state->Gamepad.sThumbLX);
ByteSwap(state->Gamepad.sThumbLY);
ByteSwap(state->Gamepad.sThumbRX);
ByteSwap(state->Gamepad.sThumbRY);
result = ERROR_SUCCESS;
}
return result;
}
SWA_API uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration)
{
//printf("!!! STUB !!! XamInputSetState\n");
ByteSwap(vibration->wLeftMotorSpeed);
ByteSwap(vibration->wRightMotorSpeed);
return hid::SetState(userIndex, vibration);
}

View File

@@ -0,0 +1,109 @@
#pragma once
#include <xbox.h>
#define MSGID(Area, Number) (DWORD)((WORD)(Area) << 16 | (WORD)(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);
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 XamShowMessageBoxUI(DWORD dwUserIndex, XWORD* wszTitle, XWORD* wszText, DWORD cButtons,
xpointer<XWORD>* pwszButtons, DWORD dwFocusButton, DWORD dwFlags, XLPDWORD 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 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 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);
SWA_API uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration);

View File

@@ -0,0 +1,49 @@
#include <stdafx.h>
#include "xdm.h"
#include "FreeList.h"
FreeList<std::tuple<std::unique_ptr<char>, TypeDestructor_t>> gKernelObjects;
Mutex gKernelLock;
void* ObQueryObject(size_t handle)
{
std::lock_guard guard{ gKernelLock };
if (handle >= gKernelObjects.items.size())
{
return nullptr;
}
return std::get<0>(gKernelObjects[handle]).get();
}
uint32_t ObInsertObject(void* object, TypeDestructor_t destructor)
{
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;
}
void ObCloseHandle(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);
}

View File

@@ -0,0 +1,56 @@
#pragma once
#define DUMMY_HANDLE (DWORD)('HAND')
#define OBJECT_SIGNATURE (DWORD)'XBOX'
extern Mutex gKernelLock;
void* ObQueryObject(size_t handle);
uint32_t ObInsertObject(void* object, TypeDestructor_t destructor);
void ObCloseHandle(uint32_t handle);
template<typename T>
T* ObQueryObject(XDISPATCHER_HEADER& header)
{
std::lock_guard guard{ gKernelLock };
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>);
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;
}
// Get object without initialisation
template<typename T>
T* ObTryQueryObject(XDISPATCHER_HEADER& header)
{
if (header.WaitListHead.Flink != OBJECT_SIGNATURE)
return nullptr;
return static_cast<T*>(ObQueryObject(header.WaitListHead.Blink));
}
template<typename T>
T* ObTryQueryObject(int handle)
{
return static_cast<T*>(ObQueryObject(handle));
}

104
UnleashedRecomp/main.cpp Normal file
View File

@@ -0,0 +1,104 @@
#include <stdafx.h>
#include <cpu/code_cache.h>
#include <cpu/guest_thread.h>
#include <kernel/function.h>
#include <kernel/memory.h>
#include <kernel/heap.h>
#include <kernel/xam.h>
#include <kernel/io/file_system.h>
#include <file.h>
#include <xex.h>
#define GAME_XEX_PATH "game:\\default.xex"
const size_t XMAIOBegin = 0x7FEA0000;
const size_t XMAIOEnd = XMAIOBegin + 0x0000FFFF;
Memory gMemory{ reinterpret_cast<void*>(0x100000000), 0x100000000 };
Heap gUserHeap;
CodeCache gCodeCache;
int main()
{
#ifdef _WIN32
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
#endif
gMemory.Alloc(0x10000, 0x1000, MEM_COMMIT);
gUserHeap.Init();
gCodeCache.Init();
gMemory.Alloc(XMAIOBegin, 0xFFFF, MEM_COMMIT);
const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game");
const auto updateContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Update");
XamRegisterContent(gameContent, DirectoryExists(".\\game") ? ".\\game" : ".");
XamRegisterContent(updateContent, ".\\update");
if (FileExists(".\\save\\SYS-DATA"))
{
XamRegisterContent(XamMakeContent(XCONTENTTYPE_SAVEDATA, "SYS-DATA"), ".\\save");
}
else if (FileExists(".\\SYS-DATA"))
{
XamRegisterContent(XamMakeContent(XCONTENTTYPE_SAVEDATA, "SYS-DATA"), ".");
}
// Mount game
XamContentCreateEx(0, "game", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr);
XamContentCreateEx(0, "update", &updateContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr);
// OS mounts game data to D:
XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr);
auto loadResult = LoadFile(FileSystem::TransformPath(GAME_XEX_PATH));
if (!loadResult.has_value())
{
assert("Failed to load default.xex" && false);
return 1;
}
auto* xex = reinterpret_cast<XEX_HEADER*>(loadResult->data());
auto headers = reinterpret_cast<XEX_OPTIONAL_HEADER*>(&xex[1]);
auto security = reinterpret_cast<XEX2_SECURITY_INFO*>((char*)xex + xex->AddressOfSecurityInfo);
gMemory.Alloc(security->ImageBase, security->SizeOfImage, MEM_COMMIT);
auto format = Xex2FindOptionalHeader<XEX_FILE_FORMAT_INFO>(xex, XEX_HEADER_FILE_FORMAT_INFO);
auto entry = *Xex2FindOptionalHeader<uint32_t>(xex, XEX_HEADER_ENTRY_POINT);
ByteSwap(entry);
assert(format->CompressionType >= 1);
if (format->CompressionType == 1)
{
auto srcData = (char*)xex + xex->SizeOfHeader;
auto destData = (char*)gMemory.Translate(security->ImageBase);
auto numBlocks = (format->SizeOfHeader / sizeof(XEX_BASIC_FILE_COMPRESSION_INFO)) - 1;
auto blocks = reinterpret_cast<const XEX_BASIC_FILE_COMPRESSION_INFO*>(format + 1);
for (size_t i = 0; i < numBlocks; i++)
{
memcpy(destData, srcData, blocks[i].SizeOfData);
srcData += blocks[i].SizeOfData;
destData += blocks[i].SizeOfData;
memset(destData, 0, blocks[i].SizeOfPadding);
destData += blocks[i].SizeOfPadding;
}
}
GuestThread::Start(entry);
return 0;
}
GUEST_FUNCTION_STUB(__imp__vsprintf);
GUEST_FUNCTION_STUB(__imp___vsnprintf);
GUEST_FUNCTION_STUB(__imp__sprintf);
GUEST_FUNCTION_STUB(__imp___snprintf);
GUEST_FUNCTION_STUB(__imp___snwprintf);
GUEST_FUNCTION_STUB(__imp__vswprintf);
GUEST_FUNCTION_STUB(__imp___vscwprintf);
GUEST_FUNCTION_STUB(__imp__swprintf);

1
UnleashedRecomp/ppc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
ppc_*

15
UnleashedRecomp/stdafx.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#define NOMINMAX
#include <windows.h>
#include <mutex>
#include <vector>
#include <string>
#include <cassert>
#include <xbox.h>
#include <xxhash.h>
#include <ankerl/unordered_dense.h>
#include "framework.h"
#include "Mutex.h"
#include "Config.h"

View File

@@ -0,0 +1,14 @@
#pragma once
struct xxHash
{
using is_avalanching = void;
uint64_t operator()(XXH64_hash_t const& x) const noexcept
{
return x;
}
};
template<typename T>
using xxHashMap = ankerl::unordered_dense::map<XXH64_hash_t, T, xxHash>;