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