diff --git a/Inkay-pretendo.wps b/Inkay-pretendo.wps index 75974b1..71555e5 100644 Binary files a/Inkay-pretendo.wps and b/Inkay-pretendo.wps differ diff --git a/Makefile b/Makefile index 6da1339..1ae0b48 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ WUMS_ROOT := $(DEVKITPRO)/wums #------------------------------------------------------------------------------- TARGET := Inkay-pretendo BUILD := build -SOURCES := src src/utils +SOURCES := src src/utils src/patcher src/patcher/patches DATA := data INCLUDES := src @@ -33,7 +33,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__ -CXXFLAGS := $(CFLAGS) +CXXFLAGS := $(CFLAGS) -std=c++20 ASFLAGS := -g $(ARCH) LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) diff --git a/src/main.cpp b/src/main.cpp index 1edd5bf..41cc333 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include "wut_extra.h" #include #include "url_patches.h" +#include "patcher/ingame.h" /** Mandatory plugin information. @@ -48,22 +49,6 @@ bool prevSkipValue = false; #include #include -//#ifdef OLD_WUPS -extern "C" { -OSDynLoad_Error -OSDynLoad_IsModuleLoaded(char const *name, - OSDynLoad_Module *outModule); -} -//#endif - -const char original_url[] = "discovery.olv.nintendo.net/v1/endpoint"; -const char new_url[] = "discovery.olv.pretendo.cc/v1/endpoint"; -_Static_assert(sizeof(original_url) > sizeof(new_url), - "new_url too long! Must be less than 38chars."); - -// We'll keep a handle to nn_olv, just to ensure it doesn't get unloaded -static OSDynLoad_Module olv_handle; - //thanks @Gary#4139 :p static void write_string(uint32_t addr, const char* str) { @@ -209,69 +194,14 @@ WUPS_CONFIG_CLOSED() { prevSkipValue = skipPatches; } -bool checkForOlvLibs() { - OSDynLoad_Module olv_handle = 0; - OSDynLoad_Error dret; - - dret = OSDynLoad_IsModuleLoaded("nn_olv", &olv_handle); - if (dret == OS_DYNLOAD_OK && olv_handle != 0) { - return true; - } - - dret = OSDynLoad_IsModuleLoaded("nn_olv2", &olv_handle); - if (dret == OS_DYNLOAD_OK && olv_handle != 0) { - return true; - } - - return false; -} - -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) { - for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) { - int ret = memcmp(original_val, (void*)addr, original_val_sz); - if (ret == 0) { - DEBUG_FUNCTION_LINE("found str @%08x: %s", addr, (const char*)addr); - KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t)new_val), new_val_sz); - DEBUG_FUNCTION_LINE("new str @%08x: %s", addr, (const char*)addr); - return true; - } - } - - return false; -} - ON_APPLICATION_START() { WHBLogUdpInit(); DEBUG_FUNCTION_LINE("Inkay: hewwo!\n"); - auto olvLoaded = checkForOlvLibs(); - - if (!olvLoaded) { - DEBUG_FUNCTION_LINE("Inkay: no olv, quitting for now\n"); - return; - } - - if (!skipPatches) { - OSDynLoad_Acquire("nn_olv", &olv_handle); - DEBUG_FUNCTION_LINE("Inkay: olv! %08x\n", olv_handle); - - //wish there was a better way than "blow through MEM2" - uint32_t base_addr, size; - if (OSGetMemBound(OS_MEM2, &base_addr, &size)) { - DEBUG_FUNCTION_LINE("Inkay: OSGetMemBound failed!"); - return; - } - - replace(base_addr, size, original_url, sizeof(original_url), new_url, sizeof(new_url)); - } - else { - DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped."); - } - + if (!skipPatches) RunPatcher(); } ON_APPLICATION_ENDS() { DEBUG_FUNCTION_LINE("Inkay: shutting down...\n"); - OSDynLoad_Release(olv_handle); } diff --git a/src/patcher/ingame.cpp b/src/patcher/ingame.cpp new file mode 100644 index 0000000..473cd38 --- /dev/null +++ b/src/patcher/ingame.cpp @@ -0,0 +1,56 @@ +/* Copyright 2022 Pretendo Network contributors + Copyright 2022 Ash Logan + + 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 "ingame.h" +#include "rplinfo.h" +#include "utils/logger.h" +#include "utils/asm.h" + +#include + +#include "patches/nn_olv.h" + +void RunPatcher() { + auto orplinfo = TryGetRPLInfo(); + if (!orplinfo) { + DEBUG_FUNCTION_LINE("Couldn't get RPL info - trying patches"); + bool bret = PatchDynLoadFunctions(); + if (!bret) { + DEBUG_FUNCTION_LINE("OSDynLoad patches failed! Quitting"); + return; + } + + //try again + orplinfo = TryGetRPLInfo(); + if (!orplinfo) { + DEBUG_FUNCTION_LINE("Still couldn't get RPL info - quitting"); + return; + } + } + + auto rpls = *orplinfo; + + /*for (const auto& rpl : rpls) { + DEBUG_FUNCTION_LINE("rpl[0]: [%s] %08x %08x %08x|%08x %08x %08x|%08x %08x %08x", + rpl.name, rpl.textAddr, rpl.textOffset, rpl.textSize, + rpl.dataAddr, rpl.dataOffset, rpl.dataSize, + rpl.readAddr, rpl.readOffset, rpl.readSize + ); + }*/ + + uint64_t titleId = OSGetTitleID(); + auto titleVer = __OSGetTitleVersion(); + + // "always" patches + Patch_nn_olv(titleVer, titleId, rpls); +} diff --git a/src/patcher/ingame.h b/src/patcher/ingame.h new file mode 100644 index 0000000..7ec5345 --- /dev/null +++ b/src/patcher/ingame.h @@ -0,0 +1,19 @@ +/* Copyright 2022 Pretendo Network contributors + Copyright 2022 Ash Logan + + 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. +*/ + +#ifndef INKAY_INGAME_H +#define INKAY_INGAME_H + +void RunPatcher(); + +#endif //INKAY_INGAME_H diff --git a/src/patcher/patcher.cpp b/src/patcher/patcher.cpp new file mode 100644 index 0000000..1bdfc6c --- /dev/null +++ b/src/patcher/patcher.cpp @@ -0,0 +1,20 @@ +#include "patcher.h" + +#include "utils/logger.h" + +#include +#include + +bool replace_string(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz) { + for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) { + int ret = memcmp(original_val, (void*)addr, original_val_sz); + if (ret == 0) { + DEBUG_FUNCTION_LINE("found str @%08x: %s", addr, (const char*)addr); + KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t)new_val), new_val_sz); + DEBUG_FUNCTION_LINE("new str @%08x: %s", addr, (const char*)addr); + return true; + } + } + + return false; +} diff --git a/src/patcher/patcher.h b/src/patcher/patcher.h new file mode 100644 index 0000000..4881854 --- /dev/null +++ b/src/patcher/patcher.h @@ -0,0 +1,9 @@ +#ifndef INKAY_PATCHER_H +#define INKAY_PATCHER_H + +#include +#include + +bool replace_string(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz); + +#endif //INKAY_PATCHER_H diff --git a/src/patcher/patches/nn_olv.cpp b/src/patcher/patches/nn_olv.cpp new file mode 100644 index 0000000..e018130 --- /dev/null +++ b/src/patcher/patches/nn_olv.cpp @@ -0,0 +1,30 @@ +#include "nn_olv.h" +#include "utils/logger.h" +#include "patcher/patcher.h" + +#include + +const char original_url[] = "discovery.olv.nintendo.net/v1/endpoint"; +const char new_url[] = "discovery.olv.pretendo.cc/v1/endpoint"; +_Static_assert(sizeof(original_url) > sizeof(new_url), + "new_url too long! Must be less than 38chars."); + +std::optional checkForOlvLibs(const rplinfo& rpls) { + auto res = FindRPL(rpls, "nn_olv.rpl"); + if (res) return res; + + res = FindRPL(rpls, "nn_olv2.rpl"); + if (res) return res; + + return std::nullopt; +} + +void Patch_nn_olv(uint32_t titleVer, uint64_t titleId, const rplinfo& rpls) { + auto olv_rpl = checkForOlvLibs(rpls); + if (!olv_rpl) { + DEBUG_FUNCTION_LINE("olv not loaded?"); + return; + } + + replace_string(olv_rpl->dataAddr, olv_rpl->dataSize, original_url, sizeof(original_url), new_url, sizeof(new_url)); +} diff --git a/src/patcher/patches/nn_olv.h b/src/patcher/patches/nn_olv.h new file mode 100644 index 0000000..f31c773 --- /dev/null +++ b/src/patcher/patches/nn_olv.h @@ -0,0 +1,8 @@ +#ifndef _TPATCH_NN_OLV_H +#define _TPATCH_NN_OLV_H + +#include "patcher/rplinfo.h" + +void Patch_nn_olv(uint32_t titleVer, uint64_t titleId, const rplinfo& rpls); + +#endif //_TPATCH_NN_OLV_H diff --git a/src/patcher/rplinfo.cpp b/src/patcher/rplinfo.cpp new file mode 100644 index 0000000..f5b7e4f --- /dev/null +++ b/src/patcher/rplinfo.cpp @@ -0,0 +1,70 @@ +/* Copyright 2022 Pretendo Network contributors + Copyright 2022 Ash Logan + + 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 "rplinfo.h" + +#include "utils/logger.h" + +#include + +#include +#include + +std::optional> TryGetRPLInfo() { + int num_rpls = OSDynLoad_GetNumberOfRPLs(); + if (num_rpls == 0) { + return std::nullopt; + } + + DEBUG_FUNCTION_LINE("num_rpls: %d", num_rpls); + + std::vector rpls; + rpls.resize(num_rpls); + + bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data()); + if (!ret) { + return std::nullopt; + } + + return rpls; +} + +bool PatchInstruction(void* instr, uint32_t original, uint32_t replacement) { + uint32_t current = *(uint32_t*)instr; + DEBUG_FUNCTION_LINE("current instr %08x", current); + if (current != original) return current == replacement; + + KernelCopyData(OSEffectiveToPhysical((uint32_t)instr), OSEffectiveToPhysical((uint32_t)&replacement), sizeof(replacement)); + //Only works on AROMA! WUPS 0.1's KernelCopyData is uncached, needs DCInvalidate here instead + DCFlushRange(instr, 4); + ICInvalidateRange(instr, 4); + + current = *(uint32_t*)instr; + DEBUG_FUNCTION_LINE("patched instr %08x", current); + + return true; +} + +bool PatchDynLoadFunctions() { + uint32_t *patch1 = ((uint32_t *) &OSDynLoad_GetNumberOfRPLs) + 6; + uint32_t *patch2 = ((uint32_t *) &OSDynLoad_GetRPLInfo) + 22; + + if (!PatchInstruction(patch1, 0x41820038 /* beq +38 */, 0x60000000 /*nop*/)) { + return false; + } + if (!PatchInstruction(patch2, 0x41820100 /* beq +100 */, 0x60000000 /*nop*/)) { + return false; + } + + return true; +} diff --git a/src/patcher/rplinfo.h b/src/patcher/rplinfo.h new file mode 100644 index 0000000..e5a622b --- /dev/null +++ b/src/patcher/rplinfo.h @@ -0,0 +1,35 @@ +/* Copyright 2022 Pretendo Network contributors + Copyright 2022 Ash Logan + + 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. +*/ + +#ifndef INKAY_RPLINFO_H +#define INKAY_RPLINFO_H + +#include +#include +#include +#include + +#include + +using rplinfo = std::vector; + +std::optional TryGetRPLInfo(); +bool PatchDynLoadFunctions(); + +constexpr inline std::optional FindRPL(const rplinfo& rpls, const std::string& name) { + auto res = std::find_if(rpls.cbegin(), rpls.cend(), [&](const OSDynLoad_NotifyData& data){ return std::string(data.name).ends_with(name); }); + if (res == rpls.cend()) return std::nullopt; + return *res; +} + +#endif //INKAY_RPLINFO_H diff --git a/src/utils/asm.S b/src/utils/asm.S new file mode 100644 index 0000000..67a8fe8 --- /dev/null +++ b/src/utils/asm.S @@ -0,0 +1,5 @@ + .global __OSGetTitleVersion +__OSGetTitleVersion: + li %r0, 0x7000 + sc + blr diff --git a/src/utils/asm.h b/src/utils/asm.h new file mode 100644 index 0000000..d944c2d --- /dev/null +++ b/src/utils/asm.h @@ -0,0 +1,12 @@ +#ifndef INKAY_ASM_H +#define INKAY_ASM_H + +#include + +extern "C" { + +uint32_t __OSGetTitleVersion(); + +} + +#endif //INKAY_ASM_H