diff --git a/Dockerfile b/Dockerfile index f746cda..46c49d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libkernel:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO -COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /artifacts $DEVKITPRO +COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20250208 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20240505 /artifacts $DEVKITPRO WORKDIR /app diff --git a/plugin/src/config.cpp b/plugin/src/config.cpp index 0b99ffa..98c2323 100644 --- a/plugin/src/config.cpp +++ b/plugin/src/config.cpp @@ -33,6 +33,7 @@ #include #include #include + #include static config_strings strings; diff --git a/plugin/src/main.cpp b/plugin/src/main.cpp index 4845a38..73968ea 100644 --- a/plugin/src/main.cpp +++ b/plugin/src/main.cpp @@ -62,7 +62,8 @@ DEINITIALIZE_PLUGIN() { } ON_APPLICATION_START() { - + // Tell the module the plugin is running! + Inkay_SetPluginRunning(); } ON_APPLICATION_ENDS() { diff --git a/plugin/src/module.cpp b/plugin/src/module.cpp index 5fd9c2a..c20d980 100644 --- a/plugin/src/module.cpp +++ b/plugin/src/module.cpp @@ -15,17 +15,18 @@ */ #include "module.h" -#include -#include "config.h" #include "Notification.h" #include "utils/logger.h" #include "sysconfig.h" #include "lang.h" +#include + static OSDynLoad_Module module; static void (*moduleInitialize)(bool) = nullptr; static InkayStatus (*moduleGetStatus)() = nullptr; +static void (*moduleSetPluginRunning)() = nullptr; static const char *get_module_not_found_message() { return get_config_strings(get_system_language()).module_not_found.data(); @@ -61,6 +62,7 @@ void Inkay_Finalize() { OSDynLoad_Release(module); moduleInitialize = nullptr; moduleGetStatus = nullptr; + moduleSetPluginRunning = nullptr; } } @@ -76,3 +78,16 @@ InkayStatus Inkay_GetStatus() { return moduleGetStatus(); } + +void Inkay_SetPluginRunning() { + if (!module) { + return; + } + + if (!moduleSetPluginRunning && OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_SetPluginRunning", reinterpret_cast(&moduleSetPluginRunning)) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE("Failed to find \"Inkay_SetPluginRunning\" function"); + return; + } + + moduleSetPluginRunning(); +} diff --git a/plugin/src/module.h b/plugin/src/module.h index b6247a4..ab7836d 100644 --- a/plugin/src/module.h +++ b/plugin/src/module.h @@ -27,3 +27,4 @@ enum class InkayStatus { void Inkay_Initialize(bool apply_patches); void Inkay_Finalize(); InkayStatus Inkay_GetStatus(); +void Inkay_SetPluginRunning(); diff --git a/src/config.cpp b/src/config.cpp index 56c8740..66a78c4 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -19,4 +19,6 @@ bool Config::connect_to_network = false; bool Config::initialized = false; -bool Config::shown_uninitialized_warning = false; +bool Config::shown_warning = false; +bool Config::plugin_is_loaded = false; +bool Config::block_initialize = false; diff --git a/src/config.h b/src/config.h index 96f6775..8455bb0 100644 --- a/src/config.h +++ b/src/config.h @@ -5,16 +5,18 @@ #ifndef INKAY_CONFIG_H #define INKAY_CONFIG_H -#include -#include - class Config { public: + static bool connect_to_network; static bool initialized; - static bool shown_uninitialized_warning; + static bool shown_warning; + + static bool plugin_is_loaded; + + static bool block_initialize; }; #endif //INKAY_CONFIG_H diff --git a/src/main.cpp b/src/main.cpp index 7c4612d..7e6d196 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,22 +16,6 @@ along with this program. If not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "export.h" #include "iosu_url_patches.h" #include "config.h" @@ -39,16 +23,22 @@ #include "patches/olv_urls.h" #include "patches/game_matchmaking.h" -#include -#include +#include + +#include +#include + +#include +#include + #include -#include -#include +#include + +#include +#include #include "ca_pem.h" -#include - #define INKAY_VERSION "v2.6.0" /** @@ -67,7 +57,6 @@ WUMS_DEPENDS_ON(homebrew_notifications); WUMS_USE_WUT_DEVOPTAB(); -#include #include #include #include "patches/account_settings.h" @@ -118,6 +107,10 @@ static const char *get_pretendo_message() { return get_config_strings(get_system_language()).using_pretendo_network.data(); } +static void Inkay_SetPluginRunning() { + Config::plugin_is_loaded = true; +} + static InkayStatus Inkay_GetStatus() { if (!Config::initialized) return InkayStatus::Uninitialized; @@ -131,7 +124,12 @@ static InkayStatus Inkay_GetStatus() { static void Inkay_Initialize(bool apply_patches) { if (Config::initialized) - return; + return; + + if (Config::block_initialize) { + ShowNotification("Cannot load Inkay while the system is running. Please restart the console"); + return; + } // if using pretendo then (try to) apply the ssl patches if (apply_patches) { @@ -177,9 +175,7 @@ WUMS_INITIALIZE() { WHBLogCafeInit(); WHBLogUdpInit(); - auto res = Mocha_InitLibrary(); - - if (res != MOCHA_RESULT_SUCCESS) { + if (const auto res = Mocha_InitLibrary(); res != MOCHA_RESULT_SUCCESS) { DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res); return; } @@ -207,24 +203,36 @@ WUMS_DEINITIALIZE() { WUMS_APPLICATION_STARTS() { DEBUG_FUNCTION_LINE_VERBOSE("Inkay " INKAY_VERSION " starting up...\n"); - // TODO - Add a way to reliably check this. We can't do it here since this path gets triggered before - // the plugin gets initialized. - // - // if (!Config::initialized && !Config::shown_uninitialized_warning) { - // DEBUG_FUNCTION_LINE("Inkay module not initialized"); - // ShowNotification("Inkay module was not initialized. Ensure you have the Inkay plugin loaded"); - // Config::shown_uninitialized_warning = true; - // } + // Reset plugin loaded flag + Config::plugin_is_loaded = false; +} +WUMS_ALL_APPLICATION_STARTS_DONE() { + // we need to do the patches here because otherwise the Config::connect_to_network flag might be set yet setup_olv_libs(); peertopeer_patch(); matchmaking_notify_titleswitch(); hotpatchAccountSettings(); + + if (Config::initialized && !Config::plugin_is_loaded) { + DEBUG_FUNCTION_LINE("Inkay is running but the plugin got unloaded"); + if (!Config::block_initialize) { + ShowNotification("Inkay module is still running. Please restart the console"); + } + Config::shown_warning = true; + } else if (!Config::initialized && !Config::shown_warning) { + DEBUG_FUNCTION_LINE("Inkay module not initialized"); + ShowNotification("Inkay module was not initialized. Ensure you have the Inkay plugin loaded"); + Config::shown_warning = true; + } + if (!Config::initialized) { + Config::block_initialize = true; + } } WUMS_APPLICATION_ENDS() { - } WUMS_EXPORT_FUNCTION(Inkay_Initialize); WUMS_EXPORT_FUNCTION(Inkay_GetStatus); +WUMS_EXPORT_FUNCTION(Inkay_SetPluginRunning); diff --git a/src/patches/account_settings.cpp b/src/patches/account_settings.cpp index 3a0e7bb..cac45ba 100644 --- a/src/patches/account_settings.cpp +++ b/src/patches/account_settings.cpp @@ -24,12 +24,12 @@ #include "inkay_config.h" #include -#include -#include -#include + #include #include -#include + +#include +#include #include "ca_pem.h" // generated at buildtime @@ -163,7 +163,7 @@ bool hotpatchAccountSettings() { } void unpatchAccountSettings() { - for (auto handle: account_patches) { + for (const auto handle: account_patches) { FunctionPatcher_RemoveFunctionPatch(handle); } account_patches.clear(); diff --git a/src/patches/game_peertopeer.cpp b/src/patches/game_peertopeer.cpp index 2651156..d986f36 100644 --- a/src/patches/game_peertopeer.cpp +++ b/src/patches/game_peertopeer.cpp @@ -28,18 +28,23 @@ using namespace std::string_view_literals; static struct { std::array tid; + uint16_t version; 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 }, +} generic_patch_games[] = { + { + // MARIO KART 8 + {0x00050000'1010ec00, 0x00050000'1010ed00, 0x00050000'1010eb00}, + 81, 0x101a9a52, 0x101a9a54, "Turbo.rpx"sv, }, - { // Splatoon - { 0x00050000'10176900, 0x00050000'10176a00, 0x00050000'10162b00 }, + { + // Splatoon + {0x00050000'10176900, 0x00050000'10176a00, 0x00050000'10162b00}, + 288, 0x101e8952, 0x101e8954, "Gambit.rpx"sv, @@ -48,8 +53,16 @@ static struct { static void 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; + } else { + title_version = *version_opt; + DEBUG_FUNCTION_LINE("Title version detected: %d", title_version); + } - for (const auto& patch : generic_patch_games) { + 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); @@ -58,6 +71,12 @@ static void generic_peertopeer_patch() { return; } + if (title_version != patch.version) { + DEBUG_FUNCTION_LINE("Unexpected title version. Expected %d but got %d (%s)", patch.version, title_version, + patch.rpx.data()); + continue; + } + auto port = get_console_peertopeer_port(); DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, game->textAddr); @@ -76,6 +95,10 @@ static void minecraft_peertopeer_patch() { DEBUG_FUNCTION_LINE("Couldn't find minecraft rpx!"); return; } + if (const auto version_opt = get_current_title_version(); !version_opt || *version_opt != 688) { + DEBUG_FUNCTION_LINE("Wrong mincecraft version detected"); + return; + } auto port = get_console_peertopeer_port(); DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, minecraft->textAddr); diff --git a/src/utils/rpl_info.cpp b/src/utils/rpl_info.cpp index 2148e1e..1bfe9fb 100644 --- a/src/utils/rpl_info.cpp +++ b/src/utils/rpl_info.cpp @@ -16,6 +16,10 @@ #include #include #include +#include +#include + +#include "logger.h" // if we get more than like.. two callsites for this, it should really be refactored out rather than doing a fresh // search every time @@ -37,3 +41,24 @@ std::optional search_for_rpl(std::string_view name) { return *rpl; } + +std::optional get_current_title_version() { + const auto mcpHandle = MCP_Open(); + MCPTitleListType titleInfo; + int32_t res = -1; + const uint64_t curTitleId = OSGetTitleID(); + if ((curTitleId & 0x0000000F00000000) == 0) { + res = MCP_GetTitleInfo(mcpHandle, curTitleId | 0x0000000E00000000, &titleInfo); + } + if (res != 0) { + res = MCP_GetTitleInfo(mcpHandle, curTitleId, &titleInfo); + } + MCP_Close(mcpHandle); + if (res != 0) { + DEBUG_FUNCTION_LINE("Failed to get title version of %016llX.", curTitleId); + return {}; + } + MCP_Close(mcpHandle); + const auto tmp_result = titleInfo.titleVersion; // make the compiler happy because we access a packed struct + return tmp_result; +} diff --git a/src/utils/rpl_info.h b/src/utils/rpl_info.h index 15f59c7..c290fe8 100644 --- a/src/utils/rpl_info.h +++ b/src/utils/rpl_info.h @@ -26,3 +26,5 @@ constexpr void *rpl_addr(OSDynLoad_NotifyData rpl, uint32_t cemu_addr) { return (void *)(rpl.dataAddr + cemu_addr - 0x1000'0000); } } + +std::optional get_current_title_version(); \ No newline at end of file