Add guest_stack_var, improve shared_ptr implementation.

This commit is contained in:
Skyth 2024-11-24 21:16:33 +03:00
parent 588b5677eb
commit 3c2922a583
4 changed files with 232 additions and 34 deletions

View file

@ -5,35 +5,117 @@
namespace boost
{
namespace detail
{
class sp_counted_base
{
protected:
struct vftable_t
{
be<uint32_t> destructor;
be<uint32_t> dispose;
be<uint32_t> destroy;
be<uint32_t> get_deleter;
};
xpointer<vftable_t> vftable_;
be<uint32_t> use_count_;
be<uint32_t> weak_count_;
public:
// TODO
sp_counted_base() = delete;
void add_ref()
{
be<uint32_t> original, incremented;
do
{
original = use_count_;
incremented = original + 1;
} while (InterlockedCompareExchange((unsigned long*)&use_count_, incremented.value, original.value) != original.value);
}
void release()
{
be<uint32_t> original, decremented;
do
{
original = use_count_;
decremented = original - 1;
} while (InterlockedCompareExchange((unsigned long*)&use_count_, decremented.value, original.value) != original.value);
if (decremented == 0)
{
GuestToHostFunction<void>(vftable_->dispose, this);
weak_release();
}
}
void weak_release()
{
be<uint32_t> original, decremented;
do
{
original = weak_count_;
decremented = original - 1;
} while (InterlockedCompareExchange((unsigned long*)&weak_count_, decremented.value, original.value) != original.value);
if (decremented == 0)
{
GuestToHostFunction<void>(vftable_->destroy, this);
}
}
uint32_t use_count() const
{
return use_count_;
}
};
template< class T > struct sp_dereference
{
typedef T& type;
};
template<> struct sp_dereference< void >
{
typedef void type;
};
}
template<typename T>
class shared_ptr
{
private:
xpointer<T> m_pObject;
xpointer<uint32_t> m_pRefCount;
xpointer<T> px;
xpointer<boost::detail::sp_counted_base> pn;
void add_ref()
{
if (pn)
pn->add_ref();
}
void release()
{
if (m_pRefCount && --(*m_pRefCount) == 0)
{
delete m_pObject;
delete m_pRefCount;
}
if (pn)
pn->release();
}
public:
shared_ptr() : m_pObject(nullptr), m_pRefCount(nullptr) {}
shared_ptr() : px(nullptr), pn(nullptr) {}
explicit shared_ptr(T* p) : m_pObject(p), m_pRefCount(new uint32_t(1)) {}
// TODO
explicit shared_ptr(T* p) = delete;
shared_ptr(const shared_ptr& other) : m_pObject(other.m_pObject), m_pRefCount(other.m_pRefCount)
shared_ptr(const shared_ptr& other) : px(other.px), pn(other.pn)
{
if (m_pRefCount)
++(*m_pRefCount);
add_ref();
}
shared_ptr(shared_ptr&& other) noexcept : m_pObject(std::exchange(other.m_pObject, nullptr)),
m_pRefCount(std::exchange(other.m_pRefCount, nullptr)) {}
shared_ptr(shared_ptr&& other) noexcept : px(std::exchange(other.px, nullptr)),
pn(std::exchange(other.pn, nullptr)) {}
~shared_ptr()
{
@ -46,11 +128,10 @@ namespace boost
{
release();
m_pObject = other.m_pObject;
m_pRefCount = other.m_pRefCount;
px = other.px;
pn = other.pn;
if (m_pRefCount)
++(*m_pRefCount);
add_ref();
}
return *this;
@ -62,20 +143,22 @@ namespace boost
{
release();
m_pObject = std::exchange(other.m_pObject, nullptr);
m_pRefCount = std::exchange(other.m_pRefCount, nullptr);
px = std::exchange(other.px, nullptr);
pn = std::exchange(other.pn, nullptr);
}
return *this;
}
T* get() const { return m_pObject; }
T* get() const { return px; }
T& operator*() const { assert(m_pObject); return *m_pObject; }
T* operator->() const { assert(m_pObject); return m_pObject; }
detail::sp_dereference<T> operator*() const { assert(px); return *px; }
T* operator->() const { assert(px); return px; }
explicit operator bool() const { return m_pObject != nullptr; }
explicit operator bool() const { return px != nullptr; }
size_t use_count() const { return m_pRefCount ? *m_pRefCount : 0; }
size_t use_count() const { return pn ? pn->use_count() : 0; }
};
using anonymous_shared_ptr = shared_ptr<void>;
}

View file

