760 lines
33 KiB
C++
760 lines
33 KiB
C++
#include <jni.h>
|
|
#include <cstdint>
|
|
#include <android/log.h>
|
|
#include <dobby.h>
|
|
#include <memory.h>
|
|
#include <dlfcn.h>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include "Utils.h"
|
|
#include <string>
|
|
#include <thread>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include <libgen.h>
|
|
#include "Config.h"
|
|
#include <cocos2d.h>
|
|
#include "libmadomagi.h"
|
|
#include "rest/MagiaRest.h"
|
|
|
|
const char* libName = "libmadomagi_native.so";
|
|
const char* hookName = "libuwasa.so";
|
|
|
|
JavaVM* gJvm = nullptr;
|
|
static jobject gClassLoader;
|
|
static jmethodID gFindClassMethod;
|
|
uintptr_t libBase = 0;
|
|
|
|
//namespace fs = std::filesystem; ndk 22+ required
|
|
|
|
struct hook_loop_args {
|
|
std::string libso;
|
|
};
|
|
|
|
uintptr_t storyMessageUnitStartOffset = 0;
|
|
uintptr_t storyMessageUnitCreateMessageAreaOffset = 0;
|
|
uintptr_t storyLogUnitAddMessageOffset = 0;
|
|
uintptr_t storyLogUnitAddNarrationOffset = 0;
|
|
uintptr_t storyCharaUnitonTextHomeOffset = 0;
|
|
uintptr_t storyNarrationUnitCreateLabelOffset = 0;
|
|
uintptr_t initCenterWidthOutline = 0;
|
|
|
|
int max_threads = 10;
|
|
|
|
void* openMessageBoxPtr = nullptr;
|
|
uintptr_t* resourceUrlPtr = nullptr;
|
|
|
|
bool initialized = false;
|
|
|
|
const std::string assetBase = "/magica/resource";
|
|
const std::string assetTrunk = "/download/asset/master";
|
|
const std::string assetScenario = "/resource/scenario";
|
|
std::vector<std::shared_ptr<std::string>> urlEndpoints(3);
|
|
|
|
const std::string koruriFont("fonts/koruri-semibold.ttf");
|
|
|
|
typedef int *(*setUrlType)(int *);
|
|
typedef int *(*setResourceType)(int *, unsigned int *);
|
|
typedef uintptr_t *(*UrlConfigImplResourceType)(uintptr_t &a1, int a2, unsigned int a3, int a4);
|
|
|
|
// Hooked functions
|
|
void *(*setPositionHooked)(uintptr_t label, cocos2d::Vec2 const& position);
|
|
void *(*setMaxLineWidthHooked)(uintptr_t label, float length);
|
|
void *(*setDimensionsHooked)(uintptr_t label, float width, float a3);
|
|
|
|
const std::string* (*urlConfigResourceHooked)(void* a1, UrlConfigResourceType type); // There is also api, chat, web, etc for other endpoints
|
|
|
|
//void* urlConfig_ImplObj = nullptr;
|
|
|
|
// Cocos functions
|
|
typedef cocos2d::Director* (*director_type)(void* dummy);
|
|
typedef const cocos2d::Size& (*get_win_size_type)(cocos2d::Director* director);
|
|
typedef cocos2d::Vec2 (*get_visible_origin_type)(cocos2d::Director* director);
|
|
|
|
director_type getDirector;
|
|
get_win_size_type getWinSize;
|
|
get_visible_origin_type getVisibleOrigin;
|
|
|
|
/* BROKEN
|
|
void testDialogue() {
|
|
if (openMessageBoxPtr != nullptr) {
|
|
auto x = DialogueBoxProxy();
|
|
x.DialogueBox(openMessageBoxPtr);
|
|
}
|
|
}*/
|
|
jclass findClass(JNIEnv* env, const char* name) {
|
|
return static_cast<jclass>(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name)));
|
|
}
|
|
|
|
void displayMessage(const std::string& title, const std::string& description) {
|
|
auto env = getEnv(gJvm);
|
|
const char* cocosHelper = "org/cocos2dx/lib/Cocos2dxHelper";
|
|
const char* showDialogueBox = "showDialog";
|
|
std::string signature = "(" + std::string(getJNISignature(title, description)) + ")V";
|
|
|
|
jclass klass = findClass(env, cocosHelper);
|
|
if (klass == nullptr) {
|
|
LOGE("No Cocos2dxHelper found.");
|
|
return;
|
|
}
|
|
|
|
jmethodID mid = env->GetStaticMethodID(klass, showDialogueBox, signature.c_str());
|
|
if (mid == nullptr) {
|
|
LOGE("No showDialog found.");
|
|
return;
|
|
}
|
|
jstring str1 = env->NewStringUTF(title.c_str());
|
|
jstring str2 = env->NewStringUTF(description.c_str());
|
|
env->CallStaticObjectMethod(klass, mid, str1, str2);
|
|
}
|
|
|
|
int *(*sceneLayerManagerCreateSceneLayerOld)(uintptr_t *sceneLayerManager, BaseSceneLayerInfo* sceneLayerInfo);
|
|
|
|
int *sceneLayerManagerCreateSceneLayer(uintptr_t *sceneLayerManager, BaseSceneLayerInfo* sceneLayerInfo) {
|
|
auto sceneType = sceneLayerInfo->layerType;
|
|
if (sceneType >= BaseSceneLayerType::BaseSceneLayerTypeMaxValue || sceneType < 0) {
|
|
LOGW("Unknown scene triggered. %d", sceneType);
|
|
}
|
|
else {
|
|
LOGI("Scene layer changed to: %s", BaseSceneLayerTypeStrings[sceneType]);
|
|
}
|
|
|
|
if (!initialized && sceneType == BaseSceneLayerType::WebSceneLayer) { // Set up everything here
|
|
auto rest = MagiaRest(gJvm);
|
|
switch(rest.Endpoint()) {
|
|
case MAGIAREST_EMPTY:
|
|
{
|
|
auto emptyMessage = string_format("Unable to connect to the translation server. Restart the app to retry, or continue to play in Japanese. (Response length: %zu)",
|
|
rest.EndpointStringLength());
|
|
|
|
displayMessage("MagiaTranslate Error", emptyMessage.c_str());
|
|
break;
|
|
}
|
|
case MAGIAREST_ERROR:
|
|
{
|
|
auto errorMessage = string_format("An error has occurred. Restart the app to retry, or continue to play in Japanese.\nError: %s", rest.GetEndpointError().c_str());
|
|
displayMessage("MagiaTranslate Error", errorMessage.c_str());
|
|
break;
|
|
}
|
|
case MAGIAREST_SUCCESS:
|
|
{
|
|
auto ver = rest.GetEndpointVersion();
|
|
if (MT_VERSION < ver) {
|
|
LOGI("Version update required.");
|
|
auto updateMessage = string_format("A new version of MagiaTranslate is available, please update your app at kamihama.io. Continuing may result in crashes.\nApp version installed: %d\nApp version available: %d",
|
|
MT_VERSION, ver);
|
|
displayMessage("MagiaTranslate Update", updateMessage.c_str());
|
|
}
|
|
auto endpointUrl = rest.GetEndpointUrl();
|
|
if (endpointUrl.empty()) {
|
|
LOGW("Empty endpoint URL.");
|
|
displayMessage("MagiaTranslate Error", "Error 115 has occurred, the returned translate endpoint URL is empty, please try again later.");
|
|
break;
|
|
}
|
|
|
|
// Set max download threads
|
|
auto mt = rest.GetMaxThreads();
|
|
if (mt > 0) {
|
|
LOGD("Set maximum threads from API, value %d.", mt);
|
|
max_threads = mt;
|
|
}
|
|
|
|
const std::string assetNameBase = endpointUrl + assetBase;
|
|
const std::string assetNameFull = endpointUrl + assetBase + assetTrunk;
|
|
const std::string assetNameScript = endpointUrl + assetBase + assetTrunk + assetScenario;
|
|
|
|
//std::string assetNameBaseProxy(assetNameBase.c_str());
|
|
//std::string assetNameFullProxy(assetNameFull.c_str());
|
|
//std::string assetNameScriptProxy(assetNameScript.c_str());
|
|
|
|
LOGD("Setting endpoint URLs.");
|
|
LOGD("%s", assetNameScript.c_str());
|
|
|
|
urlEndpoints.at(UrlConfigResourceType::BaseUrl) = std::make_shared<std::string>(assetNameBase);
|
|
urlEndpoints.at(UrlConfigResourceType::TrunkUrl) = std::make_shared<std::string>(assetNameFull);
|
|
urlEndpoints.at(UrlConfigResourceType::ScenarioUrl) = std::make_shared<std::string>(assetNameScript);
|
|
LOGD("Finished setting endpoint URLs.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
initialized = true;
|
|
auto y = sceneLayerManagerCreateSceneLayerOld(sceneLayerManager, sceneLayerInfo);
|
|
return y;
|
|
}
|
|
|
|
return sceneLayerManagerCreateSceneLayerOld(sceneLayerManager, sceneLayerInfo);
|
|
|
|
}
|
|
|
|
// Change function to fetch resource URLs
|
|
const std::string* urlConfigResource(void* a1, UrlConfigResourceType type) {
|
|
LOGD("Fetching URL config resource %d", (int)type);
|
|
if (type < UrlConfigResourceType::UrlConfigResourceTypeMaxValue) {
|
|
try {
|
|
if (urlEndpoints.at(type) != nullptr && urlEndpoints.at(type).get() != nullptr) {
|
|
auto url = (urlEndpoints.at(type)).get();
|
|
LOGD("URL: %s", url->c_str());
|
|
return url;
|
|
}
|
|
else {
|
|
LOGW("Empty endpoint found for endpoint type %d!", (int)type);
|
|
}
|
|
}
|
|
catch (std::out_of_range const& exc) {
|
|
LOGW("Out of range for endpoint type %d!", (int)type);
|
|
}
|
|
}
|
|
return urlConfigResourceHooked(a1, type);
|
|
}
|
|
|
|
|
|
void* (*cocosCreateLabelHooked)(const uintptr_t* textPtr, const std::string &fontPtr, float textSize, cocos2d::Size const& cocosSize, cocos2d::TextHAlignment hAlign, cocos2d::TextVAlignment vAlign);
|
|
void* cocosCreateLabel(const uintptr_t* textPtr, const std::string &fontPtr, float textSize, cocos2d::Size const& cocosSize, cocos2d::TextHAlignment hAlign, cocos2d::TextVAlignment vAlign) {
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
|
|
LOGD("Label created at %p (%p), size %.1f.", (void*) addr, (void*)(addr - libBase), textSize);
|
|
if (storyMessageUnitCreateMessageAreaOffset != 0 && addr >= storyMessageUnitCreateMessageAreaOffset) {
|
|
uintptr_t difference = addr - storyMessageUnitCreateMessageAreaOffset;
|
|
if (difference <= 0x200) {
|
|
LOGD("Setting new text font for main story text.");
|
|
if (textSize == 27.0) {
|
|
textSize = 30.0;
|
|
}
|
|
return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign);
|
|
}
|
|
}
|
|
if (storyNarrationUnitCreateLabelOffset != 0 && addr >= storyNarrationUnitCreateLabelOffset) {
|
|
uintptr_t difference = addr - storyNarrationUnitCreateLabelOffset;
|
|
|
|
if (difference <= 0x200) { // 0x8e
|
|
LOGD("Setting new narration text font. Difference: %p", (void*)difference);
|
|
return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign);
|
|
}
|
|
}
|
|
if (initCenterWidthOutline != 0 && addr >= initCenterWidthOutline) {
|
|
uintptr_t difference = addr - initCenterWidthOutline;
|
|
|
|
if (difference <= 0x200) {
|
|
LOGD("Setting new home text font and size.");
|
|
textSize = textSize * 0.85;
|
|
return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign);
|
|
}
|
|
}
|
|
if (storyLogUnitAddMessageOffset != 0 && addr >= storyLogUnitAddMessageOffset) {
|
|
uintptr_t difference = addr - storyLogUnitAddMessageOffset;
|
|
|
|
if (difference <= 0x640) { // 0xec, 0x54e
|
|
LOGD("Setting new log text font. Difference: %p", (void*)difference);
|
|
return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign);
|
|
}
|
|
}
|
|
if (storyLogUnitAddNarrationOffset != 0 && addr >= storyLogUnitAddNarrationOffset) {
|
|
uintptr_t difference = addr - storyLogUnitAddNarrationOffset;
|
|
if (difference <= 0x640) { // 0x43e, 0x5b0
|
|
LOGD("Setting new log text font (narration). Difference: %p", (void*)difference);
|
|
return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign);
|
|
}
|
|
}
|
|
return cocosCreateLabelHooked(textPtr, fontPtr, textSize, cocosSize, hAlign, vAlign);
|
|
|
|
}
|
|
|
|
// New functions
|
|
void *setPositionNew(uintptr_t label, cocos2d::Vec2 const& position) {
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
|
|
//LOGI("Move at %p to x: %.2f, y: %.2f", (addr - libBase), position.x, position.y);
|
|
|
|
// Story message boxes
|
|
if (storyMessageUnitStartOffset != 0 && addr >= storyMessageUnitStartOffset) {
|
|
uintptr_t difference = addr - storyMessageUnitStartOffset;
|
|
|
|
// Alignment of main text in box
|
|
if (difference <= 0x200) { // Offset = 0x76 as of 2.15
|
|
LOGD("Moving story text.");
|
|
//LOGI("Difference: %p", difference);
|
|
//LOGD("old [message] x: %.2f, y: %.2f", position.x, position.y);
|
|
|
|
// Move text to the left and up a bit
|
|
if (position.x == -222.0 && position.y == 20.0) {
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(-368.0, 25.0);
|
|
//LOGD("new 1 x: %.2f, y: %.2f", newPosition.x, newPosition.y);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
else if (position.x == -207.0 && position.y == 30.0) {
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(-360.0, 40.0);
|
|
//LOGD("new 2 x: %.2f, y: %.2f", newPosition.x, newPosition.y);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Names alignment
|
|
if (storyMessageUnitCreateMessageAreaOffset != 0 && addr >= storyMessageUnitCreateMessageAreaOffset) {
|
|
|
|
uintptr_t difference = addr - storyMessageUnitCreateMessageAreaOffset;
|
|
//LOGI("Difference: %p", difference);
|
|
|
|
// Move names into the right place
|
|
if (difference <= 0x600) { // Offset = 0x35e as of 2.15
|
|
//LOGD("old [name] x: %.2f, y: %.2f", position.x, position.y);
|
|
LOGD("Moving story name.");
|
|
if (position.x == -215.0 && position.y == 57.0) { // Left names
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(-320.0, 55.0);
|
|
//LOGD("new l-1 x: %.2f, y: %.2f", newPosition.x, newPosition.y);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
else if (position.x == -55.0 && position.y == 57.0) { // Center names
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(30.0, 55.0);
|
|
//LOGD("new m-1 x: %.2f, y: %.2f", newPosition.x, newPosition.y);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
else if (position.x == 215.0 && position.y == 57.0) { // Right names
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(320.0, 55.0);
|
|
//LOGD("new r-1 x: %.2f, y: %.2f", newPosition.x, newPosition.y);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// History
|
|
if (storyLogUnitAddMessageOffset != 0 && addr >= storyLogUnitAddMessageOffset) {
|
|
uintptr_t difference = addr - storyLogUnitAddMessageOffset;
|
|
LOGD("LOG MESSAGE: %.2f %.2f", position.x, position.y);
|
|
LOGD("Difference: %p", (void *)difference);
|
|
//if (position.y >= 55.50 && position.y <= 56.50 && difference <= 0x1000) {
|
|
if ((position.y >= 37.5 && position.y <= 39.5 && difference <= 0x1300)
|
|
|| (position.y >= 55.00 && position.y <= 56.00 && difference <= 0x1300)) {
|
|
float newPosX = position.x + 125.0;
|
|
LOGD("Moved log text from %.2f to %.2f", position.x, newPosX);
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(newPosX, 66.50);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
else if (difference <= 0x400 && position.x == 70.00) {
|
|
LOGD("Moving left-aligned name down in the log.");
|
|
auto newY = position.y;
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(position.x, newY);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
else if (difference <= 0x400 && position.x == 500.00) { // Names on the right
|
|
LOGD("Moving name further to the right in log.");
|
|
auto newX = position.x + 200;
|
|
//auto newY = position.y - 15.0;
|
|
auto newY = position.y;
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(newX, newY);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
else if (difference <= 0x400 && position.x == 280.00) { // Names in the center, 280.00?
|
|
LOGD("Moving center name to the left in the log.");
|
|
auto newX = 71.50;
|
|
auto newY = position.y;
|
|
cocos2d::Vec2 newPosition = cocos2d::Vec2(newX, newY);
|
|
return setPositionHooked(label, newPosition);
|
|
}
|
|
}
|
|
return setPositionHooked(label, position);
|
|
}
|
|
|
|
void *setMaxLineWidthNew(uintptr_t label, float length) {
|
|
//LOGI("Hook triggered - line length");
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
|
|
//LOGI("%p", (addr - libBase));
|
|
|
|
if (storyMessageUnitCreateMessageAreaOffset != 0 && addr >= storyMessageUnitCreateMessageAreaOffset) {
|
|
uintptr_t difference = addr - storyMessageUnitCreateMessageAreaOffset;
|
|
//LOGI("Difference: %p", difference);
|
|
|
|
// Make lines longer
|
|
if (difference <= 0x244 && length == 410.0) { // Offset = 0x144 as of 2.15
|
|
LOGD("Set line length from 410.0 to 810.0");
|
|
length = 810.0;
|
|
}
|
|
}
|
|
return setMaxLineWidthHooked(label, length);
|
|
}
|
|
|
|
void *setDimensionsNew(uintptr_t label, float width, float height) {
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
|
|
uintptr_t difference = addr - storyLogUnitAddMessageOffset;
|
|
//LOGI("Difference [dimensions]: %p, addr: %p", difference, addr);
|
|
|
|
if (storyLogUnitAddMessageOffset != 0 && addr >= storyLogUnitAddMessageOffset) {
|
|
//LOGI("%p, %.2f, %.2f", label, width, a3);
|
|
if (difference <= 0x900 && width == 410.0) { // Offset = 0x5ac as of 2.15
|
|
LOGD("Set dimensions for log from 410.0 to 710.0.");
|
|
return setDimensionsHooked(label, 710.0, height);
|
|
}
|
|
}
|
|
//LOGD("Dimensions: %f, %f", width, height);
|
|
return setDimensionsHooked(label, width, height);
|
|
}
|
|
|
|
// Fix the position of homeText under live2d girls
|
|
cocos2d::Size (*lbGetViewPositionHooked)(float x, float y);
|
|
cocos2d::Size lbGetViewPositionNew(float x, float y) {
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
|
|
uintptr_t difference = addr - storyCharaUnitonTextHomeOffset;
|
|
//LOGD("Difference (viewPos): %p, addr: %p", (void*)difference, (void*)addr);
|
|
|
|
if (storyCharaUnitonTextHomeOffset != 0 && addr >= storyCharaUnitonTextHomeOffset) {
|
|
if (difference <= 0x1300) {
|
|
auto oldx = x;
|
|
auto oldy = y;
|
|
if (x > -100.0) {
|
|
x = -120.0;
|
|
}
|
|
if (x == -100.0) {
|
|
x = -250.0;
|
|
}
|
|
x = x - 30.0;
|
|
y = y - 30.0;
|
|
LOGD("Set live2d subtitle dimensions from (%f, %f) to (%f, %f).", oldx, oldy, x, y);
|
|
}
|
|
}
|
|
|
|
#if defined(__aarch64__)
|
|
return lbGetViewPositionHooked(x, y);
|
|
#endif
|
|
//LOGI("Size 1: %f, size 2: %f", sizes.width, sizes.height);
|
|
|
|
// Reimplement from scratch because arm is bugged (also this segfaults on arm64)
|
|
auto director = getDirector((void *)0x00);
|
|
//LOGI("Obtained director at %p.", (void*) director);
|
|
|
|
auto dirSize = getWinSize(director);
|
|
//LOGI("Obtained winsize, %f %f.", dirSize.width, dirSize.height);
|
|
auto origin = getVisibleOrigin(director);
|
|
//LOGI("Obtained visible origin, %f %f.", origin.x, origin.y);
|
|
auto sizes = cocos2d::Size();
|
|
sizes.width = origin.x + x + (float)dirSize.width * 0.5;
|
|
sizes.height = origin.y + y + (float)dirSize.height * 0.5;
|
|
//sizes.width = origin.x + x + (float)dirSize.width * 0.5;
|
|
//sizes.height = origin.y + y + (float)dirSize.height * 0.5;
|
|
|
|
//LOGI("NEW size 1: %f, size 2: %f", sizes.width, sizes.height);
|
|
return sizes;
|
|
}
|
|
|
|
|
|
pthread_mutex_t *(*setUriDebugOld)(uintptr_t a1, const std::string &st);
|
|
pthread_mutex_t *setUriDebug(uintptr_t a1, const std::string &stri) {
|
|
auto mut = setUriDebugOld(a1, stri);
|
|
|
|
auto outstr = stri.c_str();
|
|
LOGI("Uri base set: %s", outstr);
|
|
return mut;
|
|
}
|
|
|
|
pthread_mutex_t *(*http2SessionSetMaxConnectionNumOld)(uintptr_t *session, int max);
|
|
|
|
pthread_mutex_t *http2SessionSetMaxConnectionNum(uintptr_t *session, int max) {
|
|
if (max == 4) {
|
|
max = max_threads;
|
|
}
|
|
LOGD("Set max number of connections to %d.", max);
|
|
return http2SessionSetMaxConnectionNumOld(session, max);
|
|
}
|
|
|
|
void initialization_error(const char* error) {
|
|
LOGE("%s", error);
|
|
auto errorMsg = string_format("A critical error has occurred, MagiaTranslate will not work properly and may crash. Please report this error on GitHub or Discord.\n%s", error);
|
|
displayMessage("MagiaTranslate Error", errorMsg);
|
|
}
|
|
|
|
// Hook loop function. We run this in a separate thread so it doesn't block the main thread.
|
|
void *hook_loop(void *arguments) {
|
|
std::unique_ptr<hook_loop_args> args((struct hook_loop_args *)arguments);
|
|
auto libLocation = (args->libso).c_str();
|
|
|
|
LOGI("Library location: %s", libLocation);
|
|
|
|
while(libBase == 0) {
|
|
libBase = get_libBase(libName);
|
|
}
|
|
LOGI("Base address: %p", (void*)libBase);
|
|
|
|
// Hook resource endpoint
|
|
void *resourceHook = lookup_symbol(libLocation, "_ZNK9UrlConfig8resourceENS_8Resource4TypeE"); // UrlConfig::resource(UrlConfig::Resource::Type)const
|
|
if (DobbyHook(resourceHook, (void *)urlConfigResource, (void **)&urlConfigResourceHooked) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked UrlConfig::resource.");
|
|
}
|
|
else {
|
|
initialization_error("Failed to hook UrlConfig::resource.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
|
|
// Hook scene creator
|
|
void *sceneHook = lookup_symbol(libLocation, "_ZN17SceneLayerManager16createSceneLayerEP18BaseSceneLayerInfo"); //_DWORD __fastcall SceneLayerManager::createSceneLayer(SceneLayerManager *__hidden this, BaseSceneLayerInfo *)
|
|
if (DobbyHook(sceneHook, (void *)sceneLayerManagerCreateSceneLayer, (void **)&sceneLayerManagerCreateSceneLayerOld) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked SceneLayerManager::createSceneLayer.");
|
|
}
|
|
else {
|
|
initialization_error("Failed to hook SceneLayerManager::createSceneLayer.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
// Speed up downloads
|
|
void *maxDlHook = lookup_symbol(libLocation, "_ZN5http212Http2Session19setMaxConnectionNumEi"); //_DWORD __fastcall http2::Http2Session::setMaxConnectionNum(http2::Http2Session *__hidden this, int)
|
|
if (DobbyHook(maxDlHook, (void *)http2SessionSetMaxConnectionNum, (void **)&http2SessionSetMaxConnectionNumOld) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked http2::Http2Session::setMaxConnectionNum.");
|
|
}
|
|
else {
|
|
LOGW("Failed to hook http2::Http2Session::setMaxConnectionNum.");
|
|
}
|
|
|
|
|
|
//openMessageBoxPtr = lookup_symbol(libLocation, "_ZN10MessageBox4openEPKcS1_S1_RKSt8functionIFvPN7cocos2d3RefEEEb");
|
|
//LOGI("Set openMessageBox ptr to %p", openMessageBoxPtr);
|
|
|
|
// For debugging
|
|
//DobbyHook(lookup_symbol(libLocation, "_ZN5http212Http2Session6setURIERKSs"), (void *)setUriDebug, (void **)&setUriDebugOld); - crashes arm32 now.
|
|
|
|
|
|
// Hooks
|
|
void *cocos2dnodeSetPosition = lookup_symbol(libLocation, "_ZN7cocos2d4Node11setPositionERKNS_4Vec2E");
|
|
// cocos2d::Node::setPosition(cocos2d::Vec2 const&)
|
|
if (cocos2dnodeSetPosition != nullptr) {
|
|
LOGD("Found cocos2d::Node::setPosition at %p.", (void *)cocos2dnodeSetPosition);
|
|
if (DobbyHook(cocos2dnodeSetPosition, (void *)setPositionNew, (void **)&setPositionHooked) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked cocos2d::Node::setPosition.");
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Node::setPosition.");
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Node::setPosition.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
void *cocos2dlineLength = lookup_symbol(libLocation, "_ZN7cocos2d5Label15setMaxLineWidthEf");
|
|
// cocos2d::Label::setMaxLineWidth(float)
|
|
if (cocos2dlineLength != nullptr) {
|
|
LOGD("Found cocos2d::Label::setMaxLineWidth at %p.", (void *)cocos2dlineLength);
|
|
if (DobbyHook(cocos2dlineLength, (void *)setMaxLineWidthNew, (void **)&setMaxLineWidthHooked) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked cocos2d::Label::setMaxLineWidth.");
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Label::setMaxLineWidth.");
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Label::setMaxLineWidth.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
void *cocos2dsetDimensions = lookup_symbol(libLocation, "_ZN7cocos2d5Label13setDimensionsEff");
|
|
//_DWORD __fastcall cocos2d::Label::setDimensions(cocos2d::Label *__hidden this, float, float)
|
|
if (cocos2dsetDimensions != nullptr) {
|
|
LOGD("Found cocos2d::Label::setDimensions at %p.", (void *)cocos2dsetDimensions);
|
|
if (DobbyHook(cocos2dsetDimensions, (void *)setDimensionsNew, (void **)&setDimensionsHooked) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked cocos2d::Label::setDimensions.");
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Label::setDimensions.");
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Label::setDimensions.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
// For moving live2d subtitles
|
|
void *lbGetViewPosition = lookup_symbol(libLocation, "_ZN9LbUtility15getViewPositionEff");
|
|
//_DWORD __fastcall LbUtility::getViewPosition(LbUtility *__hidden this, float, float)
|
|
if (lbGetViewPosition != nullptr) {
|
|
LOGD("Found LbUtility::getViewPosition at %p.", (void *)lbGetViewPosition);
|
|
if (DobbyHook(lbGetViewPosition, (void *)lbGetViewPositionNew, (void **)&lbGetViewPositionHooked) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked LbUtility::getViewPosition.");
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook LbUtility::getViewPosition.");
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook LbUtility::getViewPosition.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
// Change font
|
|
void *cocosCreateLabelPtr = lookup_symbol(libLocation, "_ZN7cocos2d5Label13createWithTTFERKNSt6__ndk112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_fRKNS_4SizeENS_14TextHAlignmentENS_14TextVAlignmentE");
|
|
|
|
if (cocosCreateLabelPtr != nullptr) {
|
|
LOGD("Found cocos2d::Label::createWithTTF at %p.", (void *)cocosCreateLabel);
|
|
if (DobbyHook(cocosCreateLabelPtr, (void*) cocosCreateLabel, (void **)& cocosCreateLabelHooked) == RS_SUCCESS) {
|
|
LOGI("Successfully hooked cocos2d::Label::createWithTTF.");
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Label::createWithTTF.");
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
else {
|
|
initialization_error("Unable to hook cocos2d::Label::createWithTTF.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
|
|
// Find key functions, TODO: Tidy up into 1 nice loop
|
|
|
|
void *storyMessageUnitTextStart = lookup_symbol(libLocation, "_ZN16StoryMessageUnit9textStartENS_11TextPosType13TextPosType__E");
|
|
// StoryMessageUnit::textStart(StoryMessageUnit::TextPosType::TextPosType__)
|
|
if (storyMessageUnitTextStart == nullptr) {
|
|
initialization_error("Unable to find a pointer for StoryMessageUnit::textStart.");
|
|
pthread_exit(NULL);
|
|
}
|
|
storyMessageUnitStartOffset = reinterpret_cast<uintptr_t>(storyMessageUnitTextStart);
|
|
|
|
void *storyMessageUnitCreateMessageArea = lookup_symbol(libLocation, "_ZN16StoryMessageUnit17createMessageAreaENS_11TextPosType13TextPosType__E");
|
|
// StoryMessageUnit::createMessageArea(StoryMessageUnit::TextPosType::TextPosType__)
|
|
if (storyMessageUnitCreateMessageArea == nullptr) {
|
|
initialization_error("Unable to find a pointer for StoryMessageUnit::createMessageArea.");
|
|
pthread_exit(NULL);
|
|
}
|
|
storyMessageUnitCreateMessageAreaOffset = reinterpret_cast<uintptr_t>(storyMessageUnitCreateMessageArea);
|
|
|
|
void *storyLogUnitAddMessage = lookup_symbol(libLocation, "_ZN12StoryLogUnit10addMessageENS_11MessageType13MessageType__ERKNSt6__ndk112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE");
|
|
|
|
if (storyLogUnitAddMessage == nullptr) {
|
|
initialization_error("Unable to find a pointer for StoryLogUnit::addMessage.");
|
|
pthread_exit(NULL);
|
|
}
|
|
storyLogUnitAddMessageOffset = reinterpret_cast<uintptr_t>(storyLogUnitAddMessage);
|
|
|
|
void *storyLogUnitAddNarrationOffsetPtr = lookup_symbol(libLocation, "_ZN12StoryLogUnit19addNarrationMessageERKNSt6__ndk112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE");
|
|
|
|
if (storyLogUnitAddNarrationOffsetPtr == nullptr) {
|
|
initialization_error("Unable to find a pointer for StoryLogUnit::addNarrationMessage.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
storyLogUnitAddNarrationOffset = reinterpret_cast<uintptr_t>(storyLogUnitAddNarrationOffsetPtr);
|
|
|
|
void *storyCharaUnitonTextHome = lookup_symbol(libLocation, "_ZN14StoryCharaUnit10onTextHomeENSt6__ndk110shared_ptrI16StoryTurnCommandEEb");
|
|
// StoryCharaUnit::onTextHome(std::shared_ptr<StoryTurnCommand>, bool)
|
|
|
|
if (storyCharaUnitonTextHome == nullptr) {
|
|
initialization_error("Unable to find a pointer for StoryCharaUnit::onTextHome.");
|
|
pthread_exit(NULL);
|
|
}
|
|
storyCharaUnitonTextHomeOffset = reinterpret_cast<uintptr_t>(storyCharaUnitonTextHome);
|
|
|
|
void *initLabelCWO = lookup_symbol(libLocation, "_ZN9LbUtility27initLabelCenterWidthOutlineEPN7cocos2d4NodeERPNS0_5LabelEPKcfNS0_4Vec2EiNS0_4SizeEiNS0_7Color4BEiSA_");
|
|
// LbUtility::initLabelCenterWidthOutline(cocos2d::Node *, cocos2d::Label *&, char const*, float, cocos2d::Vec2, int, cocos2d::Size, int, cocos2d::Color4B, int, cocos2d::Color4B)
|
|
|
|
if (initLabelCWO == nullptr) {
|
|
initialization_error("Unable to find a pointer for LbUtility::initLabelCenterWidthOutline.");
|
|
pthread_exit(NULL);
|
|
}
|
|
initCenterWidthOutline = reinterpret_cast<uintptr_t>(initLabelCWO);
|
|
|
|
void* storyNarrationUnitCreateLabelPtr = lookup_symbol(libLocation, "_ZN18StoryNarrationUnit11createLabelEv");
|
|
|
|
if (storyNarrationUnitCreateLabelPtr == nullptr) {
|
|
initialization_error("Unable to find a pointer for StoryNarrationUnit::createLabel.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
storyNarrationUnitCreateLabelOffset = reinterpret_cast<uintptr_t>(storyNarrationUnitCreateLabelPtr);
|
|
|
|
// Cocos functions
|
|
void *directorPtr = lookup_symbol(libLocation, "_ZN7cocos2d8Director11getInstanceEv");
|
|
if (directorPtr == nullptr) {
|
|
initialization_error("Unable to find a pointer for cocos2d::Director::getInstance()");
|
|
pthread_exit(NULL);
|
|
}
|
|
getDirector = (director_type) directorPtr;
|
|
|
|
void* getWinSizePtr = lookup_symbol(libLocation, "_ZNK7cocos2d8Director10getWinSizeEv");
|
|
if (getWinSizePtr == nullptr) {
|
|
initialization_error("Unable to find a pointer for cocos2d::Director::getWinSize()");
|
|
pthread_exit(NULL);
|
|
}
|
|
getWinSize = (get_win_size_type) getWinSizePtr;
|
|
|
|
void* getVisibleOriginPtr = lookup_symbol(libLocation, "_ZNK7cocos2d8Director16getVisibleOriginEv");
|
|
if (getVisibleOriginPtr == nullptr) {
|
|
initialization_error("Unable to find a pointer for cocos2d::Director::getVisibleOrigin()");
|
|
pthread_exit(NULL);
|
|
}
|
|
getVisibleOrigin = (get_visible_origin_type) getVisibleOriginPtr;
|
|
|
|
|
|
|
|
LOGI("Exiting hook thread.");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
__attribute__((constructor))
|
|
void hook_main() {
|
|
|
|
}
|
|
|
|
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
LOGI("Starting MagiaHook.");
|
|
|
|
gJvm = vm; // cache the JavaVM pointer
|
|
Dl_info dlInfo;
|
|
JNIEnv* env;
|
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// https://stackoverflow.com/a/16302771/9665729
|
|
// Cache ClassLoader object due to threading bug
|
|
auto randomClass = env->FindClass("org/cocos2dx/lib/Cocos2dxHelper");
|
|
jclass classClass = env->GetObjectClass(randomClass);
|
|
auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
|
|
auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
|
|
"()Ljava/lang/ClassLoader;");
|
|
gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod));
|
|
gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
|
|
"(Ljava/lang/String;)Ljava/lang/Class;");
|
|
|
|
|
|
if (dladdr((const void*) hook_main, &dlInfo))
|
|
{
|
|
//fs::path ilso = fs::path(dlInfo.dli_fname).remove_filename(); ndk 22+ required
|
|
//ilso /= "il2cpp.so"; ndk 22+ required
|
|
hook_loop_args* args = new hook_loop_args;
|
|
auto ilso = (std::string) dirname((char *)dlInfo.dli_fname);
|
|
|
|
ilso += "/";
|
|
ilso += libName;
|
|
|
|
//if (fs::exists(ilso)) { ndk 22+ required
|
|
if (!file_exists(ilso)) {
|
|
// Fix for some devices
|
|
auto this_ilso = get_libFoldername(hookName);
|
|
if (this_ilso.empty()) {
|
|
LOGE("Failed to locate shared library %s. Checked: %s", libName, ilso.c_str());
|
|
return JNI_VERSION_1_6;
|
|
}
|
|
|
|
auto ilso2 = this_ilso.substr(0, this_ilso.length() - strlen(hookName) - 1);
|
|
ilso2 += libName;
|
|
if (!file_exists(ilso2)) {
|
|
LOGE("Failed to load shared library %s. Checked: %s", libName, ilso2.c_str());
|
|
return JNI_VERSION_1_6;
|
|
}
|
|
args->libso = ilso2;
|
|
}
|
|
else {
|
|
args->libso = ilso;
|
|
}
|
|
pthread_t ptid;
|
|
if (pthread_create(&ptid, NULL, &hook_loop, args) != 0) {
|
|
LOGE("Hooking thread failed to start.");
|
|
return JNI_VERSION_1_6;
|
|
}
|
|
}
|
|
return JNI_VERSION_1_6;
|
|
} |