Compare commits

..
60 changed files with 483 additions and 2655 deletions

View file

@ -1,22 +1,18 @@
name: Inkay-CI name: Inkay-CI
permissions:
contents: read
on: on: push
- push
- pull_request
jobs: jobs:
build-inkay: build-inkay:
runs-on: debian-trixie runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v2
- name: build toolchain container - name: build toolchain container
run: docker build . -t builder run: docker build . -t builder
- uses: https://github.com/ammaraskar/gcc-problem-matcher@master - uses: ammaraskar/gcc-problem-matcher@master
- name: build Inkay - name: build Inkay
run: docker run --rm -v ${PWD}:/app -w /app builder run: docker run --rm -v ${PWD}:/app -w /app builder
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@master
with: with:
name: inkay name: inkay
path: dist/ path: "*.wps"

6
.gitignore vendored
View file

@ -1,12 +1,6 @@
.cache/
.vscode/ .vscode/
.idea/ .idea/
/build /build
/plugin/build
/dist
*.elf *.elf
*.wms
*.wps *.wps
certs/ certs/
*.lst
inkay_config.local.h

View file

@ -1,11 +1,10 @@
FROM ghcr.io/wiiu-env/devkitppc:20260504 FROM ghcr.io/wiiu-env/devkitppc:20230417
COPY --from=ghcr.io/wiiu-env/libnotifications:20240426 /artifacts $DEVKITPRO COPY --from=wiiuenv/libnotifications:20230126 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libkernel:20230621 /artifacts $DEVKITPRO COPY --from=wiiuenv/libkernel:20220724 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO COPY --from=wiiuenv/libmocha:20220903 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20250219 /artifacts $DEVKITPRO COPY --from=wiiuenv/wiiupluginsystem:20230126 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20240505 /artifacts $DEVKITPRO
WORKDIR /app WORKDIR /app
CMD make -f Makefile -j$(nproc) CMD make -f Makefile -j$(nproc)

View file

@ -8,10 +8,10 @@ endif
TOPDIR ?= $(CURDIR) TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wums/share/wums_rules include $(DEVKITPRO)/wups/share/wups_rules
WUMS_ROOT := $(DEVKITPRO)/wums
WUT_ROOT := $(DEVKITPRO)/wut WUT_ROOT := $(DEVKITPRO)/wut
WUMS_ROOT := $(DEVKITPRO)/wums
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# TARGET is the name of the output # TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed # BUILD is the directory where object files & intermediate files will be placed
@ -19,38 +19,34 @@ WUT_ROOT := $(DEVKITPRO)/wut
# DATA is a list of directories containing data files # DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files # INCLUDES is a list of directories containing header files
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
TARGET := Malamar-spfn TARGET := Inkay-pretendo
BUILD := build BUILD := build
SOURCES := src src/patches src/utils src/ext/inih common SOURCES := src src/patches src/utils src/ext/inih
DATA := data DATA := data
INCLUDES := src src/ext/inih src/lang common INCLUDES := src src/ext/inih
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# options for code generation # options for code generation
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
OPT := -Os -fno-exceptions -fno-asynchronous-unwind-tables CFLAGS := -g -Wall -O2 -ffunction-sections \
CFLAGS := -Wall -ffunction-sections -fdata-sections \ $(MACHDEP)
$(MACHDEP) $(OPT)
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
CXXFLAGS := $(CFLAGS) -std=c++20 CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(OPT) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -Wl,-gc-sections -T$(WUMS_ROOT)/share/libkernel.ld $(WUMSSPECS) LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
ifeq ($(DEBUG),1) LDFLAGS += -T$(WUMS_ROOT)/share/libkernel.ld $(WUPSSPECS)
CXXFLAGS += -DDEBUG -g
CFLAGS += -DDEBUG -g
endif
LIBS := -lwums -lmocha -lkernel -lwut -lfunctionpatcher -lnotifications LIBS := -lwups -lmocha -lkernel -lwut -lnotifications -lfunctionpatcher
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level # list of directories containing libraries, this must be the top level
# containing include and lib # containing include and lib
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) $(WUT_ROOT)/usr LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional # no real need to edit anything past this point unless you need to add additional
@ -102,20 +98,14 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
all: $(BUILD) all: $(BUILD)
$(BUILD): $(BUILD):
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD)) @$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @$(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: clean:
@echo clean ... @echo clean ...
@rm -fr dist $(BUILD) $(TARGET).wms $(TARGET).elf @rm -fr $(BUILD) $(TARGET).wps $(TARGET).elf
@$(MAKE) --no-print-directory -C $(CURDIR)/plugin -f $(CURDIR)/plugin/Makefile clean
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
else else
@ -126,10 +116,10 @@ DEPENDS := $(OFILES:.o=.d)
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# main targets # main targets
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
all : $(OUTPUT).wms all : $(OUTPUT).wps
$(OUTPUT).wms : $(OUTPUT).elf $(OUTPUT).wps : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES) $(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN) $(OFILES_SRC) : $(HFILES_BIN)
@ -146,11 +136,6 @@ $(OFILES_SRC) : $(HFILES_BIN)
@echo $(notdir $<) @echo $(notdir $<)
@$(bin2o) @$(bin2o)
#---------------------------------------------------------------------------------
%.o: %.s
@echo $(notdir $<)
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
-include $(DEPENDS) -include $(DEPENDS)
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------

View file

