#pragma once #include "vm_ref.h" class ppu_thread; namespace vm { // SFINAE helper 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::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; _ptr_base(vm::addr_t addr) : m_addr(addr) { } addr_type addr() const { return m_addr; } void set(addr_type addr) { this->m_addr = addr; } static _ptr_base make(addr_type addr) { _ptr_base result; result.m_addr = addr; return result; } // Enable only the conversions which are originally possible between pointer types template::value>> operator _ptr_base() const { return vm::cast(m_addr, HERE); } explicit operator bool() const { return m_addr != 0; } // Get vm pointer to a struct member template > _ptr_base ptr(MT T2::*const mptr) const { return vm::cast(vm::cast(m_addr, HERE) + offset32(mptr)); } // 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(vm::cast(m_addr, HERE) + offset32(mptr) + u32{sizeof(ET)} * index); } // Get vm reference to a struct member template > _ref_base ref(MT T2::*const mptr) const { return vm::cast(vm::cast(m_addr, HERE) + offset32(mptr)); } // 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(vm::cast(m_addr, HERE) + offset32(mptr) + u32{sizeof(ET)} * index); } // Get vm reference _ref_base ref() const { return vm::cast(m_addr, HERE); } T* get_ptr() const { return static_cast(vm::base(vm::cast(m_addr, HERE))); } T* operator ->() const { return get_ptr(); } std::add_lvalue_reference_t operator *() const { return *static_cast(vm::base(vm::cast(m_addr, HERE))); } std::add_lvalue_reference_t operator [](u32 index) const { return *static_cast(vm::base(vm::cast(m_addr, HERE) + u32{sizeof(T)} * index)); } // Test address for arbitrary alignment: (addr & (align - 1)) == 0 bool aligned(u32 align = alignof(T)) const { return (m_addr & (align - 1)) == 0; } // Get type size static constexpr u32 size() { return sizeof(T); } // Get type alignment static constexpr u32 align() { return alignof(T); } _ptr_base operator +() const { return vm::cast(m_addr, HERE); } _ptr_base operator +(u32 count) const { return vm::cast(vm::cast(m_addr, HERE) + count * size()); } _ptr_base operator -(u32 count) const { return vm::cast(vm::cast(m_addr, HERE) - count * size()); } friend _ptr_base operator +(u32 count, const _ptr_base& ptr) { return vm::cast(vm::cast(ptr.m_addr, HERE) + count * size()); } // Pointer difference operator template std::enable_if_t::value && std::is_same, std::decay_t>::value, s32> operator -(const _ptr_base& right) const { return static_cast(vm::cast(m_addr, HERE) - vm::cast(right.m_addr, HERE)) / size(); } _ptr_base operator ++(int) { _ptr_base result = *this; m_addr = vm::cast(m_addr, HERE) + size(); return result; } _ptr_base& operator ++() { m_addr = vm::cast(m_addr, HERE) + size(); return *this; } _ptr_base operator --(int) { _ptr_base result = *this; m_addr = vm::cast(m_addr, HERE) - size(); return result; } _ptr_base& operator --() { m_addr = vm::cast(m_addr, HERE) - size(); return *this; } _ptr_base& operator +=(s32 count) { m_addr = vm::cast(m_addr, HERE) + count * size(); return *this; } _ptr_base& operator -=(s32 count) { m_addr = vm::cast(m_addr, HERE) - count * size(); return *this; } }; template class _ptr_base { AT m_addr; public: using addr_type = std::remove_cv_t; _ptr_base() = default; _ptr_base(vm::addr_t addr) : m_addr(addr) { } addr_type addr() const { return m_addr; } void set(addr_type addr) { m_addr = addr; } static _ptr_base make(addr_type addr) { _ptr_base result; result.m_addr = addr; return result; } // Conversion to another function pointer template operator _ptr_base() const { return vm::cast(m_addr, HERE); } explicit operator bool() const { return m_addr != 0; } _ptr_base operator +() const { return vm::cast(m_addr, HERE); } // Callback; defined in PPUCallback.h, passing context is mandatory RT operator()(ppu_thread& ppu, T... args) const; }; template class _ptr_base { 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>; template using cptrl = ptrl; // Native endianness pointer to BE data template using ptrb = _ptr_base, AT>; template using cptrb = ptrb; // 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>; inline namespace ps3_ { // Default pointer type for PS3 HLE functions (Native endianness pointer to BE data) template using ptr = ptrb; template using cptr = ptr; // Default pointer to pointer type for PS3 HLE functions (Native endianness pointer to BE pointer to BE data) template using pptr = ptr, AT>; template using cpptr = pptr; // Default pointer type for PS3 HLE structures (BE pointer to BE data) template using bptr = bptrb; template using bcptr = bptr; // Default pointer to pointer type for PS3 HLE structures (BE pointer to BE pointer to BE data) template using bpptr = bptr, AT>; 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(), HERE); } // 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(), HERE); } } struct null_t { template operator _ptr_base() const { return _ptr_base{}; } template friend bool operator ==(const null_t&, const _ptr_base& ptr) { return !ptr; } template friend bool operator ==(const _ptr_base& ptr, const null_t&) { return !ptr; } template friend bool operator !=(const null_t&, const _ptr_base& ptr) { return ptr.operator bool(); } template friend bool operator !=(const _ptr_base& ptr, const null_t&) { return ptr.operator bool(); } template friend bool operator <(const null_t&, const _ptr_base& ptr) { return ptr.operator bool(); } template friend bool operator <(const _ptr_base&, const null_t&) { return false; } template friend bool operator <=(const null_t&, const _ptr_base&) { return true; } template friend bool operator <=(const _ptr_base& ptr, const null_t&) { return !ptr.operator bool(); } template friend bool operator >(const null_t&, const _ptr_base&) { return false; } template friend bool operator >(const _ptr_base& ptr, const null_t&) { return ptr.operator bool(); } template friend bool operator >=(const null_t&, const _ptr_base& ptr) { return !ptr; } template friend bool operator >=(const _ptr_base&, const null_t&) { return true; } }; // Null pointer convertible to any vm::ptr* type static null_t null; } template inline vm::if_comparable_t operator ==(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() == right.addr(); } template inline vm::if_comparable_t operator !=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() != right.addr(); } template inline vm::if_comparable_t operator <(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() < right.addr(); } template inline vm::if_comparable_t operator <=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() <= right.addr(); } template inline vm::if_comparable_t operator >(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() > right.addr(); } template inline vm::if_comparable_t operator >=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() >= right.addr(); } // Change AT endianness to BE/LE template struct to_se, Se> { using type = vm::_ptr_base::type>; }; // Format pointer template struct fmt_unveil, void> { using type = vm::_ptr_base; // Use only T, ignoring AT static inline auto get(const vm::_ptr_base& arg) { return fmt_unveil::get(arg.addr()); } }; template<> struct fmt_class_string, void> { static void format(std::string& out, u64 arg); }; template struct fmt_class_string, void> : fmt_class_string, void> { // Classify all pointers as const void* }; template<> struct fmt_class_string, void> { static void format(std::string& out, u64 arg); }; template<> struct fmt_class_string, void> : fmt_class_string> { // Classify char* as const char* };