#pragma once #include "util/types.hpp" #include "util/to_endian.hpp" #include "Utilities/StrFmt.h" #include "vm.h" class ppu_thread; struct ppu_func_opd_t; namespace vm { template class _ref_base; // Enables comparison between comparable types of pointers template concept PtrComparable = requires (T1* t1, T2* t2) { t1 == t2; }; 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; ENABLE_BITWISE_SERIALIZATION; _ptr_base() = default; _ptr_base(vm::addr_t addr) noexcept : 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 requires (std::is_convertible_v) operator _ptr_base() const noexcept { return vm::cast(m_addr); } explicit operator bool() const noexcept { return m_addr != 0u; } // Get vm pointer to a struct member template requires PtrComparable _ptr_base ptr(MT T2::*const mptr) const { return vm::cast(vm::cast(m_addr) + offset32(mptr)); } // Get vm pointer to a struct member with array subscription template > requires PtrComparable _ptr_base ptr(MT T2::*const mptr, u32 index) const { return vm::cast(vm::cast(m_addr) + offset32(mptr) + u32{sizeof(ET)} * index); } // Get vm reference to a struct member template requires PtrComparable && (!std::is_void_v) _ref_base ref(MT T2::*const mptr) const { return vm::cast(vm::cast(m_addr) + offset32(mptr)); } // Get vm reference to a struct member with array subscription template > requires PtrComparable && (!std::is_void_v) _ref_base ref(MT T2::*const mptr, u32 index) const { return vm::cast(vm::cast(m_addr) + offset32(mptr) + u32{sizeof(ET)} * index); } // Get vm reference _ref_base ref() const requires (!std::is_void_v) { return vm::cast(m_addr); } T* get_ptr() const { return static_cast(vm::base(vm::cast(m_addr))); } T* operator ->() const requires (!std::is_void_v) { return get_ptr(); } std::add_lvalue_reference_t operator *() const requires (!std::is_void_v) { return *static_cast(vm::base(vm::cast(m_addr))); } std::add_lvalue_reference_t operator [](u32 index) const requires (!std::is_void_v) { return *static_cast(vm::base(vm::cast(m_addr) + 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)) == 0u; } // Get type size static constexpr u32 size() noexcept requires (!std::is_void_v) { return sizeof(T); } // Get type alignment static constexpr u32 align() noexcept requires (!std::is_void_v) { return alignof(T); } _ptr_base operator +() const { return vm::cast(m_addr); } _ptr_base operator +(u32 count) const requires (!std::is_void_v) { return vm::cast(vm::cast(m_addr) + count * size()); } _ptr_base operator -(u32 count) const requires (!std::is_void_v) { return vm::cast(vm::cast(m_addr) - count * size()); } friend _ptr_base operator +(u32 count, const _ptr_base& ptr) requires (!std::is_void_v) { return vm::cast(vm::cast(ptr.m_addr) + count * size()); } // Pointer difference operator template requires (std::is_object_v && std::is_same_v, std::decay_t>) s32 operator -(const _ptr_base& right) const { return static_cast(vm::cast(m_addr) - vm::cast(right.m_addr)) / size(); } _ptr_base operator ++(int) requires (!std::is_void_v) { _ptr_base result = *this; m_addr = vm::cast(m_addr) + size(); return result; } _ptr_base& operator ++() requires (!std::is_void_v) { m_addr = vm::cast(m_addr) + size(); return *this; } _ptr_base operator --(int) requires (!std::is_void_v) { _ptr_base result = *this; m_addr = vm::cast(m_addr) - size(); return result; } _ptr_base& operator --() requires (!std::is_void_v) { m_addr = vm::cast(m_addr) - size(); return *this; } _ptr_base& operator +=(s32 count) requires (!std::is_void_v) { m_addr = vm::cast(m_addr) + count * size(); return *this; } _ptr_base& operator -=(s32 count) requires (!std::is_void_v) { m_addr = vm::cast(m_addr) - count * size(); return *this; } std::pair, char, std::remove_const_t>> try_read() const requires (std::is_copy_constructible_v) { alignas(sizeof(T) >= 16 ? 16 : 8) char buf[sizeof(T)]{}; const bool ok = vm::try_access(vm::cast(m_addr), buf, sizeof(T), false); return { ok, std::bit_cast(buf) }; } bool try_read(std::conditional_t, char, std::remove_const_t>& out) const requires (!std::is_void_v) { return vm::try_access(vm::cast(m_addr), std::addressof(out), sizeof(T), false); } bool try_write(const std::conditional_t, char, T>& _in) const requires (!std::is_void_v) { return vm::try_access(vm::cast(m_addr), const_cast(std::addressof(_in)), sizeof(T), true); } // Don't use auto& raw() { return m_addr; } }; template class _ptr_base { AT m_addr; public: using addr_type = std::remove_cv_t; ENABLE_BITWISE_SERIALIZATION; _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); } explicit operator bool() const { return m_addr != 0u; } _ptr_base operator +() const { return vm::cast(m_addr); } // Don't use auto& raw() { return m_addr; } // Callback; defined in PPUCallback.h, passing context is mandatory RT operator()(ppu_thread& ppu, T... args) const; const ppu_func_opd_t& opd() 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, u32> static_ptr_cast(const _ptr_base& other) { return vm::cast(other.addr()); } // Perform const_cast (for example, vm::cptr to vm::ptr) template*>(std::declval()))> inline _ptr_base, u32> const_ptr_cast(const _ptr_base& other) { return vm::cast(other.addr()); } // Perform reinterpret cast template *>(std::declval()))> inline _ptr_base, u32> unsafe_ptr_cast(const _ptr_base& other) { return vm::cast(other.addr()); } } struct null_t { template operator _ptr_base() const { return _ptr_base{}; } template constexpr bool operator ==(const _ptr_base& ptr) const { return !ptr; } template constexpr bool operator <(const _ptr_base& ptr) const { return 0 < ptr.addr(); } }; // Null pointer convertible to any vm::ptr* type constexpr null_t null{}; } template requires vm::PtrComparable inline bool operator ==(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() == right.addr(); } template requires vm::PtrComparable inline bool operator <(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() < right.addr(); } template requires vm::PtrComparable inline bool operator <=(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() <= right.addr(); } template requires vm::PtrComparable inline bool operator >(const vm::_ptr_base& left, const vm::_ptr_base& right) { return left.addr() > right.addr(); } template requires vm::PtrComparable inline bool 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* }; template struct fmt_class_string, void> : fmt_class_string> { // Classify const char[] as const char* }; template struct fmt_class_string, void> : fmt_class_string> { // Classify char[] as const char* };