@ -1,40 +1,16 @@
# Malamar - Aroma patches for Splatfestival Network/Spacebar # Inkay - Aroma patches for Pretendo
Malamar is an Aroma/WUPS plugin based on Pretendo's Malamar that patches various Nintendo Network URLs on a Wii U to use Splatfestival/Spacebar Network instead. It also (for the time being) bypasses SSL verification in most cases. It redirects Nintendo Network in: [![Pretendo network logo](https://github.com/PretendoNetwork/website/raw/master/public/assets/images/opengraph/opengraph-image.png)](https://pretendo.network)
- IOSU-side connections (Friends, SpotPass, accounts etc.) Inkay is an Aroma/WUPS plugin that patches various Nintendo Network URLs on a Wii U to use Pretendo Network instead. It also (for the time being) bypasses SSL verification in most cases.
- Account Settings
- NNCS
- Nintendo eShop
- Miiverse (in-game)
- Miiverse applet
Malamar also includes game-specific patches to add extra features: Inkay does not currently include the game-specific patches present in [Nimble](https://github.com/PretendoNetwork/Nimble). These will be implemented soon™.
- Modpack-specific matchmaking for global, regional rooms (by simulating extra DLC) - **Mario Kart 8**
- P2P port override for better connection stability (if you port forward) - **Minecraft: Wii U Edition**, **Mario Kart 8**, **Splatoon**
## Requirements ## Dependencies
Malamar is only supported on the release version of Aroma configured for autoboot/coldboot. Other configurations (specifically lacking coldboot) may cause issues with SpotPass. Inkay is only supported on the release version of Aroma configured for autoboot/coldboot. For Tiramisu, see [Nimble](https://github.com/PretendoNetwork/Nimble).
## Safety ## Safety
Malamar's patches are all temporary, and only applied in-memory without modifying your console. The SSL patch, while also temporary, could reduce the overall security of your console while active - this is because it no longer checks if a server is verified. However, this does not apply to the Internet Browser, where SSL still works as expected. Inkay's patches are all temporary, and only applied in-memory without modifying your console. The SSL patch, while also temporary, could reduce the overall security of your console while active - this is because it no longer checks if a server is verified. However, this does not apply to the Internet Browser, where SSL still works as expected.
## Compiling - Docker ## TODO
Malamar's dependencies and build tooling can be handled as a container, which is recommended for WUPS plugins. Using `docker` or `podman`: See [Issues](https://github.com/PretendoNetwork/Inkay/issues).
```shell
docker build -t malamar .
docker run --rm -v $(pwd):/app malamar make
# you can replace "make" with other commands - e.g. make clean
```
If using `podman` on SELinux systems (like Fedora Linux), you might need to use `$(pwd):/app:Z` instead of `$(pwd):/app`.
## Compiling - System
Malamar has the following dependencies aside from devkitPPC and wut:
- [WiiUPluginSystem](https://github.com/wiiu-env/WiiUPluginSystem)
- [WiiUModuleSystem](https://github.com/wiiu-env/WiiUModuleSystem)
- [libmocha](https://github.com/wiiu-env/libmocha)
- [libkernel](https://github.com/wiiu-env/libkernel/)
- [libnotifications](https://github.com/wiiu-env/libnotifications/)
- [libfunctionpatcher](https://github.com/wiiu-env/libfunctionpatcher)
Each of these should be `make install`-able. After that, you can compile Malamar with `make`.

View file

@ -1,31 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#include "Notification.h"
#include <notifications/notification_defines.h>
#include <notifications/notifications.h>
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);
}

View file

@ -1,3 +0,0 @@
#pragma once
void ShowNotification(const char* notification);

View file

@ -1,21 +0,0 @@
//
// Created by ash on 17/12/24.
//
#ifndef INKAY_INKAY_CONFIG_H
#define INKAY_INKAY_CONFIG_H
#ifdef __has_include
#if __has_include("inkay_config.local.h")
#include "inkay_config.local.h"
#define INKAY_CUSTOM 1
#endif
#endif
#ifndef NETWORK_BASEURL
#define NETWORK_BASEURL "spbr.net"
#endif
#endif //INKAY_INKAY_CONFIG_H

View file

@ -1,47 +0,0 @@
//
// Created by ash on 21/11/24.
//
#include "lang.h"
config_strings get_config_strings(inkay_language language) {
switch (language) {
case inkay_language::English:
default: return {
#include "en_US.lang"
};
case inkay_language::Spanish: return {
#include "es_ES.lang"
};
case inkay_language::French: return {
#include "fr_FR.lang"
};
case inkay_language::Italian: return {
#include "it_IT.lang"
};
case inkay_language::German: return {
#include "de_DE.lang"
};
case inkay_language::SimplifiedChinese: return {
#include "zh_CN.lang"
};
case inkay_language::Portuguese: return {
#include "pt_BR.lang"
};
case inkay_language::Japanese: return {
#include "ja_JP.lang"
};
case inkay_language::Dutch: return {
#include "nl_NL.lang"
};
case inkay_language::Russian: return {
#include "ru_RU.lang"
};
case inkay_language::Korean: return {
#include "ko_KR.lang"
};
case inkay_language::EnUwU: return {
#include "en@uwu.lang"
};
}
}

View file

@ -1,47 +0,0 @@
#pragma once
#include <string_view>
enum inkay_language {
// Wii U System Languages
Japanese = 0,
English,
French,
German,
Italian,
Spanish,
SimplifiedChinese,
Korean,
Dutch,
Portuguese,
Russian,
TraditionalChinese,
Invalid,
// Custom Languages
System,
EnUwU,
};
struct config_strings {
const char *plugin_name;
std::string_view network_category;
std::string_view connect_to_network_setting;
std::string_view show_startup_toast_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;
std::string_view multiplayer_port_display;
std::string_view module_not_found;
std::string_view module_init_not_found;
std::string_view setting_yes;
std::string_view setting_no;
std::string_view language_setting;
std::string_view language_name;
std::string_view system_language;
};
config_strings get_config_strings(inkay_language language);

View file

@ -1,115 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Copyright 2020-2022 V10lator <v10lator@myway.de>
Copyright 2022 Xpl0itU <DaThinkingChair@protonmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "sysconfig.h"
#include "utils/logger.h"
#include "utils/scope_exit.h"
#include <coreinit/mcp.h>
#include <coreinit/userconfig.h>
#include <optional>
nn::swkbd::LanguageType get_system_language() {
static std::optional <nn::swkbd::LanguageType> cached_language{};
if (cached_language) return *cached_language;
UCHandle handle = UCOpen();
if (handle < 0) {
DEBUG_FUNCTION_LINE("Error opening UC: %d", handle);
return nn::swkbd::LanguageType::English;
}
scope_exit uc_c([&] { UCClose(handle); });
nn::swkbd::LanguageType language;
alignas(0x40) UCSysConfig settings = {
.name = "cafe.language",
.access = 0,
.dataType = UC_DATATYPE_UNSIGNED_INT,
.error = UC_ERROR_OK,
.dataSize = sizeof(language),
.data = &language,
};
UCError err = UCReadSysConfig(handle, 1, &settings);
if (err != UC_ERROR_OK) {
DEBUG_FUNCTION_LINE("Error reading UC: %d!", err);
return nn::swkbd::LanguageType::English;
}
DEBUG_FUNCTION_LINE_VERBOSE("System language found: %d", static_cast<int>(language));
cached_language = language;
return language;
}
static std::optional<MCPSysProdSettings> mcp_config;
static std::optional<MCPSystemVersion> mcp_os_version;
static void get_mcp_config() {
int mcp = MCP_Open();
scope_exit mcp_c([&] { MCP_Close(mcp); });
alignas(0x40) MCPSysProdSettings config {};
if (MCP_GetSysProdSettings(mcp, &config)) {
DEBUG_FUNCTION_LINE("Could not get MCP system config!");
return;
}
mcp_config = config;
//get os version
MCPSystemVersion os_version;
if (MCP_GetSystemVersion(mcp, &os_version)) {
DEBUG_FUNCTION_LINE("Could not get MCP system config!");
return;
}
mcp_os_version = os_version;
DEBUG_FUNCTION_LINE_VERBOSE("Running on %d.%d.%d%c; %s%s",
os_version.major, os_version.minor, os_version.patch, os_version.region,
config.code_id, config.serial_id
);
}
const char * get_console_serial() {
if (!mcp_config) get_mcp_config();
return mcp_config ? mcp_config->serial_id : "123456789";
}
MCPSystemVersion get_console_os_version() {
if (!mcp_os_version) get_mcp_config();
return mcp_os_version.value_or((MCPSystemVersion) { .major = 5, .minor = 5, .patch = 5, .region = 'E' });
}
static inline int digit(char a) {
if (a < '0' || a > '9') return 0;
return a - '0';
}
uint16_t get_console_peertopeer_port() {
const char * serial = get_console_serial();
uint16_t port = 50000 +
(digit(serial[4]) * 1000) +
(digit(serial[5]) * 100 ) +
(digit(serial[6]) * 10 ) +
(digit(serial[7]) * 1 );
return port;
}

View file

@ -1,16 +0,0 @@
//
// Created by ash on 9/04/24.
//
#ifndef INKAY_SYSCONFIG_H
#define INKAY_SYSCONFIG_H
#include <nn/swkbd.h>
#include <coreinit/mcp.h>
nn::swkbd::LanguageType get_system_language();
const char * get_console_serial();
MCPSystemVersion get_console_os_version();
unsigned short get_console_peertopeer_port();
#endif //INKAY_SYSCONFIG_H

View file

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

View file

@ -1,149 +0,0 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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 := Malamar-spfn
BUILD := build
SOURCES := src src/utils ../common
DATA := data
INCLUDES := src ../src/lang ../common
#DEBUG := 1
#-------------------------------------------------------------------------------
# options for code generation
#-------------------------------------------------------------------------------
OPT := -Os -fno-exceptions -fno-asynchronous-unwind-tables
CFLAGS := -Wall -ffunction-sections -fdata-sections \
$(MACHDEP) $(OPT)
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) $(OPT) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -Wl,-gc-sections -Wl,--discard-all
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
#-------------------------------------------------------------------------------

View file

@ -1,331 +0,0 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "wut_extra.h"
#include "utils/logger.h"
#include "sysconfig.h"
#include "lang.h"
#include <wups.h>
#include <wups/storage.h>
#include <wups/config_api.h>
#include <wups/config/WUPSConfigItemMultipleValues.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#include <wups/config/WUPSConfigItemStub.h>
#include <coreinit/title.h>
#include <coreinit/launch.h>
#include <sysapp/title.h>
#include <sysapp/launch.h>
#include <nn/act.h>
#include <format>
static config_strings strings;
bool Config::connect_to_network = true;
bool Config::show_startup_toast = true;
bool Config::need_relaunch = false;
bool Config::unregister_task_item_pressed = false;
bool Config::is_wiiu_menu = false;
uint32_t Config::language = 13;
inkay_language Config::current_language = English;
static WUPSConfigAPICallbackStatus report_error(WUPSConfigAPIStatus err, int line) {
DEBUG_FUNCTION_LINE_VERBOSE("WUPS config error: %s (L%04d)", WUPSConfigAPI_GetStatusStr(err), line);
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 = WUPSStorageAPI_StoreBool(nullptr, "connect_to_network", Config::connect_to_network);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}
static void language_changed(ConfigItemMultipleValues *item, uint32_t new_value) {
DEBUG_FUNCTION_LINE_VERBOSE("language changed to: %d", new_value);
if (new_value != Config::language) {
if (new_value == inkay_language::System)
Config::current_language = (inkay_language) get_system_language();
else
Config::current_language = (inkay_language) new_value;
}
Config::language = new_value;
WUPSStorageError res = WUPSStorageAPI_StoreU32(nullptr, "language", Config::language);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}
static void show_startup_toast_changed(ConfigItemBoolean *item, bool new_value) {
DEBUG_FUNCTION_LINE_VERBOSE("show_startup_toast changed to: %d", new_value);
Config::show_startup_toast = new_value;
WUPSStorageError res = WUPSStorageAPI_StoreBool(nullptr, "show_startup_toast", Config::show_startup_toast);
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 root_cat) {
WUPSConfigAPIStatus err;
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(Config::current_language);
// Network submenu
// Connect to Pretendo Network Yes/No
// Using UDP port 5000 for multiplayer
WUPSConfigCategoryHandle network_cat;
err = WUPSConfigAPI_Category_Create({strings.network_category.data()}, &network_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
WUPSConfigItemHandle connect_item;
err = WUPSConfigItemBoolean_CreateEx(
"connect_to_network", strings.connect_to_network_setting.data(), true, Config::connect_to_network,
&connect_to_network_changed, strings.setting_yes.data(), strings.setting_no.data(), &connect_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(network_cat, connect_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
const uint16_t port = get_console_peertopeer_port();
char buffer[256];
snprintf(buffer, sizeof(buffer), strings.multiplayer_port_display.data(), port);
WUPSConfigItemHandle multiplayer_port_display;
err = WUPSConfigItemStub_Create(buffer, &multiplayer_port_display);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(network_cat, multiplayer_port_display);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// Add to root and finish
err = WUPSConfigAPI_Category_AddCategory(root_cat, network_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// Other category
// Reset Wara Wara Plaza Press A
// Language English/Spanish/etc..
// Show startup toast Yes/No
WUPSConfigCategoryHandle other_cat;
err = WUPSConfigAPI_Category_Create({strings.other_category.data()}, &other_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
WUPSConfigItemHandle unregisterTasksItem;
err = WUPSConfigAPI_Item_Create({
.displayName = strings.reset_wwp_setting.data(),
.context = nullptr,
.callbacks = {
.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
},
}, &unregisterTasksItem);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(other_cat, unregisterTasksItem);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// TODO localise these!
constexpr std::array<ConfigItemMultipleValuesPair, 14> languages = {
{
{0, "Japanese"},
{1, "English"},
{2, "French"},
{3, "German"},
{4, "Italian"},
{5, "Spanish"},
{6, "Simplified Chinese"},
{7, "Korean"},
{8, "Dutch"},
{9, "Portuguese"},
{10, "Russian"},
{11, "Traditional Chinese"},
{13, "System"},
{14, "English (UwU)"},
}
};
constexpr auto find_lang = [languages](const uint32_t lang) -> int {
return std::ranges::find_if(languages, [lang](auto const l) { return l.value == lang; }) -
std::begin(languages);
};
constexpr int system_language = find_lang(13 /* System */);
const int current_language = find_lang(Config::language);
WUPSConfigItemHandle language_item;
err = WUPSConfigItemMultipleValues_Create("language", strings.language_setting.data(), system_language,
current_language,
const_cast<ConfigItemMultipleValuesPair *>(languages.data()), // Yikes!
languages.size(), &language_changed, &language_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(other_cat, language_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
WUPSConfigItemHandle startup_toast_item;
err = WUPSConfigItemBoolean_CreateEx("show_startup_toast", strings.show_startup_toast_setting.data(), true,
Config::show_startup_toast, &show_startup_toast_changed,
strings.setting_yes.data(), strings.setting_no.data(), &startup_toast_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
err = WUPSConfigAPI_Category_AddItem(other_cat, startup_toast_item);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
// finish up
err = WUPSConfigAPI_Category_AddCategory(root_cat, other_cat);
if (err != WUPSCONFIG_API_RESULT_SUCCESS) return report_error(err, __LINE__);
return WUPSCONFIG_API_CALLBACK_RESULT_SUCCESS;
}
static void ConfigMenuClosedCallback() {
// Save all changes
WUPSStorageError res = WUPSStorageAPI_SaveStorage(false);
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() {
// Init the config api
WUPSConfigAPIStatus cres =
WUPSConfigAPI_Init({.name = "Inkay"}, ConfigMenuOpenedCallback, ConfigMenuClosedCallback);
if (cres != WUPSCONFIG_API_RESULT_SUCCESS) return (void) report_error(cres, __LINE__);
WUPSStorageError res;
// Try to get values from storage
res = WUPSStorageAPI_GetBool(nullptr, "connect_to_network", &Config::connect_to_network);
if (res == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE_VERBOSE("Connect to network value not found, attempting to migrate/create");
bool skipPatches = false;
if (WUPSStorageAPI_GetBool(nullptr, "skipPatches", &skipPatches) == WUPS_STORAGE_ERROR_SUCCESS) {
// Migrate old config value
Config::connect_to_network = !skipPatches;
WUPSStorageAPI_DeleteItem(nullptr, "skipPatches");
}
// Add the value to the storage if it's missing.
res = WUPSStorageAPI_StoreBool(nullptr, "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);
res = WUPSStorageAPI_GetU32(nullptr, "language", &Config::language);
if (res == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE_VERBOSE("Language value not found, attempting to create");
// Add the value to the storage.
res = WUPSStorageAPI_StoreU32(nullptr, "language", Config::language);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
} else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
res = WUPSStorageAPI_GetBool(nullptr, "show_startup_toast", &Config::show_startup_toast);
if (res == WUPS_STORAGE_ERROR_NOT_FOUND) {
DEBUG_FUNCTION_LINE_VERBOSE("Show startup toast value not found, attempting to create");
// Add the value to the storage if it's missing.
res = WUPSStorageAPI_StoreBool(nullptr, "show_startup_toast", show_startup_toast);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
} else if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
// Set the language that's currently used
if (Config::language == inkay_language::System)
Config::current_language = (inkay_language) get_system_language();
else
Config::current_language = (inkay_language) Config::language;
// Save storage
res = WUPSStorageAPI_SaveStorage(false);
if (res != WUPS_STORAGE_ERROR_SUCCESS) return report_storage_error(res);
}

View file

@ -1,31 +0,0 @@
//
// Created by ash on 10/12/22.
//
#ifndef INKAY_CONFIG_H
#define INKAY_CONFIG_H
#include <stdint.h>
#include "lang.h"
class Config {
public:
static void Init();
// wups config items
static bool connect_to_network;
static uint32_t language;
static bool show_startup_toast;
// private stuff
static bool need_relaunch;
// private stuff
static bool is_wiiu_menu;
static inkay_language current_language;
static bool unregister_task_item_pressed;
};
#endif //INKAY_CONFIG_H

View file

@ -1,152 +0,0 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
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 <http://www.gnu.org/licenses/>.
*/
#include <wups.h>
#include <notifications/notifications.h>
#include <utils/logger.h>
#include "config.h"
#include<coreinit/title.h>
#include<coreinit/dynload.h>
#include<vector>
#include "module.h"
#define INKAY_VERSION "v3.1.0"
/**
Mandatory plugin information.
If not set correctly, the loader will refuse to use the plugin.
**/
WUPS_META(name, "Malamar (SPFN)");
WUPS_META(wups, WUPS_VERSION_STR);
WUPS_USE_WUT_MALLOC();
WUPS_USE_WUT_SOCKETS();
WUPS_USE_WUT_NEWLIB();
WUPS___INIT_WRAPPER();
WUPS___FINI_WRAPPER();
WUPS_INIT_CONFIG_FUNCTIONS();
WUPS_META(buildtimestamp, __DATE__ " " __TIME__);
extern const char wups_meta_plugin_name[] WUPS_SECTION("meta");
const char wups_meta_plugin_name[] = "Malamar (SPFN)";
extern const char wups_meta_info_dump[] WUPS_SECTION("meta");
const char wups_meta_info_dump[] = "(plugin: Malamar (SPFN);" \
"wups " WUPS_VERSION_STR ";" \
"buildtime: " __DATE__ " " __TIME__ ")";
extern const char wups_meta_info_linking_order[] WUPS_SECTION("meta");
const char wups_meta_info_linking_order[] = "Loading \"Malamar (SPFN)\" failed.\n" \
"Function \"wut_get_thread_specific\" returned unexpected value.\n" \
"Please check linking order (expected \"-lwups -lwut\")";
WUPS_PLUGIN_DESCRIPTION("SPFN Network Patcher");
WUPS_PLUGIN_VERSION(INKAY_VERSION);
WUPS_PLUGIN_AUTHOR("redbinder0526, kittentm and Pretendo contributors");
WUPS_PLUGIN_LICENSE("GPLv3");
WUPS_USE_STORAGE("inkay");
WUPS_USE_WUT_DEVOPTAB();
bool is_current_game_splatoon(){
auto current_game = OSGetTitleID();
if(
current_game == 0x0005000010162B00 ||
current_game == 0x0005000010176900 ||
current_game == 0x0005000010176A00
){
return true;
}
return false;
}
void splatoon_patcher(){
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Splatoon patches skipped");
return;
}
if(is_current_game_splatoon()){
NotificationModule_AddInfoNotification("Splatoon Detected, applying patch");
auto rpl_count = OSDynLoad_GetNumberOfRPLs();
std::vector<OSDynLoad_NotifyData> rpls(rpl_count);
if(!OSDynLoad_GetRPLInfo(0, rpl_count, rpls.data())){
NotificationModule_AddInfoNotification("failed to get rpls");
}
for(auto rpl: rpls){
if(rpl.name != 0){
if(strstr(rpl.name, "Gambit") != NULL){
NotificationModule_AddInfoNotification(rpl.name);
auto text_color = NMColor();
text_color.r = 255;
text_color.g = 255;
text_color.b = 255;
text_color.a = 255;
auto bg_color = NMColor();
bg_color.r = 0;
bg_color.g = 0;
bg_color.b = 0;
bg_color.a = 0;
NotificationModule_AddInfoNotificationEx(rpl.name, 30.0f, text_color, bg_color, nullptr, nullptr, true);
NotificationModule_AddInfoNotification("applying patches!!!!");
void* game_data_region = (void*)rpl.dataAddr;
auto str_loc = (char*)(0x000009ac + (uint32_t)game_data_region);
strcpy(str_loc, "https://splatpost.spbr.net/post");
}
}
}
}
}
INITIALIZE_PLUGIN() {
WHBLogCafeInit();
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, Config::show_startup_toast);
}
DEINITIALIZE_PLUGIN() {
Inkay_Finalize();
NotificationModule_DeInitLibrary();
WHBLogCafeDeinit();
}
ON_APPLICATION_START() {
// Tell the module the plugin is running!
splatoon_patcher();
Inkay_SetPluginRunning();
}
ON_APPLICATION_ENDS() {
}

View file

@ -1,93 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "Notification.h"
#include "utils/logger.h"
#include "config.h"
#include "lang.h"
#include <coreinit/dynload.h>
static OSDynLoad_Module module;
static void (*moduleInitialize)(bool, bool, inkay_language) = nullptr;
static InkayStatus (*moduleGetStatus)() = nullptr;
static void (*moduleSetPluginRunning)() = nullptr;
static const char *get_module_not_found_message() {
return get_config_strings(Config::current_language).module_not_found.data();
}
static const char *get_module_init_not_found_message() {
return get_config_strings(Config::current_language).module_init_not_found.data();
}
void Inkay_Initialize(bool apply_patches, bool show_startup_toast) {
if (module) {
return;
}
if (OSDynLoad_Acquire("inkay", &module) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to acquire module");
ShowNotification(get_module_not_found_message());
return;
}
if (OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_Initialize", reinterpret_cast<void * *>(&moduleInitialize)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to find function %s", "Inkay_Initialize");
ShowNotification(get_module_init_not_found_message());
OSDynLoad_Release(module);
return;
}
moduleInitialize(apply_patches, show_startup_toast, Config::current_language);
}
void Inkay_Finalize() {
if (module) {
OSDynLoad_Release(module);
moduleInitialize = nullptr;
moduleGetStatus = nullptr;
moduleSetPluginRunning = nullptr;
}
}
InkayStatus Inkay_GetStatus() {
if (!module) {
return InkayStatus::Error;
}
if (!moduleGetStatus && OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_GetStatus", reinterpret_cast<void * *>(&moduleGetStatus)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to find function %s", "Inkay_GetStatus");
return InkayStatus::Error;
}
return moduleGetStatus();
}
void Inkay_SetPluginRunning() {
if (!module) {
return;
}
if (!moduleSetPluginRunning && OSDynLoad_FindExport(module, OS_DYNLOAD_EXPORT_FUNC, "Inkay_SetPluginRunning", reinterpret_cast<void * *>(&moduleSetPluginRunning)) != OS_DYNLOAD_OK) {
DEBUG_FUNCTION_LINE("Failed to find function %s", "Inkay_SetPluginRunning");
return;
}
moduleSetPluginRunning();
}

View file

@ -1,30 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#pragma once
enum class InkayStatus {
Uninitialized, ///< The module isn't initialized
Nintendo, ///< The module is initialized but hasn't applied any patches
Pretendo, ///< The module is initialized and has applied the Pretendo patches
Error = -1 ///< Failed to retrieve the module status
};
void Inkay_Initialize(bool apply_patches, bool show_startup_toast);
void Inkay_Finalize();
InkayStatus Inkay_GetStatus();
void Inkay_SetPluginRunning();

View file

@ -1,36 +0,0 @@
#pragma once
#include <string.h>
#include <whb/log.h>
#include <whb/log_module.h>
#include <whb/log_cafe.h>
#include <whb/log_udp.h>
#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

View file

@ -1,3 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

39
src/Notification.cpp Normal file
View file

@ -0,0 +1,39 @@
#include "Notification.h"
#include <coreinit/cache.h>
#include <coreinit/thread.h>
#include <coreinit/title.h>
#include <notifications/notification_defines.h>
#include <notifications/notifications.h>
#include <thread>
static std::unique_ptr<std::thread> sShowHintThread;
static bool sShutdownHintThread = false;
void ShowNotification(const char * notification) {
// Wait for notification module to be ready
bool isOverlayReady = false;
while (!sShutdownHintThread && NotificationModule_IsOverlayReady(&isOverlayReady) == NOTIFICATION_MODULE_RESULT_SUCCESS && !isOverlayReady)
OSSleepTicks(OSMillisecondsToTicks(16));
if (sShutdownHintThread || !isOverlayReady) return;
NotificationModuleStatus err = NotificationModule_SetDefaultValue(NOTIFICATION_MODULE_NOTIFICATION_TYPE_INFO, NOTIFICATION_MODULE_DEFAULT_OPTION_DURATION_BEFORE_FADE_OUT, 15.0f);
if(err != NOTIFICATION_MODULE_RESULT_SUCCESS) return;
NotificationModule_AddInfoNotification(notification);
}
void StartNotificationThread(const char * value) {
uint64_t titleID = OSGetTitleID();
if (titleID == 0x0005001010040000L || titleID == 0x0005001010040100L || titleID == 0x0005001010040200L) {
sShutdownHintThread = false;
sShowHintThread = std::make_unique<std::thread>(ShowNotification, value);
}
}
void StopNotificationThread() {
if (sShowHintThread != nullptr) {
sShutdownHintThread = true;
OSMemoryBarrier();
sShowHintThread->join();
sShowHintThread.reset();
}
}

7
src/Notification.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
void ShowNotification(const char * notification);
void StartNotificationThread(const char * value);
void StopNotificationThread();

View file

@ -1,25 +1,157 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network> //
Copyright 2022 Ash Logan <ash@heyquark.com> // Created by ash on 10/12/22.
//
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 <http://www.gnu.org/licenses/>.
*/
#include "config.h" #include "config.h"
bool Config::connect_to_network = false; #include "wut_extra.h"
bool Config::show_startup_toast = false; #include "utils/logger.h"
bool Config::initialized = false; #include <wups.h>
bool Config::shown_warning = false; #include <wups/config/WUPSConfigItemBoolean.h>
bool Config::plugin_is_loaded = false;
bool Config::block_initialize = false; #include <coreinit/title.h>
#include <coreinit/launch.h>
#include <sysapp/title.h>
#include <sysapp/launch.h>
#include <nn/act.h>
bool Config::connect_to_network = true;
bool Config::need_relaunch = false;
bool Config::unregister_task_item_pressed = false;
bool Config::is_wiiu_menu = false;
void Config::Init() {
WUPSStorageError storageRes = WUPS_OpenStorage();
if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
}
else {
// Try to get value from storage
if ((storageRes = WUPS_GetBool(nullptr, "connect_to_network", &connect_to_network)) == WUPS_STORAGE_ERROR_NOT_FOUND) {
bool skipPatches = false;
if ((storageRes = WUPS_GetBool(nullptr, "skipPatches", &skipPatches)) != WUPS_STORAGE_ERROR_NOT_FOUND) {
// Migrate old config value
connect_to_network = !skipPatches;
}
// Add the value to the storage if it's missing.
if (WUPS_StoreBool(nullptr, "connect_to_network", connect_to_network) != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to store bool");
}
}
else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
}
// Close storage
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to close storage");
}
}
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);
}
static void connect_to_network_changed(ConfigItemBoolean* item, bool new_value) {
DEBUG_FUNCTION_LINE("New value in skipPatchesChanged: %d", new_value);
if (new_value != Config::connect_to_network) {
Config::need_relaunch = true;
}
Config::connect_to_network = new_value;
WUPS_StoreInt(nullptr, "connect_to_network", Config::connect_to_network);
}
static int32_t unregister_task_item_get_display_value(void *context, char *out_buf, int32_t out_size) {
if(!Config::is_wiiu_menu)
strncpy(out_buf, "From WiiU menu only", out_size);
else
strncpy(out_buf, Config::unregister_task_item_pressed ? "Restart to apply" : "Press A", out_size);
return 0;
}
static void unregister_task_item_pressed_cb(void *context, WUPSConfigButtons button) {
if (!Config::unregister_task_item_pressed && Config::is_wiiu_menu && button == 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) == true)
{
if (nn::act::IsNetworkAccountEx(i) == true)
{
nn::boss::Task task;
nn::act::PersistentId persistentId = nn::act::GetPersistentIdEx(i);
__ct__Q3_2nn4boss4TaskFv(&task);
Initialize__Q3_2nn4boss4TaskFPCcUi(&task, "oltopic", persistentId);
uint32_t res = Unregister__Q3_2nn4boss4TaskFv(&task);
WHBLogPrintf("Unregistered oltopic for: SlotNo %d | Persistent ID %08x -> 0x%08x", i, persistentId, res);
}
}
}
Finalize__Q2_2nn4bossFv();
nn::act::Finalize();
Config::unregister_task_item_pressed = !Config::unregister_task_item_pressed;
Config::need_relaunch = true;
}
}
static bool unregister_task_item_is_movement_allowed(void *context) {
return true;
}
WUPS_GET_CONFIG() {
// We open the storage so we can persist the configuration the user did.
if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to open storage");
return 0;
}
WUPSConfigHandle config;
WUPSConfig_CreateHandled(&config, "Inkay");
WUPSConfigCategoryHandle patching_cat;
WUPSConfig_AddCategoryByNameHandled(config, "Patching", &patching_cat);
WUPSConfigItemBoolean_AddToCategoryHandled(config, patching_cat, "connect_to_network", "Connect to the Pretendo network", Config::connect_to_network, &connect_to_network_changed);
WUPSConfigCategoryHandle boss_cat;
WUPSConfig_AddCategoryByNameHandled(config, "BOSS settings", &boss_cat);
WUPSConfigCallbacks_t unregisterTasksItemCallbacks = {
.getCurrentValueDisplay = unregister_task_item_get_display_value,
.getCurrentValueSelectedDisplay = unregister_task_item_get_display_value,
.onSelected = nullptr,
.restoreDefault = nullptr,
.isMovementAllowed = unregister_task_item_is_movement_allowed,
.callCallback = nullptr,
.onButtonPressed = unregister_task_item_pressed_cb,
.onDelete = nullptr
};
WUPSConfigItemHandle unregisterTasksItem;
WUPSConfigItem_Create(&unregisterTasksItem, "unregister_task_item", "Unregister Wara Wara Plaza BOSS tasks", unregisterTasksItemCallbacks, &Config::unregister_task_item_pressed);
WUPSConfigCategory_AddItem(boss_cat, unregisterTasksItem);
return config;
}
WUPS_CONFIG_CLOSED() {
// Save all changes
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to close storage");
}
if (Config::need_relaunch) {
// Need to reload the console so the patches reset
OSForceFullRelaunch();
SYSLaunchMenu();
Config::need_relaunch = false;
}
}

View file

@ -7,18 +7,18 @@
class Config { class Config {
public: public:
static void Init();
// wups config items
static bool connect_to_network; static bool connect_to_network;
static bool show_startup_toast; // private stuff
static bool need_relaunch;
// private stuff
static bool is_wiiu_menu;
static bool initialized; static bool unregister_task_item_pressed;
static bool shown_warning;
static bool plugin_is_loaded;
static bool block_initialize;
}; };
#endif //INKAY_CONFIG_H #endif //INKAY_CONFIG_H

View file

@ -1,25 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#pragma once
enum class InkayStatus {
Uninitialized, ///< The module isn't initialized
Nintendo, ///< The module is initialized but hasn't applied any patches
Pretendo, ///< The module is initialized and has applied the Pretendo patches
Error = -1 ///< Failed to retrieve the module status
};

View file

@ -1,42 +1,42 @@
#ifndef _PATCHER_H #ifndef _PATCHER_H
#define _PATCHER_H #define _PATCHER_H
typedef struct URL_Patch { typedef struct URL_Patch
{
unsigned int address; unsigned int address;
char url[80]; char url[80];
} URL_Patch; } URL_Patch;
static const URL_Patch url_patches[] = { static const URL_Patch url_patches[] = {
//nim-boss .rodata //nim-boss .rodata
{0xE2282550, "http://pushmorewupshop.spbr.net/pushmore/r/%s"}, {0xE2282550, "http://pushmore.wup.shop.pretendo.cc/pushmore/r/%s"},
{0xE229A0A0, "http://npns-devcapp.spbr.net/bst.dat"}, {0xE229A0A0, "http://npns-dev.c.app.pretendo.cc/bst.dat"},
{0xE229A0D0, "http://npns-devcapp.spbr.net/bst2.dat"}, {0xE229A0D0, "http://npns-dev.c.app.pretendo.cc/bst2.dat"},
{0xE2281964, "https://tagayawupshop.spbr.net/tagaya/versionlist/%s/%s/%s"}, {0xE2281964, "https://tagaya.wup.shop.pretendo.cc/tagaya/versionlist/%s/%s/%s"},
{0xE22819B4, "https://tagayawupshop.spbr.net/tagaya/versionlist/%s/%s/latest_version"}, {0xE22819B4, "https://tagaya.wup.shop.pretendo.cc/tagaya/versionlist/%s/%s/latest_version"},
{0xE2282584, "http://pushmowupshop.spbr.net/pushmo/d/%s/%u"}, {0xE2282584, "http://pushmo.wup.shop.pretendo.cc/pushmo/d/%s/%u"},
{0xE22825B8, "http://pushmowupshop.spbr.net/pushmo/c/%u/%u"}, {0xE22825B8, "http://pushmo.wup.shop.pretendo.cc/pushmo/c/%u/%u"},
{0xE2282DB4, "https://ecswupshop.spbr.net/ecs/services/ECommerceSOAP"}, {0xE2282DB4, "https://ecs.wup.shop.pretendo.cc/ecs/services/ECommerceSOAP"},
{0xE22830A0, "https://ecswupshop.spbr.net/ecs/services/ECommerceSOAP"}, {0xE22830A0, "https://ecs.wup.shop.pretendo.cc/ecs/services/ECommerceSOAP"},
{0xE22830E0, "https://nuswupshop.spbr.net/nus/services/NetUpdateSOAP"}, {0xE22830E0, "https://nus.wup.shop.pretendo.cc/nus/services/NetUpdateSOAP"},
{0xE2299990, "policylist.spbr.net"}, {0xE2299990, "nppl.app.pretendo.cc"},
{0xE229A600, "https://plswupshop.spbr.net/pls/upload"}, {0xE229A600, "https://pls.wup.shop.pretendo.cc/pls/upload"},
{0xE229A6AC, "https://npvk-devapp.spbr.net/reports"}, {0xE229A6AC, "https://npvk-dev.app.pretendo.cc/reports"},
{0xE229A6D8, "https://npvkapp.spbr.net/reports"}, {0xE229A6D8, "https://npvk.app.pretendo.cc/reports"},
{0xE229B1F4, "https://tasksheets.spbr.net/p01/tasksheet/%s/%s/%s/%s?c=%s&l=%s"}, {0xE229B1F4, "https://npts.app.pretendo.cc/p01/tasksheet/%s/%s/%s/%s?c=%s&l=%s"},
{0xE229B238, "https://tasksheets.spbr.net/p01/tasksheet/%s/%s/%s?c=%s&l=%s"}, {0xE229B238, "https://npts.app.pretendo.cc/p01/tasksheet/%s/%s/%s?c=%s&l=%s"},
{0xE22AB2D8, "https://idbe-wupcdn.spbr.net/icondata/%02X/%016llX.idbe"}, {0xE22AB2D8, "https://idbe-wup.cdn.pretendo.cc/icondata/%02X/%016llX.idbe"},
{0xE22AB318, "https://idbe-ctrcdn.spbr.net/icondata/%02X/%016llX.idbe"}, {0xE22AB318, "https://idbe-ctr.cdn.pretendo.cc/icondata/%02X/%016llX.idbe"},
{0xE22AB358, "https://idbe-wupcdn.spbr.net/icondata/%02X/%016llX-%d.idbe"}, {0xE22AB358, "https://idbe-wup.cdn.pretendo.cc/icondata/%02X/%016llX-%d.idbe"},
{0xE22AB398, "https://idbe-ctrcdn.spbr.net/icondata/%02X/%016llX-%d.idbe"}, {0xE22AB398, "https://idbe-ctr.cdn.pretendo.cc/icondata/%02X/%016llX-%d.idbe"},
{0xE22B3EF8, "https://ecscshop.spbr.net"}, {0xE22B3EF8, "https://ecs.c.shop.pretendo.cc"},
{0xE22B3F30, "https://ecscshop.spbr.net/ecs/services/ECommerceSOAP"}, {0xE22B3F30, "https://ecs.c.shop.pretendo.cc/ecs/services/ECommerceSOAP"},
{0xE22B3F70, "https://iascshop.spbr.net/ias/services/IdentityAuthenticationSOAP"}, {0xE22B3F70, "https://ias.c.shop.pretendo.cc/ias/services/IdentityAuthenticationSOAP"},
{0xE22B3FBC, "https://cascshop.spbr.net/cas/services/CatalogingSOAP"}, {0xE22B3FBC, "https://cas.c.shop.pretendo.cc/cas/services/CatalogingSOAP"},
{0xE22B3FFC, "https://nuscshop.spbr.net/nus/services/NetUpdateSOAP"}, {0xE22B3FFC, "https://nus.c.shop.pretendo.cc/nus/services/NetUpdateSOAP"},
{0xE229DE0C, "n.app.spbr.net"}, {0xE229DE0C, "n.app.pretendo.cc"},
//nim-boss .bss //nim-boss .bss
{0xE24B8A24, "https://policylist.spbr.net/p01/policylist/1/1/UNK"}, {0xE31930D4, "https://%s%saccount.pretendo.cc/v%u/api/"}
{0xE31930D4, "https://%s%saccount.spbr.net/v%u/api/"}
}; };

View file

@ -1,18 +0,0 @@
.plugin_name="Inkay"
,.network_category="Netzwerkauswahl"
,.connect_to_network_setting="Verbinde zum SPFN"
,.other_category="Andere Einstellungen"
,.reset_wwp_setting="Wara Wara Plaza zurücksetzen"
,.press_a_action="Drücke A"
,.restart_to_apply_action="Neustarten zum Anwenden"
,.need_menu_action="Nur vom Wii U-Menü aus"
,.using_nintendo_network="Nutze Nintendo Network"
,.using_pretendo_network="Nutze SPFN"
,.multiplayer_port_display="Nutze UDP Port %hu für Mehrspieler"
,.module_not_found="SPFN Patch fehlgeschlagen - nutze Aroma Updater zum Reparieren (686-1001 Module missing)"
,.module_init_not_found="SPFN Patch fehlgeschlagen - nutze Aroma Updater zum Reparieren (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Deutsch"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inky ^w^"
,.network_category="Newowk sewectiown! :3"
,.connect_to_network_setting="use dah pretender!!!!"
,.show_startup_toast_setting="show da deets :3"
,.other_category="oth3r stuffzz"
,.reset_wwp_setting="warah wara go BYEBYE x3c"
,.press_a_action="Press A plz?"
,.restart_to_apply_action="ima restart ur consoel >:3c"
,.need_menu_action="no menu?? only menu -w-"
,.using_nintendo_network="meanie network!!!"
,.using_pretendo_network="pretender netw3rk >:D"
,.multiplayer_port_display="mah secret word is %hu... ur modem will know"
,.module_not_found="oh noez! pls get aroma updater help :( (686-1001 Module missing)"
,.module_init_not_found="oh noez! pls get aroma updater help :( (686-1002 Module init)"
,.setting_yes="yaah!"
,.setting_no="nu >w<"
,.language_setting="spek..."
,.language_name="kitteh speak!!"
,.system_language="System"

View file

@ -1,19 +0,0 @@
.plugin_name="Malamar"
,.network_category="Network selection"
,.connect_to_network_setting="Connect to SPFN"
,.show_startup_toast_setting="Show startup toast"
,.other_category="Other settings"
,.reset_wwp_setting="Reset Wara Wara Plaza"
,.press_a_action="Press A"
,.restart_to_apply_action="Restart to apply"
,.need_menu_action="From WiiU menu only"
,.using_nintendo_network="Using Nintendo Network"
,.using_pretendo_network="Using SPFN"
,.multiplayer_port_display="Using UDP port %hu for multiplayer"
,.module_not_found="SPFN patch failed - use Aroma Updater to repair (686-1001 Module missing)"
,.module_init_not_found="SPFN patch failed - use Aroma Updater to repair (686-1002 Module init)"
,.setting_yes="Yes"
,.setting_no="No"
,.language_setting="Language"
,.language_name="English"
,.system_language="System"

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="Seleccionar red"
,.connect_to_network_setting="Conectar a SPFN"
,.show_startup_toast_setting=""
,.other_category="Otros ajustes"
,.reset_wwp_setting="Restablecer Plaza Wara Wara"
,.press_a_action="Pulsa A"
,.restart_to_apply_action="Reiniciar para aplicar"
,.need_menu_action="Solo desde el Menú de Wii U"
,.using_nintendo_network="Usando Nintendo Network"
,.using_pretendo_network="Usando SPFN"
,.multiplayer_port_display="Usando puerto UDP %hu para multijugador"
,.module_not_found="Ha fallado el parche SPFN - usa Aroma Updater para repararlo (686-1001 Falta un módulo)"
,.module_init_not_found="Ha fallado el parche SPFN - usa Aroma Updater para repararlo (686-1002 Inicialización del módulo)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Español"
,.system_language=""

View file

@ -1,18 +0,0 @@
.plugin_name="Inkay"
,.network_category="Sélection du réseau"
,.connect_to_network_setting="Connexion au SPFN"
,.other_category="Autres paramètres"
,.reset_wwp_setting="Réinitialiser la place WaraWara"
,.press_a_action="Appuyez sur A"
,.restart_to_apply_action="Redémarrer pour appliquer"
,.need_menu_action="(retournez d'abord au menu Wii U)"
,.using_nintendo_network="Réseau sélectionné : Nintendo Network"
,.using_pretendo_network="Réseau sélectionné : SPFN"
,.multiplayer_port_display="Le port UDP %hu sera utilisé pour le multijoueur"
,.module_not_found="Le patch SPFN a rencontré un problème, veuillez le réparer avec Aroma Updater (686-1001 Module missing)"
,.module_init_not_found="Le patch SPFN a rencontré un problème, veuillez le réparer avec Aroma Updater (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Français"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="Seleziona la rete"
,.connect_to_network_setting="Connettiti a SPFN"
,.show_startup_toast_setting=""
,.other_category="Altre impostazioni"
,.reset_wwp_setting="Ripristina Wara Wara Plaza"
,.press_a_action="Premi A"
,.restart_to_apply_action="Riavvia per applicare"
,.need_menu_action="Solo dal menu WiiU"
,.using_nintendo_network="In uso Nintendo Network"
,.using_pretendo_network="In uso SPFN"
,.multiplayer_port_display="È in uso la porta UDP %hu per il multigiocatore"
,.module_not_found="Patch di SPFN fallita - usa Aroma Updater per correggere (686-1001 Modulo mancante)"
,.module_init_not_found="Patch di SPFN fallita - usa Aroma Updater per correggere (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Italiano"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="ネットワークの選択"
,.connect_to_network_setting="SPFNネットワークに接続する"
,.show_startup_toast_setting=""
,.other_category="その他の設定"
,.reset_wwp_setting="わらわら広場をリセット"
,.press_a_action="Aボタンを押そう"
,.restart_to_apply_action="再起動後に反映されます"
,.need_menu_action="Wii Uメニューからのみ実行可能"
,.using_nintendo_network="選択済み:ニンテンドーネットワーク"
,.using_pretendo_network="選択済みSPFN ネットワーク"
,.multiplayer_port_display="マルチプレイに UDPポート %hu を使用します"
,.module_not_found="SPFN パッチに失敗しました。Aroma Updaterを使用して解決してみてください。(686-1001 Module missing)"
,.module_init_not_found="SPFN パッチに失敗しました。Aroma Updaterを使用して解決してみてください。(686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="日本語"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="네트워크 선택"
,.connect_to_network_setting="SPFN 사용"
,.show_startup_toast_setting=""
,.other_category="기타 설정"
,.reset_wwp_setting="Wara Wara Plaza 초기화"
,.press_a_action="A 버튼을 누르세요"
,.restart_to_apply_action="재시작 후 적용"
,.need_menu_action="Wii U 메뉴에서만"
,.using_nintendo_network="Nintendo Network 사용 중"
,.using_pretendo_network="SPFN 사용 중"
,.multiplayer_port_display="멀티플레이어에 UDP 포트 %hu을(를) 사용합니다"
,.module_not_found="패치를 적용하는 데 실패했습니다. Aroma Updater로 복구해 주세요. (686-1001 모듈 없음)"
,.module_init_not_found="패치를 적용하는 데 실패했습니다. Aroma Updater로 복구해 주세요. (686-1002 모듈 초기화)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="한국어"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="Netwerkselectie"
,.connect_to_network_setting="Verbind met het SPFN-netwerk"
,.show_startup_toast_setting=""
,.other_category="Overige instellingen"
,.reset_wwp_setting="Reset het Wara Wara Plaza"
,.press_a_action="Druk A"
,.restart_to_apply_action="Herstart om toe te passen"
,.need_menu_action="Alleen vanuit het WiiU-menu"
,.using_nintendo_network="Nintendo Network wordt gebruikt"
,.using_pretendo_network="SPFN wordt gebruikt"
,.multiplayer_port_display="Gebruikt UDP port %hu voor multiplayer"
,.module_not_found="SPFN patch mislukt - gebruik Aroma Updater om het te herstellen (686-1001 Module ontbreekt)"
,.module_init_not_found="SPFN patch mislukt - gebruik Aroma Updater om het te herstellen (686-1002 Module init)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Nederlands"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="Selecionar rede"
,.connect_to_network_setting="Conecta-se à SPFN"
,.show_startup_toast_setting=""
,.other_category="Outras configurações"
,.reset_wwp_setting="Resetar Wara Wara Plaza"
,.press_a_action="Aperte A"
,.restart_to_apply_action="Reinicie para aplicar"
,.need_menu_action="Apenas no menu do Wii U"
,.using_nintendo_network="Usando Nintendo Network"
,.using_pretendo_network="Usando SPFN"
,.multiplayer_port_display="Usando UDP port %hu para a jogatina multiplayer"
,.module_not_found="Atualização do SPFN falhou - use o Aroma Updater para corrigir (686-1001 Módulo faltando)"
,.module_init_not_found="Atualização do SPFN falhou - use o Aroma Updater para corrigir (686-1002 Módulo de início)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Português"
,.system_language=""

View file

@ -1,19 +0,0 @@
.plugin_name="Inkay"
,.network_category="Выбор сети"
,.connect_to_network_setting="Подключиться к SPFN"
,.show_startup_toast_setting=""
,.other_category="Другие настройки"
,.reset_wwp_setting="Сбросить Wara Wara Plaza"
,.press_a_action="Нажмите A"
,.restart_to_apply_action="Перезагрузите для применения изменений"
,.need_menu_action="Только из меню Wii U"
,.using_nintendo_network="Используется Nintendo Network"
,.using_pretendo_network="Используется SPFN"
,.multiplayer_port_display="Используем UDP-порт %hu для сетевой игры"
,.module_not_found="Ошибка установки патча SPFN - для восстановления, запустите Aroma Updater (686-1001 Модуль отсутствует)"
,.module_init_not_found="Ошибка установки патча SPFN - для восстановления, запустите Aroma Updater (686-1002 Инициализация модуля)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="Русский язык"
,.system_language=""

View file

@ -1,18 +0,0 @@
.plugin_name="Inkay"
,.network_category="选择网络"
,.connect_to_network_setting="连接到SPFN"
,.other_category="其他设置"
,.reset_wwp_setting="重置Wara Wara Plaza"
,.press_a_action="请按 A"
,.restart_to_apply_action="重启以应用设置"
,.need_menu_action="仅来自WiiU Menu"
,.using_nintendo_network="使用 Nintendo Network"
,.using_pretendo_network="使用 SPFN"
,.multiplayer_port_display="多人游戏使用 UDP 端口 %hu"
,.module_not_found="SPFN 补丁失败 - 使用 Aroma Updater 修复686-1001 模块丢失)"
,.module_init_not_found="SPFN 补丁失败 - 使用 Aroma Updater 修复686-1002 模块初始化)"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="中文(简体)"
,.system_language=""

View file

@ -1,20 +0,0 @@
.plugin_name="Inkay"
,.network_category="選擇網路"
,.connect_to_network_setting="連接到SPFN"
,.show_startup_toast_setting=""
,.other_category="其他設定"
,.reset_wwp_setting="重置Wara Wara Plaza"
,.press_a_action="請按 A"
,.restart_to_apply_action="重啓以套用設定"
,.need_menu_action="僅來自WiiU Menu"
,.using_nintendo_network="使用 Nintendo Network"
,.using_pretendo_network="使用 SPFN"
,.multiplayer_port_display="Using UDP port %hu for multiplayer"
,.module_not_found="SPFN patch failed - use Aroma Updater to repair (686-1001 Module missing)"
,.module_init_not_found="SPFN patch failed - use Aroma Updater to repair (686-1002 Module init)"
,.multiplayer_port_display="使用 UDP 端口 %hu 進行多人遊戲"
,.setting_yes=""
,.setting_no=""
,.language_setting=""
,.language_name="中文(繁體)"
,.system_language=""

View file

@ -2,108 +2,84 @@
Copyright 2022 Ash Logan <ash@heyquark.com> Copyright 2022 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell Copyright 2019 Maschell
This program is free software: you can redistribute it and/or modify Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
it under the terms of the GNU General Public License as published by granted, provided that the above copyright notice and this permission notice appear in all copies.
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, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
but WITHOUT ANY WARRANTY; without even the implied warranty of IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
GNU General Public License for more details. IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "export.h" #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <wups.h>
#include <optional>
#include <nsysnet/nssl.h>
#include <coreinit/cache.h>
#include <coreinit/dynload.h>
#include <coreinit/mcp.h>
#include <coreinit/memory.h>
#include <coreinit/memorymap.h>
#include <coreinit/memexpheap.h>
#include <notifications/notifications.h>
#include <utils/logger.h>
#include "iosu_url_patches.h" #include "iosu_url_patches.h"
#include "config.h" #include "config.h"
#include "Notification.h" #include "Notification.h"
#include "patches/olv_urls.h" #include "patches/olv_urls.h"
#include "patches/game_matchmaking.h" #include "patches/game_matchmaking.h"
#include <wums.h> #include <coreinit/filesystem.h>
#include <coreinit/dynload.h>
#include <coreinit/mcp.h>
#include <notifications/notifications.h>
#include <utils/logger.h>
#include <string>
#include <optional>
#include <cstring> #include <cstring>
#include <cstdint> #include <string>
#include <nn/erreula/erreula_cpp.h>
#include <nn/act/client_cpp.h>
#include <curl/curl.h>
#include "ca_pem.h" #include "ca_pem.h"
#define INKAY_VERSION "v3.1.0" #include <gx2/surface.h>
/** /**
Mandatory plugin information. Mandatory plugin information.
If not set correctly, the loader will refuse to use the plugin. If not set correctly, the loader will refuse to use the plugin.
**/ **/
WUMS_META(export_name, "inkay"); WUPS_PLUGIN_NAME("Inkay");
WUMS_META(wums, WUMS_VERSION); WUPS_PLUGIN_DESCRIPTION("Pretendo Network Patcher");
WUMS_USE_WUT_MALLOC(); WUPS_PLUGIN_VERSION("v2.3");
WUMS_USE_WUT_SOCKETS(); WUPS_PLUGIN_AUTHOR("Pretendo contributors");
WUMS_USE_WUT_NEWLIB(); WUPS_PLUGIN_LICENSE("ISC");
WUMS___INIT_WRAPPER();
WUMS___FINI_WRAPPER();
WUMS_META(buildtimestamp, __DATE__ " " __TIME__);
extern const char wums_meta_module_name[] WUMS_SECTION("meta");
const char wums_meta_module_name[] = "inkay";
extern const char wums_meta_info_dump[] WUMS_SECTION("meta");
const char wums_meta_info_dump[] = "(module: inkay;" \
"wums " WUMS_VERSION ";" \
"buildtime: " __DATE__ " " __TIME__ ")";
extern const char wums_meta_info_linking_order[] WUMS_SECTION("meta");
const char wums_meta_info_linking_order[] = "Loading module \"inkay\" failed.\n" \
"Function \"wut_get_thread_specific\" returned unexpected value.\n" \
"Please check linking order (expected \"-lwums -lwut\")";
WUMS_MODULE_DESCRIPTION("SPFN Network Patcher"); WUPS_USE_STORAGE("inkay");
WUMS_MODULE_VERSION(INKAY_VERSION); WUPS_USE_WUT_DEVOPTAB();
WUMS_MODULE_AUTHOR("Pretendo contributors and RusticMaple");
WUMS_MODULE_LICENSE("GPLv3");
WUMS_DEPENDS_ON(homebrew_functionpatcher);
WUMS_DEPENDS_ON(homebrew_kernel);
WUMS_DEPENDS_ON(homebrew_notifications);
WUMS_USE_WUT_DEVOPTAB();
#include <kernel/kernel.h>
#include <mocha/mocha.h> #include <mocha/mocha.h>
#include <function_patcher/function_patching.h> #include <function_patcher/function_patching.h>
#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 "sysconfig.h"
#include "lang.h"
//thanks @Gary#4139 :p //thanks @Gary#4139 :p
static void write_string(uint32_t addr, const char *str) { static void write_string(uint32_t addr, const char* str)
{
int len = strlen(str) + 1; int len = strlen(str) + 1;
int remaining = len % 4; int remaining = len % 4;
int num = len - remaining; int num = len - remaining;
for (int i = 0; i < (num / 4); i++) { for (int i = 0; i < (num / 4); i++) {
Mocha_IOSUKernelWrite32(addr + i * 4, *(uint32_t * )(str + i * 4)); Mocha_IOSUKernelWrite32(addr + i * 4, *(uint32_t*)(str + i * 4));
} }
if (remaining > 0) { if (remaining > 0) {
uint8_t buf[4]; uint8_t buf[4];
Mocha_IOSUKernelRead32(addr + num, (uint32_t * ) & buf); Mocha_IOSUKernelRead32(addr + num, (uint32_t*)&buf);
for (int i = 0; i < remaining; i++) { for (int i = 0; i < remaining; i++) {
buf[i] = *(str + num + i); buf[i] = *(str + num + i);
} }
Mocha_IOSUKernelWrite32(addr + num, *(uint32_t * ) & buf); Mocha_IOSUKernelWrite32(addr + num, *(uint32_t*)&buf);
} }
} }
@ -111,95 +87,15 @@ static bool is555(MCPSystemVersion version) {
return (version.major == 5) && (version.minor == 5) && (version.patch >= 5); return (version.major == 5) && (version.minor == 5) && (version.patch >= 5);
} }
static const char *get_nintendo_network_message(inkay_language language) { INITIALIZE_PLUGIN() {
// TL note: "Nintendo Network" is a proper noun - "Network" is part of the name
// TL note: "Using" instead of "Connected" is deliberate - we don't know if a successful connection exists, we are
// only specifying what we'll *attempt* to connect to
return get_config_strings(language).using_nintendo_network.data();
}
static const char *get_pretendo_message(inkay_language language) {
// TL note: "Pretendo Network" is also a proper noun - though "Pretendo" alone can refer to us as a project
// TL note: "Using" instead of "Connected" is deliberate - we don't know if a successful connection exists, we are
// only specifying what we'll *attempt* to connect to
return get_config_strings(language).using_pretendo_network.data();
}
static void Inkay_SetPluginRunning() {
Config::plugin_is_loaded = true;
}
static InkayStatus Inkay_GetStatus() {
if (!Config::initialized)
return InkayStatus::Uninitialized;
if (Config::connect_to_network) {
return InkayStatus::Pretendo;
} else {
return InkayStatus::Nintendo;
}
}
static void Inkay_Initialize(bool apply_patches, bool show_startup_toast, inkay_language language) {
if (Config::initialized)
return;
Config::show_startup_toast = show_startup_toast;
if (Config::block_initialize) {
ShowNotification("Cannot load Inkay while the system is running. Please restart the console");
return;
}
// if using pretendo then (try to) apply the ssl patches
if (apply_patches) {
Config::connect_to_network = true;
if (is555(get_console_os_version())) {
Mocha_IOSUKernelWrite32(0xE1019F78, 0xE3A00001); // mov r0, #1
} else {
Mocha_IOSUKernelWrite32(0xE1019E84, 0xE3A00001); // mov r0, #1
}
for (const auto &patch: url_patches) {
write_string(patch.address, patch.url);
}
// IOS-NIM-BOSS GlobalPolicyList->state: poking this forces a refresh after we changed the url
Mocha_IOSUKernelWrite32(0xE24B3D90, 4);
DEBUG_FUNCTION_LINE_VERBOSE("Pretendo URL and NoSSL patches applied successfully.");
if (Config::show_startup_toast) {
ShowNotification(get_pretendo_message(language));
}
Config::initialized = true;
} else {
DEBUG_FUNCTION_LINE_VERBOSE("Pretendo URL and NoSSL patches skipped.");
if(Config::show_startup_toast) {
ShowNotification(get_nintendo_network_message(language));
}
Config::initialized = true;
return;
}
if (FunctionPatcher_InitLibrary() == FUNCTION_PATCHER_RESULT_SUCCESS) {
patchDNS();
patchEshop();
patchOlvApplet();
patchAccountSettings();
install_matchmaking_patches();
} else {
DEBUG_FUNCTION_LINE("FunctionPatcher_InitLibrary failed");
}
}
WUMS_INITIALIZE() {
WHBLogCafeInit();
WHBLogUdpInit(); WHBLogUdpInit();
WHBLogCafeInit();
if (const auto res = Mocha_InitLibrary(); res != MOCHA_RESULT_SUCCESS) { Config::Init();
auto res = Mocha_InitLibrary();
if (res != MOCHA_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res); DEBUG_FUNCTION_LINE("Mocha init failed with code %d!", res);
return; return;
} }
@ -207,56 +103,66 @@ WUMS_INITIALIZE() {
if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) { if (NotificationModule_InitLibrary() != NOTIFICATION_MODULE_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed"); DEBUG_FUNCTION_LINE("NotificationModule_InitLibrary failed");
} }
}
WUMS_DEINITIALIZE() { //get os version
unpatchDNS(); MCPSystemVersion os_version;
unpatchEshop(); int mcp = MCP_Open();
unpatchOlvApplet(); int ret = MCP_GetSystemVersion(mcp, &os_version);
unpatchAccountSettings(); if (ret < 0) {
DEBUG_FUNCTION_LINE("getting system version failed (%d/%d)!", mcp, ret);
os_version = (MCPSystemVersion) {
.major = 5, .minor = 5, .patch = 5, .region = 'E'
};
}
DEBUG_FUNCTION_LINE("Running on %d.%d.%d%c",
os_version.major, os_version.minor, os_version.patch, os_version.region
);
if (Config::connect_to_network) {
if (is555(os_version)) {
Mocha_IOSUKernelWrite32(0xE1019F78, 0xE3A00001); // mov r0, #1
}
else {
Mocha_IOSUKernelWrite32(0xE1019E84, 0xE3A00001); // mov r0, #1
}
for (const auto& patch : url_patches) {
write_string(patch.address, patch.url);
}
DEBUG_FUNCTION_LINE("Pretendo URL and NoSSL patches applied successfully.");
StartNotificationThread("Using Pretendo Network");
}
else {
DEBUG_FUNCTION_LINE("Pretendo URL and NoSSL patches skipped.");
StartNotificationThread("Using Nintendo Network");
}
MCP_Close(mcp);
if (FunctionPatcher_InitLibrary() == FUNCTION_PATCHER_RESULT_SUCCESS) {
install_matchmaking_patches();
}
}
DEINITIALIZE_PLUGIN() {
remove_matchmaking_patches(); remove_matchmaking_patches();
WHBLogUdpDeinit();
Mocha_DeInitLibrary(); Mocha_DeInitLibrary();
NotificationModule_DeInitLibrary(); NotificationModule_DeInitLibrary();
FunctionPatcher_DeInitLibrary(); FunctionPatcher_DeInitLibrary();
WHBLogCafeDeinit();
WHBLogUdpDeinit();
} }
WUMS_APPLICATION_STARTS() { ON_APPLICATION_START() {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay " INKAY_VERSION " starting up...\n"); WHBLogUdpInit();
WHBLogCafeInit();
// Reset plugin loaded flag DEBUG_FUNCTION_LINE("Inkay: hewwo!\n");
Config::plugin_is_loaded = false;
}
WUMS_ALL_APPLICATION_STARTS_DONE() {
// we need to do the patches here because otherwise the Config::connect_to_network flag might be set yet
setup_olv_libs(); setup_olv_libs();
peertopeer_patch();
matchmaking_notify_titleswitch(); matchmaking_notify_titleswitch();
hotpatchAccountSettings();
if (Config::initialized && !Config::plugin_is_loaded) {
DEBUG_FUNCTION_LINE("Inkay is running but the plugin got unloaded");
if (!Config::block_initialize) {
ShowNotification("Inkay module is still running. Please restart the console");
}
Config::shown_warning = true;
} else if (!Config::initialized && !Config::shown_warning) {
DEBUG_FUNCTION_LINE("Inkay module not initialized");
ShowNotification("Inkay module was not initialized. Ensure you have the Inkay plugin loaded");
Config::shown_warning = true;
}
if (!Config::initialized) {
Config::block_initialize = true;
}
} }
WUMS_APPLICATION_ENDS() { ON_APPLICATION_ENDS() {
DEBUG_FUNCTION_LINE("Inkay: shutting down...\n");
StopNotificationThread();
} }
WUMS_EXPORT_FUNCTION(Inkay_Initialize);
WUMS_EXPORT_FUNCTION(Inkay_GetStatus);
WUMS_EXPORT_FUNCTION(Inkay_SetPluginRunning);

View file

@ -1,170 +0,0 @@
/* Copyright 2023 Pretendo Network contributors <pretendo.network>
Copyright 2023 Jemma Poffinbarger <jemma@jemsoftware.dev>
Copyright 2023 Ash Logan <ash@heyquark.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "olv_urls.h"
#include "utils/logger.h"
#include "utils/replace_mem.h"
#include "inkay_config.h"
#include <function_patcher/function_patching.h>
#include <coreinit/filesystem.h>
#include <coreinit/title.h>
#include <vector>
#include <optional>
#include "ca_pem.h" // generated at buildtime
#define ACCOUNT_SETTINGS_TID_J 0x000500101004B000
#define ACCOUNT_SETTINGS_TID_U 0x000500101004B100
#define ACCOUNT_SETTINGS_TID_E 0x000500101004B200
struct account_settings_allowlist {
char scheme[16];
char domain[128];
char path[128]; // unverified
uint32_t flags;
};
constexpr struct account_settings_allowlist original_entry = {
.scheme = "https",
.domain = "account.nintendo.net",
.path = "",
.flags = 0x01010101,
};
constexpr struct account_settings_allowlist new_entry = {
.scheme = "https",
.domain = "account." NETWORK_BASEURL,
.path = "",
.flags = 0x01010101,
};
constexpr char wave_original[] = "saccount.nintendo.net";
constexpr char wave_new[] = "saccount." NETWORK_BASEURL;
static bool isAccountSettingsTitle() {
return (OSGetTitleID() != 0 && (
OSGetTitleID() == ACCOUNT_SETTINGS_TID_J ||
OSGetTitleID() == ACCOUNT_SETTINGS_TID_U ||
OSGetTitleID() == ACCOUNT_SETTINGS_TID_E
));
}
static std::optional<FSFileHandle> rootca_pem_handle{};
std::vector<PatchedFunctionHandle> account_patches;
DECL_FUNCTION(int, FSOpenFile_accSettings, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle,
int error) {
if(!isAccountSettingsTitle()) {
return real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
}
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: account settings patches skipped.");
return real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
}
// Check for root CA file and take note of its handle
if (strcmp("vol/content/browser/rootca.pem", path) == 0) {
int ret = real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
rootca_pem_handle = *handle;
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found account settings CA, replacing...");
return ret;
}
return real_FSOpenFile_accSettings(client, block, path, mode, handle, error);
}
DECL_FUNCTION(FSStatus, FSReadFile_accSettings, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count,
FSFileHandle handle, uint32_t unk1, uint32_t flags) {
if(!isAccountSettingsTitle()) {
return real_FSReadFile_accSettings(client, block, buffer, size, count, handle, unk1, flags);
}
if (size != 1) {
DEBUG_FUNCTION_LINE("Inkay: account settings CA replacement failed!");
}
if (rootca_pem_handle && *rootca_pem_handle == handle) {
strlcpy((char *) buffer, (const char *) ca_pem, size * count);
return (FSStatus) count;
}
return real_FSReadFile_accSettings(client, block, buffer, size, count, handle, unk1, flags);
}
DECL_FUNCTION(FSStatus, FSCloseFile_accSettings, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
if(!isAccountSettingsTitle()) {
return real_FSCloseFile_accSettings(client, block, handle, errorMask);
}
if (handle == rootca_pem_handle) {
rootca_pem_handle.reset();
}
return real_FSCloseFile_accSettings(client, block, handle, errorMask);
}
bool patchAccountSettings() {
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, FSCloseFile, FP_TARGET_PROCESS_GAME), "FSCloseFile_accSettings");
return true;
}
bool hotpatchAccountSettings() {
if(!isAccountSettingsTitle()) {
return false;
}
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: account settings patches skipped.");
return false;
}
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: hewwo account settings!\n");
if (!replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new))) {
DEBUG_FUNCTION_LINE("Inkay: We didn't find the url /)>~<(\\");
return false;
}
if (!replace(0x10000000, 0x10000000, (const char *)&original_entry, sizeof(original_entry), (const char *)&new_entry, sizeof(new_entry))) {
DEBUG_FUNCTION_LINE("Inkay: We didn't find the whitelist /)>~<(\\");
return false;
}
return true;
}
void unpatchAccountSettings() {
for (const auto handle: account_patches) {
FunctionPatcher_RemoveFunctionPatch(handle);
}
account_patches.clear();
}

View file

@ -1,20 +0,0 @@
/* Copyright 2023 Pretendo Network contributors <pretendo.network>
Copyright 2023 Jemma Poffinbarger <jemma@jemsoftware.dev>
Copyright 2023 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
bool patchAccountSettings();
bool hotpatchAccountSettings();
void unpatchAccountSettings();

View file

@ -1,76 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
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 <http://www.gnu.org/licenses/>.
*/
#include <netdb.h>
#include "config.h"
#include "utils/logger.h"
#include "inkay_config.h"
#include <array>
#include <vector>
#include <function_patcher/function_patching.h>
std::vector<PatchedFunctionHandle> dns_patches;
constexpr std::pair<const char *, const char *> dns_replacements[] = {
// NNCS servers
{ "nncs1.app.nintendowifi.net", "nncs1.app." NETWORK_BASEURL },
{ "nncs2.app.nintendowifi.net", "nncs2.app." NETWORK_BASEURL },
};
static const char * replace_dns_name(const char *dns_name) {
if (!Config::connect_to_network) return dns_name;
for (auto [original, replacement] : dns_replacements) {
if (strcmp(original, dns_name) == 0)
return replacement;
}
return dns_name;
}
DECL_FUNCTION(struct hostent *, gethostbyname, const char *dns_name) {
return real_gethostbyname(replace_dns_name(dns_name));
}
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);
}
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();
}