@ -0,0 +1,117 @@
#pragma once
#include "ppc_context.h"
#include <kernel/memory.h>
// DO NOT use this type as anything other than a local variable.
// This includes returning. It'll cause memory to leak in the guest stack!
template<typename T>
class guest_stack_var
{
private:
uint32_t m_ptr = NULL;
uint32_t m_oldStackPtr = NULL;
void AllocGuestStackMemory()
{
auto ctx = GetPPCContext();
m_oldStackPtr = ctx->r1.u32;
m_ptr = (ctx->r1.u32 - sizeof(T)) & ~(std::max<uint32_t>(alignof(T), 8) - 1);
ctx->r1.u32 = m_ptr;
}
public:
T* get()
{
return reinterpret_cast<T*>(g_memory.Translate(m_ptr));
}
const T* get() const
{
return reinterpret_cast<const T*>(g_memory.Translate(m_ptr));
}
template<typename... Args>
guest_stack_var(Args&&... args)
{
AllocGuestStackMemory();
new (get()) T(std::forward<Args>(args)...);
}
guest_stack_var(const guest_stack_var<T>& other)
{
AllocGuestStackMemory();
new (get()) T(*other->get());
}
guest_stack_var(guest_stack_var<T>&& other)
{
AllocGuestStackMemory();
new (get()) T(std::move(*other->get()));
}
~guest_stack_var()
{
get()->~T();
auto ctx = GetPPCContext();
// This assert will fail if the type was used as anything other than a local variable.
assert(ctx->r1.u32 == m_ptr);
ctx->r1.u32 = m_oldStackPtr;
}
void operator=(const guest_stack_var<T>& other)
{
if (this != &other)
*get() = *other->get();
}
void operator=(guest_stack_var<T>&& other)
{
if (this != &other)
*get() = std::move(*other->get());
}
void operator=(const T& other)
{
if (get() != &other)
*get() = *other;
}
void operator=(T&& other)
{
if (get() != &other)
*get() = std::move(*other);
}
operator const T* () const
{
return get();
}
operator T* ()
{
return get();
}
const T* operator->() const
{
return get();
}
T* operator->()
{
return get();
}
const T& operator*() const
{
return *get();
}
T& operator*()
{
return *get();
}
};

View file

@ -1,25 +1,23 @@
#include <kernel/function.h>
#include <kernel/heap.h>
#include <kernel/memory.h>
#include <cpu/guest_stack_var.h>
#include <ui/window.h>
#include <api/boost/smart_ptr/shared_ptr.h>
SWA_API void Game_PlaySound(const char* pName)
{
void* soundPlayerSharedPtr = g_userHeap.Alloc(8);
GuestToHostFunction<void>(sub_82B4DF50, soundPlayerSharedPtr, ((be<uint32_t>*)g_memory.Translate(0x83367900))->get(), 7, 0, 0);
guest_stack_var<boost::anonymous_shared_ptr> soundPlayer;
GuestToHostFunction<void>(sub_82B4DF50, soundPlayer.get(), ((be<uint32_t>*)g_memory.Translate(0x83367900))->get(), 7, 0, 0);
auto soundPlayer = (be<uint32_t>*)g_memory.Translate(*(be<uint32_t>*)soundPlayerSharedPtr);
auto soundPlayerVtable = (be<uint32_t>*)g_memory.Translate(*soundPlayer);
auto soundPlayerVtable = (be<uint32_t>*)g_memory.Translate(*(be<uint32_t>*)soundPlayer->get());
uint32_t virtualFunction = *(soundPlayerVtable + 1);
size_t strLen = strlen(pName);
void* strAllocation = g_userHeap.Alloc(strLen + 1);
memcpy(strAllocation, pName, strLen + 1);
GuestToHostFunction<void>(virtualFunction, soundPlayer, strAllocation, 0);
GuestToHostFunction<void>(virtualFunction, soundPlayer->get(), strAllocation, 0);
g_userHeap.Free(strAllocation);
GuestToHostFunction<void>(sub_822C0890, *((be<uint32_t>*)soundPlayerSharedPtr + 1));
g_userHeap.Free(soundPlayerSharedPtr);
}
SWA_API void Window_SetFullscreen(bool isEnabled)

View file

@ -306,8 +306,8 @@ FORCEINLINE T GuestToHostFunction(const TFunction& func, TArgs... argv)
{
auto args = std::make_tuple(argv...);
auto& currentCtx = *GetPPCContext();
auto newCtx = PPCContext{};
PPCContext newCtx; // NOTE: No need for zero initialization, has lots of unnecessary code generation.
newCtx.fn = currentCtx.fn;
newCtx.r1 = currentCtx.r1;
newCtx.r13 = currentCtx.r13;