#pragma once #include "vm_ref.h" class PPUThread; class ARMv7Thread; namespace vm { // helper SFINAE type for vm::_ptr_base comparison operators (enables comparison between equal types and between any type and void*) template using if_comparable_t = std::enable_if_t< std::is_void::value || std::is_void::value || std::is_same, std::remove_cv_t>::value, RT>; template class _ptr_base { AT m_addr; static_assert(!std::is_pointer::value, "vm::_ptr_base<> error: invalid type (pointer)"); static_assert(!std::is_reference::value, "vm::_ptr_base<> error: invalid type (reference)"); public: using type = T; using addr_type = std::remove_cv_t; _ptr_base() = default; constexpr _ptr_base(addr_type addr, const addr_tag_t&) : m_addr(addr) { } constexpr addr_type addr() const { return m_addr; } // get vm pointer to a struct member template> _ptr_base ptr(MT T2::*const mptr) const { return{ VM_CAST(m_addr) + get_offset(mptr), vm::addr }; } // get vm pointer to a struct member with array subscription template, typename = if_comparable_t> _ptr_base ptr(MT T2::*const mptr, u32 index) const { return{ VM_CAST(m_addr) + get_offset(mptr) + SIZE_32(ET) * index, vm::addr }; } // get vm reference to a struct member template> _ref_base ref(MT T2::*const mptr) const { return{ VM_CAST(m_addr) + get_offset(mptr), vm::addr }; } // get vm reference to a struct member with array subscription template, typename = if_comparable_t> _ref_base ref(MT T2::*const mptr, u32 index) const { return{ VM_CAST(m_addr) + get_offset(mptr) + SIZE_32(ET) * index, vm::addr }; } // get vm reference _ref_base ref() const { return{ VM_CAST(m_addr), vm::addr }; } /*[[deprecated("Use constructor instead")]]*/ void set(addr_type value) { m_addr = value; } /*[[deprecated("Use constructor instead")]]*/ static _ptr_base make(addr_type addr) { return{ addr, vm::addr }; } T* get_ptr() const { return static_cast(vm::base(VM_CAST(m_addr))); } T* get_ptr_priv() const { return static_cast(vm::base_priv(VM_CAST(m_addr))); } T* operator ->() const { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator-> is not available for void pointers"); return get_ptr(); } std::add_lvalue_reference_t operator [](u32 index) const { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator[] is not available for void pointers"); return *static_cast(vm::base(VM_CAST(m_addr) + SIZE_32(T) * index)); } // enable only the conversions which are originally possible between pointer types template::value>> operator _ptr_base() const { return{ VM_CAST(m_addr), vm::addr }; } //template::value>> explicit operator T2*() const //{ // return get_ptr(); //} explicit operator bool() const { return m_addr != 0; } // Test address for arbitrary alignment: (addr & (align - 1)) == 0 bool aligned(u32 align) const { return (m_addr & (align - 1)) == 0; } // Test address alignment using alignof(T) bool aligned() const { static_assert(!std::is_void::value, "vm::_ptr_base<> error: aligned() is not available for void pointers"); return aligned(ALIGN_32(T)); } // Test address for arbitrary alignment: (addr & (align - 1)) != 0 explicit_bool_t operator %(u32 align) const { return !aligned(align); } // pointer increment (postfix) _ptr_base operator ++(int) { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator++ is not available for void pointers"); const addr_type result = m_addr; m_addr = VM_CAST(m_addr) + SIZE_32(T); return{ result, vm::addr }; } // pointer increment (prefix) _ptr_base& operator ++() { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator++ is not available for void pointers"); m_addr = VM_CAST(m_addr) + SIZE_32(T); return *this; } // pointer decrement (postfix) _ptr_base operator --(int) { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator-- is not available for void pointers"); const addr_type result = m_addr; m_addr = VM_CAST(m_addr) - SIZE_32(T); return{ result, vm::addr }; } // pointer decrement (prefix) _ptr_base& operator --() { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator-- is not available for void pointers"); m_addr = VM_CAST(m_addr) - SIZE_32(T); return *this; } _ptr_base& operator +=(s32 count) { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator+= is not available for void pointers"); m_addr = VM_CAST(m_addr) + count * SIZE_32(T); return *this; } _ptr_base& operator -=(s32 count) { static_assert(!std::is_void::value, "vm::_ptr_base<> error: operator-= is not available for void pointers"); m_addr = VM_CAST(m_addr) - count * SIZE_32(T); return *this; } }; template class _ptr_base { AT m_addr; public: using addr_type = std::remove_cv_t; _ptr_base() = default; constexpr _ptr_base(addr_type addr, const addr_tag_t&) : m_addr(addr) { } constexpr addr_type addr() const { return m_addr; } /*[[deprecated("Use constructor instead")]]*/ void set(addr_type value) { m_addr = value; } /*[[deprecated("Use constructor instead")]]*/ static _ptr_base make(addr_type addr) { return{ addr, vm::addr }; } // defined in CB_FUNC.h, passing context is mandatory RT operator()(PPUThread& CPU, T... args) const; // defined in ARMv7Callback.h, passing context is mandatory RT operator()(ARMv7Thread& context, T... args) const; // conversion to another function pointer template operator _ptr_base() const { return{ VM_CAST(m_addr), vm::addr }; } explicit operator bool() const { return m_addr != 0; } }; template class _ptr_base { AT m_addr; static_assert(!sizeof(AT), "vm::_ptr_base<> error: use RT(T...) format for functions instead of RT(*)(T...)"); }; // Native endianness pointer to LE data template using ptrl = _ptr_base, AT>; // Native endianness pointer to BE data template using ptrb = _ptr_base, AT>; // BE pointer to LE data template using bptrl = _ptr_base, to_be_t>; // BE pointer to BE data template using bptrb = _ptr_base, to_be_t>; // LE pointer to LE data template using lptrl = _ptr_base, to_le_t>; // LE pointer to BE data template using lptrb = _ptr_base, to_le_t>; namespace ps3 { // default pointer for PS3 HLE functions (Native endianness pointer to BE data) template using ptr = ptrb; // default pointer to pointer for PS3 HLE functions (Native endianness pointer to BE pointer to BE data) template using pptr = ptr, AT>; // default pointer for PS3 HLE structures (BE pointer to BE data) template using bptr = bptrb; // default pointer to pointer for PS3 HLE structures (BE pointer to BE pointer to BE data) template using bpptr = bptr, AT>; // native endianness pointer to const BE data template using cptr = ptr; // BE pointer to const BE data template using bcptr = bptr; template using cpptr = pptr; template using bcpptr = bpptr; // perform static_cast (for example, vm::ptr to vm::ptr) template*>(std::declval()))> inline _ptr_base> static_ptr_cast(const _ptr_base& other) { return{ VM_CAST(other.addr()), vm::addr }; } // perform const_cast (for example, vm::cptr to vm::ptr) template*>(std::declval()))> inline _ptr_base> const_ptr_cast(const _ptr_base& other) { return{ VM_CAST(other.addr()), vm::addr }; } } namespace psv { // default pointer for PSV HLE functions (Native endianness pointer to LE data) template using ptr = ptrl; // default pointer to pointer for PSV HLE functions (Native endianness pointer to LE pointer to LE data) template using pptr = ptr>; // default pointer for PSV HLE structures (LE pointer to LE data) template using lptr = lptrl; // default pointer to pointer for PSV HLE structures (LE pointer to LE pointer to LE data) template using lpptr = lptr>; // native endianness pointer to const LE data template using cptr = ptr; // LE pointer to const LE data template using lcptr = lptr; template using cpptr = pptr; template using lcpptr = lpptr; // perform static_cast (for example, vm::ptr to vm::ptr) template*>(std::declval()))> inline _ptr_base> static_ptr_cast(const _ptr_base& other) { return{ VM_CAST(other.addr()), vm::addr }; } // perform const_cast (for example, vm::cptr to vm::ptr) template*>(std::declval()))> inline _ptr_base> const_ptr_cast(const _ptr_base& other) { return{ VM_CAST(other.addr()), vm::addr }; } } struct null_t { template operator _ptr_base() const { return{ 0, vm::addr }; } }; // vm::null is convertible to any vm::ptr type as null pointer in virtual memory static null_t null; // Call wait_op() for specified vm pointer template inline auto wait_op(named_thread_t& thread, const _ptr_base& ptr, F pred, Args&&... args) -> decltype(static_cast(pred(args...))) { return wait_op(thread, ptr.addr(), SIZE_32(T), std::move(pred), std::forward(args)...); } // Call notify_at() for specified vm pointer template inline void notify_at(const vm::_ptr_base& ptr) { return notify_at(ptr.addr(), SIZE_32(T)); } } // unary plus operator for vm::_ptr_base (always available) template inline vm::_ptr_base operator +(const vm::_ptr_base& ptr) { return ptr; } // indirection operator for vm::_ptr_base template inline std::enable_if_t::value, T&> operator *(const vm::_ptr_base& ptr) { return *ptr.get_ptr(); } // addition operator for vm::_ptr_base (pointer + integer) template inline std::enable_if_t::value, vm::_ptr_base> operator +(const vm::_ptr_base& ptr, u32 count) { return{ VM_CAST(ptr.addr()) + count * SIZE_32(T), vm::addr }; } // addition operator for vm::_ptr_base (integer + pointer) template inline std::enable_if_t::value, vm::_ptr_base> operator +(u32 count, const vm::_ptr_base& ptr) { return{ VM_CAST(ptr.addr()) + count * SIZE_32(T), vm::addr }; } // subtraction operator for vm::_ptr_base (pointer - integer) template inline std::enable_if_t::value, vm::_ptr_base> operator -(const vm::_ptr_base& ptr, u32 count) { return{ VM_CAST(ptr.addr()) - count * SIZE_32(T), vm::addr }; } // pointer difference operator for vm::_ptr_base template inline std::enable_if_t< std::is_object::value && std::is_object::value && std::is_same, std::remove_cv_t>::value, s32> operator -(const vm::_ptr_base& left, const vm::_ptr_base& right) { return static_cast(VM_CAST(left.addr()) - VM_CAST(right.addr())) / SIZE_32(T1); } // comparison operator for vm::_ptr_base (pointer1 == pointer2) template inline vm::if_comparable_t operator ==(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() == right.addr(); } template inline bool operator ==(const vm::null_t&, const vm::_ptr_base& ptr) { return !ptr.operator bool(); } template inline bool operator ==(const vm::_ptr_base& ptr, const vm::null_t&) { return !ptr.operator bool(); } // comparison operator for vm::_ptr_base (pointer1 != pointer2) template inline vm::if_comparable_t operator !=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() != right.addr(); } template inline bool operator !=(const vm::null_t&, const vm::_ptr_base& ptr) { return ptr.operator bool(); } template inline bool operator !=(const vm::_ptr_base& ptr, const vm::null_t&) { return ptr.operator bool(); } // comparison operator for vm::_ptr_base (pointer1 < pointer2) template inline vm::if_comparable_t operator <(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() < right.addr(); } template inline bool operator <(const vm::null_t&, const vm::_ptr_base& ptr) { return ptr.operator bool(); } template inline bool operator <(const vm::_ptr_base&, const vm::null_t&) { return false; } // comparison operator for vm::_ptr_base (pointer1 <= pointer2) template inline vm::if_comparable_t operator <=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() <= right.addr(); } template inline bool operator <=(const vm::null_t&, const vm::_ptr_base&) { return true; } template inline bool operator <=(const vm::_ptr_base& ptr, const vm::null_t&) { return !ptr.operator bool(); } // comparison operator for vm::_ptr_base (pointer1 > pointer2) template inline vm::if_comparable_t operator >(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() > right.addr(); } template inline bool operator >(const vm::null_t&, const vm::_ptr_base&) { return false; } template inline bool operator >(const vm::_ptr_base& ptr, const vm::null_t&) { return ptr.operator bool(); } // comparison operator for vm::_ptr_base (pointer1 >= pointer2) template inline vm::if_comparable_t operator >=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() >= right.addr(); } template inline bool operator >=(const vm::null_t&, const vm::_ptr_base& ptr) { return !ptr.operator bool(); } template inline bool operator >=(const vm::_ptr_base&, const vm::null_t&) { return true; } // external specialization for to_se<> (change AT endianness to BE/LE) template struct to_se, Se> { using type = vm::_ptr_base::type>; }; // external specialization for to_ne<> (change AT endianness to native) template struct to_ne> { using type = vm::_ptr_base::type>; }; namespace fmt { // external specialization for fmt::format function template struct unveil, false> { using result_type = typename unveil::result_type; static inline result_type get_value(const vm::_ptr_base& arg) { return unveil::get_value(arg.addr()); } }; } // external specializations for PPU GPR (SC_FUNC.h, CB_FUNC.h) template struct cast_ppu_gpr; template struct cast_ppu_gpr, false> { static inline u64 to_gpr(const vm::_ptr_base& value) { return cast_ppu_gpr::value>::to_gpr(value.addr()); } static inline vm::_ptr_base from_gpr(const u64 reg) { return{ cast_ppu_gpr::value>::from_gpr(reg), vm::addr }; } }; // external specializations for ARMv7 GPR template struct cast_armv7_gpr; template struct cast_armv7_gpr, false> { static inline u32 to_gpr(const vm::_ptr_base& value) { return cast_armv7_gpr::value>::to_gpr(value.addr()); } static inline vm::_ptr_base from_gpr(const u32 reg) { return{ cast_armv7_gpr::value>::from_gpr(reg), vm::addr }; } };