View file

@ -1,20 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#pragma once
void patchDNS();
void unpatchDNS();

View file

@ -1,136 +0,0 @@
/* Copyright 2023 Pretendo Network contributors <pretendo.network>
Copyright 2023 Ash Logan <ash@heyquark.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "olv_urls.h"
#include "utils/logger.h"
#include "utils/replace_mem.h"
#include "inkay_config.h"
#include <vector>
#include <function_patcher/function_patching.h>
#include <optional>
#include <coreinit/debug.h>
#include <coreinit/filesystem.h>
#include <nsysnet/nssl.h>
#include "ca_pem.h" // generated at buildtime
constexpr char wave_original[] = "https://ninja.wup.shop.nintendo.net/ninja/wood_index.html?";
constexpr char wave_new[] = "http://samurai.wup.shop." NETWORK_BASEURL "/ninja/wood_index.html?";
struct eshop_allowlist {
char scheme[16];
char domain[128];
char path[128]; // unverified
unsigned char flags[5];
};
constexpr struct eshop_allowlist original_entry = {
.scheme = "https",
.domain = "samurai.wup.shop.nintendo.net",
.path = "",
.flags = {1, 1, 1, 1, 0},
};
constexpr struct eshop_allowlist new_entry = {
.scheme = "http",
.domain = "samurai.wup.shop." NETWORK_BASEURL,
.path = "",
.flags = {1, 1, 1, 1, 0},
};
static std::optional<FSFileHandle> rootca_pem_handle{};
std::vector<PatchedFunctionHandle> eshop_patches;
DECL_FUNCTION(int, FSOpenFile_eShop, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle,
int error) {
const char *initialOma = "vol/content/initial.oma";
if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: eShop patches skipped.");
return real_FSOpenFile_eShop(client, block, path, mode, handle, error);
}
if (strcmp(initialOma, path) == 0) {
//below is a hacky (yet functional!) way to get Inkay to redirect URLs from the Miiverse applet
//we do it when loading this file since it should only load once, preventing massive lag spikes as it searches all of MEM2 xD
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: hewwo eShop!\n");
if (!replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new)))
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: We didn't find the url /)>~<(\\");
if (!replace(0x10000000, 0x10000000, (const char *)&original_entry, sizeof(original_entry), (const char *)&new_entry, sizeof(new_entry)))
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: We didn't find the whitelist /)>~<(\\");
// Check for root CA file and take note of its handle
} else if (strcmp("vol/content/browser/rootca.pem", path) == 0) {
int ret = real_FSOpenFile_eShop(client, block, path, mode, handle, error);
rootca_pem_handle = *handle;
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found eShop CA, replacing...");
return ret;
}
return real_FSOpenFile_eShop(client, block, path, mode, handle, error);
}
DECL_FUNCTION(FSStatus, FSReadFile_eShop, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count,
FSFileHandle handle, uint32_t unk1, uint32_t flags) {
if (size != 1) {
DEBUG_FUNCTION_LINE("Inkay: eShop CA replacement failed!");
}
if (rootca_pem_handle && *rootca_pem_handle == handle) {
strlcpy((char *) buffer, (const char *) ca_pem, size * count);
return (FSStatus) count;
}
return real_FSReadFile_eShop(client, block, buffer, size, count, handle, unk1, flags);
}
DECL_FUNCTION(FSStatus, FSCloseFile_eShop, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
if (handle == rootca_pem_handle) {
rootca_pem_handle.reset();
}
return real_FSCloseFile_eShop(client, block, handle, errorMask);
}
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();
}

View file

@ -1,20 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#pragma once
void patchEshop();
void unpatchEshop();

View file

@ -2,22 +2,18 @@
Copyright 2023 Ash Logan <ash@heyquark.com> Copyright 2023 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell Copyright 2019 Maschell
This program is free software: you can redistribute it and/or modify Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
it under the terms of the GNU General Public License as published by granted, provided that the above copyright notice and this permission notice appear in all copies.
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, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
but WITHOUT ANY WARRANTY; without even the implied warranty of IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
GNU General Public License for more details. IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h"
#include "game_matchmaking.h" #include "game_matchmaking.h"
#include "config.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "ini.h" #include "ini.h"
@ -58,7 +54,7 @@ static int handler(void *user, const char *section, const char *name, const char
static void check_modpack() { static void check_modpack() {
modpack mod; modpack mod;
if (ini_parse("fs:/vol/content/pretendo.ini", handler, &mod)) { if (ini_parse("fs:/vol/content/pretendo.ini", handler, &mod)) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay/MK8: Doesn't look like a modpack"); DEBUG_FUNCTION_LINE("Inkay/MK8: Doesn't look like a modpack");
} }
DEBUG_FUNCTION_LINE("Inkay/MK8: Playing %s (%08x)", mod.name.c_str(), mod.dlc_id); DEBUG_FUNCTION_LINE("Inkay/MK8: Playing %s (%08x)", mod.name.c_str(), mod.dlc_id);
@ -72,7 +68,7 @@ DECL_FUNCTION(void, mk8_MatchmakeSessionSearchCriteria_SetAttribute, void *_this
const int dlc_id = dlc_modpack->dlc_id; const int dlc_id = dlc_modpack->dlc_id;
if (dlc_id != -1) { if (dlc_id != -1) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay/MK8: Searching for %s session (%08x)", dlc_modpack->name.c_str(), dlc_id); DEBUG_FUNCTION_LINE("Inkay/MK8: Searching for %s session (%08x)", dlc_modpack->name.c_str(), dlc_id);
attributeValue = dlc_id; attributeValue = dlc_id;
} }
} }
@ -86,7 +82,7 @@ DECL_FUNCTION(void, mk8_MatchmakeSession_SetAttribute, void *_this, uint32_t att
const int dlc_id = dlc_modpack->dlc_id; const int dlc_id = dlc_modpack->dlc_id;
if (dlc_id != -1) { if (dlc_id != -1) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay/MK8: Creating %s session (%08x)", dlc_modpack->name.c_str(), dlc_id); DEBUG_FUNCTION_LINE("Inkay/MK8: Creating %s session (%08x)", dlc_modpack->name.c_str(), dlc_id);
attributeValue = dlc_id; attributeValue = dlc_id;
} }
} }

View file

@ -1,131 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <coreinit/title.h>
#include <coreinit/dynload.h>
#include "game_peertopeer.h"
#include "config.h"
#include "sysconfig.h"
#include "utils/logger.h"
#include "utils/rpl_info.h"
#include "utils/replace_mem.h"
#include <optional>
#include <algorithm>
#include <string_view>
using namespace std::string_view_literals;
static struct {
std::array<uint64_t, 3> tid;
uint16_t version;
uint32_t min_port_addr;
uint32_t max_port_addr;
std::string_view rpx;
} generic_patch_games[] = {
{
// MARIO KART 8
{0x00050000'1010ec00, 0x00050000'1010ed00, 0x00050000'1010eb00},
81,
0x101a9a52,
0x101a9a54,
"Turbo.rpx"sv,
},
{
// Splatoon
{0x00050000'10176900, 0x00050000'10176a00, 0x00050000'10162b00},
288,
0x101e8952,
0x101e8954,
"Gambit.rpx"sv,
},
};
static void generic_peertopeer_patch() {
uint64_t tid = OSGetTitleID();
uint16_t title_version = 0;
if (const auto version_opt = get_current_title_version(); !version_opt) {
DEBUG_FUNCTION_LINE("Failed to detect current title version");
return;
} else {
title_version = *version_opt;
DEBUG_FUNCTION_LINE("Title version detected: %d", title_version);
}
for (const auto &patch: generic_patch_games) {
if (std::ranges::find(patch.tid, tid) == patch.tid.end()) continue;
std::optional<OSDynLoad_NotifyData> game = search_for_rpl(patch.rpx);
if (!game) {
DEBUG_FUNCTION_LINE("Couldn't find game rpx! (%s)", patch.rpx.data());
return;
}
if (title_version != patch.version) {
DEBUG_FUNCTION_LINE("Unexpected title version. Expected %d but got %d (%s)", patch.version, title_version,
patch.rpx.data());
continue;
}
auto port = get_console_peertopeer_port();
DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, game->textAddr);
auto target = (uint16_t *)rpl_addr(*game, patch.min_port_addr);
replace_unsigned<uint16_t>(target, 0xc000, port);
target = (uint16_t *)rpl_addr(*game, patch.max_port_addr);
replace_unsigned<uint16_t>(target, 0xffff, port);
break;
}
}
static void minecraft_peertopeer_patch() {
std::optional<OSDynLoad_NotifyData> minecraft = search_for_rpl("Minecraft.Client.rpx"sv);
if (!minecraft) {
DEBUG_FUNCTION_LINE("Couldn't find minecraft rpx!");
return;
}
if (const auto version_opt = get_current_title_version(); !version_opt || *version_opt != 688) {
DEBUG_FUNCTION_LINE("Wrong mincecraft version detected");
return;
}
auto port = get_console_peertopeer_port();
DEBUG_FUNCTION_LINE_VERBOSE("Will use port %d. %08x", port, minecraft->textAddr);
auto target_func = (uint32_t *)rpl_addr(*minecraft, 0x03579530);
replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0
replace_instruction(&target_func[1], 0x3863c000, 0x60630000 | port); // ori r3, r3, port
// blr
target_func = (uint32_t *)rpl_addr(*minecraft, 0x0357953c);
replace_instruction(&target_func[0], 0x3c600001, 0x3c600000); // li r3, 0
replace_instruction(&target_func[1], 0x3863ffff, 0x60630000 | port); // ori r3, r3, port
// blr
}
void peertopeer_patch() {
if (!Config::connect_to_network) {
return;
}
uint64_t tid = OSGetTitleID();
if (tid == 0x00050000'101D7500 || // EUR
tid == 0x00050000'101D9D00 || // USA
tid == 0x00050000'101DBE00) { // JPN
minecraft_peertopeer_patch();
} else {
generic_peertopeer_patch();
}
}

View file

@ -1,16 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
void peertopeer_patch();

View file

@ -2,18 +2,14 @@
Copyright 2023 Ash Logan <ash@heyquark.com> Copyright 2023 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell Copyright 2019 Maschell
This program is free software: you can redistribute it and/or modify Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
it under the terms of the GNU General Public License as published by granted, provided that the above copyright notice and this permission notice appear in all copies.
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, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
but WITHOUT ANY WARRANTY; without even the implied warranty of IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
GNU General Public License for more details. IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h" #include "config.h"
@ -21,36 +17,24 @@
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/replace_mem.h" #include "utils/replace_mem.h"
#include <vector> #include <wups.h>
#include <optional> #include <optional>
#include <coreinit/debug.h> #include <coreinit/debug.h>
#include <coreinit/filesystem.h> #include <coreinit/filesystem.h>
#include <nsysnet/nssl.h> #include <nsysnet/nssl.h>
#include <function_patcher/function_patching.h>
#include "ca_pem.h" // generated at buildtime #include "ca_pem.h" // generated at buildtime
struct olv_allowlist { const char wave_original[] = {
char scheme[16]; 0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
char domain[128]; 0x00, 0x00, 0x00, 0x00, 0x2E, 0x6E, 0x69, 0x6E, 0x74, 0x65, 0x6E, 0x64,
char path[128]; // unverified 0x6F, 0x2E, 0x6E, 0x65, 0x74
unsigned char flags[5];
}; };
const char wave_new[] = {
constexpr struct olv_allowlist original_entry = { 0x68, 0x74, 0x74, 0x70, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
.scheme = "https", 0x00, 0x00, 0x00, 0x00, 0x2E, 0x70, 0x72, 0x65, 0x74, 0x65, 0x6E, 0x64,
.domain = ".nintendo.net", 0x6F, 0x2E, 0x63, 0x63, 0x00
.path = "",
.flags = {1, 1, 1, 1, 1},
}; };
constexpr struct olv_allowlist new_entry = {
.scheme = "https",
.domain = ".spbr.net",
.path = "",
.flags = {1, 1, 1, 1, 1},
};
const unsigned char miiverse_green_highlight[] = { const unsigned char miiverse_green_highlight[] = {
0x82, 0xff, 0x05, 0xff, 0x82, 0xff, 0x05, 0xff, 0x1d, 0xff, 0x04, 0xff, 0x1d, 0xff, 0x04, 0xff 0x82, 0xff, 0x05, 0xff, 0x82, 0xff, 0x05, 0xff, 0x1d, 0xff, 0x04, 0xff, 0x1d, 0xff, 0x04, 0xff
}; };
@ -77,15 +61,13 @@ const replacement replacements[] = {
}; };
static std::optional<FSFileHandle> rootca_pem_handle{}; static std::optional<FSFileHandle> rootca_pem_handle{};
static std::optional<FSFileHandle> valid_tld_handle{};
std::vector<PatchedFunctionHandle> olv_patches;
DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle, DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path, const char *mode, uint32_t *handle,
int error) { int error) {
const char *initialOma = "vol/content/initial.oma"; const char *initialOma = "vol/content/initial.oma";
if (!Config::connect_to_network) { if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Miiverse patches skipped."); DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
return real_FSOpenFile(client, block, path, mode, handle, error); return real_FSOpenFile(client, block, path, mode, handle, error);
} }
@ -94,25 +76,17 @@ DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path,
//we do it when loading this file since it should only load once, preventing massive lag spikes as it searches all of MEM2 xD //we do it when loading this file since it should only load once, preventing massive lag spikes as it searches all of MEM2 xD
//WHBLogUdpInit(); //WHBLogUdpInit();
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: hewwo!\n"); DEBUG_FUNCTION_LINE("Inkay: hewwo!\n");
auto olv_ok = setup_olv_libs(); auto olv_ok = setup_olv_libs();
// Patch applet binary too // Patch applet binary too
if (olv_ok) if (olv_ok)
replace(0x10000000, 0x10000000, (const char *)&original_entry, sizeof(original_entry), (const char *)&new_entry, sizeof(new_entry)); replace(0x10000000, 0x10000000, wave_original, sizeof(wave_original), wave_new, sizeof(wave_new));
// Check for root CA file and take note of its handle // Check for root CA file and take note of its handle
} else if (strcmp("vol/content/browser/rootca.pem", path) == 0) { } else if (strcmp("vol/content/browser/rootca.pem", path) == 0) {
int ret = real_FSOpenFile(client, block, path, mode, handle, error); int ret = real_FSOpenFile(client, block, path, mode, handle, error);
rootca_pem_handle = *handle; rootca_pem_handle = *handle;
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found Miiverse CA, replacing..."); DEBUG_FUNCTION_LINE("Inkay: Found Miiverse CA, replacing...");
return ret;
}
else if (strcmp("vol/content/browser/effective_tld_names.dat", path) == 0)
{
// we patch the TLD because .cafe isnt on the list
int ret = real_FSOpenFile(client, block, path, mode, handle, error);
valid_tld_handle = *handle;
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Found Miiverse TLD List, patching...");
return ret; return ret;
} }
@ -120,83 +94,52 @@ DECL_FUNCTION(int, FSOpenFile, FSClient *client, FSCmdBlock *block, char *path,
} }
DECL_FUNCTION(FSStatus, FSReadFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count, DECL_FUNCTION(FSStatus, FSReadFile, FSClient *client, FSCmdBlock *block, uint8_t *buffer, uint32_t size, uint32_t count,
FSFileHandle handle, uint32_t unk1, uint32_t flags) FSFileHandle handle, uint32_t unk1, uint32_t flags) {
{ if (size != 1) {
if (size != 1) DEBUG_FUNCTION_LINE("Inkay: Miiverse CA replacement failed!");
{
DEBUG_FUNCTION_LINE("Inkay: Miiverse CA/LTD replacement failed!");
} }
if (rootca_pem_handle && *rootca_pem_handle == handle) if (rootca_pem_handle && *rootca_pem_handle == handle) {
{ strlcpy((char *) buffer, (const char *) ca_pem, size * count);
strlcpy((char *)buffer, (const char *)ca_pem, size * count);
// this can't be done above (in the FSOpenFile hook) since it's not loaded yet. //this can't be done above (in the FSOpenFile hook) since it's not loaded yet.
// the hardcoded offsets suck but they really are at Random Places In The Heap //the hardcoded offsets suck but they really are at Random Places In The Heap
return (FSStatus)count; replaceBulk(0x11000000, 0x02000000, replacements);
} return (FSStatus) count;
else if (valid_tld_handle && *valid_tld_handle == handle)
{
// we patch the tld "aero"...
FSStatus ret = real_FSReadFile(
client, block, buffer, size, count, handle, unk1, flags);
if (ret > 0)
{
uint32_t totalSize = size * count;
char *buf = (char *)buffer;
for (uint32_t i = 0; i + 4 <= totalSize; i++)
{
if (memcmp(&buf[i], "aero", 4) == 0)
{
memcpy(&buf[i], "cafe", 4);
DEBUG_FUNCTION_LINE_VERBOSE(
"Inkay: patched TLD aero -> cafe");
}
}
}
return ret;
} }
return real_FSReadFile(client, block, buffer, size, count, handle, unk1, flags); return real_FSReadFile(client, block, buffer, size, count, handle, unk1, flags);
} }
DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) { DECL_FUNCTION(FSStatus, FSCloseFile, FSClient *client, FSCmdBlock *block, FSFileHandle handle, FSErrorFlag errorMask) {
if (handle == rootca_pem_handle) { if (handle == rootca_pem_handle) {
rootca_pem_handle.reset(); rootca_pem_handle.reset();
} }
if (handle == valid_tld_handle)
{
valid_tld_handle.reset();
}
return real_FSCloseFile(client, block, handle, errorMask); return real_FSCloseFile(client, block, handle, errorMask);
} }
void patchOlvApplet() { DECL_FUNCTION(uint32_t, NSSLExportInternalServerCertificate, NSSLServerCertId cert, int unk, void *unk2, void *unk3) {
olv_patches.reserve(3); if (cert == NSSL_SERVER_CERT_THAWTE_PREMIUM_SERVER_CA) { // Martini patches
OSFatal("[598-0069] Please uninstall Martini patches to continue.\n" \
auto add_patch = [](function_replacement_data_t repl, const char *name) { "See pretendo.network/docs/search for more info.\n\n" \
PatchedFunctionHandle handle = 0; "Hold the POWER button for 4 seconds to shut down.\n\n"
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) { " .\n"
DEBUG_FUNCTION_LINE("Inkay/OLV: Failed to patch %s!", name); ".---------.'---.\n"
} "'. : .'\n"
olv_patches.push_back(handle); " '. .::: .'\n"
}; " '.'::'.'\n"
" '||'\n"
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSOpenFile, LIBRARY_COREINIT, FSOpenFile, FP_TARGET_PROCESS_MIIVERSE), "FSOpenFile"); " ||\n"
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSReadFile, LIBRARY_COREINIT, FSReadFile, FP_TARGET_PROCESS_MIIVERSE), "FSReadFile"); " ||\n"
add_patch(REPLACE_FUNCTION_FOR_PROCESS(FSCloseFile, LIBRARY_COREINIT, FSCloseFile, FP_TARGET_PROCESS_MIIVERSE), "FSCloseFile"); "mrz ||\n"
} " ---====---");
void unpatchOlvApplet() {
for (auto handle: olv_patches) {
FunctionPatcher_RemoveFunctionPatch(handle);
} }
olv_patches.clear(); return real_NSSLExportInternalServerCertificate(cert, unk, unk2, unk3);
} }
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);
WUPS_MUST_REPLACE_FOR_PROCESS(NSSLExportInternalServerCertificate, WUPS_LOADER_LIBRARY_NSYSNET,
NSSLExportInternalServerCertificate, WUPS_FP_TARGET_PROCESS_MIIVERSE);

View file

@ -1,20 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
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 <http://www.gnu.org/licenses/>.
*/
#pragma once
void patchOlvApplet();
void unpatchOlvApplet();

