diff --git a/src/main.cpp b/src/main.cpp index 8270d80..560ec42 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,6 +68,7 @@ WUPS_USE_WUT_DEVOPTAB(); #include #include #include "patches/account_settings.h" +#include "patches/game_peertopeer.h" #include "utils/sysconfig.h" //thanks @Gary#4139 :p @@ -127,23 +128,9 @@ INITIALIZE_PLUGIN() { DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed"); } - //get os version - MCPSystemVersion os_version; - int mcp = MCP_Open(); - int ret = MCP_GetSystemVersion(mcp, &os_version); - if (ret < 0) { - DEBUG_FUNCTION_LINE("getting system version failed (%d/%d)!", mcp, ret); - os_version = (MCPSystemVersion) { - .major = 5, .minor = 5, .patch = 5, .region = 'E' - }; - } - DEBUG_FUNCTION_LINE_VERBOSE("Running on %d.%d.%d%c", - os_version.major, os_version.minor, os_version.patch, os_version.region - ); - // if using pretendo then (try to) apply the ssl patches if (Config::connect_to_network) { - if (is555(os_version)) { + if (is555(get_console_os_version())) { Mocha_IOSUKernelWrite32(0xE1019F78, 0xE3A00001); // mov r0, #1 } else { Mocha_IOSUKernelWrite32(0xE1019E84, 0xE3A00001); // mov r0, #1 @@ -165,8 +152,6 @@ INITIALIZE_PLUGIN() { ShowNotification(get_nintendo_network_message()); } - MCP_Close(mcp); - if (FunctionPatcher_InitLibrary() == FUNCTION_PATCHER_RESULT_SUCCESS) { install_matchmaking_patches(); } @@ -187,6 +172,7 @@ ON_APPLICATION_START() { DEBUG_FUNCTION_LINE_VERBOSE("Inkay " INKAY_VERSION " starting up...\n"); setup_olv_libs(); + peertopeer_patch(); matchmaking_notify_titleswitch(); patchAccountSettings(); } diff --git a/src/patches/game_peertopeer.cpp b/src/patches/game_peertopeer.cpp new file mode 100644 index 0000000..e51f2a2 --- /dev/null +++ b/src/patches/game_peertopeer.cpp @@ -0,0 +1,73 @@ +/* Copyright 2024 Pretendo Network contributors + Copyright 2024 Ash Logan + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include "game_peertopeer.h" + +#include "utils/sysconfig.h" +#include "utils/logger.h" +#include "utils/rpl_info.h" +#include "utils/replace_mem.h" + +#include + +static inline int digit(char a) { + if (a < '0' || a > '9') return 0; + return a - '0'; +} + +static unsigned short peertopeer_port() { + const char * serial = get_console_serial(); + + unsigned short port = 50000 + + (digit(serial[4]) * 1000) + + (digit(serial[5]) * 100 ) + + (digit(serial[6]) * 10 ) + + (digit(serial[7]) * 1 ); + + return port; +} + +static void minecraft_peertopeer_patch() { + std::optional minecraft = search_for_rpl("Minecraft.Client.rpx"); + if (!minecraft) { + DEBUG_FUNCTION_LINE("Couldn't find minecraft rpx!"); + return; + } + + auto port = peertopeer_port(); + DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, minecraft->textAddr); + + uint32_t *target_func = 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); + replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0 + replace_instruction(&target_func[1], 0x3863ffff, 0x60630000 | port); // ori r3, r3, port + // blr +} + +void peertopeer_patch() { + uint64_t tid = OSGetTitleID(); + if (tid == 0x00050000'101D7500 || // EUR + tid == 0x00050000'101D9D00 || // USA + tid == 0x00050000'101DBE00) { // JPN + + minecraft_peertopeer_patch(); + } else { + DEBUG_FUNCTION_LINE_VERBOSE("Game has no p2p patches, will skip.\n"); + } +} diff --git a/src/patches/game_peertopeer.h b/src/patches/game_peertopeer.h new file mode 100644 index 0000000..8211c00 --- /dev/null +++ b/src/patches/game_peertopeer.h @@ -0,0 +1,16 @@ +/* Copyright 2024 Pretendo Network contributors + Copyright 2024 Ash Logan + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +void peertopeer_patch(); diff --git a/src/utils/replace_mem.cpp b/src/utils/replace_mem.cpp index 1879519..b1f6ba0 100644 --- a/src/utils/replace_mem.cpp +++ b/src/utils/replace_mem.cpp @@ -22,6 +22,7 @@ #include #include #include +#include bool replace(uint32_t start, uint32_t size, const char *original_val, size_t original_val_sz, const char *new_val, size_t new_val_sz) { @@ -71,3 +72,18 @@ 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; + + KernelCopyData( + OSEffectiveToPhysical((uint32_t) inst), + OSEffectiveToPhysical((uint32_t) &new_value), + sizeof(new_value) + ); + DCFlushRange(inst, sizeof(new_value)); + ICInvalidateRange(inst, sizeof(new_value)); + + DEBUG_FUNCTION_LINE_VERBOSE("%08x is now %08x", inst, *inst); + return *inst == new_value; +} diff --git a/src/utils/replace_mem.h b/src/utils/replace_mem.h index 377f2ac..dbf8eb6 100644 --- a/src/utils/replace_mem.h +++ b/src/utils/replace_mem.h @@ -27,3 +27,5 @@ 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); diff --git a/src/utils/rpl_info.cpp b/src/utils/rpl_info.cpp new file mode 100644 index 0000000..2148e1e --- /dev/null +++ b/src/utils/rpl_info.cpp @@ -0,0 +1,39 @@ +/* Copyright 2024 Pretendo Network contributors + Copyright 2024 Ash Logan + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "rpl_info.h" + +#include +#include +#include + +// if we get more than like.. two callsites for this, it should really be refactored out rather than doing a fresh +// search every time +std::optional search_for_rpl(std::string_view name) { + int num_rpls = OSDynLoad_GetNumberOfRPLs(); + if (!num_rpls) + return std::nullopt; + + // allocates but we free it at return + std::vector rpls(num_rpls); + if (!OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data())) + return std::nullopt; + + auto rpl = std::find_if(rpls.begin(), rpls.end(), [=](const OSDynLoad_NotifyData &rpl) { + return std::string_view(rpl.name).ends_with(name); + }); + if (rpl == rpls.end()) + return std::nullopt; + + return *rpl; +} diff --git a/src/utils/rpl_info.h b/src/utils/rpl_info.h new file mode 100644 index 0000000..5eebe7a --- /dev/null +++ b/src/utils/rpl_info.h @@ -0,0 +1,28 @@ +/* Copyright 2024 Pretendo Network contributors + Copyright 2024 Ash Logan + + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby + granted, provided that the above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +#include +#include +#include + +std::optional search_for_rpl(std::string_view name); + +constexpr uint32_t *rpl_addr(OSDynLoad_NotifyData rpl, uint32_t cemu_addr) { + if (cemu_addr < 0x1000'0000) { + return (uint32_t *)(rpl.textAddr + cemu_addr - 0x0200'0000); + } else { + return (uint32_t *)(rpl.dataAddr + cemu_addr - 0x1000'0000); + } +} diff --git a/src/utils/scope_exit.h b/src/utils/scope_exit.h new file mode 100644 index 0000000..c9a357c --- /dev/null +++ b/src/utils/scope_exit.h @@ -0,0 +1,13 @@ +#pragma once +#include + +// https://stackoverflow.com/a/61242721 +template +struct scope_exit +{ + F func; + explicit scope_exit(F&& f): func(std::forward(f)) {} + ~scope_exit() { func(); } +}; + +template scope_exit(F&& frv) -> scope_exit; diff --git a/src/utils/sysconfig.cpp b/src/utils/sysconfig.cpp index cef6d78..97ae4a3 100644 --- a/src/utils/sysconfig.cpp +++ b/src/utils/sysconfig.cpp @@ -1,5 +1,6 @@ /* Copyright 2024 Pretendo Network contributors Copyright 2024 Ash Logan + Copyright 2023 Maschell Copyright 2020-2022 V10lator Copyright 2022 Xpl0itU @@ -19,7 +20,9 @@ #include "sysconfig.h" #include "utils/logger.h" +#include "utils/scope_exit.h" +#include #include #include @@ -28,30 +31,69 @@ nn::swkbd::LanguageType get_system_language() { if (cached_language) return *cached_language; UCHandle handle = UCOpen(); - if (handle >= 0) { - nn::swkbd::LanguageType language; - - UCSysConfig settings __attribute__((__aligned__(0x40))) = { - .name = "cafe.language", - .access = 0, - .dataType = UC_DATATYPE_UNSIGNED_INT, - .error = UC_ERROR_OK, - .dataSize = sizeof(language), - .data = &language, - }; - - UCError err = UCReadSysConfig(handle, 1, &settings); - UCClose(handle); - if (err != UC_ERROR_OK) { - DEBUG_FUNCTION_LINE("Error reading UC: %d!", err); - return nn::swkbd::LanguageType::English; - } else { - DEBUG_FUNCTION_LINE_VERBOSE("System language found: %d", language); - cached_language = language; - return language; - } - } else { + if (handle < 0) { DEBUG_FUNCTION_LINE("Error opening UC: %d", handle); return nn::swkbd::LanguageType::English; } + scope_exit uc_c([&] { UCClose(handle); }); + + nn::swkbd::LanguageType language; + + alignas(0x40) UCSysConfig settings = { + .name = "cafe.language", + .access = 0, + .dataType = UC_DATATYPE_UNSIGNED_INT, + .error = UC_ERROR_OK, + .dataSize = sizeof(language), + .data = &language, + }; + + UCError err = UCReadSysConfig(handle, 1, &settings); + if (err != UC_ERROR_OK) { + DEBUG_FUNCTION_LINE("Error reading UC: %d!", err); + return nn::swkbd::LanguageType::English; + } + + DEBUG_FUNCTION_LINE_VERBOSE("System language found: %d", language); + cached_language = language; + return language; +} + +static std::optional mcp_config; +static std::optional mcp_os_version; +static void get_mcp_config() { + int mcp = MCP_Open(); + scope_exit mcp_c([&] { MCP_Close(mcp); }); + + alignas(0x40) MCPSysProdSettings config {}; + if (MCP_GetSysProdSettings(mcp, &config)) { + DEBUG_FUNCTION_LINE("Could not get MCP system config!"); + return; + } + mcp_config = config; + + //get os version + MCPSystemVersion os_version; + if (MCP_GetSystemVersion(mcp, &os_version)) { + DEBUG_FUNCTION_LINE("Could not get MCP system config!"); + return; + } + mcp_os_version = os_version; + + DEBUG_FUNCTION_LINE_VERBOSE("Running on %d.%d.%d%c; %s%s", + os_version.major, os_version.minor, os_version.patch, os_version.region + config.code_id, config.serial_id + ); +} + +const char * get_console_serial() { + if (!mcp_config) get_mcp_config(); + + return mcp_config ? mcp_config->serial_id : "123456789"; +} + +MCPSystemVersion get_console_os_version() { + if (!mcp_os_version) get_mcp_config(); + + return mcp_os_version.value_or((MCPSystemVersion) { .major = 5, .minor = 5, .patch = 5, .region = 'E' }); } diff --git a/src/utils/sysconfig.h b/src/utils/sysconfig.h index 3bac817..3b3e075 100644 --- a/src/utils/sysconfig.h +++ b/src/utils/sysconfig.h @@ -6,7 +6,10 @@ #define INKAY_SYSCONFIG_H #include +#include nn::swkbd::LanguageType get_system_language(); +const char * get_console_serial(); +MCPSystemVersion get_console_os_version(); #endif //INKAY_SYSCONFIG_H