feat(p2p): Allow overriding the p2p udp port for Minecraft

the idea is that you might port forward this in your router and give yourself NAT type A for free
This commit is contained in:
Ash Logan 2024-11-17 23:34:46 +11:00
parent a1375ca608
commit 22bebff027
10 changed files with 258 additions and 40 deletions

View File

@ -68,6 +68,7 @@ WUPS_USE_WUT_DEVOPTAB();
#include <mocha/mocha.h>
#include <function_patcher/function_patching.h>
#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();
}

View File

@ -0,0 +1,73 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
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 <coreinit/title.h>
#include <coreinit/dynload.h>
#include "game_peertopeer.h"
#include "utils/sysconfig.h"
#include "utils/logger.h"
#include "utils/rpl_info.h"
#include "utils/replace_mem.h"
#include <optional>
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<OSDynLoad_NotifyData> 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");
}
}

View File

@ -0,0 +1,16 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
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();

View File

@ -22,6 +22,7 @@
#include <kernel/kernel.h>
#include <coreinit/memorymap.h>
#include <algorithm>
#include <coreinit/cache.h>
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<const replacement> 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;
}

View File

@ -27,3 +27,5 @@ struct replacement {
};
void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> replacements);
bool replace_instruction(uint32_t *inst, uint32_t orignal_value, uint32_t new_value);

39
src/utils/rpl_info.cpp Normal file
View File

@ -0,0 +1,39 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
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 <vector>
#include <algorithm>
#include <string_view>
// 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<OSDynLoad_NotifyData> 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<OSDynLoad_NotifyData> 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;
}

28
src/utils/rpl_info.h Normal file
View File

@ -0,0 +1,28 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
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 <optional>
#include <string_view>
#include <coreinit/dynload.h>
std::optional<OSDynLoad_NotifyData> 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);
}
}

13
src/utils/scope_exit.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <functional>
// https://stackoverflow.com/a/61242721
template<typename F>
struct scope_exit
{
F func;
explicit scope_exit(F&& f): func(std::forward<F>(f)) {}
~scope_exit() { func(); }
};
template<typename F> scope_exit(F&& frv) -> scope_exit<F>;

View File

@ -1,5 +1,6 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Copyright 2023 Maschell
Copyright 2020-2022 V10lator <v10lator@myway.de>
Copyright 2022 Xpl0itU <DaThinkingChair@protonmail.com>
@ -19,7 +20,9 @@
#include "sysconfig.h"
#include "utils/logger.h"
#include "utils/scope_exit.h"
#include <coreinit/mcp.h>
#include <coreinit/userconfig.h>
#include <optional>
@ -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<MCPSysProdSettings> mcp_config;
static std::optional<MCPSystemVersion> 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' });
}

View File

@ -6,7 +6,10 @@
#define INKAY_SYSCONFIG_H
#include <nn/swkbd.h>
#include <coreinit/mcp.h>
nn::swkbd::LanguageType get_system_language();
const char * get_console_serial();
MCPSystemVersion get_console_os_version();
#endif //INKAY_SYSCONFIG_H