mirror of
https://github.com/PretendoNetwork/GiveMiiYouTube.git
synced 2025-04-02 10:52:55 -04:00
Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
|
1037e1b119 |
10 changed files with 266 additions and 31 deletions
6
Dockerfile
Normal file
6
Dockerfile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FROM wiiuenv/devkitppc:20220806
|
||||||
|
|
||||||
|
COPY --from=wiiuenv/libkernel:20220724 /artifacts $DEVKITPRO
|
||||||
|
COPY --from=wiiuenv/wiiupluginsystem:20220826 /artifacts $DEVKITPRO
|
||||||
|
|
||||||
|
WORKDIR /project
|
13
Makefile
13
Makefile
|
@ -11,6 +11,7 @@ TOPDIR ?= $(CURDIR)
|
||||||
include $(DEVKITPRO)/wups/share/wups_rules
|
include $(DEVKITPRO)/wups/share/wups_rules
|
||||||
|
|
||||||
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
|
||||||
|
@ -20,9 +21,9 @@ WUT_ROOT := $(DEVKITPRO)/wut
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := .
|
SOURCES := source source/patcher
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES :=
|
INCLUDES := source
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
|
@ -32,18 +33,18 @@ CFLAGS := -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=gnu++20
|
||||||
|
|
||||||
ASFLAGS := $(ARCH)
|
ASFLAGS := $(ARCH)
|
||||||
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
|
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUPSSPECS)
|
||||||
|
|
||||||
LIBS := -lwups -lwut
|
LIBS := -lkernel -lwups -lwut
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# 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) $(WUPS_ROOT) $(WUT_ROOT)
|
LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# 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
|
||||||
|
|
19
README.md
19
README.md
|
@ -13,10 +13,21 @@ This endpoint now responds with:
|
||||||
The app then checks if acquiring this token has been sucessful and if not it will display the error. The acquired token itself is not used.
|
The app then checks if acquiring this token has been sucessful and if not it will display the error. The acquired token itself is not used.
|
||||||
This is a Wii U Plugin System plugin for Aroma which just makes this request always return sucess and thus allowing the YouTube app to continue functioning.
|
This is a Wii U Plugin System plugin for Aroma which just makes this request always return sucess and thus allowing the YouTube app to continue functioning.
|
||||||
|
|
||||||
|
## Info about version 2.0
|
||||||
|
On November 1st, 2022 YouTube started returning response 404 for the `WiiU` useragent platform.
|
||||||
|
Since v2.0 this plugin patches the platform in the useragent for the YouTube app to `NoU` instead of `WiiU`, which bypasses this block.
|
||||||
|
|
||||||
|
## Special Thanks to
|
||||||
|
[@ashquarky](https://github.com/ashquarky) for the awesome patcher framework which was taken from [Nimble](https://github.com/PretendoNetwork/Nimble).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
```bash
|
||||||
|
# Build docker image (only needed once)
|
||||||
|
docker build . -t givemiiyoutube_builder
|
||||||
|
|
||||||
For building you need:
|
# make
|
||||||
- [wups](https://github.com/wiiu-env/WiiUPluginSystem)
|
docker run -it --rm -v ${PWD}:/project givemiiyoutube_builder make
|
||||||
- [wut](https://github.com/decaf-emu/wut)
|
|
||||||
|
|
||||||
Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself.
|
# make clean
|
||||||
|
docker run -it --rm -v ${PWD}:/project givemiiyoutube_builder make clean
|
||||||
|
```
|
||||||
|
|
21
main.cpp
21
main.cpp
|
@ -1,21 +0,0 @@
|
||||||
#include <wups.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
WUPS_PLUGIN_NAME("GiveMiiYouTube");
|
|
||||||
WUPS_PLUGIN_DESCRIPTION("Allows the YouTube app to continue functioning after its discontinuation");
|
|
||||||
WUPS_PLUGIN_VERSION("v1.0");
|
|
||||||
WUPS_PLUGIN_AUTHOR("GaryOderNichts");
|
|
||||||
WUPS_PLUGIN_LICENSE("MIT");
|
|
||||||
|
|
||||||
#define CLIENT_ID_YT "e921a604fce89365498613fdf001b492"
|
|
||||||
|
|
||||||
DECL_FUNCTION(int, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, uint8_t* token, const char* client_id)
|
|
||||||
{
|
|
||||||
if (strcmp(client_id, CLIENT_ID_YT) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return real_AcquireIndependentServiceToken__Q2_2nn3actFPcPCc(token, client_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
WUPS_MUST_REPLACE(AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, WUPS_LOADER_LIBRARY_NN_ACT, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc);
|
|
81
source/main.cpp
Normal file
81
source/main.cpp
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#include <wups.h>
|
||||||
|
#include <whb/log.h>
|
||||||
|
#include <whb/log_module.h>
|
||||||
|
#include <whb/log_cafe.h>
|
||||||
|
#include <whb/log_udp.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <coreinit/title.h>
|
||||||
|
|
||||||
|
#include <patcher/rplinfo.h>
|
||||||
|
#include <patcher/patcher.h>
|
||||||
|
|
||||||
|
WUPS_PLUGIN_NAME("GiveMiiYouTube");
|
||||||
|
WUPS_PLUGIN_DESCRIPTION("Allows the YouTube app to continue functioning after its discontinuation");
|
||||||
|
WUPS_PLUGIN_VERSION("v2.0");
|
||||||
|
WUPS_PLUGIN_AUTHOR("GaryOderNichts");
|
||||||
|
WUPS_PLUGIN_LICENSE("MIT");
|
||||||
|
|
||||||
|
#define YOUTUBE_CLIENT_ID "e921a604fce89365498613fdf001b492"
|
||||||
|
#define YOUTUBE_TITLE_ID 0x0005000010105700llu
|
||||||
|
#define YOUTUBE_USERAGENT_PLATFORM "WiiU; "
|
||||||
|
#define YOUTUBE_USERAGENT_PLATFORM_REPLACEMENT "NoU; "
|
||||||
|
|
||||||
|
ON_APPLICATION_START()
|
||||||
|
{
|
||||||
|
// If this is not the YouTube app no need to do anything
|
||||||
|
if (OSGetTitleID() != YOUTUBE_TITLE_ID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init logging
|
||||||
|
if (!WHBLogModuleInit()) {
|
||||||
|
WHBLogCafeInit();
|
||||||
|
WHBLogUdpInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
WHBLogPrintf("GiveMiiYouTube: applying patches...");
|
||||||
|
|
||||||
|
// Patch the dynload functions so GetRPLInfo works
|
||||||
|
if (!PatchDynLoadFunctions()) {
|
||||||
|
WHBLogPrintf("GiveMiiYouTube: Failed to patch dynload functions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the RPLInfo
|
||||||
|
auto rpl_info = TryGetRPLInfo();
|
||||||
|
if (!rpl_info) {
|
||||||
|
WHBLogPrintf("GiveMiiYouTube: Failed to get RPL info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the rpx
|
||||||
|
rplinfo rpls = *rpl_info;
|
||||||
|
auto lb_shell_rpx = FindRPL(rpls, "lb_shell.rpx");
|
||||||
|
if (!lb_shell_rpx) {
|
||||||
|
WHBLogPrintf("GiveMiiYouTube: Failed to find lb_shell.rpx");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch the useragent platform
|
||||||
|
OSDynLoad_NotifyData rpx_data = *lb_shell_rpx;
|
||||||
|
if (!replace_string(rpx_data.dataAddr, rpx_data.dataSize,
|
||||||
|
YOUTUBE_USERAGENT_PLATFORM, sizeof(YOUTUBE_USERAGENT_PLATFORM),
|
||||||
|
YOUTUBE_USERAGENT_PLATFORM_REPLACEMENT, sizeof(YOUTUBE_USERAGENT_PLATFORM_REPLACEMENT))) {
|
||||||
|
WHBLogPrintf("GiveMiiYouTube: Failed to replace useragent platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_FUNCTION(int, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, uint8_t* token, const char* client_id)
|
||||||
|
{
|
||||||
|
// If this is the YouTube client, return sucess
|
||||||
|
if (client_id && strcmp(client_id, YOUTUBE_CLIENT_ID) == 0) {
|
||||||
|
WHBLogPrintf("GiveMiiYouTube: Faking service sucess for '%s'", client_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return real_AcquireIndependentServiceToken__Q2_2nn3actFPcPCc(token, client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
WUPS_MUST_REPLACE(AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, WUPS_LOADER_LIBRARY_NN_ACT, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc);
|
20
source/patcher/patcher.cpp
Normal file
20
source/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;
|
||||||
|
}
|
6
source/patcher/patcher.h
Normal file
6
source/patcher/patcher.h
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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);
|
70
source/patcher/rplinfo.cpp
Normal file
70
source/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;
|
||||||
|
}
|
31
source/patcher/rplinfo.h
Normal file
31
source/patcher/rplinfo.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
30
source/utils/logger.h
Normal file
30
source/utils/logger.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <whb/log.h>
|
||||||
|
|
||||||
|
#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("[%s]%s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
|
||||||
|
do { \
|
||||||
|
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \
|
||||||
|
do { \
|
||||||
|
WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue