diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c40d066 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/ext/libplum"] + path = src/ext/libplum + url = https://github.com/PretendoNetwork/libplum diff --git a/Makefile b/Makefile index fa5360d..55b7692 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,9 @@ WUT_ROOT := $(DEVKITPRO)/wut #------------------------------------------------------------------------------- TARGET := Inkay-pretendo BUILD := build -SOURCES := src src/patches src/utils src/ext/inih common +SOURCES := src src/patches src/utils src/ext/inih src/ext/libplum/src common DATA := data -INCLUDES := src src/ext/inih src/lang common +INCLUDES := src src/ext/inih src/ext/libplum/include/plum src/lang common #------------------------------------------------------------------------------- # options for code generation diff --git a/src/ext/libplum b/src/ext/libplum new file mode 160000 index 0000000..70e85c9 --- /dev/null +++ b/src/ext/libplum @@ -0,0 +1 @@ +Subproject commit 70e85c9eedf52d8048177876037f98e0c0cf91ff diff --git a/src/main.cpp b/src/main.cpp index df482b3..1139f0d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "patches/olv_urls.h" #include "patches/game_matchmaking.h" +#include #include #include @@ -189,6 +190,12 @@ WUMS_INITIALIZE() { if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed"); } + + const plum_config_t plum_config = { + .log_level = PLUM_LOG_LEVEL_VERBOSE, + .log_callback = [](const plum_log_level_t level, const char *message) { DEBUG_FUNCTION_LINE("[PLUM-%d] %s", level, message) }, + }; + plum_init(&plum_config); } WUMS_DEINITIALIZE() { @@ -202,6 +209,8 @@ WUMS_DEINITIALIZE() { NotificationModule_DeInitLibrary(); FunctionPatcher_DeInitLibrary(); + plum_cleanup(); + WHBLogCafeDeinit(); WHBLogUdpDeinit(); } @@ -237,6 +246,7 @@ WUMS_ALL_APPLICATION_STARTS_DONE() { } WUMS_APPLICATION_ENDS() { + peertopeer_notify_titleswitch(); } WUMS_EXPORT_FUNCTION(Inkay_Initialize); diff --git a/src/patches/game_peertopeer.cpp b/src/patches/game_peertopeer.cpp index d986f36..3a54018 100644 --- a/src/patches/game_peertopeer.cpp +++ b/src/patches/game_peertopeer.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "game_peertopeer.h" #include "config.h" @@ -26,6 +27,31 @@ #include using namespace std::string_view_literals; +static std::optional upnp_id; + +static void upnp_cb(int id, plum_state_t state, const plum_mapping_t *mapping) { + DEBUG_FUNCTION_LINE("Plum result %d, %s:%hu", state, mapping->external_host, mapping->external_port); +} + +static void upnp_go() { + const plum_mapping_t mapping = { + .protocol = PLUM_IP_PROTOCOL_UDP, + .internal_port = get_console_peertopeer_port(), + .user_ptr = nullptr, + }; + int id = plum_create_mapping(&mapping, upnp_cb); + if (id < 0) return; + + upnp_id = id; +} + +static void upnp_stop() { + if (!upnp_id) return; + + plum_destroy_mapping(*upnp_id); + upnp_id = std::nullopt; +} + static struct { std::array tid; uint16_t version; @@ -51,24 +77,25 @@ static struct { }, }; -static void generic_peertopeer_patch() { +static bool generic_peertopeer_patch() { uint64_t tid = OSGetTitleID(); uint16_t title_version = 0; if (const auto version_opt = get_current_title_version(); !version_opt) { DEBUG_FUNCTION_LINE("Failed to detect current title version"); - return; + return false; } else { title_version = *version_opt; DEBUG_FUNCTION_LINE("Title version detected: %d", title_version); } + bool result = false; 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; + return false; } if (title_version != patch.version) { @@ -85,19 +112,23 @@ static void generic_peertopeer_patch() { target = (uint16_t *)rpl_addr(*game, patch.max_port_addr); replace_unsigned(target, 0xffff, port); + + result = true; break; } + + return result; } -static void minecraft_peertopeer_patch() { +static bool minecraft_peertopeer_patch() { std::optional minecraft = search_for_rpl("Minecraft.Client.rpx"sv); if (!minecraft) { DEBUG_FUNCTION_LINE("Couldn't find minecraft rpx!"); - return; + return false; } if (const auto version_opt = get_current_title_version(); !version_opt || *version_opt != 688) { DEBUG_FUNCTION_LINE("Wrong mincecraft version detected"); - return; + return false; } auto port = get_console_peertopeer_port(); @@ -112,6 +143,8 @@ static void minecraft_peertopeer_patch() { replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0 replace_instruction(&target_func[1], 0x3863ffff, 0x60630000 | port); // ori r3, r3, port // blr + + return true; } void peertopeer_patch() { @@ -119,13 +152,21 @@ void peertopeer_patch() { return; } - uint64_t tid = OSGetTitleID(); + bool patched; + const uint64_t tid = OSGetTitleID(); if (tid == 0x00050000'101D7500 || // EUR tid == 0x00050000'101D9D00 || // USA tid == 0x00050000'101DBE00) { // JPN - minecraft_peertopeer_patch(); + patched = minecraft_peertopeer_patch(); } else { - generic_peertopeer_patch(); + patched = generic_peertopeer_patch(); } + + if (patched) + upnp_go(); +} + +void peertopeer_notify_titleswitch() { + upnp_stop(); } diff --git a/src/patches/game_peertopeer.h b/src/patches/game_peertopeer.h index 8211c00..d8dc714 100644 --- a/src/patches/game_peertopeer.h +++ b/src/patches/game_peertopeer.h @@ -14,3 +14,4 @@ #pragma once void peertopeer_patch(); +void peertopeer_notify_titleswitch();