#pragma once #include // for size_t #include "version.h" #include "platform.h" #define FMT_HEADER_ONLY #define FMT_USE_GRISU 1 #include #include #include #if FMT_VERSION > 80000 #include // needed for wchar_t support #endif #define SDL_MAIN_HANDLED // #if FMT_VERSION > 80102 // // restore generic formatter for enum (class) to underlying. We currently utilize this for HLE call logging // template ::value, int> = 0> // constexpr auto format_as(Enum e) noexcept -> std::underlying_type { // return static_cast>(e); // } // #endif // arch defines #if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) #define ARCH_X86_64 #endif // c includes #include #include #include #include #include #if defined(ARCH_X86_64) #include #endif // c++ includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; #include "enumFlags.h" // base types using uint64 = uint64_t; using uint32 = uint32_t; using uint16 = uint16_t; using uint8 = uint8_t; using sint64 = int64_t; using sint32 = int32_t; using sint16 = int16_t; using sint8 = int8_t; // types with explicit big endian order #include "betype.h" // types with explicit little endian order (on a big-endian system these would be implemented like betype.h) using uint64le = uint64_t; using uint32le = uint32_t; using uint16le = uint16_t; using uint8le = uint8_t; // logging #include "Cemu/Logging/CemuDebugLogging.h" #include "Cemu/Logging/CemuLogging.h" // manual endian-swapping #if _MSC_VER inline uint64 _swapEndianU64(uint64 v) { return _byteswap_uint64(v); } inline uint32 _swapEndianU32(uint32 v) { return _byteswap_ulong(v); } inline sint32 _swapEndianS32(sint32 v) { return (sint32)_byteswap_ulong((uint32)v); } inline uint16 _swapEndianU16(uint16 v) { return (v >> 8) | (v << 8); } inline sint16 _swapEndianS16(sint16 v) { return (sint16)(((uint16)v >> 8) | ((uint16)v << 8)); } #else inline uint64 _swapEndianU64(uint64 v) { #if BOOST_OS_MACOS return OSSwapInt64(v); #else return bswap_64(v); #endif } inline uint32 _swapEndianU32(uint32 v) { #if BOOST_OS_MACOS return OSSwapInt32(v); #else return bswap_32(v); #endif } inline sint32 _swapEndianS32(sint32 v) { #if BOOST_OS_MACOS return (sint32)OSSwapInt32((uint32)v); #else return (sint32)bswap_32((uint32)v); #endif } inline uint16 _swapEndianU16(uint16 v) { return (v >> 8) | (v << 8); } inline sint16 _swapEndianS16(sint16 v) { return (sint16)(((uint16)v >> 8) | ((uint16)v << 8)); } inline uint64 _umul128(uint64 multiplier, uint64 multiplicand, uint64 *highProduct) { unsigned __int128 x = (unsigned __int128)multiplier * (unsigned __int128)multiplicand; *highProduct = (x >> 64); return x & 0xFFFFFFFFFFFFFFFF; } typedef uint8_t BYTE; typedef uint32_t DWORD; typedef int32_t LONG; typedef int64_t LONGLONG; typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; }; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER, *PLARGE_INTEGER; #define DEFINE_ENUM_FLAG_OPERATORS(T) \ inline T operator~ (T a) { return static_cast( ~static_cast::type>(a) ); } \ inline T operator| (T a, T b) { return static_cast( static_cast::type>(a) | static_cast::type>(b) ); } \ inline T operator& (T a, T b) { return static_cast( static_cast::type>(a) & static_cast::type>(b) ); } \ inline T operator^ (T a, T b) { return static_cast( static_cast::type>(a) ^ static_cast::type>(b) ); } \ inline T& operator|= (T& a, T b) { return reinterpret_cast( reinterpret_cast::type&>(a) |= static_cast::type>(b) ); } \ inline T& operator&= (T& a, T b) { return reinterpret_cast( reinterpret_cast::type&>(a) &= static_cast::type>(b) ); } \ inline T& operator^= (T& a, T b) { return reinterpret_cast( reinterpret_cast::type&>(a) ^= static_cast::type>(b) ); } #endif template inline T GetBits(T value, uint32 index, uint32 numBits) { T mask = (1<>index) & mask; } template inline void SetBits(T& value, uint32 index, uint32 numBits, uint32 bitValue) { T mask = (1< #define DEBUG_BREAK raise(SIGTRAP) #endif #if defined(_MSC_VER) #define DLLEXPORT __declspec(dllexport) #elif defined(__GNUC__) #if BOOST_OS_WINDOWS #define DLLEXPORT __attribute__((dllexport)) #else #define DLLEXPORT #endif #else #error No definition for DLLEXPORT #endif #if BOOST_OS_WINDOWS #define NOEXPORT #elif defined(__GNUC__) #define NOEXPORT __attribute__ ((visibility ("hidden"))) #endif // On aarch64 we handle some of the x86 intrinsics by implementing them as wrappers #if defined(__aarch64__) inline void _mm_pause() { asm volatile("yield"); } inline uint64 __rdtsc() { uint64 t; asm volatile("mrs %0, cntvct_el0" : "=r" (t)); return t; } inline void _mm_mfence() { } inline unsigned char _addcarry_u64(unsigned char carry, unsigned long long a, unsigned long long b, unsigned long long *result) { *result = a + b + (unsigned long long)carry; if (*result < a) return 1; return 0; } #endif // asserts inline void cemu_assert(bool _condition) { if ((_condition) == false) { DEBUG_BREAK; } } #ifndef CEMU_DEBUG_ASSERT //#define cemu_assert_debug(__cond) -> Forcing __cond not to be evaluated currently has unexpected side-effects inline void cemu_assert_debug(bool _condition) { } inline void cemu_assert_unimplemented() { } inline void cemu_assert_suspicious() { } inline void cemu_assert_error() { DEBUG_BREAK; } #else inline void cemu_assert_debug(bool _condition) { if ((_condition) == false) DEBUG_BREAK; } inline void cemu_assert_unimplemented() { DEBUG_BREAK; } inline void cemu_assert_suspicious() { DEBUG_BREAK; } inline void cemu_assert_error() { DEBUG_BREAK; } #endif #define assert_dbg() DEBUG_BREAK // old style unconditional generic assert // MEMPTR #include "Common/MemPtr.h" template constexpr bool HAS_FLAG(T1 flags, T2 test_flag) { return (flags & (T1)test_flag) == (T1)test_flag; } template constexpr bool HAS_BIT(T1 value, T2 index) { return (value & ((T1)1 << index)) != 0; } template constexpr void SAFE_RELEASE(T& p) { if (p) { p->Release(); p = nullptr; } } template constexpr uint32_t ppcsizeof() { return (uint32_t) sizeof(T); } // common utility functions template void vectorAppendUnique(std::vector& vec, const T& val) // for cases where a small vector is more efficient than a set { if (std::find(vec.begin(), vec.end(), val) != vec.end()) return; vec.emplace_back(val); } template void vectorRemoveByValue(std::vector& vec, const T& val) { vec.erase(std::remove(vec.begin(), vec.end(), val), vec.end()); } template void vectorRemoveByIndex(std::vector& vec, const size_t index) { vec.erase(vec.begin() + index); } template bool match_any_of(T1&& value, Types&&... others) { return ((value == others) || ...); } // we cache the frequency in a static variable [[nodiscard]] static std::chrono::high_resolution_clock::time_point now_cached() noexcept { #ifdef _WIN32 // get current time static const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot const long long _Ctr = _Query_perf_counter(); static_assert(std::nano::num == 1, "This assumes period::num == 1."); const long long _Whole = (_Ctr / _Freq) * std::nano::den; const long long _Part = (_Ctr % _Freq) * std::nano::den / _Freq; return (std::chrono::high_resolution_clock::time_point(std::chrono::nanoseconds(_Whole + _Part))); #else return std::chrono::high_resolution_clock::now(); #endif } [[nodiscard]] static std::chrono::steady_clock::time_point tick_cached() noexcept { #if BOOST_OS_WINDOWS // get current time static const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot const long long _Ctr = _Query_perf_counter(); static_assert(std::nano::num == 1, "This assumes period::num == 1."); const long long _Whole = (_Ctr / _Freq) * std::nano::den; const long long _Part = (_Ctr % _Freq) * std::nano::den / _Freq; return (std::chrono::steady_clock::time_point(std::chrono::nanoseconds(_Whole + _Part))); #elif BOOST_OS_LINUX struct timespec tp; clock_gettime(CLOCK_MONOTONIC_RAW, &tp); return std::chrono::steady_clock::time_point( std::chrono::seconds(tp.tv_sec) + std::chrono::nanoseconds(tp.tv_nsec)); #elif BOOST_OS_MACOS return std::chrono::steady_clock::time_point( std::chrono::nanoseconds(clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW))); #endif } // Some string conversion helpers because C++20 std::u8string is too cumbersome to use in practice // mixing string types generally causes loads of issues and many of the libraries we use dont expose interfaces for u8string inline const char* _utf8WrapperPtr(const char8_t* input) { // use with care return (const char*)input; } inline std::string_view _utf8Wrapper(std::u8string_view input) { std::basic_string_view v((char*)input.data(), input.size()); return v; } // convert fs::path to utf8 encoded string inline std::string _pathToUtf8(const fs::path& path) { std::u8string strU8 = path.generic_u8string(); std::string v((const char*)strU8.data(), strU8.size()); return v; } // convert utf8 encoded string to fs::path inline fs::path _utf8ToPath(std::string_view input) { std::basic_string_view v((char8_t*)input.data(), input.size()); return fs::path(v); } // locale-independent variant of tolower() which also matches Wii U behavior inline char _ansiToLower(char c) { if (c >= 'A' && c <= 'Z') c -= ('A' - 'a'); return c; } class RunAtCemuBoot // -> replaces this with direct function calls. Linkers other than MSVC may optimize way object files entirely if they are not referenced from outside. So a source file self-registering using this would be causing issues { public: RunAtCemuBoot(void(*f)()) { f(); } }; // temporary wrapper until Concurrency TS gives us std::future .is_ready() template bool future_is_ready(std::future& f) { #if defined(__GNUC__) return f.wait_for(std::chrono::nanoseconds(0)) == std::future_status::ready; #else return f._Is_ready(); #endif } // helper function to cast raw pointers to std::atomic // this is technically not legal but works on most platforms as long as alignment restrictions are met and the implementation of atomic doesnt come with additional members template std::atomic* _rawPtrToAtomic(T* ptr) { static_assert(sizeof(T) == sizeof(std::atomic)); cemu_assert_debug((reinterpret_cast(ptr) % alignof(std::atomic)) == 0); return reinterpret_cast*>(ptr); } #if defined(__GNUC__) #define ATTR_MS_ABI __attribute__((ms_abi)) #else #define ATTR_MS_ABI #endif #if !defined(TRUE) or !defined(FALSE) #define TRUE (1) #define FALSE (0) #endif inline uint32 GetTitleIdHigh(uint64 titleId) { return titleId >> 32; } inline uint32 GetTitleIdLow(uint64 titleId) { return titleId & 0xFFFFFFFF; } #if defined(__GNUC__) #define memcpy_dwords(__dest, __src, __numDwords) memcpy((__dest), (__src), (__numDwords) * sizeof(uint32)) #define memcpy_qwords(__dest, __src, __numQwords) memcpy((__dest), (__src), (__numQwords) * sizeof(uint64)) #else #define memcpy_dwords(__dest, __src, __numDwords) __movsd((unsigned long*)(__dest), (const unsigned long*)(__src), __numDwords) #define memcpy_qwords(__dest, __src, __numQwords) __movsq((unsigned long long*)(__dest), (const unsigned long long*)(__src), __numQwords) #endif // PPC context and memory functions #include "Cafe/HW/MMU/MMU.h" #include "Cafe/HW/Espresso/PPCState.h" #include "Cafe/HW/Espresso/PPCCallback.h" // PPC stack trace printer void DebugLogStackTrace(struct OSThread_t* thread, MPTR sp, bool printSymbols = false); // generic formatter for enums (to underlying) template requires std::is_enum_v struct fmt::formatter : fmt::formatter> { auto format(const Enum& e, format_context& ctx) const { //return fmt::format_to(ctx.out(), "{}", fmt::underlying(e)); return formatter>::format(fmt::underlying(e), ctx); } }; // formatter for betype template struct fmt::formatter> : fmt::formatter { auto format(const betype& e, format_context& ctx) const { return formatter::format(static_cast(e), ctx); } }; // useful future C++ stuff namespace stdx { // std::to_underlying template {} >> constexpr std::underlying_type_t to_underlying(EnumT e) noexcept { return static_cast>(e); }; // std::scope_exit template class scope_exit { Fn m_func; bool m_released = false; public: explicit scope_exit(Fn&& f) noexcept : m_func(std::forward(f)) {} ~scope_exit() { if (!m_released) m_func(); } scope_exit(scope_exit&& other) noexcept : m_func(std::move(other.m_func)), m_released(std::exchange(other.m_released, true)) {} scope_exit(const scope_exit&) = delete; scope_exit& operator=(scope_exit) = delete; void release() { m_released = true;} }; }