View file

@ -2,22 +2,18 @@
Copyright 2023 Ash Logan <ash@heyquark.com> Copyright 2023 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell Copyright 2019 Maschell
This program is free software: you can redistribute it and/or modify Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
it under the terms of the GNU General Public License as published by granted, provided that the above copyright notice and this permission notice appear in all copies.
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, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
but WITHOUT ANY WARRANTY; without even the implied warranty of IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
GNU General Public License for more details. IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h"
#include "olv_urls.h" #include "olv_urls.h"
#include "config.h"
#include "utils/logger.h" #include "utils/logger.h"
#include "utils/replace_mem.h" #include "utils/replace_mem.h"
@ -51,20 +47,20 @@ bool path_is_olv(const char* path) {
void new_rpl_loaded(OSDynLoad_Module module, void* ctx, OSDynLoad_NotifyReason reason, OSDynLoad_NotifyData* rpl) { void new_rpl_loaded(OSDynLoad_Module module, void* ctx, OSDynLoad_NotifyReason reason, OSDynLoad_NotifyData* rpl) {
if (!Config::connect_to_network) { if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Miiverse patches skipped."); DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
return; return;
} }
// Loaded olv? // Loaded olv?
if (reason != OS_DYNLOAD_NOTIFY_LOADED) return; if (reason != OS_DYNLOAD_NOTIFY_LOADED) return;
if (!rpl->name || !path_is_olv(rpl->name)) return; if (!path_is_olv(rpl->name)) return;
replace(rpl->dataAddr, rpl->dataSize, original_url, sizeof(original_url), new_url, sizeof(new_url)); replace(rpl->dataAddr, rpl->dataSize, original_url, sizeof(original_url), new_url, sizeof(new_url));
} }
bool setup_olv_libs() { bool setup_olv_libs() {
if (!Config::connect_to_network) { if (!Config::connect_to_network) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: Miiverse patches skipped."); DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
return false; return false;
} }
@ -72,7 +68,7 @@ bool setup_olv_libs() {
auto olvLoaded = check_olv_libs(); auto olvLoaded = check_olv_libs();
if (!olvLoaded) { if (!olvLoaded) {
DEBUG_FUNCTION_LINE_VERBOSE("Inkay: no olv, quitting for now\n"); DEBUG_FUNCTION_LINE("Inkay: no olv, quitting for now\n");
return false; return false;
} }

View file

@ -15,12 +15,11 @@
#pragma once #pragma once
#include <cstdlib> #include <cstdlib>
#include "inkay_config.h"
constexpr char original_url[] = "discovery.olv.nintendo.net/v1/endpoint"; const char original_url[] = "discovery.olv.nintendo.net/v1/endpoint";
constexpr char new_url[] = "discovery.olv.spbr.net/v1/endpoint"; const char new_url[] = "discovery.olv.pretendo.cc/v1/endpoint";
_Static_assert(sizeof(original_url) >= sizeof(new_url), _Static_assert(sizeof(original_url) > sizeof(new_url),
"new_url too long! Must be less than 38chars."); "new_url too long! Must be less than 38chars.");
bool setup_olv_libs(); bool setup_olv_libs();

View file

@ -1,36 +1,29 @@
#pragma once #pragma once
#include <string.h>
#include <whb/log.h>
#include <whb/log_module.h>
#include <whb/log_cafe.h>
#include <whb/log_udp.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <string.h>
#include <whb/log.h>
#include <whb/log_udp.h>
#include <whb/log_cafe.h>
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) #define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ #define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \
OSFatal_printf("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0) } while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ #define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
WHBLogPrintf("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0); } while (0);
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ #define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \
WHBLogWritef("[(M) Inkay][%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0); } 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -2,18 +2,14 @@
Copyright 2023 Ash Logan <ash@heyquark.com> Copyright 2023 Ash Logan <ash@heyquark.com>
Copyright 2019 Maschell Copyright 2019 Maschell
This program is free software: you can redistribute it and/or modify Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
it under the terms of the GNU General Public License as published by granted, provided that the above copyright notice and this permission notice appear in all copies.
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, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
but WITHOUT ANY WARRANTY; without even the implied warranty of IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
GNU General Public License for more details. IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "replace_mem.h" #include "replace_mem.h"
@ -22,16 +18,14 @@
#include <kernel/kernel.h> #include <kernel/kernel.h>
#include <coreinit/memorymap.h> #include <coreinit/memorymap.h>
#include <algorithm> #include <algorithm>
#include <coreinit/cache.h>
bool replace(uint32_t start, uint32_t size, const char *original_val, size_t original_val_sz, const char *new_val, bool replace(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz) {
size_t new_val_sz) {
for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) { for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) {
int ret = memcmp(original_val, (void *) addr, original_val_sz); int ret = memcmp(original_val, (void*)addr, original_val_sz);
if (ret == 0) { if (ret == 0) {
DEBUG_FUNCTION_LINE_VERBOSE("found str @%08x: %s", addr, (const char *) addr); DEBUG_FUNCTION_LINE("found str @%08x: %s", addr, (const char*)addr);
KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t) new_val), new_val_sz); KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t)new_val), new_val_sz);
DEBUG_FUNCTION_LINE_VERBOSE("new str @%08x: %s", addr, (const char *) addr); DEBUG_FUNCTION_LINE("new str @%08x: %s", addr, (const char*)addr);
return true; return true;
} }
} }
@ -41,24 +35,24 @@ bool replace(uint32_t start, uint32_t size, const char *original_val, size_t ori
void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> replacements) { void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> replacements) {
// work out the biggest input replacement // work out the biggest input replacement
auto max_sz = std::max_element(replacements.begin(), replacements.end(), [](auto &a, auto &b) { auto max_sz = std::max_element(replacements.begin(), replacements.end(), [](auto& a, auto& b) {
return a.orig.size_bytes() < b.orig.size_bytes(); return a.orig.size_bytes() < b.orig.size_bytes();
})->orig.size_bytes(); })->orig.size_bytes();
int counts[replacements.size()]; int counts[replacements.size()];
for (auto &c: counts) { for (auto& c : counts) {
c = 0; c = 0;
} }
for (uint32_t addr = start; addr < start + size - max_sz; addr++) { for (uint32_t addr = start; addr < start + size - max_sz; addr++) {
for (int i = 0; i < (int) replacements.size(); i++) { for (int i = 0; i < (int)replacements.size(); i++) {
const auto &replacement = replacements[i]; const auto& replacement = replacements[i];
int ret = memcmp((void *) addr, replacement.orig.data(), replacement.orig.size_bytes()); int ret = memcmp((void*)addr, replacement.orig.data(), replacement.orig.size_bytes());
if (ret == 0) { if (ret == 0) {
KernelCopyData( KernelCopyData(
OSEffectiveToPhysical(addr), OSEffectiveToPhysical(addr),
OSEffectiveToPhysical((uint32_t) replacement.repl.data()), OSEffectiveToPhysical((uint32_t)replacement.repl.data()),
replacement.repl.size_bytes() replacement.repl.size_bytes()
); );
counts[i]++; counts[i]++;
@ -66,36 +60,7 @@ void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> rep
} }
} }
} }
#ifdef DEBUG for (auto c : counts) {
for (auto c: counts) {
DEBUG_FUNCTION_LINE("replaced %d times", c); DEBUG_FUNCTION_LINE("replaced %d times", c);
} }
#endif
}
template <typename U>
requires std::integral<U>
bool replace_unsigned(U *addr, U original_value, U new_value) {
if (*addr != original_value) return false;
KernelCopyData(
OSEffectiveToPhysical((uint32_t) addr),
OSEffectiveToPhysical((uint32_t) &new_value),
sizeof(new_value)
);
DCFlushRange(addr, sizeof(new_value));
return *addr == new_value;
}
template bool replace_unsigned<uint64_t>(uint64_t *, uint64_t, uint64_t);
template bool replace_unsigned<uint32_t>(uint32_t *, uint32_t, uint32_t);
template bool replace_unsigned<uint16_t>(uint16_t *, uint16_t, uint16_t);
template bool replace_unsigned<uint8_t>(uint8_t *, uint8_t, uint8_t);
bool replace_instruction(uint32_t *inst, uint32_t original_value, uint32_t new_value) {
bool res = replace_unsigned<uint32_t>(inst, original_value, new_value);
if (!res) return res;
ICInvalidateRange(inst, sizeof(new_value));
return true;
} }

