From f4aeee80341a5272af303fc21a26a24ed63a751e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sat, 19 Oct 2024 19:10:08 +0100 Subject: [PATCH] feat!: Split Inkay into a module and a plugin This is necessary for a future Aroma feature. This also makes more clear the immutability of the patches that Pretendo requires, since they cannot be reverted easily. All patching code is now managed inside the module, which the plugin triggers by calling an exported function from it. The WWP reset code remains within the plugin as it isn't strictly tied to the patches. --- .github/workflows/ci.yml | 6 +- .gitignore | 6 +- Dockerfile | 3 +- Makefile | 45 +++--- plugin/Makefile | 148 ++++++++++++++++++ plugin/src/Notification.cpp | 31 ++++ plugin/src/Notification.h | 3 + plugin/src/config.cpp | 258 +++++++++++++++++++++++++++++++ plugin/src/config.h | 42 +++++ plugin/src/main.cpp | 92 +++++++++++ plugin/src/module.cpp | 38 +++++ plugin/src/module.h | 19 +++ plugin/src/utils/logger.h | 36 +++++ plugin/src/utils/sysconfig.cpp | 57 +++++++ plugin/src/utils/sysconfig.h | 12 ++ plugin/src/wut_extra.h | 41 +++++ src/config.cpp | 209 +------------------------ src/config.h | 11 -- src/main.cpp | 78 ++++++---- src/patches/account_settings.cpp | 27 +++- src/patches/account_settings.h | 3 +- src/patches/dns_hooks.cpp | 35 ++++- src/patches/dns_hooks.h | 20 +++ src/patches/eshop_applet.cpp | 29 +++- src/patches/eshop_applet.h | 20 +++ src/patches/game_matchmaking.cpp | 2 +- src/patches/olv_applet.cpp | 29 +++- src/patches/olv_applet.h | 20 +++ src/patches/olv_urls.cpp | 2 +- src/utils/logger.h | 6 +- 30 files changed, 1034 insertions(+), 294 deletions(-) create mode 100644 plugin/Makefile create mode 100644 plugin/src/Notification.cpp create mode 100644 plugin/src/Notification.h create mode 100644 plugin/src/config.cpp create mode 100644 plugin/src/config.h create mode 100644 plugin/src/main.cpp create mode 100644 plugin/src/module.cpp create mode 100644 plugin/src/module.h create mode 100644 plugin/src/utils/logger.h create mode 100644 plugin/src/utils/sysconfig.cpp create mode 100644 plugin/src/utils/sysconfig.h create mode 100644 plugin/src/wut_extra.h create mode 100644 src/patches/dns_hooks.h create mode 100644 src/patches/eshop_applet.h create mode 100644 src/patches/olv_applet.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adfbbeb..60c8bf2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,13 +6,13 @@ jobs: build-inkay: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: build toolchain container run: docker build . -t builder - uses: ammaraskar/gcc-problem-matcher@master - name: build Inkay run: docker run --rm -v ${PWD}:/app -w /app builder - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 with: name: inkay - path: "*.wps" + path: dist/ diff --git a/.gitignore b/.gitignore index 982b963..3f9ac48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ +.cache/ .vscode/ .idea/ /build +/plugin/build +/dist *.elf +*.wms *.wps certs/ -*.lst \ No newline at end of file +*.lst diff --git a/Dockerfile b/Dockerfile index 4147038..0f51ffa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,8 @@ 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/wiiupluginsystem:20240505 /artifacts $DEVKITPRO WORKDIR /app -CMD make -f Makefile -j$(nproc) \ No newline at end of file +CMD make -f Makefile -j$(nproc) diff --git a/Makefile b/Makefile index 2d63adc..c645f43 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,10 @@ endif TOPDIR ?= $(CURDIR) -include $(DEVKITPRO)/wups/share/wups_rules +include $(DEVKITPRO)/wums/share/wums_rules -WUT_ROOT := $(DEVKITPRO)/wut WUMS_ROOT := $(DEVKITPRO)/wums +WUT_ROOT := $(DEVKITPRO)/wut #------------------------------------------------------------------------------- # TARGET is the name of the output # BUILD is the directory where object files & intermediate files will be placed @@ -24,34 +24,32 @@ BUILD := build SOURCES := src src/patches src/utils src/ext/inih DATA := data INCLUDES := src src/ext/inih -#DEBUG := 1 #------------------------------------------------------------------------------- # options for code generation #------------------------------------------------------------------------------- -CFLAGS := -Wall -O2 -ffunction-sections -fdata-sections \ +CFLAGS := -Wall -Wextra -O2 -ffunction-sections\ $(MACHDEP) -CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ - -ifeq ($(DEBUG),1) - CFLAGS += -DDEBUG -g -endif +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ CXXFLAGS := $(CFLAGS) -std=c++20 ASFLAGS := -g $(ARCH) -LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -Wl,-gc-sections +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUMSSPECS) -LDFLAGS += -T$(WUMS_ROOT)/share/libkernel.ld $(WUPSSPECS) +ifeq ($(DEBUG),1) +CXXFLAGS += -DDEBUG -g +CFLAGS += -DDEBUG -g +endif -LIBS := -lwups -lmocha -lkernel -lwut -lnotifications -lfunctionpatcher +LIBS := -lwums -lmocha -lkernel -lwut -lfunctionpatcher -lnotifications #------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level # containing include and lib #------------------------------------------------------------------------------- -LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) $(WUT_ROOT)/usr #------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -103,14 +101,20 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) #------------------------------------------------------------------------------- all: $(BUILD) + $(BUILD): - @$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD)) + @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + @$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile + mkdir -p dist/ + cp *.wms dist/ + cp plugin/*.wps dist/ #------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf + @rm -fr dist $(BUILD) $(TARGET).wms $(TARGET).elf + @$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile clean #------------------------------------------------------------------------------- else @@ -121,10 +125,10 @@ DEPENDS := $(OFILES:.o=.d) #------------------------------------------------------------------------------- # main targets #------------------------------------------------------------------------------- -all : $(OUTPUT).wps +all : $(OUTPUT).wms -$(OUTPUT).wps : $(OUTPUT).elf -$(OUTPUT).elf : $(OFILES) +$(OUTPUT).wms : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) $(OFILES_SRC) : $(HFILES_BIN) @@ -141,6 +145,11 @@ $(OFILES_SRC) : $(HFILES_BIN) @echo $(notdir $<) @$(bin2o) +#--------------------------------------------------------------------------------- +%.o: %.s + @echo $(notdir $<) + @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER) + -include $(DEPENDS) #------------------------------------------------------------------------------- diff --git a/plugin/Makefile b/plugin/Makefile new file mode 100644 index 0000000..125fb0c --- /dev/null +++ b/plugin/Makefile @@ -0,0 +1,148 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wups/share/wups_rules + +WUT_ROOT := $(DEVKITPRO)/wut +WUMS_ROOT := $(DEVKITPRO)/wums +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := Inkay-pretendo +BUILD := build +SOURCES := src src/utils +DATA := data +INCLUDES := src ../src/lang +#DEBUG := 1 + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -Wall -O2 -ffunction-sections -fdata-sections \ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ + +ifeq ($(DEBUG),1) + CFLAGS += -DDEBUG -g +endif + +CXXFLAGS := $(CFLAGS) -std=c++20 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -Wl,-gc-sections + +LDFLAGS += $(WUPSSPECS) + +LIBS := -lwups -lwut -lnotifications + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD)) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wps + +$(OUTPUT).wps : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +%.pem.o %_pem.h : %.pem +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/plugin/src/Notification.cpp b/plugin/src/Notification.cpp new file mode 100644 index 0000000..cc6177e --- /dev/null +++ b/plugin/src/Notification.cpp @@ -0,0 +1,31 @@ +/* Copyright 2024 Pretendo Network contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "Notification.h" +#include +#include + +void ShowNotification(const char* notification) { + auto err1 = NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, + NOTIFICATION_MODULE_DEFAULT_OPTION_KEEP_UNTIL_SHOWN, true); + auto err2 = NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, + NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, + 15.0f); + + if (err1 != NOTIFICATION_MODULE_RESULT_SUCCESS || err2 != NOTIFICATION_MODULE_RESULT_SUCCESS) return; + + NotificationModule_AddInfoNotification(notification); +} diff --git a/plugin/src/Notification.h b/plugin/src/Notification.h new file mode 100644 index 0000000..ebdc7a3 --- /dev/null +++ b/plugin/src/Notification.h @@ -0,0 +1,3 @@ +#pragma once + +void ShowNotification(const char* notification); diff --git a/plugin/src/config.cpp b/plugin/src/config.cpp new file mode 100644 index 0000000..a0fc590 --- /dev/null +++ b/plugin/src/config.cpp @@ -0,0 +1,258 @@ +/* Copyright 2022 Pretendo Network contributors + Copyright 2022 Ash Logan + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "config.h" + +#include "wut_extra.h" +#include "utils/logger.h" +#include "utils/sysconfig.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +bool Config::connect_to_network = true; +bool Config::need_relaunch = false; +bool Config::unregister_task_item_pressed = false; +bool Config::is_wiiu_menu = false; + +static config_strings strings; + +config_strings get_config_strings(nn::swkbd::LanguageType language) { + switch (language) { + case nn::swkbd::LanguageType::English: + default: return { +#include "en_US.lang" + }; + case nn::swkbd::LanguageType::Spanish: return { +#include "es_ES.lang" + }; + case nn::swkbd::LanguageType::French: return { +#include "fr_FR.lang" + }; + case nn::swkbd::LanguageType::Italian: return { +#include "it_IT.lang" + }; + case nn::swkbd::LanguageType::German: return { +#include "de_DE.lang" + }; + case nn::swkbd::LanguageType::SimplifiedChinese: return { +#include "zh_CN.lang" + }; + case nn::swkbd::LanguageType::TraditionalChinese: return { +#include "zh_Hant.lang" + }; + case nn::swkbd::LanguageType::Portuguese: return { +#include "pt_BR.lang" + }; + case nn::swkbd::LanguageType::Japanese: return { +#include "ja_JP.lang" + }; + case nn::swkbd::LanguageType::Dutch: return { +#include "nl_NL.lang" + }; + case nn::swkbd::LanguageType::Russian: return { +#include "ru_RU.lang" + }; + } +} + +static WUPSConfigAPICallbackStatus report_error(WUPSConfigAPIStatus err) { + DEBUG_FUNCTION_LINE_VERBOSE("WUPS config error: %s", WUPSConfigAPI_GetStatusStr(err)); + return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; +} + +static void report_storage_error(WUPSStorageError err) { + DEBUG_FUNCTION_LINE_VERBOSE("WUPS storage error: %s", WUPSStorageAPI_GetStatusStr(err)); +} + +static void connect_to_network_changed(ConfigItemBoolean* item, bool new_value) { + DEBUG_FUNCTION_LINE_VERBOSE("connect_to_network changed to: %d", new_value); + if (new_value != Config::connect_to_network) { + Config::need_relaunch = true; + } + Config::connect_to_network = new_value; + + WUPSStorageError res; + res = WUPSStorageAPI::Store("connect_to_network", Config::connect_to_network); + if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); +} + +static void unregister_task_item_on_input_cb(void *context, WUPSConfigSimplePadData input) { + if (!Config::unregister_task_item_pressed && Config::is_wiiu_menu && ((input.buttons_d & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A)) { + + nn::act::Initialize(); + Initialize__Q2_2nn4bossFv(); + + for (uint8_t i = 1; i <= nn::act::GetNumOfAccounts(); i++) + { + if (nn::act::IsSlotOccupied(i) && nn::act::IsNetworkAccountEx(i)) + { + nn::boss::Task task{}; + nn::act::PersistentId persistentId = nn::act::GetPersistentIdEx(i); + + __ct__Q3_2nn4boss4TaskFv(&task); + Initialize__Q3_2nn4boss4TaskFPCcUi(&task, "oltopic", persistentId); + + // bypasses compiler warning about unused variable + #ifdef DEBUG + uint32_t res = Unregister__Q3_2nn4boss4TaskFv(&task); + DEBUG_FUNCTION_LINE_VERBOSE("Unregistered oltopic for: SlotNo %d | Persistent ID %08x -> 0x%08x", i, persistentId, res); + #else + Unregister__Q3_2nn4boss4TaskFv(&task); + #endif + } + } + + Finalize__Q2_2nn4bossFv(); + nn::act::Finalize(); + + Config::unregister_task_item_pressed = !Config::unregister_task_item_pressed; + Config::need_relaunch = true; + } +} + +static int32_t unregister_task_item_get_display_value(void *context, char *out_buf, int32_t out_size) { + auto string = strings.need_menu_action; + if (Config::is_wiiu_menu) { + if (Config::unregister_task_item_pressed) { + string = strings.restart_to_apply_action; + } else { + string = strings.press_a_action; + } + } + + if ((int)string.length() > out_size - 1) return -1; + string.copy(out_buf, string.length()); + out_buf[string.length()] = '\0'; + + return 0; +} + +static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) { + WUPSConfigAPIStatus err; + bool res; + + uint64_t current_title_id = OSGetTitleID(); + uint64_t wiiu_menu_tid = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_WII_U_MENU); + Config::is_wiiu_menu = (current_title_id == wiiu_menu_tid); + + // get translation strings + strings = get_config_strings(get_system_language()); + + // create root config category + WUPSConfigCategory root = WUPSConfigCategory(rootHandle); + + auto network_cat = WUPSConfigCategory::Create(strings.network_category, err); + if (!network_cat) return report_error(err); + + // config id display name default current value changed callback + auto connect_item = WUPSConfigItemBoolean::Create("connect_to_network", strings.connect_to_network_setting, true, Config::connect_to_network, &connect_to_network_changed, err); + if (!connect_item) return report_error(err); + + res = network_cat->add(std::move(*connect_item), err); + if (!res) return report_error(err); + + res = root.add(std::move(*network_cat), err); + if (!res) return report_error(err); + + auto other_cat = WUPSConfigCategory::Create(strings.other_category, err); + if (!other_cat) return report_error(err); + + WUPSConfigAPIItemCallbacksV2 unregisterTasksItemCallbacks = { + .getCurrentValueDisplay = unregister_task_item_get_display_value, + .getCurrentValueSelectedDisplay = unregister_task_item_get_display_value, + .onSelected = nullptr, + .restoreDefault = nullptr, + .isMovementAllowed = nullptr, + .onCloseCallback = nullptr, + .onInput = unregister_task_item_on_input_cb, + .onInputEx = nullptr, + .onDelete = nullptr + }; + + WUPSConfigAPIItemOptionsV2 unregisterTasksItemOptions = { + .displayName = strings.reset_wwp_setting.data(), + .context = nullptr, + .callbacks = unregisterTasksItemCallbacks, + }; + + WUPSConfigItemHandle unregisterTasksItem; + err = WUPSConfigAPI_Item_Create(unregisterTasksItemOptions, &unregisterTasksItem); + if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err); + + err = WUPSConfigAPI_Category_AddItem(other_cat->getHandle(), unregisterTasksItem); + if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err); + + res = root.add(std::move(*other_cat), err); + if (!res) return report_error(err); + + return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS; +} + +static void ConfigMenuClosedCallback() { + // Save all changes + WUPSStorageError res; + res = WUPSStorageAPI::SaveStorage(); + if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); + + if (Config::need_relaunch) { + // Need to reload the console so the patches reset + OSForceFullRelaunch(); + SYSLaunchMenu(); + Config::need_relaunch = false; + } +} + +void Config::Init() { + WUPSConfigAPIStatus cres; + + // Init the config api + WUPSConfigAPIOptionsV1 configOptions = { .name = "Inkay" }; + cres = WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback); + if (cres != WUPSCONFIG_API_RESULT_SUCCESS) return (void)report_error(cres); + + WUPSStorageError res; + // Try to get value from storage + res = WUPSStorageAPI::Get("connect_to_network", Config::connect_to_network); + if (res == WUPS_STORAGE_ERROR_NOT_FOUND) { + DEBUG_FUNCTION_LINE("Connect to network value not found, attempting to migrate/create"); + + bool skipPatches = false; + if (WUPSStorageAPI::Get("skipPatches", skipPatches) == WUPS_STORAGE_ERROR_SUCCESS) { + // Migrate old config value + Config::connect_to_network = !skipPatches; + WUPSStorageAPI::DeleteItem("skipPatches"); + } + + // Add the value to the storage if it's missing. + res = WUPSStorageAPI::Store("connect_to_network", connect_to_network); + if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); + } + else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); + + // Save storage + res = WUPSStorageAPI::SaveStorage(); + if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); +} diff --git a/plugin/src/config.h b/plugin/src/config.h new file mode 100644 index 0000000..2fa9812 --- /dev/null +++ b/plugin/src/config.h @@ -0,0 +1,42 @@ +// +// Created by ash on 10/12/22. +// + +#ifndef INKAY_CONFIG_H +#define INKAY_CONFIG_H + +#include +#include + +class Config { +public: + static void Init(); + + // wups config items + static bool connect_to_network; + + // private stuff + static bool need_relaunch; + + // private stuff + static bool is_wiiu_menu; + + static bool unregister_task_item_pressed; +}; + +struct config_strings { + const char *plugin_name; + std::string_view network_category; + std::string_view connect_to_network_setting; + std::string_view other_category; + std::string_view reset_wwp_setting; + std::string_view press_a_action; + std::string_view restart_to_apply_action; + std::string_view need_menu_action; + std::string_view using_nintendo_network; + std::string_view using_pretendo_network; +}; + +config_strings get_config_strings(nn::swkbd::LanguageType language); + +#endif //INKAY_CONFIG_H diff --git a/plugin/src/main.cpp b/plugin/src/main.cpp new file mode 100644 index 0000000..5cb5b19 --- /dev/null +++ b/plugin/src/main.cpp @@ -0,0 +1,92 @@ +/* Copyright 2022 Pretendo Network contributors + Copyright 2022 Ash Logan + Copyright 2019 Maschell + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "module.h" +#include "Notification.h" + +#include +#include +#include +#include +#include + +#include + +#define INKAY_VERSION "v2.6.0" + +/** + Mandatory plugin information. + If not set correctly, the loader will refuse to use the plugin. +**/ +WUPS_PLUGIN_NAME("Inkay"); +WUPS_PLUGIN_DESCRIPTION("Pretendo Network Patcher"); +WUPS_PLUGIN_VERSION(INKAY_VERSION); +WUPS_PLUGIN_AUTHOR("Pretendo contributors"); +WUPS_PLUGIN_LICENSE("GPLv3"); + +WUPS_USE_STORAGE("inkay"); + +WUPS_USE_WUT_DEVOPTAB(); + +#include "utils/sysconfig.h" + +INITIALIZE_PLUGIN() { + WHBLogCafeInit(); + WHBLogUdpInit(); + + Config::Init(); + + if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed"); + } + + // if using pretendo then (try to) apply the ssl patches + Inkay_Initialize(Config::connect_to_network); +} + +DEINITIALIZE_PLUGIN() { + NotificationModule_DeInitLibrary(); + + WHBLogCafeDeinit(); + WHBLogUdpDeinit(); +} + +ON_APPLICATION_START() { + +} + +ON_APPLICATION_ENDS() { + +} diff --git a/plugin/src/module.cpp b/plugin/src/module.cpp new file mode 100644 index 0000000..6a201fa --- /dev/null +++ b/plugin/src/module.cpp @@ -0,0 +1,38 @@ +/* Copyright 2024 Pretendo Network contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +#include "utils/logger.h" + +void Inkay_Initialize(bool apply_patches) { + OSDynLoad_Module module; + void (*moduleInitialize)(bool) = nullptr; + + if (OSDynLoad_Acquire("inkay", &module) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE("Failed to acquire module"); + return; + } + + if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_Initialize", reinterpret_cast(&moduleInitialize)) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE("Failed to find initialization function"); + return; + } + + moduleInitialize(apply_patches); + + OSDynLoad_Release(module); +} diff --git a/plugin/src/module.h b/plugin/src/module.h new file mode 100644 index 0000000..3da8b0a --- /dev/null +++ b/plugin/src/module.h @@ -0,0 +1,19 @@ +/* Copyright 2024 Pretendo Network contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +void Inkay_Initialize(bool apply_patches); diff --git a/plugin/src/utils/logger.h b/plugin/src/utils/logger.h new file mode 100644 index 0000000..f7278b6 --- /dev/null +++ b/plugin/src/utils/logger.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ + OSFatal_printf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + WHBLogPrintf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0); + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ + WHBLogWritef("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0); + +#ifdef DEBUG +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) DEBUG_FUNCTION_LINE(FMT, ##ARGS) +#else +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/plugin/src/utils/sysconfig.cpp b/plugin/src/utils/sysconfig.cpp new file mode 100644 index 0000000..cef6d78 --- /dev/null +++ b/plugin/src/utils/sysconfig.cpp @@ -0,0 +1,57 @@ +/* Copyright 2024 Pretendo Network contributors + Copyright 2024 Ash Logan + Copyright 2020-2022 V10lator + Copyright 2022 Xpl0itU + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "sysconfig.h" +#include "utils/logger.h" + +#include +#include + +nn::swkbd::LanguageType get_system_language() { + static std::optional cached_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 { + DEBUG_FUNCTION_LINE("Error opening UC: %d", handle); + return nn::swkbd::LanguageType::English; + } +} diff --git a/plugin/src/utils/sysconfig.h b/plugin/src/utils/sysconfig.h new file mode 100644 index 0000000..3bac817 --- /dev/null +++ b/plugin/src/utils/sysconfig.h @@ -0,0 +1,12 @@ +// +// Created by ash on 9/04/24. +// + +#ifndef INKAY_SYSCONFIG_H +#define INKAY_SYSCONFIG_H + +#include + +nn::swkbd::LanguageType get_system_language(); + +#endif //INKAY_SYSCONFIG_H diff --git a/plugin/src/wut_extra.h b/plugin/src/wut_extra.h new file mode 100644 index 0000000..b6a65c3 --- /dev/null +++ b/plugin/src/wut_extra.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +namespace nn::boss { + class Task { + public: + nn::act::PersistentId persistentId; + uint32_t unk1; + char task_name[8]; + uint64_t title_id; + uint32_t unk2; + uint32_t unk3; + }; + + static_assert(sizeof(Task) == 32, "nn::boss::Task must be 32 bytes."); +} + +extern "C" uint32_t Initialize__Q2_2nn4bossFv(); +extern "C" uint32_t Finalize__Q2_2nn4bossFv(); + +extern "C" void __ct__Q3_2nn4boss4TaskFv(nn::boss::Task *task); +extern "C" uint32_t Initialize__Q3_2nn4boss4TaskFPCcUi(nn::boss::Task *task, char const *, unsigned int); +extern "C" uint32_t Unregister__Q3_2nn4boss4TaskFv(nn::boss::Task *task); +extern "C" void __dt__Q3_2nn4boss4TaskFv(nn::boss::Task *task); +extern "C" uint32_t StartScheduling__Q3_2nn4boss4TaskFb(nn::boss::Task *task, bool queueTaskOnCall); +extern "C" uint32_t GetState__Q3_2nn4boss4TaskCFPUi(nn::boss::Task *task, uint32_t *outExecCount); +extern "C" uint32_t Run__Q3_2nn4boss4TaskFb(nn::boss::Task *task, bool unk); +extern "C" uint32_t UpdateIntervalSec__Q3_2nn4boss4TaskFUi(nn::boss::Task *task, uint32_t seconds); +extern "C" bool IsRegistered__Q3_2nn4boss4TaskCFv(nn::boss::Task *task); + +#ifdef __cplusplus +} +#endif diff --git a/src/config.cpp b/src/config.cpp index d7e6ffc..4923515 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -17,29 +17,7 @@ #include "config.h" -#include "wut_extra.h" -#include "utils/logger.h" -#include "utils/sysconfig.h" -#include "patches/game_peertopeer.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -bool Config::connect_to_network = true; -bool Config::need_relaunch = false; -bool Config::unregister_task_item_pressed = false; -bool Config::is_wiiu_menu = false; +bool Config::connect_to_network = false; static config_strings strings; @@ -81,188 +59,3 @@ config_strings get_config_strings(nn::swkbd::LanguageType language) { }; } } - -static WUPSConfigAPICallbackStatus report_error(WUPSConfigAPIStatus err) { - DEBUG_FUNCTION_LINE_VERBOSE("WUPS config error: %s", WUPSConfigAPI_GetStatusStr(err)); - return WUPSCONFIG_API_CALLBACK_RESULT_ERROR; -} - -static void report_storage_error(WUPSStorageError err) { - DEBUG_FUNCTION_LINE_VERBOSE("WUPS storage error: %s", WUPSStorageAPI_GetStatusStr(err)); -} - -static void connect_to_network_changed(ConfigItemBoolean* item, bool new_value) { - DEBUG_FUNCTION_LINE_VERBOSE("connect_to_network changed to: %d", new_value); - if (new_value != Config::connect_to_network) { - Config::need_relaunch = true; - } - Config::connect_to_network = new_value; - - WUPSStorageError res; - res = WUPSStorageAPI::Store("connect_to_network", Config::connect_to_network); - if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); -} - -static void unregister_task_item_on_input_cb(void *context, WUPSConfigSimplePadData input) { - if (!Config::unregister_task_item_pressed && Config::is_wiiu_menu && ((input.buttons_d & WUPS_CONFIG_BUTTON_A) == WUPS_CONFIG_BUTTON_A)) { - - nn::act::Initialize(); - Initialize__Q2_2nn4bossFv(); - - for (uint8_t i = 1; i <= nn::act::GetNumOfAccounts(); i++) - { - if (nn::act::IsSlotOccupied(i) && nn::act::IsNetworkAccountEx(i)) - { - nn::boss::Task task{}; - nn::act::PersistentId persistentId = nn::act::GetPersistentIdEx(i); - - __ct__Q3_2nn4boss4TaskFv(&task); - Initialize__Q3_2nn4boss4TaskFPCcUi(&task, "oltopic", persistentId); - - // bypasses compiler warning about unused variable - #ifdef DEBUG - uint32_t res = Unregister__Q3_2nn4boss4TaskFv(&task); - DEBUG_FUNCTION_LINE_VERBOSE("Unregistered oltopic for: SlotNo %d | Persistent ID %08x -> 0x%08x", i, persistentId, res); - #else - Unregister__Q3_2nn4boss4TaskFv(&task); - #endif - } - } - - Finalize__Q2_2nn4bossFv(); - nn::act::Finalize(); - - Config::unregister_task_item_pressed = !Config::unregister_task_item_pressed; - Config::need_relaunch = true; - } -} - -static int32_t unregister_task_item_get_display_value(void *context, char *out_buf, int32_t out_size) { - auto string = strings.need_menu_action; - if (Config::is_wiiu_menu) { - if (Config::unregister_task_item_pressed) { - string = strings.restart_to_apply_action; - } else { - string = strings.press_a_action; - } - } - - if ((int)string.length() > out_size - 1) return -1; - string.copy(out_buf, string.length()); - out_buf[string.length()] = '\0'; - - return 0; -} - -static WUPSConfigAPICallbackStatus ConfigMenuOpenedCallback(WUPSConfigCategoryHandle rootHandle) { - WUPSConfigAPIStatus err; - bool res; - - uint64_t current_title_id = OSGetTitleID(); - uint64_t wiiu_menu_tid = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_WII_U_MENU); - Config::is_wiiu_menu = (current_title_id == wiiu_menu_tid); - - // get translation strings - strings = get_config_strings(get_system_language()); - - // create root config category - WUPSConfigCategory root = WUPSConfigCategory(rootHandle); - - auto network_cat = WUPSConfigCategory::Create(strings.network_category, err); - if (!network_cat) return report_error(err); - - // config id display name default current value changed callback - auto connect_item = WUPSConfigItemBoolean::Create("connect_to_network", strings.connect_to_network_setting, true, Config::connect_to_network, &connect_to_network_changed, err); - if (!connect_item) return report_error(err); - - res = network_cat->add(std::move(*connect_item), err); - if (!res) return report_error(err); - - { - std::string multiplayer_port_text = std::vformat(strings.multiplayer_port_display, std::make_format_args(peertopeer_port())); - res = network_cat->add(WUPSConfigItemStub::Create(multiplayer_port_text), err); - if (!res) return report_error(err); - } - - res = root.add(std::move(*network_cat), err); - if (!res) return report_error(err); - - auto other_cat = WUPSConfigCategory::Create(strings.other_category, err); - if (!other_cat) return report_error(err); - - WUPSConfigAPIItemCallbacksV2 unregisterTasksItemCallbacks = { - .getCurrentValueDisplay = unregister_task_item_get_display_value, - .getCurrentValueSelectedDisplay = unregister_task_item_get_display_value, - .onSelected = nullptr, - .restoreDefault = nullptr, - .isMovementAllowed = nullptr, - .onCloseCallback = nullptr, - .onInput = unregister_task_item_on_input_cb, - .onInputEx = nullptr, - .onDelete = nullptr - }; - - WUPSConfigAPIItemOptionsV2 unregisterTasksItemOptions = { - .displayName = strings.reset_wwp_setting.data(), - .context = nullptr, - .callbacks = unregisterTasksItemCallbacks, - }; - - WUPSConfigItemHandle unregisterTasksItem; - err = WUPSConfigAPI_Item_Create(unregisterTasksItemOptions, &unregisterTasksItem); - if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err); - - err = WUPSConfigAPI_Category_AddItem(other_cat->getHandle(), unregisterTasksItem); - if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err); - - res = root.add(std::move(*other_cat), err); - if (!res) return report_error(err); - - return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS; -} - -static void ConfigMenuClosedCallback() { - // Save all changes - WUPSStorageError res; - res = WUPSStorageAPI::SaveStorage(); - if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); - - if (Config::need_relaunch) { - // Need to reload the console so the patches reset - OSForceFullRelaunch(); - SYSLaunchMenu(); - Config::need_relaunch = false; - } -} - -void Config::Init() { - WUPSConfigAPIStatus cres; - - // Init the config api - WUPSConfigAPIOptionsV1 configOptions = { .name = "Inkay" }; - cres = WUPSConfigAPI_Init(configOptions, ConfigMenuOpenedCallback, ConfigMenuClosedCallback); - if (cres != WUPSCONFIG_API_RESULT_SUCCESS) return (void)report_error(cres); - - WUPSStorageError res; - // Try to get value from storage - res = WUPSStorageAPI::Get("connect_to_network", Config::connect_to_network); - if (res == WUPS_STORAGE_ERROR_NOT_FOUND) { - DEBUG_FUNCTION_LINE("Connect to network value not found, attempting to migrate/create"); - - bool skipPatches = false; - if (WUPSStorageAPI::Get("skipPatches", skipPatches) == WUPS_STORAGE_ERROR_SUCCESS) { - // Migrate old config value - Config::connect_to_network = !skipPatches; - WUPSStorageAPI::DeleteItem("skipPatches"); - } - - // Add the value to the storage if it's missing. - res = WUPSStorageAPI::Store("connect_to_network", connect_to_network); - if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); - } - else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); - - // Save storage - res = WUPSStorageAPI::SaveStorage(); - if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res); -} diff --git a/src/config.h b/src/config.h index 51c888b..99b2bdc 100644 --- a/src/config.h +++ b/src/config.h @@ -10,18 +10,7 @@ class Config { public: - static void Init(); - - // wups config items static bool connect_to_network; - - // private stuff - static bool need_relaunch; - - // private stuff - static bool is_wiiu_menu; - - static bool unregister_task_item_pressed; }; struct config_strings { diff --git a/src/main.cpp b/src/main.cpp index 560ec42..c3bf833 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -54,20 +54,23 @@ Mandatory plugin information. If not set correctly, the loader will refuse to use the plugin. **/ -WUPS_PLUGIN_NAME("Inkay"); -WUPS_PLUGIN_DESCRIPTION("Pretendo Network Patcher"); -WUPS_PLUGIN_VERSION(INKAY_VERSION); -WUPS_PLUGIN_AUTHOR("Pretendo contributors"); -WUPS_PLUGIN_LICENSE("GPLv3"); +WUMS_MODULE_EXPORT_NAME("inkay"); +WUMS_MODULE_DESCRIPTION("Pretendo Network Patcher"); +WUMS_MODULE_VERSION(INKAY_VERSION); +WUMS_MODULE_AUTHOR("Pretendo contributors"); +WUMS_MODULE_LICENSE("GPLv3"); WUPS_USE_STORAGE("inkay"); -WUPS_USE_WUT_DEVOPTAB(); +WUMS_USE_WUT_DEVOPTAB(); #include #include #include #include "patches/account_settings.h" +#include "patches/dns_hooks.h" +#include "patches/eshop_applet.h" +#include "patches/olv_applet.h" #include "patches/game_peertopeer.h" #include "utils/sysconfig.h" @@ -111,25 +114,11 @@ static const char *get_pretendo_message() { return get_config_strings(get_system_language()).using_pretendo_network.data(); } -INITIALIZE_PLUGIN() { - WHBLogCafeInit(); - WHBLogUdpInit(); - - Config::Init(); - - auto res = Mocha_InitLibrary(); - - if (res != MOCHA_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res); - return; - } - - if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) { - DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed"); - } - +static void Inkay_Initialize(bool apply_patches) { // if using pretendo then (try to) apply the ssl patches - if (Config::connect_to_network) { + if (apply_patches) { + Config::connect_to_network = true; + if (is555(get_console_os_version())) { Mocha_IOSUKernelWrite32(0xE1019F78, 0xE3A00001); // mov r0, #1 } else { @@ -150,14 +139,40 @@ INITIALIZE_PLUGIN() { DEBUG_FUNCTION_LINE_VERBOSE("Pretendo URL and NoSSL patches skipped."); ShowNotification(get_nintendo_network_message()); + return; } if (FunctionPatcher_InitLibrary() == FUNCTION_PATCHER_RESULT_SUCCESS) { + patchDNS(); + patchEshop(); + patchOlvApplet(); install_matchmaking_patches(); + } else { + DEBUG_FUNCTION_LINE("FunctionPatcher_InitLibrary failed"); } } -DEINITIALIZE_PLUGIN() { +WUMS_INITIALIZE() { + WHBLogCafeInit(); + WHBLogUdpInit(); + + auto res = Mocha_InitLibrary(); + + if (res != MOCHA_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res); + return; + } + + if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed"); + } +} + +WUMS_DEINITIALIZE() { + unpatchDNS(); + unpatchEshop(); + unpatchOlvApplet(); + unpatchAccountSettings(); remove_matchmaking_patches(); Mocha_DeInitLibrary(); @@ -168,15 +183,20 @@ DEINITIALIZE_PLUGIN() { WHBLogUdpDeinit(); } -ON_APPLICATION_START() { +WUMS_APPLICATION_STARTS() { DEBUG_FUNCTION_LINE_VERBOSE("Inkay " INKAY_VERSION " starting up...\n"); setup_olv_libs(); peertopeer_patch(); matchmaking_notify_titleswitch(); - patchAccountSettings(); + + if (isAccountSettingsTitle()) { + patchAccountSettings(); + } } -ON_APPLICATION_ENDS() { +WUMS_APPLICATION_ENDS() { } + +WUMS_EXPORT_FUNCTION(Inkay_Initialize); diff --git a/src/patches/account_settings.cpp b/src/patches/account_settings.cpp index efea324..57af0b9 100644 --- a/src/patches/account_settings.cpp +++ b/src/patches/account_settings.cpp @@ -22,7 +22,8 @@ #include "utils/logger.h" #include "utils/replace_mem.h" -#include +#include +#include #include #include #include @@ -54,6 +55,7 @@ const char wave_new[] = "saccount.pretendo.cc"; bool isAccountSettingsTitle(); static std::optional rootca_pem_handle{}; +std::vector account_patches; DECL_FUNCTION(int, FSOpenFile_accSettings, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, int error) { @@ -131,10 +133,27 @@ bool patchAccountSettings() { DEBUG_FUNCTION_LINE_VERBOSE("Inkay: We didn't find the whitelist /)>~<(\\"); return false; } + + account_patches.reserve(3); + + auto add_patch = [](function_replacement_data_t repl, const char *name) { + PatchedFunctionHandle handle = 0; + if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("Inkay/Account: Failed to patch %s!", name); + } + account_patches.push_back(handle); + }; + + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile_accSettings, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_GAME), "FSOpenFile_accSettings"); + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile_accSettings, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_GAME), "FSReadFile_accSettings"); + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile_accSettings, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_GAME), "FSCloseFile_accSettings"); return true; } -WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_GAME); -WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_GAME); -WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile_accSettings, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_GAME); +void unpatchAccountSettings() { + for (auto handle: account_patches) { + FunctionPatcher_RemoveFunctionPatch(handle); + } + account_patches.clear(); +} diff --git a/src/patches/account_settings.h b/src/patches/account_settings.h index 6d09e68..4cc77be 100644 --- a/src/patches/account_settings.h +++ b/src/patches/account_settings.h @@ -17,4 +17,5 @@ bool isAccountSettingsTitle(); -bool patchAccountSettings(); \ No newline at end of file +bool patchAccountSettings(); +void unpatchAccountSettings(); diff --git a/src/patches/dns_hooks.cpp b/src/patches/dns_hooks.cpp index d832a57..d51520b 100644 --- a/src/patches/dns_hooks.cpp +++ b/src/patches/dns_hooks.cpp @@ -15,12 +15,16 @@ along with this program. If not, see . */ -#include #include -#include "utils/logger.h" + #include "config.h" +#include "utils/logger.h" #include +#include +#include + +std::vector dns_patches; const std::pair dns_replacements[] = { // NNCS servers @@ -42,10 +46,31 @@ static const char * replace_dns_name(const char *dns_name) { DECL_FUNCTION(struct hostent *, gethostbyname, const char *dns_name) { return real_gethostbyname(replace_dns_name(dns_name)); } -// might need a WUPS_MUST_REPLACE_FOR_PROCESS for Friends -WUPS_MUST_REPLACE(gethostbyname, WUPS_LOADER_LIBRARY_NSYSNET, gethostbyname); DECL_FUNCTION(int, getaddrinfo, const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { return real_getaddrinfo(replace_dns_name(node), service, hints, res); } -WUPS_MUST_REPLACE(getaddrinfo, WUPS_LOADER_LIBRARY_NSYSNET, getaddrinfo); \ No newline at end of file + +void patchDNS() { + dns_patches.reserve(2); + + auto add_patch = [](function_replacement_data_t repl, const char *name) { + PatchedFunctionHandle handle = 0; + if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("Inkay/DNS: Failed to patch %s!", name); + } + dns_patches.push_back(handle); + }; + + // might need a REPLACE_FUNCTION_FOR_PROCESS for Friends + add_patch(REPLACE_FUNCTION(gethostbyname, LIBRARY_NSYSNET, gethostbyname), "gethostbyname"); + + add_patch(REPLACE_FUNCTION(getaddrinfo, LIBRARY_NSYSNET, getaddrinfo), "getaddrinfo"); +} + +void unpatchDNS() { + for (auto handle: dns_patches) { + FunctionPatcher_RemoveFunctionPatch(handle); + } + dns_patches.clear(); +} diff --git a/src/patches/dns_hooks.h b/src/patches/dns_hooks.h new file mode 100644 index 0000000..9bbc216 --- /dev/null +++ b/src/patches/dns_hooks.h @@ -0,0 +1,20 @@ +/* Copyright 2024 Pretendo Network contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +void patchDNS(); +void unpatchDNS(); diff --git a/src/patches/eshop_applet.cpp b/src/patches/eshop_applet.cpp index d3ee43c..33467ac 100644 --- a/src/patches/eshop_applet.cpp +++ b/src/patches/eshop_applet.cpp @@ -21,7 +21,8 @@ #include "utils/logger.h" #include "utils/replace_mem.h" -#include +#include +#include #include #include #include @@ -47,6 +48,7 @@ const char whitelist_new[] = { }; static std::optional rootca_pem_handle{}; +std::vector eshop_patches; DECL_FUNCTION(int, FSOpenFile_eShop, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, int error) { @@ -102,6 +104,25 @@ DECL_FUNCTION(FSStatus, FSCloseFile_eShop, FSClient *client, FSCmdBlock *block, return real_FSCloseFile_eShop(client, block, handle, errorMask); } -WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_ESHOP); -WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_ESHOP); -WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile_eShop, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_ESHOP); +void patchEshop() { + eshop_patches.reserve(3); + + auto add_patch = [](function_replacement_data_t repl, const char *name) { + PatchedFunctionHandle handle = 0; + if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("Inkay/eShop: Failed to patch %s!", name); + } + eshop_patches.push_back(handle); + }; + + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile_eShop, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_ESHOP), "FSOpenFile_eShop"); + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile_eShop, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_ESHOP), "FSReadFile_eShop"); + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile_eShop, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_ESHOP), "FSCloseFile_eShop"); +} + +void unpatchEshop() { + for (auto handle: eshop_patches) { + FunctionPatcher_RemoveFunctionPatch(handle); + } + eshop_patches.clear(); +} diff --git a/src/patches/eshop_applet.h b/src/patches/eshop_applet.h new file mode 100644 index 0000000..6f978ea --- /dev/null +++ b/src/patches/eshop_applet.h @@ -0,0 +1,20 @@ +/* Copyright 2024 Pretendo Network contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +void patchEshop(); +void unpatchEshop(); diff --git a/src/patches/game_matchmaking.cpp b/src/patches/game_matchmaking.cpp index c78d56c..fe26eed 100644 --- a/src/patches/game_matchmaking.cpp +++ b/src/patches/game_matchmaking.cpp @@ -16,8 +16,8 @@ along with this program. If not, see . */ -#include "game_matchmaking.h" #include "config.h" +#include "game_matchmaking.h" #include "utils/logger.h" #include "ini.h" diff --git a/src/patches/olv_applet.cpp b/src/patches/olv_applet.cpp index 9bafd44..256b3fd 100644 --- a/src/patches/olv_applet.cpp +++ b/src/patches/olv_applet.cpp @@ -21,11 +21,12 @@ #include "utils/logger.h" #include "utils/replace_mem.h" -#include +#include #include #include #include #include +#include #include "ca_pem.h" // generated at buildtime @@ -65,6 +66,7 @@ const replacement replacements[] = { }; static std::optional rootca_pem_handle{}; +std::vector olv_patches; DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, int error) { @@ -123,6 +125,25 @@ DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFile return real_FSCloseFile(client, block, handle, errorMask); } -WUPS_MUST_REPLACE_FOR_PROCESS(FSOpenFile, WUPS_LOADER_LIBRARY_COREINIT, FSOpenFile, WUPS_FP_TARGET_PROCESS_MIIVERSE); -WUPS_MUST_REPLACE_FOR_PROCESS(FSReadFile, WUPS_LOADER_LIBRARY_COREINIT, FSReadFile, WUPS_FP_TARGET_PROCESS_MIIVERSE); -WUPS_MUST_REPLACE_FOR_PROCESS(FSCloseFile, WUPS_LOADER_LIBRARY_COREINIT, FSCloseFile, WUPS_FP_TARGET_PROCESS_MIIVERSE); +void patchOlvApplet() { + olv_patches.reserve(3); + + auto add_patch = [](function_replacement_data_t repl, const char *name) { + PatchedFunctionHandle handle = 0; + if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) { + DEBUG_FUNCTION_LINE("Inkay/OLV: Failed to patch %s!", name); + } + olv_patches.push_back(handle); + }; + + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_MIIVERSE), "FSOpenFile"); + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_MIIVERSE), "FSReadFile"); + add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_MIIVERSE), "FSCloseFile"); +} + +void unpatchOlvApplet() { + for (auto handle: olv_patches) { + FunctionPatcher_RemoveFunctionPatch(handle); + } + olv_patches.clear(); +} diff --git a/src/patches/olv_applet.h b/src/patches/olv_applet.h new file mode 100644 index 0000000..4f307c7 --- /dev/null +++ b/src/patches/olv_applet.h @@ -0,0 +1,20 @@ +/* Copyright 2024 Pretendo Network contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +void patchOlvApplet(); +void unpatchOlvApplet(); diff --git a/src/patches/olv_urls.cpp b/src/patches/olv_urls.cpp index 862453d..849d29b 100644 --- a/src/patches/olv_urls.cpp +++ b/src/patches/olv_urls.cpp @@ -16,8 +16,8 @@ along with this program. If not, see . */ -#include "olv_urls.h" #include "config.h" +#include "olv_urls.h" #include "utils/logger.h" #include "utils/replace_mem.h" diff --git a/src/utils/logger.h b/src/utils/logger.h index f7278b6..e26a487 100644 --- a/src/utils/logger.h +++ b/src/utils/logger.h @@ -14,15 +14,15 @@ extern "C" { #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) #define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ - OSFatal_printf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + OSFatal_printf("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ } while (0) #define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ - WHBLogPrintf("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + WHBLogPrintf("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ } while (0); #define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ - WHBLogWritef("[(P) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + WHBLogWritef("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ } while (0); #ifdef DEBUG