forked from PretendoNetwork/Inkay
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e86bb6cf15 |
13 changed files with 268 additions and 74 deletions
Binary file not shown.
4
Makefile
4
Makefile
|
|
@ -21,7 +21,7 @@ WUMS_ROOT := $(DEVKITPRO)/wums
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
TARGET := Inkay-pretendo
|
TARGET := Inkay-pretendo
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := src src/utils
|
SOURCES := src src/utils src/patcher src/patcher/patches
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := src
|
INCLUDES := src
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS)
|
CXXFLAGS := $(CFLAGS) -std=c++20
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
|
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 "wut_extra.h"
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
#include "url_patches.h"
|
#include "url_patches.h"
|
||||||
|
#include "patcher/ingame.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Mandatory plugin information.
|
Mandatory plugin information.
|
||||||
|
|
@ -48,22 +49,6 @@ bool prevSkipValue = false;
|
||||||
#include <kernel/kernel.h>
|
#include <kernel/kernel.h>
|
||||||
#include <mocha/mocha.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
|
//thanks @Gary#4139 :p
|
||||||
static void write_string(uint32_t addr, const char* str)
|
static void write_string(uint32_t addr, const char* str)
|
||||||
{
|
{
|
||||||
|
|
@ -209,69 +194,14 @@ WUPS_CONFIG_CLOSED() {
|
||||||
prevSkipValue = skipPatches;
|
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() {
|
ON_APPLICATION_START() {
|
||||||
WHBLogUdpInit();
|
WHBLogUdpInit();
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE("Inkay: hewwo!\n");
|
DEBUG_FUNCTION_LINE("Inkay: hewwo!\n");
|
||||||
|
|
||||||
auto olvLoaded = checkForOlvLibs();
|
if (!skipPatches) RunPatcher();
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ON_APPLICATION_ENDS() {
|
ON_APPLICATION_ENDS() {
|
||||||
DEBUG_FUNCTION_LINE("Inkay: shutting down...\n");
|
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 a new issue