View file

@ -18,8 +18,7 @@
#include <cstddef> #include <cstddef>
#include <span> #include <span>
bool replace(uint32_t start, uint32_t size, const char *original_val, size_t original_val_sz, const char *new_val, bool replace(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz);
size_t new_val_sz);
struct replacement { struct replacement {
std::span<const uint8_t> orig; std::span<const uint8_t> orig;
@ -27,9 +26,3 @@ struct replacement {
}; };
void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> replacements); void replaceBulk(uint32_t start, uint32_t size, std::span<const replacement> replacements);
template <typename U>
requires std::integral<U>
bool replace_unsigned(U *addr, U original_value, U new_value);
bool replace_instruction(uint32_t *inst, uint32_t original_value, uint32_t new_value);

View file

@ -1,64 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "rpl_info.h"
#include <vector>
#include <algorithm>
#include <string_view>
#include <coreinit/mcp.h>
#include <coreinit/title.h>
#include "logger.h"
// if we get more than like.. two callsites for this, it should really be refactored out rather than doing a fresh
// search every time
std::optional<OSDynLoad_NotifyData> search_for_rpl(std::string_view name) {
int num_rpls = OSDynLoad_GetNumberOfRPLs();
if (!num_rpls)
return std::nullopt;
// allocates but we free it at return
std::vector<OSDynLoad_NotifyData> rpls(num_rpls);
if (!OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data()))
return std::nullopt;
auto rpl = std::find_if(rpls.begin(), rpls.end(), [=](const OSDynLoad_NotifyData &rpl) {
return std::string_view(rpl.name).ends_with(name);
});
if (rpl == rpls.end())
return std::nullopt;
return *rpl;
}
std::optional<uint16_t> get_current_title_version() {
const auto mcpHandle = MCP_Open();
MCPTitleListType titleInfo;
int32_t res = -1;
const uint64_t curTitleId = OSGetTitleID();
if ((curTitleId & 0x0000000F00000000) == 0) {
res = MCP_GetTitleInfo(mcpHandle, curTitleId | 0x0000000E00000000, &titleInfo);
}
if (res != 0) {
res = MCP_GetTitleInfo(mcpHandle, curTitleId, &titleInfo);
}
MCP_Close(mcpHandle);
if (res != 0) {
DEBUG_FUNCTION_LINE("Failed to get title version of %016llX.", curTitleId);
return {};
}
MCP_Close(mcpHandle);
const auto tmp_result = titleInfo.titleVersion; // make the compiler happy because we access a packed struct
return tmp_result;
}

