#pragma once #include #include #include "xbox.h" #include "memory.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 { 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)); } 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; } // TODO: get value from stack. return 0; } constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept { if (arg <= 7) { switch (arg) { case 0: ctx.r3.u64 = value; return; case 1: ctx.r4.u64 = value; return; case 2: ctx.r5.u64 = value; return; case 3: ctx.r6.u64 = value; return; case 4: ctx.r7.u64 = value; return; case 5: ctx.r8.u64 = value; return; case 6: ctx.r9.u64 = value; return; case 7: ctx.r10.u64 = value; return; [[unlikely]] default: break; } } assert(arg < 7 && "Pushing to stack memory is not yet supported."); } static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept { switch (arg) { case 0: ctx.f1.f64 = value; return; case 1: ctx.f2.f64 = value; return; case 2: ctx.f3.f64 = value; return; case 3: ctx.f4.f64 = value; return; case 4: ctx.f5.f64 = value; return; case 5: ctx.f6.f64 = value; return; case 6: ctx.f7.f64 = value; return; case 7: ctx.f8.f64 = value; return; case 8: ctx.f9.f64 = value; return; case 9: ctx.f10.f64 = value; return; case 10: ctx.f11.f64 = value; return; case 11: ctx.f12.f64 = value; return; case 12: ctx.f13.f64 = value; return; [[unlikely]] default: break; } assert(arg < 12 && "Pushing to stack memory is not yet supported."); } template 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 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)); } template constexpr static std::enable_if_t, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept { if constexpr (is_precise_v) { SetPrecisionArgumentValue(ctx, base, idx, value); } else if constexpr (std::is_null_pointer_v) { SetIntegerArgumentValue(ctx, base, idx, 0); } else if constexpr (std::is_pointer_v) { SetIntegerArgumentValue(ctx, base, idx, g_memory.MapVirtual(value)); } else { SetIntegerArgumentValue(ctx, base, idx, value); } } template constexpr static std::enable_if_t, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept { const auto v = g_memory.MapVirtual((void*)value); if (!v) { return; } SetValue(ctx, base, idx, v); } }; struct Argument { int type{}; int ordinal{}; }; template constexpr std::array> GatherFunctionArguments(const T1& tpl) { std::array> args{}; int floatOrdinal{}; size_t i{}; if constexpr (!args.empty()) { std::apply([&](const auto& first, const auto&... rest) { auto append = [&](const T2& v) { if constexpr (is_precise_v) { args[i] = { 1, floatOrdinal++ }; } else { args[i] = { 0, static_cast(i) }; // what the fuck } i++; }; append(first); (append(rest), ...); }, tpl); } return args; } template constexpr std::array::value> GatherFunctionArguments() { return GatherFunctionArguments(function_args(Func)); } template struct arg_ordinal_t { static constexpr size_t value = GatherFunctionArguments()[I].ordinal; }; template void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple&) noexcept requires (I >= sizeof...(TArgs)) { } template std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(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_to_host(ctx, base, tpl); } template void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple&) noexcept requires (I >= sizeof...(TArgs)) { } template std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple& tpl) noexcept { using T = std::tuple_element_t>; ArgTranslator::SetValue(ctx, base, GatherFunctionArguments(std::tuple{})[I].ordinal, std::get(tpl)); _translate_args_to_guest(ctx, base, tpl); } template PPC_FUNC(HostToGuestFunction) { using ret_t = decltype(std::apply(Func, function_args(Func))); auto args = function_args(Func); _translate_args_to_host(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 = 0; } } else if constexpr (is_precise_v) { ctx.f1.f64 = v; } else { ctx.r3.u64 = (uint64_t)v; } } } template T GuestToHostFunction(const TFunction& func, TArgs&&... argv) { auto args = std::make_tuple(std::forward(argv)...); auto& currentCtx = *GetPPCContext(); PPCContext newCtx; // NOTE: No need for zero initialization, has lots of unnecessary code generation. newCtx.fn = currentCtx.fn; newCtx.r1 = currentCtx.r1; newCtx.r13 = currentCtx.r13; newCtx.fpscr = currentCtx.fpscr; _translate_args_to_guest(newCtx, (uint8_t*)g_memory.base, args); SetPPCContext(newCtx); if constexpr (std::is_function_v) func(newCtx, (uint8_t*)g_memory.base); else (*(PPCFunc**)(newCtx.fn + uint64_t(func) * 2))(newCtx, (uint8_t*)g_memory.base); currentCtx.fpscr = newCtx.fpscr; SetPPCContext(currentCtx); if constexpr (std::is_pointer_v) { return reinterpret_cast((uint64_t)g_memory.Translate(newCtx.r3.u32)); } else if constexpr (is_precise_v) { return static_cast(newCtx.f1.f64); } else if constexpr (std::is_integral_v) { return static_cast(newCtx.r3.u64); } else { static_assert(std::is_void_v, "Unsupported return type."); } } #define GUEST_FUNCTION_HOOK(subroutine, function) \ PPC_FUNC(subroutine) { HostToGuestFunction(ctx, base); } #define GUEST_FUNCTION_STUB(subroutine) \ PPC_FUNC(subroutine) { }