#pragma once #include #include #include "xbox.h" template constexpr std::tuple function_args(R(*)(T...)) noexcept { return std::tuple(); } template static constexpr decltype(V) constant_v = V; template static constexpr bool is_precise_v = std::is_same_v || std::is_same_v; template struct arg_count_t { static constexpr size_t value = std::tuple_size_v; }; template std::enable_if_t<(I >= sizeof...(TArgs)), void> _tuple_for(std::tuple&, const TCallable& callable) noexcept { } template std::enable_if_t<(I < sizeof...(TArgs)), void> _tuple_for(std::tuple& tpl, const TCallable& callable) noexcept { callable(std::get(tpl), I); _tuple_for(tpl, callable); } struct ArgTranslator { FORCEINLINE constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept { if (arg <= 7) { switch (arg) { case 0: return ctx.r3.u32; case 1: return ctx.r4.u32; case 2: return ctx.r5.u32; case 3: return ctx.r6.u32; case 4: return ctx.r7.u32; case 5: return ctx.r8.u32; case 6: return ctx.r9.u32; case 7: return ctx.r10.u32; default: break; } } return *reinterpret_cast(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8)); } FORCEINLINE static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept { switch (arg) { case 0: return ctx.f1.f64; case 1: return ctx.f2.f64; case 2: return ctx.f3.f64; case 3: return ctx.f4.f64; case 4: return ctx.f5.f64; case 5: return ctx.f6.f64; case 6: return ctx.f7.f64; case 7: return ctx.f8.f64; case 8: return ctx.f9.f64; case 9: return ctx.f10.f64; case 10: return ctx.f11.f64; case 11: return ctx.f12.f64; case 12: return ctx.f13.f64; [[unlikely]] default: break; } // how did you end up here return 0; } template FORCEINLINE constexpr static std::enable_if_t, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept { if constexpr (is_precise_v) { return static_cast(GetPrecisionArgumentValue(ctx, base, idx)); } else { return static_cast(GetIntegerArgumentValue(ctx, base, idx)); } } template FORCEINLINE constexpr static std::enable_if_t, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept { const auto v = GetIntegerArgumentValue(ctx, base, idx); if (!v) { return nullptr; } return reinterpret_cast(base + static_cast(v)); } }; struct Argument { int type{}; int ordinal{}; }; template constexpr std::array::value> GatherFunctionArguments() { std::array::value> args{}; int intOrdinal{}; int floatOrdinal{}; size_t i{}; if constexpr (!args.empty()) { std::apply([&](const auto& first, const auto&... rest) { auto append = [&](const T & v) { if constexpr (is_precise_v) { args[i] = { 1, floatOrdinal++ }; } else { intOrdinal++; args[i] = { 0, static_cast(i) }; // what the fuck } i++; }; append(first); (append(rest), ...); }, function_args(Func)); } return args; } template struct arg_ordinal_t { static constexpr size_t value = GatherFunctionArguments()[I].ordinal; }; template FORCEINLINE void _translate_args(PPCContext& ctx, uint8_t* base, std::tuple&) noexcept requires (I >= sizeof...(TArgs)) { } template FORCEINLINE std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args(PPCContext& ctx, uint8_t* base, std::tuple& tpl) noexcept { using T = std::tuple_element_t>; std::get(tpl) = ArgTranslator::GetValue(ctx, base, arg_ordinal_t::value); _translate_args(ctx, base, tpl); } template FORCEINLINE PPC_FUNC(GuestFunction) { using ret_t = decltype(std::apply(Func, function_args(Func))); auto args = function_args(Func); _translate_args(ctx, base, args); if constexpr (std::is_same_v) { std::apply(Func, args); } else { auto v = std::apply(Func, args); if constexpr (std::is_pointer()) { if (v != nullptr) { ctx.r3.u64 = static_cast(reinterpret_cast(v) - reinterpret_cast(base)); } else { ctx.r3.u64 = NULL; } } else if constexpr (is_precise_v) { ctx.f1.f64 = v; } else { ctx.r3.u64 = (uint64_t)v; } } } #define GUEST_FUNCTION_HOOK(subroutine, function) \ PPC_FUNC(subroutine) { GuestFunction(ctx, base); } #define GUEST_FUNCTION_STUB(subroutine) \ PPC_FUNC(subroutine) { }