View file

@ -1,30 +0,0 @@
/* Copyright 2024 Pretendo Network contributors <pretendo.network>
Copyright 2024 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <optional>
#include <string_view>
#include <coreinit/dynload.h>
std::optional<OSDynLoad_NotifyData> search_for_rpl(std::string_view name);
constexpr void *rpl_addr(OSDynLoad_NotifyData rpl, uint32_t cemu_addr) {
if (cemu_addr < 0x1000'0000) {
return (void *)(rpl.textAddr + cemu_addr - 0x0200'0000);
} else {
return (void *)(rpl.dataAddr + cemu_addr - 0x1000'0000);
}
}
std::optional<uint16_t> get_current_title_version();

View file

@ -11,13 +11,13 @@ extern "C" {
namespace nn::boss { namespace nn::boss {
class Task { class Task {
public: public:
nn::act::PersistentId persistentId; nn::act::PersistentId persistentId;
uint32_t unk1; uint32_t unk1;
char task_name[8]; char task_name[8];
uint64_t title_id; uint64_t title_id;
uint32_t unk2; uint32_t unk2;
uint32_t unk3; uint32_t unk3;
}; };
static_assert(sizeof(Task) == 32, "nn::boss::Task must be 32 bytes."); static_assert(sizeof(Task) == 32, "nn::boss::Task must be 32 bytes.");
@ -26,15 +26,15 @@ namespace nn::boss {
extern "C" uint32_t Initialize__Q2_2nn4bossFv(); extern "C" uint32_t Initialize__Q2_2nn4bossFv();
extern "C" uint32_t Finalize__Q2_2nn4bossFv(); extern "C" uint32_t Finalize__Q2_2nn4bossFv();
extern "C" void __ct__Q3_2nn4boss4TaskFv(nn::boss::Task *task); 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 Initialize__Q3_2nn4boss4TaskFPCcUi(nn::boss::Task* task, char const *, unsigned int);
extern "C" uint32_t Unregister__Q3_2nn4boss4TaskFv(nn::boss::Task *task); extern "C" uint32_t Unregister__Q3_2nn4boss4TaskFv(nn::boss::Task* task);
extern "C" void __dt__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 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 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 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" uint32_t UpdateIntervalSec__Q3_2nn4boss4TaskFUi(nn::boss::Task* task, uint32_t seconds);
extern "C" bool IsRegistered__Q3_2nn4boss4TaskCFv(nn::boss::Task *task); extern "C" bool IsRegistered__Q3_2nn4boss4TaskCFv(nn::boss::Task* task);
#ifdef __cplusplus #ifdef __cplusplus
} }