mirror of
https://github.com/PretendoNetwork/Inkay.git
synced 2026-03-21 17:45:17 -05:00
Add framework for in-game patches
This commit is contained in:
parent
e76aec4a67
commit
e86bb6cf15
Binary file not shown.
4
Makefile
4
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)
|
||||
|
|
|
|||
74
src/main.cpp
74
src/main.cpp
|
|
@ -29,6 +29,7 @@
|
|||
#include "wut_extra.h"
|
||||
#include <utils/logger.h>
|
||||
#include "url_patches.h"
|
||||
#include "patcher/ingame.h"
|
||||
|
||||
/**
|
||||
Mandatory plugin information.
|
||||
|
|
@ -48,22 +49,6 @@ bool prevSkipValue = false;
|
|||
#include <kernel/kernel.h>
|
||||
#include <mocha/mocha.h>
|
||||
|
||||
//#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);
|
||||
}
|
||||
|
|
|
|||
56
src/patcher/ingame.cpp
Normal file
56
src/patcher/ingame.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
|
||||
Copyright 2022 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 "ingame.h"
|
||||
#include "rplinfo.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/asm.h"
|
||||
|
||||
#include <coreinit/title.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
19
src/patcher/ingame.h
Normal file
19
src/patcher/ingame.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
|
||||
Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#ifndef INKAY_INGAME_H
|
||||
#define INKAY_INGAME_H
|
||||
|
||||
void RunPatcher();
|
||||
|
||||
#endif //INKAY_INGAME_H
|
||||
20
src/patcher/patcher.cpp
Normal file
20
src/patcher/patcher.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include "patcher.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <kernel/kernel.h>
|
||||
#include <coreinit/memorymap.h>
|
||||
|
||||
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;
|
||||
}
|
||||
9
src/patcher/patcher.h
Normal file
9
src/patcher/patcher.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef INKAY_PATCHER_H
|
||||
#define INKAY_PATCHER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
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
|
||||
30
src/patcher/patches/nn_olv.cpp
Normal file
30
src/patcher/patches/nn_olv.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include "nn_olv.h"
|
||||
#include "utils/logger.h"
|
||||
#include "patcher/patcher.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
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<OSDynLoad_NotifyData> 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));
|
||||
}
|
||||
8
src/patcher/patches/nn_olv.h
Normal file
8
src/patcher/patches/nn_olv.h
Normal file
|
|
@ -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
|
||||
70
src/patcher/rplinfo.cpp
Normal file
70
src/patcher/rplinfo.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
|
||||
Copyright 2022 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 "rplinfo.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <kernel/kernel.h>
|
||||
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/memorymap.h>
|
||||
|
||||
std::optional<std::vector<OSDynLoad_NotifyData>> TryGetRPLInfo() {
|
||||
int num_rpls = OSDynLoad_GetNumberOfRPLs();
|
||||
if (num_rpls == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("num_rpls: %d", num_rpls);
|
||||
|
||||
std::vector<OSDynLoad_NotifyData> 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;
|
||||
}
|
||||
35
src/patcher/rplinfo.h
Normal file
35
src/patcher/rplinfo.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
|
||||
Copyright 2022 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.
|
||||
*/
|
||||
|
||||
#ifndef INKAY_RPLINFO_H
|
||||
#define INKAY_RPLINFO_H
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include <coreinit/dynload.h>
|
||||
|
||||
using rplinfo = std::vector<OSDynLoad_NotifyData>;
|
||||
|
||||
std::optional<rplinfo> TryGetRPLInfo();
|
||||
bool PatchDynLoadFunctions();
|
||||
|
||||
constexpr inline std::optional<OSDynLoad_NotifyData> 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
|
||||
5
src/utils/asm.S
Normal file
5
src/utils/asm.S
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.global __OSGetTitleVersion
|
||||
__OSGetTitleVersion:
|
||||
li %r0, 0x7000
|
||||
sc
|
||||
blr
|
||||
12
src/utils/asm.h
Normal file
12
src/utils/asm.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef INKAY_ASM_H
|
||||
#define INKAY_ASM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint32_t __OSGetTitleVersion();
|
||||
|
||||
}
|
||||
|
||||
#endif //INKAY_ASM_H
|
||||
Loading…
Reference in New Issue
Block a user