diff --git a/src/patches/game_peertopeer.cpp b/src/patches/game_peertopeer.cpp index e0e0fa3..2651156 100644 --- a/src/patches/game_peertopeer.cpp +++ b/src/patches/game_peertopeer.cpp @@ -22,9 +22,56 @@ #include "utils/replace_mem.h" #include +#include +#include +using namespace std::string_view_literals; + +static struct { + std::array tid; + uint32_t min_port_addr; + uint32_t max_port_addr; + std::string_view rpx; +} generic_patch_games [] = { + { // MARIO KART 8 + { 0x00050000'1010ec00, 0x00050000'1010ed00, 0x00050000'1010eb00 }, + 0x101a9a52, + 0x101a9a54, + "Turbo.rpx"sv, + }, + { // Splatoon + { 0x00050000'10176900, 0x00050000'10176a00, 0x00050000'10162b00 }, + 0x101e8952, + 0x101e8954, + "Gambit.rpx"sv, + }, +}; + +static void generic_peertopeer_patch() { + uint64_t tid = OSGetTitleID(); + + for (const auto& patch : generic_patch_games) { + if (std::ranges::find(patch.tid, tid) == patch.tid.end()) continue; + + std::optional game = search_for_rpl(patch.rpx); + if (!game) { + DEBUG_FUNCTION_LINE("Couldn't find game rpx! (%s)", patch.rpx.data()); + return; + } + + auto port = get_console_peertopeer_port(); + DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, game->textAddr); + + auto target = (uint16_t *)rpl_addr(*game, patch.min_port_addr); + replace_unsigned(target, 0xc000, port); + + target = (uint16_t *)rpl_addr(*game, patch.max_port_addr); + replace_unsigned(target, 0xffff, port); + break; + } +} static void minecraft_peertopeer_patch() { - std::optional minecraft = search_for_rpl("Minecraft.Client.rpx"); + std::optional minecraft = search_for_rpl("Minecraft.Client.rpx"sv); if (!minecraft) { DEBUG_FUNCTION_LINE("Couldn't find minecraft rpx!"); return; @@ -33,12 +80,12 @@ static void minecraft_peertopeer_patch() { auto port = get_console_peertopeer_port(); DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, minecraft->textAddr); - uint32_t *target_func = rpl_addr(*minecraft, 0x03579530); + auto target_func = (uint32_t *)rpl_addr(*minecraft, 0x03579530); replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0 replace_instruction(&target_func[1], 0x3863c000, 0x60630000 | port); // ori r3, r3, port // blr - target_func = rpl_addr(*minecraft, 0x0357953c); + target_func = (uint32_t *)rpl_addr(*minecraft, 0x0357953c); replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0 replace_instruction(&target_func[1], 0x3863ffff, 0x60630000 | port); // ori r3, r3, port // blr @@ -56,6 +103,6 @@ void peertopeer_patch() { minecraft_peertopeer_patch(); } else { - DEBUG_FUNCTION_LINE_VERBOSE("Game has no p2p patches, will skip.\n"); + generic_peertopeer_patch(); } } diff --git a/src/utils/replace_mem.cpp b/src/utils/replace_mem.cpp index b1f6ba0..03e94b2 100644 --- a/src/utils/replace_mem.cpp +++ b/src/utils/replace_mem.cpp @@ -73,17 +73,30 @@ void replaceBulk(uint32_t start, uint32_t size, std::span rep #endif } -bool replace_instruction(uint32_t *inst, uint32_t orignal_value, uint32_t new_value) { - if (*inst != orignal_value) return false; +template + requires std::integral +bool replace_unsigned(U *addr, U original_value, U new_value) { + if (*addr != original_value) return false; KernelCopyData( - OSEffectiveToPhysical((uint32_t) inst), + OSEffectiveToPhysical((uint32_t) addr), OSEffectiveToPhysical((uint32_t) &new_value), sizeof(new_value) ); - DCFlushRange(inst, sizeof(new_value)); - ICInvalidateRange(inst, sizeof(new_value)); + DCFlushRange(addr, sizeof(new_value)); DEBUG_FUNCTION_LINE_VERBOSE("%08x is now %08x", inst, *inst); - return *inst == new_value; + return *addr == new_value; +} +template bool replace_unsigned(uint64_t *, uint64_t, uint64_t); +template bool replace_unsigned(uint32_t *, uint32_t, uint32_t); +template bool replace_unsigned(uint16_t *, uint16_t, uint16_t); +template bool replace_unsigned(uint8_t *, uint8_t, uint8_t); + +bool replace_instruction(uint32_t *inst, uint32_t original_value, uint32_t new_value) { + bool res = replace_unsigned(inst, original_value, new_value); + if (!res) return res; + + ICInvalidateRange(inst, sizeof(new_value)); + return true; } diff --git a/src/utils/replace_mem.h b/src/utils/replace_mem.h index dbf8eb6..8557985 100644 --- a/src/utils/replace_mem.h +++ b/src/utils/replace_mem.h @@ -28,4 +28,8 @@ struct replacement { void replaceBulk(uint32_t start, uint32_t size, std::span replacements); -bool replace_instruction(uint32_t *inst, uint32_t orignal_value, uint32_t new_value); +template + requires std::integral +bool replace_unsigned(U *addr, U original_value, U new_value); + +bool replace_instruction(uint32_t *inst, uint32_t original_value, uint32_t new_value); diff --git a/src/utils/rpl_info.h b/src/utils/rpl_info.h index 5eebe7a..15f59c7 100644 --- a/src/utils/rpl_info.h +++ b/src/utils/rpl_info.h @@ -19,10 +19,10 @@ std::optional search_for_rpl(std::string_view name); -constexpr uint32_t *rpl_addr(OSDynLoad_NotifyData rpl, uint32_t cemu_addr) { +constexpr void *rpl_addr(OSDynLoad_NotifyData rpl, uint32_t cemu_addr) { if (cemu_addr < 0x1000'0000) { - return (uint32_t *)(rpl.textAddr + cemu_addr - 0x0200'0000); + return (void *)(rpl.textAddr + cemu_addr - 0x0200'0000); } else { - return (uint32_t *)(rpl.dataAddr + cemu_addr - 0x1000'0000); + return (void *)(rpl.dataAddr + cemu_addr - 0x1000'0000); } }