├── lib └── .gitkeep ├── apk └── Add the APK to modify here.txt ├── smali ├── MagiaNative │ ├── app │ │ ├── .gitignore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ ├── colors.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ │ ├── drawable-v24 │ │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ │ └── drawable │ │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── io │ │ │ │ │ └── kamihama │ │ │ │ │ └── magianative │ │ │ │ │ ├── RestClient$2.smali │ │ │ │ │ ├── RestClient$1.smali │ │ │ │ │ ├── RestClient.java │ │ │ │ │ └── RestClient.smali │ │ │ ├── test │ │ │ │ └── java │ │ │ │ │ └── io │ │ │ │ │ └── kamihama │ │ │ │ │ └── magianative │ │ │ │ │ └── ExampleUnitTest.java │ │ │ └── androidTest │ │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── kamihama │ │ │ │ └── magianative │ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── settings.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── build.gradle │ ├── gradle.properties │ ├── .gitignore │ ├── gradlew.bat │ └── gradlew └── loader │ ├── libLoader.smali │ └── libLoader$1.smali ├── armv7apk └── Add the ARMv7 APK to modify here.txt ├── requirements.txt ├── patches ├── koruri-semibold.ttf ├── unknown │ ├── okhttp3 │ │ └── internal │ │ │ └── publicsuffix │ │ │ └── publicsuffixes.gz │ └── google-services.json ├── Hook.patch ├── Backtrace.patch ├── NativeBridge.patch └── strings.xml ├── ci_versions ├── src_apk.sh └── deps.sh ├── src ├── Config.h ├── rest │ ├── MagiaRest.h │ └── MagiaRest.cpp ├── Utils.h ├── libmadomagi.h ├── Utils.cpp └── MagiaClient.cpp ├── convert_smali_eol.ps1 ├── ci_check_for_update.sh ├── sign_example.bat ├── .gitmodules ├── ci_download_src_apk.sh ├── ci_install_deps.sh ├── LICENSE ├── sign_example.sh ├── ci_build.sh ├── README.md ├── CMakeLists.txt └── .gitignore /lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apk/Add the APK to modify here.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /armv7apk/Add the ARMv7 APK to modify here.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | parse==1.18.0 2 | PyTexturePacker==1.1.0 3 | -------------------------------------------------------------------------------- /smali/MagiaNative/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':magianativelib' 2 | -------------------------------------------------------------------------------- /patches/koruri-semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/patches/koruri-semibold.ttf -------------------------------------------------------------------------------- /ci_versions/src_apk.sh: -------------------------------------------------------------------------------- 1 | SRCAPK_CERT_SHA256="3dc9c8238c830ac58a6e705353eaa0dbe0f90e302f1259042bbcab6b4c3d8c6e" 2 | SRCAPK_VER="3.2.0" -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MagiaNative 3 | 4 | -------------------------------------------------------------------------------- /smali/MagiaNative/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /patches/unknown/okhttp3/internal/publicsuffix/publicsuffixes.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/patches/unknown/okhttp3/internal/publicsuffix/publicsuffixes.gz -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayshift/magiatranslate/HEAD/smali/MagiaNative/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /src/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | /************************** 8 | * Magia Translate Ver * 9 | **************************/ 10 | #define MT_VERSION 123 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /smali/MagiaNative/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 21 21:15:19 BST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /convert_smali_eol.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | 3 | Get-ChildItem -Recurse .\build\app\smali | ForEach-Object -Process { 4 | $filepath = $_.FullName 5 | if ($filepath.EndsWith(".smali")) { 6 | [IO.File]::WriteAllText($filepath, ([IO.File]::ReadAllText($filepath) -replace "`r`n", "`n")) 7 | } 8 | } -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ci_versions/deps.sh: -------------------------------------------------------------------------------- 1 | COMMANDLINETOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip" 2 | COMMANDLINETOOLS_SHA256="bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0" 3 | CMAKE_VER="3.22.1" 4 | NDK_VER="25.2.9519653" 5 | BUILD_TOOLS_VER="33.0.2" 6 | APKTOOL_VER="2.8.1" 7 | APKTOOL_SHA256="7b4a8e1703e228d206db29644b71141687d8a111b55b039b08b02dfa443ab0f9" -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/test/java/io/kamihama/magianative/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.kamihama.magianative; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /patches/Hook.patch: -------------------------------------------------------------------------------- 1 | diff --git a/build/app/smali/jp/f4samurai/MyApplication.smali b/build/app/smali/jp/f4samurai/MyApplication.smali 2 | index 2a83c1d..c18fa78 100644 3 | --- "a/build/app/smali/jp/f4samurai/MyApplication.smali" 4 | +++ "b/build/app/smali/jp/f4samurai/MyApplication.smali" 5 | @@ -6,6 +6,7 @@ 6 | # direct methods 7 | .method public constructor ()V 8 | .locals 0 9 | + invoke-static {}, Lcom/loadLib/libLoader;->loadLib()V 10 | 11 | .line 9 12 | invoke-direct {p0}, Landroid/app/Application;->()V 13 | -------------------------------------------------------------------------------- /smali/MagiaNative/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.4.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /ci_check_for_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | 5 | UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 6 | 7 | CHECKSUM_URL="https://jp.rika.ren/apk/Origin/checksum.txt" 8 | 9 | . ci_versions/src_apk.sh 10 | 11 | LATEST_SRCAPK_VER=$(curl -s -A "${UA}" -L "${CHECKSUM_URL}" | grep -E -i "^Version\s+name:\s+" | tail -n1 | grep -P -o "(\\d+\\.)+\\d+$") 12 | 13 | if echo "${SRCAPK_VER}" | grep -q -P "^(\\d+\\.)+\\d+$" && echo "${LATEST_SRCAPK_VER}" | grep -q -P "^(\\d+\\.)+\\d+$"; then 14 | if [[ "${SRCAPK_VER}" != "${LATEST_SRCAPK_VER}" ]]; then 15 | echo "latest-src-apk-ver=${LATEST_SRCAPK_VER}" 16 | echo "new-version-available=true" 17 | fi 18 | fi -------------------------------------------------------------------------------- /sign_example.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if not exist "%zipalign%" ( 4 | echo zipalign not found 5 | goto errorexit 6 | ) 7 | if not exist "%apksigner%" ( 8 | echo apksigner not found 9 | goto errorexit 10 | ) 11 | 12 | echo Doing zipalign... 13 | "%zipalign%" -f -p 4 "%~dp0\build\magia_patched.apk" "%~dp0\build\magia_patched_aligned.apk" 14 | if errorlevel 1 ( 15 | echo Failed to zipalign! 16 | goto errorexit 17 | ) 18 | echo Removing tmp file... 19 | del /f /q "%~dp0\build\magia_patched.apk" 20 | rename "%~dp0\build\magia_patched_aligned.apk" magia_patched.apk 21 | 22 | echo Doing apksign... 23 | call "%apksigner%" sign --ks "%~dp0\changeme.keystore" --ks-pass pass:changeme --ks-key-alias name "%~dp0\build\magia_patched.apk" 24 | if errorlevel 1 goto errorexit 25 | 26 | exit /b 27 | 28 | :errorexit 29 | exit /b 3 -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/Dobby"] 2 | path = lib/Dobby 3 | url = https://github.com/rayshift/Dobby 4 | branch = master 5 | [submodule "lib/cocos"] 6 | path = lib/cocos 7 | url = https://github.com/neobenedict/cocos2d-src-only/ 8 | branch = master 9 | [submodule "smali/okhttp-smali"] 10 | path = smali/okhttp-smali 11 | url = https://github.com/neobenedict/okhttp-smali 12 | branch = master 13 | [submodule "lib/untp"] 14 | path = lib/untp 15 | url = https://github.com/neobenedict/untp 16 | branch = develop 17 | [submodule "patches/images"] 18 | path = patches/images 19 | url = https://git.rayshift.io/kamihama/magia-client-image-patches 20 | branch = master 21 | [submodule "patches/magia-en-apk-assets"] 22 | path = patches/magia-en-apk-assets 23 | url = https://git.rayshift.io/kamihama/magia-en-apk-assets 24 | branch = master 25 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /smali/loader/libLoader.smali: -------------------------------------------------------------------------------- 1 | .class public Lcom/loadLib/libLoader; 2 | .super Landroid/app/Activity; 3 | .source "libLoader.java" 4 | 5 | 6 | # direct methods 7 | .method public constructor ()V 8 | .locals 0 9 | 10 | .prologue 11 | .line 12 12 | invoke-direct {p0}, Landroid/app/Activity;->()V 13 | 14 | return-void 15 | .end method 16 | 17 | .method public static loadLib()V 18 | .locals 4 19 | 20 | .prologue 21 | .line 14 22 | new-instance v0, Landroid/os/Handler; 23 | 24 | invoke-direct {v0}, Landroid/os/Handler;->()V 25 | 26 | new-instance v1, Lcom/loadLib/libLoader$1; 27 | 28 | invoke-direct {v1}, Lcom/loadLib/libLoader$1;->()V 29 | 30 | const-wide/16 v2, 0x1 31 | 32 | invoke-virtual {v0, v1, v2, v3}, Landroid/os/Handler;->postDelayed(Ljava/lang/Runnable;J)Z 33 | 34 | .line 20 35 | return-void 36 | .end method 37 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/androidTest/java/io/kamihama/magianative/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.kamihama.magianative; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getTargetContext(); 24 | 25 | assertEquals("io.kamihama.magianative", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/rest/MagiaRest.h: -------------------------------------------------------------------------------- 1 | #ifndef MAGIAREST_H 2 | #define MAGIAREST_H 3 | #include 4 | #include 5 | #include 6 | 7 | using json = nlohmann::json; 8 | 9 | #define MAGIAREST_EMPTY 0 10 | #define MAGIAREST_SUCCESS 1 11 | #define MAGIAREST_ERROR 2 12 | 13 | // Rest class using JNI 14 | class MagiaRest { 15 | public: 16 | MagiaRest(JavaVM* gJvm); 17 | 18 | int Endpoint(); 19 | 20 | size_t EndpointStringLength(); 21 | 22 | std::string GetEndpointUrl(); 23 | 24 | std::string GetEndpointError(); 25 | int GetEndpointVersion(); 26 | 27 | int GetMaxThreads(); 28 | 29 | ~MagiaRest(); 30 | private: 31 | JNIEnv* env; 32 | jclass klass; 33 | jobject magiaRestObj; 34 | 35 | jstring endpointJString = NULL; 36 | const char *endpointChar = NULL; 37 | 38 | bool endpointValid = false; 39 | json endpointJson = NULL; 40 | 41 | }; 42 | 43 | #endif -------------------------------------------------------------------------------- /ci_download_src_apk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | verify_apk() { 5 | . ci_versions/deps.sh 6 | "./deps/Android/Sdk/build-tools/${BUILD_TOOLS_VER}/apksigner" verify --print-certs "$1" > /tmp/result.txt || exit 1 7 | grep -q "$2" /tmp/result.txt && return 0 8 | echo "Cert SHA256 digest mismatch" >&2 9 | exit 2 10 | } 11 | 12 | UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 13 | 14 | URL_PREFIX="https://jp.rika.ren/apk/Origin/com.aniplex.magireco" 15 | ARMV8_URL="${URL_PREFIX}.arm8.apk" 16 | ARMV7_URL="${URL_PREFIX}.arm7.apk" 17 | 18 | . ci_versions/src_apk.sh 19 | 20 | rm -fr apk armv7apk 21 | mkdir -p apk armv7apk 22 | 23 | curl -A "${UA}" -o out.apk -L "${ARMV8_URL}" 24 | verify_apk out.apk "${SRCAPK_CERT_SHA256}" && mv out.apk "./apk/src_${SRCAPK_VER}.apk" 25 | 26 | curl -A "${UA}" -o out.apk -L "${ARMV7_URL}" 27 | verify_apk out.apk "${SRCAPK_CERT_SHA256}" && mv out.apk "./armv7apk/armv7src_${SRCAPK_VER}.apk" -------------------------------------------------------------------------------- /smali/loader/libLoader$1.smali: -------------------------------------------------------------------------------- 1 | .class final Lcom/loadLib/libLoader$1; 2 | .super Ljava/lang/Object; 3 | .source "libLoader.java" 4 | 5 | # interfaces 6 | .implements Ljava/lang/Runnable; 7 | 8 | 9 | # annotations 10 | .annotation system Ldalvik/annotation/EnclosingMethod; 11 | value = Lcom/JvRuit/Ldr;->loadLib()V 12 | .end annotation 13 | 14 | .annotation system Ldalvik/annotation/InnerClass; 15 | accessFlags = 0x8 16 | name = null 17 | .end annotation 18 | 19 | 20 | # direct methods 21 | .method constructor ()V 22 | .locals 0 23 | 24 | .prologue 25 | .line 14 26 | invoke-direct {p0}, Ljava/lang/Object;->()V 27 | 28 | return-void 29 | .end method 30 | 31 | 32 | # virtual methods 33 | .method public run()V 34 | .locals 1 35 | 36 | .prologue 37 | .line 17 38 | const-string/jumbo v0, "uwasa" 39 | 40 | invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 41 | 42 | .line 18 43 | return-void 44 | .end method 45 | -------------------------------------------------------------------------------- /ci_install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 5 | 6 | . ci_versions/deps.sh 7 | 8 | rm -fr deps 9 | mkdir deps 10 | cd deps 11 | 12 | mkdir -p Android/Sdk 13 | pushd Android/Sdk 14 | curl -A "${UA}" -o "commandlinetools-linux.zip" -L "${COMMANDLINETOOLS_URL}" 15 | sha256sum "commandlinetools-linux.zip" | grep -q "${COMMANDLINETOOLS_SHA256}" 16 | unzip "commandlinetools-linux.zip" 17 | rm "commandlinetools-linux.zip" 18 | yes | ./cmdline-tools/bin/sdkmanager --sdk_root="$(realpath .)" --licenses 19 | for PKG in "cmake;${CMAKE_VER}" "ndk;${NDK_VER}" "build-tools;${BUILD_TOOLS_VER}"; do 20 | ./cmdline-tools/bin/sdkmanager --sdk_root="$(realpath .)" --install "${PKG}" 21 | done 22 | popd 23 | 24 | # apktool 25 | APKTOOL="apktool_${APKTOOL_VER}.jar" 26 | curl -A "${UA}" -o "${APKTOOL}" -L "https://bitbucket.org/iBotPeaches/apktool/downloads/${APKTOOL}" 27 | sha256sum "${APKTOOL}" | grep -q "${APKTOOL_SHA256}" -------------------------------------------------------------------------------- /smali/MagiaNative/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "io.kamihama.magianative" 8 | minSdkVersion 19 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'androidx.appcompat:appcompat:1.2.0' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'androidx.test:runner:1.3.0' 27 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 28 | implementation("com.squareup.okhttp3:okhttp:3.12.9") 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2024 Rayshift 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/java/io/kamihama/magianative/RestClient$2.smali: -------------------------------------------------------------------------------- 1 | .class final Lio/kamihama/magianative/RestClient$2; 2 | .super Ljava/lang/Object; 3 | .source "RestClient.java" 4 | 5 | # interfaces 6 | .implements Ljavax/net/ssl/HostnameVerifier; 7 | 8 | 9 | # annotations 10 | .annotation system Ldalvik/annotation/EnclosingMethod; 11 | value = Lio/kamihama/magianative/RestClient;->getUnsafeOkHttpClient()Lokhttp3/OkHttpClient; 12 | .end annotation 13 | 14 | .annotation system Ldalvik/annotation/InnerClass; 15 | accessFlags = 0x8 16 | name = null 17 | .end annotation 18 | 19 | 20 | # direct methods 21 | .method constructor ()V 22 | .registers 1 23 | 24 | .prologue 25 | .line 110 26 | invoke-direct {p0}, Ljava/lang/Object;->()V 27 | 28 | return-void 29 | .end method 30 | 31 | 32 | # virtual methods 33 | .method public verify(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z 34 | .registers 4 35 | .param p1, "hostname" # Ljava/lang/String; 36 | .param p2, "session" # Ljavax/net/ssl/SSLSession; 37 | 38 | .prologue 39 | .line 113 40 | const/4 v0, 0x1 41 | 42 | return v0 43 | .end method 44 | -------------------------------------------------------------------------------- /smali/MagiaNative/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /sign_example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | BASEDIR="$(realpath "$(dirname "${0}")")" 3 | 4 | # env-based 5 | ZIPALIGN="${MT_ZIPALIGN:-zipalign}" # ~/android-sdk/build-tools/zipalign 6 | APKSIGNER="${MT_APKSIGNER:-apksigner}" # ~/android-sdk/build-tools/apksigner 7 | 8 | # hardcode-based 9 | #ZIPALIGN="${BASEDIR}/abt/zipalign" 10 | #APKSIGNER="${BASEDIR}/abt/apksigner" 11 | 12 | # arg-based 13 | APK="${1:-${BASEDIR}/build/io.kamihama.magiatranslate.v0.50.apk}" 14 | KEYSTORE="${2:-${BASEDIR}/changeme.keystore}" 15 | 16 | _errorexit() { 17 | [ -z "${2}" ] || echo "${2}" 18 | echo "Signing failed." 19 | exit ${1} 20 | } 21 | 22 | [ -f "${APK}" ] || _errorexit 1 "Missing apk to sign! Tried file: ${APK}" 23 | [ -f "${KEYSTORE}" ] || _errorexit 2 "Missing keystore! Tried file: ${KEYSTORE}" 24 | 25 | echo "Doing zipalign..." 26 | "${ZIPALIGN}" -f -p 4 "${APK}" "${APK}.tmp" 27 | [ "$?" -ne "0" ] && _errorexit 3 "Failed to zipalign!" 28 | echo "Removing tmp file..." 29 | mv "${APK}.tmp" "${APK}" 30 | 31 | echo "Doing apksign..." 32 | "${APKSIGNER}" sign --ks "${KEYSTORE}" --ks-pass pass:changeme --ks-key-alias name "${APK}" 33 | [ "$?" -ne "0" ] && _errorexit 4 "Failed to apksign!" 34 | 35 | exit 0 36 | -------------------------------------------------------------------------------- /patches/unknown/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "48695978581", 4 | "firebase_url": "https://kamihama-io.firebaseio.com", 5 | "project_id": "kamihama-io", 6 | "storage_bucket": "kamihama-io.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:48695978581:android:bc7cc007c2de2d0e79c2dc", 12 | "android_client_info": { 13 | "package_name": "io.kamihama.magiatranslate" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "48695978581-l3g2vae3su7td5tilbunf8fpb1875np4.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyBlHoCTAsEa1zsIhKfE8nvMVewd0ULYmkw" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "48695978581-l3g2vae3su7td5tilbunf8fpb1875np4.apps.googleusercontent.com", 32 | "client_type": 3 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | ], 39 | "configuration_version": "1" 40 | } -------------------------------------------------------------------------------- /patches/Backtrace.patch: -------------------------------------------------------------------------------- 1 | diff --git a/build/app/smali/jp/f4samurai/backtrace/BacktraceHandler.smali b/build/app/smali/jp/f4samurai/backtrace/BacktraceHandler.smali 2 | index 6bc2d4d..67e0489 100644 3 | --- a/build/app/smali/jp/f4samurai/backtrace/BacktraceHandler.smali 4 | +++ b/build/app/smali/jp/f4samurai/backtrace/BacktraceHandler.smali 5 | @@ -6,9 +6,9 @@ 6 | # static fields 7 | .field private static final ANR_TIMEOUT:I = 0xbb8 8 | 9 | -.field private static final ENDPOINT_URL:Ljava/lang/String; = "https://f4samurai.sp.backtrace.io:6098" 10 | +.field private static final ENDPOINT_URL:Ljava/lang/String; = "https://walpurgisnacht.rayshift.io/backtrace" 11 | 12 | -.field private static final SUBMISSION_TOKEN:Ljava/lang/String; = "a798a9b785f35254cfee3086e38053e7597ba9a49cdcc8b0a90d64ef052786b6" 13 | +.field private static final SUBMISSION_TOKEN:Ljava/lang/String; = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 14 | 15 | .field private static sBacktraceClient:Lbacktraceio/library/BacktraceClient; 16 | 17 | @@ -90,9 +90,9 @@ 18 | :cond_0 19 | new-instance v0, Lbacktraceio/library/BacktraceCredentials; 20 | 21 | - const-string v1, "https://f4samurai.sp.backtrace.io:6098" 22 | + const-string v1, "https://walpurgisnacht.rayshift.io/backtrace" 23 | 24 | - const-string v2, "a798a9b785f35254cfee3086e38053e7597ba9a49cdcc8b0a90d64ef052786b6" 25 | + const-string v2, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 26 | 27 | invoke-direct {v0, v1, v2}, Lbacktraceio/library/BacktraceCredentials;->(Ljava/lang/String;Ljava/lang/String;)V 28 | 29 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/java/io/kamihama/magianative/RestClient$1.smali: -------------------------------------------------------------------------------- 1 | .class final Lio/kamihama/magianative/RestClient$1; 2 | .super Ljava/lang/Object; 3 | .source "RestClient.java" 4 | 5 | # interfaces 6 | .implements Ljavax/net/ssl/X509TrustManager; 7 | 8 | 9 | # annotations 10 | .annotation system Ldalvik/annotation/EnclosingMethod; 11 | value = Lio/kamihama/magianative/RestClient;->getUnsafeOkHttpClient()Lokhttp3/OkHttpClient; 12 | .end annotation 13 | 14 | .annotation system Ldalvik/annotation/InnerClass; 15 | accessFlags = 0x8 16 | name = null 17 | .end annotation 18 | 19 | 20 | # direct methods 21 | .method constructor ()V 22 | .registers 1 23 | 24 | .prologue 25 | .line 86 26 | invoke-direct {p0}, Ljava/lang/Object;->()V 27 | 28 | return-void 29 | .end method 30 | 31 | 32 | # virtual methods 33 | .method public checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V 34 | .registers 3 35 | .param p1, "chain" # [Ljava/security/cert/X509Certificate; 36 | .param p2, "authType" # Ljava/lang/String; 37 | .annotation system Ldalvik/annotation/Throws; 38 | value = { 39 | Ljava/security/cert/CertificateException; 40 | } 41 | .end annotation 42 | 43 | .prologue 44 | .line 89 45 | return-void 46 | .end method 47 | 48 | .method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V 49 | .registers 3 50 | .param p1, "chain" # [Ljava/security/cert/X509Certificate; 51 | .param p2, "authType" # Ljava/lang/String; 52 | .annotation system Ldalvik/annotation/Throws; 53 | value = { 54 | Ljava/security/cert/CertificateException; 55 | } 56 | .end annotation 57 | 58 | .prologue 59 | .line 93 60 | return-void 61 | .end method 62 | 63 | .method public getAcceptedIssuers()[Ljava/security/cert/X509Certificate; 64 | .registers 2 65 | 66 | .prologue 67 | .line 97 68 | const/4 v0, 0x0 69 | 70 | new-array v0, v0, [Ljava/security/cert/X509Certificate; 71 | 72 | return-object v0 73 | .end method 74 | -------------------------------------------------------------------------------- /smali/MagiaNative/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | # Built application files 15 | *.apk 16 | *.aar 17 | *.ap_ 18 | *.aab 19 | 20 | # Files for the ART/Dalvik VM 21 | *.dex 22 | 23 | # Java class files 24 | *.class 25 | 26 | # Generated files 27 | bin/ 28 | gen/ 29 | out/ 30 | # Uncomment the following line in case you need and you don't have the release build type files in your app 31 | # release/ 32 | 33 | # Gradle files 34 | .gradle/ 35 | build/ 36 | 37 | # Local configuration file (sdk path, etc) 38 | local.properties 39 | 40 | # Proguard folder generated by Eclipse 41 | proguard/ 42 | 43 | # Log Files 44 | *.log 45 | 46 | # Android Studio Navigation editor temp files 47 | .navigation/ 48 | 49 | # Android Studio captures folder 50 | captures/ 51 | 52 | # IntelliJ 53 | *.iml 54 | .idea/workspace.xml 55 | .idea/tasks.xml 56 | .idea/gradle.xml 57 | .idea/assetWizardSettings.xml 58 | .idea/dictionaries 59 | .idea/libraries 60 | # Android Studio 3 in .gitignore file. 61 | .idea/caches 62 | .idea/modules.xml 63 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 64 | .idea/navEditor.xml 65 | 66 | # Keystore files 67 | # Uncomment the following lines if you do not want to check your keystore files in. 68 | #*.jks 69 | #*.keystore 70 | 71 | # External native build folder generated in Android Studio 2.2 and later 72 | .externalNativeBuild 73 | .cxx/ 74 | 75 | # Google Services (e.g. APIs or Firebase) 76 | # google-services.json 77 | 78 | # Freeline 79 | freeline.py 80 | freeline/ 81 | freeline_project_description.json 82 | 83 | # fastlane 84 | fastlane/report.xml 85 | fastlane/Preview.html 86 | fastlane/screenshots 87 | fastlane/test_output 88 | fastlane/readme.md 89 | 90 | # Version control 91 | vcs.xml 92 | 93 | # lint 94 | lint/intermediates/ 95 | lint/generated/ 96 | lint/outputs/ 97 | lint/tmp/ 98 | # lint/reports/ 99 | 100 | # Android Profiling 101 | *.hprof 102 | -------------------------------------------------------------------------------- /smali/MagiaNative/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /ci_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | BASEDIR="$(realpath "$(dirname "${0}")")" 5 | 6 | # prepare signing key 7 | [[ "${KEYSTORE_BASE64}" == "" ]] && echo "KEYSTORE_BASE64 is not set" >&2 && exit 1 8 | [[ "${KS_PASS}" == "" ]] && echo "KS_PASS is not set" >&2 && exit 1 9 | echo "${KEYSTORE_BASE64}" | base64 -d > "${BASEDIR}/builder.jks" 2> /dev/null 10 | KEYSTORE_ARGS="--ks \"${BASEDIR}/builder.jks\" --ks-pass \"pass:${KS_PASS}\"" 11 | [[ "${KEY_PASS}" != "" ]] && echo "KEY_PASS is set!" >&2 && KEYSTORE_ARGS="${KEYSTORE_ARGS} --key-pass \"pass:${KEY_PASS}\"" 12 | [[ "${KS_KEY_ALIAS}" != "" ]] && echo "KS_KEY_ALIAS is set!" >&2 && KEYSTORE_ARGS="${KEYSTORE_ARGS} --ks-key-alias \"${KS_KEY_ALIAS}\"" 13 | unset KEYSTORE_BASE64 14 | unset KS_PASS 15 | unset KEY_PASS 16 | unset KS_KEY_ALIAS 17 | cp sign_example.sh sign.sh 18 | sed -E -i "s@^(\"\\\$\{APKSIGNER\}\" sign ).+( \"\\\$\{APK\}\")\$@\\1${KEYSTORE_ARGS}\\2@" sign.sh 2>&1 > /dev/null 19 | sed -i "s@^KEYSTORE=.*@KEYSTORE=\"${BASEDIR}/builder.jks\"@" sign.sh 2>&1 > /dev/null 20 | chmod +x sign.sh 21 | 22 | # prepare source APKs 23 | MT_VER=$(grep -P -o "(?<=^#define MT_VERSION )\d+$" src/Config.h) 24 | . ci_versions/src_apk.sh 25 | SRCAPK="${BASEDIR}/apk/src_${SRCAPK_VER}.apk" 26 | export ARMV7SRCAPK="${BASEDIR}/armv7apk/armv7src_${SRCAPK_VER}.apk" 27 | VERSION="v${SRCAPK_VER}_v${MT_VER}" 28 | 29 | # load deps versions 30 | . ci_versions/deps.sh 31 | DEPS_DIR="${BASEDIR}/deps" 32 | # prepare apktool 33 | export MT_APKTOOL="apktool_${APKTOOL_VER}.jar" 34 | mkdir -p "${BASEDIR}/build" 35 | cp "${DEPS_DIR}/${MT_APKTOOL}" "${BASEDIR}/build/${MT_APKTOOL}" 36 | # prepare Android SDK 37 | SDK_ROOT="${DEPS_DIR}/Android/Sdk" 38 | NDK="${SDK_ROOT}/ndk/${NDK_VER}" 39 | CMAKE_BIN_DIR="${SDK_ROOT}/cmake/${CMAKE_VER}/bin" 40 | BUILD_TOOLS_DIR="${SDK_ROOT}/build-tools/${BUILD_TOOLS_VER}" 41 | export MT_CMAKE="${CMAKE_BIN_DIR}/cmake" 42 | export MT_NINJA="${CMAKE_BIN_DIR}/ninja" 43 | export MT_ZIPALIGN="${BUILD_TOOLS_DIR}/zipalign" 44 | export MT_APKSIGNER="${BUILD_TOOLS_DIR}/apksigner" 45 | 46 | RESULT="${BASEDIR}/build/io.kamihama.magiatranslate.${VERSION}.apk" 47 | 48 | # build main APK which contains audiofix 49 | MT_AUDIOFIX_3_0_1=Y "${BASEDIR}/build_release.sh" "${SRCAPK}" "${VERSION}" "${NDK}" 50 | MAIN_APK="MagiaTranslate_${VERSION}.apk" 51 | mv "${RESULT}" "${BASEDIR}/${MAIN_APK}" 52 | echo "MAIN_APK=${MAIN_APK}" >> "$GITHUB_ENV" 53 | 54 | # build failsafe APK which does not contain audiofix 55 | MT_AUDIOFIX_3_0_1=N "${BASEDIR}/build_release.sh" "${SRCAPK}" "${VERSION}" "${NDK}" 56 | FAILSAFE_APK="MagiaTranslate_${VERSION}_failsafe.apk" 57 | mv "${RESULT}" "${BASEDIR}/${FAILSAFE_APK}" 58 | echo "FAILSAFE_APK=${FAILSAFE_APK}" >> "$GITHUB_ENV" 59 | 60 | rm "${BASEDIR}/builder.jks" -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef NDEBUG 17 | #define RELEASE_BUILD 18 | #else 19 | #define DEBUG_BUILD 20 | #endif 21 | 22 | #ifdef DEBUG_BUILD 23 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "MagiaHook", __VA_ARGS__) 24 | #else 25 | #define LOGD(...) do {} while(false) 26 | #endif 27 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MagiaHook", __VA_ARGS__) 28 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MagiaHook", __VA_ARGS__) 29 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MagiaHook", __VA_ARGS__) 30 | 31 | typedef unsigned long DWORD; 32 | 33 | uintptr_t get_libBase(const char* libName); 34 | 35 | std::string get_libFoldername(const char* libName); 36 | 37 | void* lookup_symbol(const char* path, const char* symbolname); 38 | 39 | bool file_exists (const std::string& name); 40 | 41 | std::string getJNISignature(); 42 | std::string getJNISignature(bool); 43 | std::string getJNISignature(char); 44 | std::string getJNISignature(short); 45 | std::string getJNISignature(int); 46 | std::string getJNISignature(long); 47 | std::string getJNISignature(float); 48 | std::string getJNISignature(double); 49 | std::string getJNISignature(const char*); 50 | std::string getJNISignature(const std::string&); 51 | 52 | template 53 | std::string getJNISignature(T x) { 54 | // This template should never be instantiated 55 | static_assert(sizeof(x) == 0, "Unsupported argument type"); 56 | return ""; 57 | } 58 | 59 | template 60 | std::string getJNISignature(T x, Ts... xs) { 61 | return getJNISignature(x) + getJNISignature(xs...); 62 | } 63 | 64 | 65 | JNIEnv* getEnv(JavaVM* gJvm); 66 | jclass findClass(JNIEnv* env, jobject gClassLoader, jmethodID gFindClassMethod, const char* name); 67 | 68 | // https://stackoverflow.com/a/26221725/9665729 69 | template 70 | std::string string_format( const std::string& format, Args ... args ) 71 | { 72 | size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' 73 | if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); } 74 | std::unique_ptr buf( new char[ size ] ); 75 | snprintf( buf.get(), size, format.c_str(), args ... ); 76 | return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside 77 | } 78 | 79 | std::string longlong_to_string( unsigned long long value ); 80 | #endif 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magia Translate 2 | [![GitHub Release](https://img.shields.io/github/release/rayshift/magiatranslate.svg?style=flat)](https://github.com/rayshift/magiatranslate/releases) [![Github All Releases](https://img.shields.io/github/downloads/rayshift/magiatranslate/total.svg?style=flat)](https://github.com/rayshift/magiatranslate/releases) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Discord Chat](https://img.shields.io/discord/665980614998097941.svg)](https://discord.gg/6vncnjj) 3 | 4 | This is the client source code for Magia Translate, an English translation modification for Magia Record JP. It is licensed under the MIT license. 5 | 6 | Magia Record JP has reached end of service as of the 31st July 2024. As such, this app has no further purpose, and has been retired. We thank you for your support over the last 4 years. 7 | 8 | ## How to build 9 | - Clone the repository including all submodules `git clone --recurse-submodules https://github.com/rayshift/magiatranslate` 10 | - If you don't have Android Studio installed, you may download [command line tools](https://developer.android.com/studio#command-tools) only. 11 | - Downlad `NDK` (`ndk;25.2.9519653`), `CMake` (`cmake;3.22.1`) and `Android SDK Build-Tools` (`build-tools;33.0.2`) with [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager), or just use its GUI to install them if you have Android Studio installed. 12 | - Install the python requirements in requirements.txt. 13 | - Move `sign_example.bat` to `sign.bat` and add your jarsigner keystore, alias and password. 14 | - Place your magia record APKs in the `apk` and `armv7apk` directory. 15 | - Run `build_release.bat`. 16 | 17 | Notes: 18 | - Use `build_debug.bat` if you want a debug build with debug symbols. 19 | - If your apk has split ABIs (armeabi-v7a/arm64), you will need to move the other `libmadomagi_native.so` into `build/app/lib/{ARCH}`. For example, if the arm7 version of the game is placed in `apk/`, you need to move the `arm8` .so manually, and vice versa. 20 | 21 | ## Contributing 22 | Create a pull request with your contributions. Please do not submit any copyrighted content (images) to this repository. 23 | 24 | Ensure you test your changes on both armeabi-v7a and arm64-v8a. Also test an emulator such as Nox. To force install a specific ABI, use something like: 25 | `adb.exe -s device install --abi arm64-v8a -r -d .\MagiaTranslate_v2.2.6_v110.apk` 26 | 27 | ## Further reading 28 | - The server source code is now public at https://github.com/rayshift/kamihama-server - in order to change the server URL, edit the URLs in the smali file `smali/MagiaNative/app/src/main/java/io/kamihama/magianative/RestClient.smali`. You can also recompile the smali file by loading the MagiaNative project in Android Studio, editing `RestClient.java`, and compiling with this plugin: https://github.com/ollide/intellij-java2smali 29 | - The hooking library used is https://github.com/jmpews/Dobby. 30 | -------------------------------------------------------------------------------- /patches/NativeBridge.patch: -------------------------------------------------------------------------------- 1 | diff --git a/build/app/smali/jp/f4samurai/bridge/NativeBridge.smali b/build/app/smali/jp/f4samurai/bridge/NativeBridge.smali 2 | index 6012fad..681a112 100644 3 | --- a/build/app/smali/jp/f4samurai/bridge/NativeBridge.smali 4 | +++ b/build/app/smali/jp/f4samurai/bridge/NativeBridge.smali 5 | @@ -8,8 +8,6 @@ 6 | 7 | .field private static sAppActivity:Ljp/f4samurai/AppActivity; 8 | 9 | -.field private static sCheatHandler:Ljp/f4samurai/bridge/CheatHandler; 10 | - 11 | .field private static sClipboardManager:Landroid/content/ClipboardManager; 12 | 13 | 14 | @@ -43,15 +41,6 @@ 15 | 16 | sput-object v0, Ljp/f4samurai/bridge/NativeBridge;->sAppActivity:Ljp/f4samurai/AppActivity; 17 | 18 | - .line 39 19 | - new-instance v0, Ljp/f4samurai/bridge/CheatHandler; 20 | - 21 | - sget-object v1, Ljp/f4samurai/bridge/NativeBridge;->sAppActivity:Ljp/f4samurai/AppActivity; 22 | - 23 | - invoke-direct {v0, v1}, Ljp/f4samurai/bridge/CheatHandler;->(Landroid/content/Context;)V 24 | - 25 | - sput-object v0, Ljp/f4samurai/bridge/NativeBridge;->sCheatHandler:Ljp/f4samurai/bridge/CheatHandler; 26 | - 27 | .line 40 28 | invoke-static {}, Ljp/f4samurai/AppActivity;->getContext()Landroid/content/Context; 29 | 30 | @@ -539,11 +528,7 @@ 31 | .locals 1 32 | 33 | .line 195 34 | - sget-object v0, Ljp/f4samurai/bridge/NativeBridge;->sCheatHandler:Ljp/f4samurai/bridge/CheatHandler; 35 | - 36 | - invoke-virtual {v0}, Ljp/f4samurai/bridge/CheatHandler;->isUnauthorizedUser()Z 37 | - 38 | - move-result v0 39 | + const v0, 0 40 | 41 | return v0 42 | .end method 43 | @@ -649,13 +634,6 @@ 44 | .locals 2 45 | 46 | .line 123 47 | - sget-object v0, Ljp/f4samurai/bridge/NativeBridge;->sAppActivity:Ljp/f4samurai/AppActivity; 48 | - 49 | - new-instance v1, Ljp/f4samurai/bridge/NativeBridge$2; 50 | - 51 | - invoke-direct {v1, p0}, Ljp/f4samurai/bridge/NativeBridge$2;->(Ljava/lang/String;)V 52 | - 53 | - invoke-virtual {v0, v1}, Ljp/f4samurai/AppActivity;->runOnUiThread(Ljava/lang/Runnable;)V 54 | 55 | return-void 56 | .end method 57 | @@ -664,13 +642,6 @@ 58 | .locals 2 59 | 60 | .line 132 61 | - sget-object v0, Ljp/f4samurai/bridge/NativeBridge;->sAppActivity:Ljp/f4samurai/AppActivity; 62 | - 63 | - new-instance v1, Ljp/f4samurai/bridge/NativeBridge$3; 64 | - 65 | - invoke-direct {v1, p0, p1}, Ljp/f4samurai/bridge/NativeBridge$3;->(Ljava/lang/String;Ljava/lang/String;)V 66 | - 67 | - invoke-virtual {v0, v1}, Ljp/f4samurai/AppActivity;->runOnUiThread(Ljava/lang/Runnable;)V 68 | 69 | return-void 70 | .end method 71 | @@ -679,13 +650,6 @@ 72 | .locals 2 73 | 74 | .line 114 75 | - sget-object v0, Ljp/f4samurai/bridge/NativeBridge;->sAppActivity:Ljp/f4samurai/AppActivity; 76 | - 77 | - new-instance v1, Ljp/f4samurai/bridge/NativeBridge$1; 78 | - 79 | - invoke-direct {v1, p0}, Ljp/f4samurai/bridge/NativeBridge$1;->(Ljava/lang/String;)V 80 | - 81 | - invoke-virtual {v0, v1}, Ljp/f4samurai/AppActivity;->runOnUiThread(Ljava/lang/Runnable;)V 82 | 83 | return-void 84 | .end method 85 | -------------------------------------------------------------------------------- /src/libmadomagi.h: -------------------------------------------------------------------------------- 1 | enum BaseSceneLayerType { 2 | DebugMenuSceneLayer, 3 | DebugSelectQuestSceneLayer, 4 | DebugSelectStorySceneLayer, 5 | SoundViewerSceneLayer, 6 | DebugSelectMysteriesSceneLayer, 7 | AnimeViewerSceneLayer, 8 | CameraSceneLayer, 9 | WebSceneLayer, 10 | StartupSceneLayer, 11 | PrologueSceneLayer, 12 | AnotherQuestSceneLayer, 13 | TopSceneLayer, 14 | EventStoryRaidSceneLayer, 15 | EventBranchSceneLayer, 16 | EventSingleRaidSceneLayer, 17 | EventDungeonSceneLayer, 18 | EventRaidSceneLayer, 19 | EventPuellaHistoriaSceneLayer, 20 | QuestBattleSceneLayer, 21 | QuestUnitTestSceneLayer, 22 | EvolutionSceneLayer, 23 | MemoriaSceneLayer, 24 | GachaSceneLayer, 25 | StorySceneLayer, 26 | StoryViewerSceneLayer, 27 | Live2dViewerSceneLayer, 28 | MovieSceneLayer, 29 | DownloadSceneLayer, 30 | DebugSelectURLSceneLayer, 31 | QuestStoredDataSceneLayer, 32 | SendReplayDataSceneLayer, 33 | InputTextSceneLayer, 34 | SpfxViewerSceneLayer, 35 | LoadingSceneLayer, 36 | ErrorSceneLayer, 37 | NetworkErrorSceneLayer, 38 | TapSceneLayer, 39 | EmotionBoardSceneLayer, 40 | GlassTapSceneLayer, 41 | QuestViewerSceneLayer, 42 | BaseSceneLayerTypeMaxValue 43 | }; 44 | 45 | char const* BaseSceneLayerTypeStrings[] { 46 | "DebugMenuSceneLayer", 47 | "DebugSelectQuestSceneLayer", 48 | "DebugSelectStorySceneLayer", 49 | "SoundViewerSceneLayer", 50 | "DebugSelectMysteriesSceneLayer", 51 | "AnimeViewerSceneLayer", 52 | "CameraSceneLayer", 53 | "WebSceneLayer", 54 | "StartupSceneLayer", 55 | "PrologueSceneLayer", 56 | "AnotherQuestSceneLayer", 57 | "TopSceneLayer", 58 | "EventStoryRaidSceneLayer", 59 | "EventBranchSceneLayer", 60 | "EventSingleRaidSceneLayer", 61 | "EventDungeonSceneLayer", 62 | "EventRaidSceneLayer", 63 | "EventPuellaHistoriaSceneLayer", 64 | "QuestBattleSceneLayer", 65 | "QuestUnitTestSceneLayer", 66 | "EvolutionSceneLayer", 67 | "MemoriaSceneLayer", 68 | "GachaSceneLayer", 69 | "StorySceneLayer", 70 | "StoryViewerSceneLayer", 71 | "Live2dViewerSceneLayer", 72 | "MovieSceneLayer", 73 | "DownloadSceneLayer", 74 | "DebugSelectURLSceneLayer", 75 | "QuestStoredDataSceneLayer", 76 | "SendReplayDataSceneLayer", 77 | "InputTextSceneLayer", 78 | "SpfxViewerSceneLayer", 79 | "LoadingSceneLayer", 80 | "ErrorSceneLayer", 81 | "NetworkErrorSceneLayer", 82 | "TapSceneLayer", 83 | "GlassTapSceneLayer", 84 | "QuestViewerSceneLayer", 85 | "EmotionBoardSceneLayer" 86 | }; 87 | 88 | #if defined(__arm__) 89 | struct BaseSceneLayerInfo { 90 | char unk[24]; 91 | BaseSceneLayerType layerType; 92 | }; 93 | #elif defined(__aarch64__) 94 | struct BaseSceneLayerInfo { 95 | char unk[36]; 96 | BaseSceneLayerType layerType; 97 | }; 98 | #endif 99 | 100 | enum UrlConfigResourceType { 101 | BaseUrl, 102 | TrunkUrl, 103 | ScenarioUrl, 104 | UrlConfigResourceTypeMaxValue 105 | }; -------------------------------------------------------------------------------- /src/rest/MagiaRest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../Utils.h" 3 | #include "../Config.h" 4 | #include 5 | #include 6 | #include 7 | #include "MagiaRest.h" 8 | 9 | const char* restClient = "io/kamihama/magianative/RestClient"; 10 | using json = nlohmann::json; 11 | 12 | MagiaRest::MagiaRest(JavaVM* gJvm) { 13 | LOGD("Setting up MagiaRest."); 14 | env = getEnv(gJvm); 15 | klass = env->FindClass(restClient); 16 | jmethodID clientCtor = env->GetMethodID(klass, "", "()V"); 17 | magiaRestObj = env->NewObject(klass, clientCtor); 18 | LOGD("Finished setting up MagiaRest."); 19 | } 20 | 21 | int MagiaRest::Endpoint() { 22 | const char* callHandshake = "GetEndpoint"; 23 | std::string signature = "(I)Ljava/lang/String;"; 24 | jmethodID messageid = env->GetMethodID(klass, callHandshake, signature.c_str()); 25 | 26 | LOGD("Calling GetEndpoint via JNI."); 27 | endpointJString = (jstring)env->CallObjectMethod(magiaRestObj, messageid, MT_VERSION); 28 | endpointChar = env->GetStringUTFChars(endpointJString, 0); 29 | 30 | if (strcmp(endpointChar, "") == 0) { 31 | LOGW("Null response returned from endpoint, an error has occurred, or Kamihama is down."); 32 | return MAGIAREST_EMPTY; 33 | } 34 | LOGD("Successfully retrieved Endpoint JSON."); 35 | 36 | if (json::accept(endpointChar)) { 37 | endpointValid = true; 38 | endpointJson = json::parse(endpointChar); 39 | 40 | if (endpointJson["status"] != 200) { 41 | LOGW("Non-success result returned."); 42 | return MAGIAREST_ERROR; 43 | } 44 | } 45 | return endpointValid ? MAGIAREST_SUCCESS : MAGIAREST_EMPTY; 46 | } 47 | 48 | size_t MagiaRest::EndpointStringLength() { 49 | if (endpointChar == NULL) { 50 | return MAGIAREST_EMPTY; 51 | } 52 | else { 53 | auto size = std::strlen(endpointChar); 54 | return size; 55 | } 56 | } 57 | 58 | std::string MagiaRest::GetEndpointUrl() { 59 | if (endpointJson != NULL && endpointJson.contains("response") && endpointJson["response"].contains("endpoint")) { 60 | return endpointJson["response"]["endpoint"]; 61 | } 62 | LOGW("No endpoint URL found in JSON response."); 63 | return ""; 64 | } 65 | 66 | std::string MagiaRest::GetEndpointError() { 67 | if (endpointJson != NULL && endpointJson.contains("message")) { 68 | return endpointJson["message"]; 69 | } 70 | LOGW("No message found in JSON response."); 71 | return ""; 72 | } 73 | int MagiaRest::GetEndpointVersion() { 74 | if (endpointJson != NULL && endpointJson.contains("response") && endpointJson["response"].contains("version")) { 75 | return (int)endpointJson["response"]["version"]; 76 | } 77 | LOGW("No version number found in JSON response."); 78 | return 0; 79 | } 80 | 81 | int MagiaRest::GetMaxThreads() { 82 | if (endpointJson != NULL && endpointJson.contains("response") && endpointJson["response"].contains("max_threads")) { 83 | return (int)endpointJson["response"]["max_threads"]; 84 | } 85 | LOGW("No version number found in JSON response."); 86 | return 0; 87 | } 88 | 89 | MagiaRest::~MagiaRest() { 90 | if (endpointJString != NULL || endpointChar != NULL) { 91 | env->ReleaseStringUTFChars(endpointJString, endpointChar); 92 | } 93 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for MagiaClient, include source and define 2 | # project specific logic here. 3 | # 4 | #set(CMAKE_CXX_STANDARD 11) 5 | cmake_minimum_required (VERSION 3.8) 6 | 7 | project ("MagiaClient") 8 | set(TARGET_NAME uwasa) 9 | #if (CMAKE_BUILD_TYPE STREQUAL "Debug") 10 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -fsanitize=address -fno-omit-frame-pointer") 11 | #else 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++") 13 | #endif(CMAKE_BUILD_TYPE STREQUAL "Debug") 14 | 15 | 16 | # Options 17 | set(compile_definitions "") 18 | 19 | option(MAGIA_TRANSLATE_AUDIOFIX_3_0_1 "Include audiofix for 3.0.1 and above" ON) 20 | 21 | if (MAGIA_TRANSLATE_AUDIOFIX_3_0_1) 22 | set(compile_definitions "${compile_definitions} -DMAGIA_TRANSLATE_AUDIOFIX_3_0_1") 23 | endif () 24 | 25 | message(STATUS "[MagiaClient] MAGIA_TRANSLATE_AUDIOFIX_3_0_1: ${MAGIA_TRANSLATE_AUDIOFIX_3_0_1}") 26 | 27 | 28 | if(CMAKE_BUILD_TYPE STREQUAL "Release") 29 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden -fvisibility-inlines-hidden -g0 -O3 -ffunction-sections -fdata-sections") 30 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility=hidden -fvisibility-inlines-hidden -g0 -O3 -ffunction-sections -fdata-sections") 31 | endif(CMAKE_BUILD_TYPE STREQUAL "Release") 32 | 33 | set(DobbyHome ${CMAKE_CURRENT_SOURCE_DIR}/lib/Dobby) 34 | include_directories( 35 | ${DobbyHome}/include 36 | ${DobbyHome}/builtin-plugin 37 | ${DobbyHome}/builtin-plugin/SymbolResolver 38 | ${DobbyHome}/external/logging 39 | ${DobbyHome}/source 40 | ${CMAKE_CURRENT_SOURCE_DIR}/lib 41 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/cocos 42 | ${CMAKE_CURRENT_SOURCE_DIR}/abiproxy/src 43 | ) 44 | 45 | include(FetchContent) 46 | 47 | FetchContent_Declare(json 48 | GIT_REPOSITORY https://github.com/ArthurSonzogni/nlohmann_json_cmake_fetchcontent 49 | GIT_TAG v3.9.1) 50 | 51 | FetchContent_GetProperties(json) 52 | if(NOT json_POPULATED) 53 | FetchContent_Populate(json) 54 | add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) 55 | endif() 56 | 57 | 58 | if(CMAKE_BUILD_TYPE STREQUAL "Release") 59 | link_libraries("-Wl,--strip-all") 60 | endif(CMAKE_BUILD_TYPE STREQUAL "Release") 61 | 62 | add_library( # Sets the name of the library. 63 | ${TARGET_NAME} 64 | # Sets the library as a shared library. 65 | SHARED 66 | 67 | # Provides a relative path to your source file(s). 68 | "lib/cocos/math/CCGeometry.cpp" 69 | "lib/cocos/math/Vec2.cpp" 70 | "src/MagiaClient.cpp" 71 | "src/Utils.cpp" 72 | "src/rest/MagiaRest.cpp" 73 | ) 74 | 75 | target_compile_definitions(${TARGET_NAME} PRIVATE "COMPILE_DEFINITIONS ${compile_definitions}") 76 | 77 | find_library(ANDROID_LOG_LIB log) 78 | target_link_libraries(${TARGET_NAME} PRIVATE ${ANDROID_LOG_LIB}) 79 | target_link_libraries(${TARGET_NAME} PRIVATE dobby_static) 80 | target_link_libraries(${TARGET_NAME} PRIVATE nlohmann_json::nlohmann_json) 81 | 82 | 83 | macro(SET_OPTION option value) 84 | set(${option} ${value} CACHE INTERNAL "" FORCE) 85 | endmacro() 86 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 87 | SET_OPTION(DOBBY_DEBUG ON) 88 | endif(CMAKE_BUILD_TYPE STREQUAL "Debug") 89 | SET_OPTION(DOBBY_GENERATE_SHARED OFF) 90 | SET_OPTION(DynamicBinaryInstrument ON) 91 | SET_OPTION(NearBranch ON) 92 | SET_OPTION(Plugin.SymbolResolver ON) 93 | 94 | add_subdirectory(${DobbyHome} dobby) 95 | -------------------------------------------------------------------------------- /src/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | typedef unsigned long DWORD; 18 | 19 | uintptr_t get_libBase(const char* libName) { 20 | FILE *fp; 21 | uintptr_t addr = 0; 22 | char filename[32], buffer[1024]; 23 | snprintf(filename, sizeof(filename), "/proc/%d/maps", getpid()); 24 | fp = fopen(filename, "rt"); 25 | if (fp != NULL) { 26 | while (fgets(buffer, sizeof(buffer), fp)) { 27 | if (strstr(buffer, libName)) { 28 | addr = (uintptr_t) strtoul(buffer, NULL, 16); 29 | break; 30 | } 31 | } 32 | fclose(fp); 33 | } 34 | return addr; 35 | } 36 | 37 | std::string get_libFoldername(const char* libName) { 38 | FILE *fp; 39 | uintptr_t addr = 0; 40 | char filename[32], buffer[1024]; 41 | snprintf(filename, sizeof(filename), "/proc/%d/maps", getpid()); 42 | fp = fopen(filename, "rt"); 43 | if (fp != NULL) { 44 | while (fgets(buffer, sizeof(buffer), fp)) { 45 | if (strstr(buffer, libName)) { 46 | std::string delimiter = "/"; 47 | std::string strBuffer = buffer; 48 | std::string token = strBuffer.substr(strBuffer.find(delimiter), std::string::npos); 49 | return token; 50 | break; 51 | } 52 | } 53 | fclose(fp); 54 | } 55 | return ""; 56 | } 57 | 58 | //uintptr_t getRealOffset(const char* libName, uintptr_t address) { 59 | //if (libBase == 0) { 60 | //libBase = get_libBase(libName); 61 | //} 62 | //return (libBase + address); 63 | //} 64 | 65 | void* lookup_symbol(const char* path, const char* symbolname) 66 | { 67 | void *imagehandle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); 68 | if (imagehandle != NULL){ 69 | void * sym = dlsym(imagehandle, symbolname); 70 | if (sym != NULL){ 71 | dlclose(imagehandle); 72 | return sym; 73 | } 74 | else{ 75 | LOGI("(lookup_symbol) dlsym didn't work: %s, %s\n", path, symbolname); 76 | dlclose(imagehandle); 77 | return NULL; 78 | } 79 | } 80 | else{ 81 | LOGI("(lookup_symbol) dlerror: %s\n",dlerror()); 82 | return NULL; 83 | } 84 | } 85 | 86 | bool file_exists (const std::string& name) { 87 | struct stat buffer; 88 | return (stat (name.c_str(), &buffer) == 0); 89 | } 90 | 91 | std::string getJNISignature() { 92 | return ""; 93 | } 94 | 95 | std::string getJNISignature(bool) { 96 | return "Z"; 97 | } 98 | 99 | std::string getJNISignature(char) { 100 | return "C"; 101 | } 102 | 103 | std::string getJNISignature(short) { 104 | return "S"; 105 | } 106 | 107 | std::string getJNISignature(int) { 108 | return "I"; 109 | } 110 | 111 | std::string getJNISignature(long) { 112 | return "J"; 113 | } 114 | 115 | std::string getJNISignature(float) { 116 | return "F"; 117 | } 118 | 119 | std::string getJNISignature(double) { 120 | return "D"; 121 | } 122 | 123 | std::string getJNISignature(const char*) { 124 | return "Ljava/lang/String;"; 125 | } 126 | 127 | std::string getJNISignature(const std::string&) { 128 | return "Ljava/lang/String;"; 129 | } 130 | 131 | JNIEnv* getEnv(JavaVM* gJvm) { 132 | JNIEnv *env; 133 | int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6); 134 | if(status < 0) { 135 | status = gJvm->AttachCurrentThread(&env, NULL); 136 | if(status < 0) { 137 | LOGW("Null JNIEnv obtained!"); 138 | return nullptr; 139 | } 140 | } 141 | return env; 142 | } 143 | 144 | jclass findClass(JNIEnv* env, jobject gClassLoader, jmethodID gFindClassMethod, const char* name) { 145 | return static_cast(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name))); 146 | } 147 | 148 | std::string longlong_to_string( unsigned long long value ){ 149 | std::ostringstream os; 150 | os << value; 151 | return os.str(); 152 | } -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/java/io/kamihama/magianative/RestClient.java: -------------------------------------------------------------------------------- 1 | package io.kamihama.magianative; 2 | 3 | import android.util.Log; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | import java.io.IOException; 9 | import java.security.cert.CertificateException; 10 | 11 | import javax.net.ssl.HostnameVerifier; 12 | import javax.net.ssl.SSLContext; 13 | import javax.net.ssl.SSLSession; 14 | import javax.net.ssl.SSLSocketFactory; 15 | import javax.net.ssl.TrustManager; 16 | import javax.net.ssl.X509TrustManager; 17 | 18 | import okhttp3.MediaType; 19 | import okhttp3.OkHttpClient; 20 | import okhttp3.Request; 21 | import okhttp3.RequestBody; 22 | import okhttp3.Response; 23 | 24 | public class RestClient { 25 | private final String Endpoint = "https://walpurgisnacht.rayshift.io"; 26 | private final String LogTag = "MagiaClientJNI"; 27 | private String UserAgent = "okhttp3 " + System.getProperty("http.agent"); 28 | 29 | public String GetEndpoint(int version) { 30 | JSONObject jsonString = new JSONObject(); 31 | 32 | try { 33 | jsonString.put("version", version); 34 | } catch (JSONException e) { 35 | Log.e(LogTag, "Error adding version: " + e.toString()); 36 | return ""; 37 | } 38 | 39 | try { 40 | return postRequest(Endpoint + "/api/v1/endpoint", jsonString.toString()); 41 | } catch (IOException e) { 42 | Log.e(LogTag, "Error with request: " + e.toString()); 43 | return ""; 44 | } 45 | } 46 | 47 | private static final MediaType JSON 48 | = MediaType.parse("application/json; charset=utf-8"); 49 | 50 | private OkHttpClient client = getUnsafeOkHttpClient(); 51 | 52 | private String postRequest (String url, String json) throws IOException { 53 | RequestBody body = RequestBody.create(JSON, json); // new 54 | // RequestBody body = RequestBody.create(JSON, json); // old 55 | Request request = new Request.Builder() 56 | .url(url) 57 | .post(body) 58 | .removeHeader("User-Agent") 59 | .addHeader("User-Agent", UserAgent) 60 | .build(); 61 | 62 | Response response = client.newCall(request).execute(); 63 | 64 | // Temporary workaround 65 | if ((response.code() == 307) || (response.code() == 308)) { 66 | String location = response.header("Location"); 67 | if (location != null) { 68 | request = request.newBuilder() 69 | .url(location) 70 | .post(body) 71 | .removeHeader("User-Agent") 72 | .addHeader("User-Agent", UserAgent) 73 | .build(); 74 | 75 | Response newResponse = client.newCall(request).execute(); 76 | return newResponse.body() != null ? newResponse.body().string() : ""; 77 | } 78 | } 79 | return response.body() != null ? response.body().string() : ""; 80 | } 81 | 82 | private static OkHttpClient getUnsafeOkHttpClient() { 83 | try { 84 | // Create a trust manager that does not validate certificate chains 85 | final TrustManager[] trustAllCerts = new TrustManager[] { 86 | new X509TrustManager() { 87 | @Override 88 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { 89 | } 90 | 91 | @Override 92 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { 93 | } 94 | 95 | @Override 96 | public java.security.cert.X509Certificate[] getAcceptedIssuers() { 97 | return new java.security.cert.X509Certificate[]{}; 98 | } 99 | } 100 | }; 101 | 102 | // Install the all-trusting trust manager 103 | final SSLContext sslContext = SSLContext.getInstance("SSL"); 104 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); 105 | // Create an ssl socket factory with our all-trusting manager 106 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); 107 | 108 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 109 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]); 110 | builder.hostnameVerifier(new HostnameVerifier() { 111 | @Override 112 | public boolean verify(String hostname, SSLSession session) { 113 | return true; 114 | } 115 | }); 116 | 117 | OkHttpClient okHttpClient = builder.build(); 118 | return okHttpClient; 119 | } catch (Exception e) { 120 | throw new RuntimeException(e); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /smali/MagiaNative/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | CMakeLists.txt.user 3 | CMakeCache.txt 4 | CMakeFiles 5 | CMakeScripts 6 | Testing 7 | Makefile 8 | cmake_install.cmake 9 | install_manifest.txt 10 | compile_commands.json 11 | CTestTestfile.cmake 12 | _deps 13 | 14 | .idea/ 15 | apk/*.apk 16 | apk/*.old 17 | armv7apk/*.apk 18 | armv7apk/*.old 19 | *.keystore 20 | *.jks 21 | 22 | # Prerequisites 23 | *.d 24 | 25 | # Compiled Object files 26 | *.slo 27 | *.lo 28 | *.o 29 | *.obj 30 | 31 | # Precompiled Headers 32 | *.gch 33 | *.pch 34 | 35 | # Compiled Dynamic libraries 36 | *.so 37 | *.dylib 38 | *.dll 39 | 40 | # Fortran module files 41 | *.mod 42 | *.smod 43 | 44 | # Compiled Static libraries 45 | *.lai 46 | *.la 47 | *.a 48 | *.lib 49 | 50 | # Executables 51 | *.exe 52 | *.out 53 | *.app 54 | 55 | # Libs 56 | #lib/cocos 57 | #lib/Dobby 58 | 59 | ## Ignore Visual Studio temporary files, build results, and 60 | ## files generated by popular Visual Studio add-ons. 61 | ## 62 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 63 | 64 | # User-specific files 65 | *.rsuser 66 | *.suo 67 | *.user 68 | *.userosscache 69 | *.sln.docstates 70 | 71 | # User-specific files (MonoDevelop/Xamarin Studio) 72 | *.userprefs 73 | 74 | # Mono auto generated files 75 | mono_crash.* 76 | 77 | # Build results 78 | [Dd]ebug/ 79 | [Dd]ebugPublic/ 80 | [Rr]elease/ 81 | [Rr]eleases/ 82 | x64/ 83 | x86/ 84 | [Ww][Ii][Nn]32/ 85 | [Aa][Rr][Mm]/ 86 | [Aa][Rr][Mm]64/ 87 | bld/ 88 | [Bb]in/ 89 | [Oo]bj/ 90 | [Ll]og/ 91 | [Ll]ogs/ 92 | 93 | # Visual Studio 2015/2017 cache/options directory 94 | .vs/ 95 | # Uncomment if you have tasks that create the project's static files in wwwroot 96 | #wwwroot/ 97 | 98 | # Visual Studio 2017 auto generated files 99 | Generated\ Files/ 100 | 101 | # MSTest test Results 102 | [Tt]est[Rr]esult*/ 103 | [Bb]uild[Ll]og.* 104 | 105 | # NUnit 106 | *.VisualState.xml 107 | TestResult.xml 108 | nunit-*.xml 109 | 110 | # Build Results of an ATL Project 111 | [Dd]ebugPS/ 112 | [Rr]eleasePS/ 113 | dlldata.c 114 | 115 | # Benchmark Results 116 | BenchmarkDotNet.Artifacts/ 117 | 118 | # .NET Core 119 | project.lock.json 120 | project.fragment.lock.json 121 | artifacts/ 122 | 123 | # ASP.NET Scaffolding 124 | ScaffoldingReadMe.txt 125 | 126 | # StyleCop 127 | StyleCopReport.xml 128 | 129 | # Files built by Visual Studio 130 | *_i.c 131 | *_p.c 132 | *_h.h 133 | *.ilk 134 | *.meta 135 | *.obj 136 | *.iobj 137 | *.pch 138 | *.pdb 139 | *.ipdb 140 | *.pgc 141 | *.pgd 142 | *.rsp 143 | *.sbr 144 | *.tlb 145 | *.tli 146 | *.tlh 147 | *.tmp 148 | *.tmp_proj 149 | *_wpftmp.csproj 150 | *.log 151 | *.vspscc 152 | *.vssscc 153 | .builds 154 | *.pidb 155 | *.svclog 156 | *.scc 157 | 158 | # Chutzpah Test files 159 | _Chutzpah* 160 | 161 | # Visual C++ cache files 162 | ipch/ 163 | *.aps 164 | *.ncb 165 | *.opendb 166 | *.opensdf 167 | *.sdf 168 | *.cachefile 169 | *.VC.db 170 | *.VC.VC.opendb 171 | 172 | # Visual Studio profiler 173 | *.psess 174 | *.vsp 175 | *.vspx 176 | *.sap 177 | 178 | # Visual Studio Trace Files 179 | *.e2e 180 | 181 | # TFS 2012 Local Workspace 182 | $tf/ 183 | 184 | # Guidance Automation Toolkit 185 | *.gpState 186 | 187 | # ReSharper is a .NET coding add-in 188 | _ReSharper*/ 189 | *.[Rr]e[Ss]harper 190 | *.DotSettings.user 191 | 192 | # TeamCity is a build add-in 193 | _TeamCity* 194 | 195 | # DotCover is a Code Coverage Tool 196 | *.dotCover 197 | 198 | # AxoCover is a Code Coverage Tool 199 | .axoCover/* 200 | !.axoCover/settings.json 201 | 202 | # Coverlet is a free, cross platform Code Coverage Tool 203 | coverage*.json 204 | coverage*.xml 205 | coverage*.info 206 | 207 | # Visual Studio code coverage results 208 | *.coverage 209 | *.coveragexml 210 | 211 | # NCrunch 212 | _NCrunch_* 213 | .*crunch*.local.xml 214 | nCrunchTemp_* 215 | 216 | # MightyMoose 217 | *.mm.* 218 | AutoTest.Net/ 219 | 220 | # Web workbench (sass) 221 | .sass-cache/ 222 | 223 | # Installshield output folder 224 | [Ee]xpress/ 225 | 226 | # DocProject is a documentation generator add-in 227 | DocProject/buildhelp/ 228 | DocProject/Help/*.HxT 229 | DocProject/Help/*.HxC 230 | DocProject/Help/*.hhc 231 | DocProject/Help/*.hhk 232 | DocProject/Help/*.hhp 233 | DocProject/Help/Html2 234 | DocProject/Help/html 235 | 236 | # Click-Once directory 237 | publish/ 238 | 239 | # Publish Web Output 240 | *.[Pp]ublish.xml 241 | *.azurePubxml 242 | # Note: Comment the next line if you want to checkin your web deploy settings, 243 | # but database connection strings (with potential passwords) will be unencrypted 244 | *.pubxml 245 | *.publishproj 246 | 247 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 248 | # checkin your Azure Web App publish settings, but sensitive information contained 249 | # in these scripts will be unencrypted 250 | PublishScripts/ 251 | 252 | # NuGet Packages 253 | *.nupkg 254 | # NuGet Symbol Packages 255 | *.snupkg 256 | # The packages folder can be ignored because of Package Restore 257 | **/[Pp]ackages/* 258 | # except build/, which is used as an MSBuild target. 259 | !**/[Pp]ackages/build/ 260 | # Uncomment if necessary however generally it will be regenerated when needed 261 | #!**/[Pp]ackages/repositories.config 262 | # NuGet v3's project.json files produces more ignorable files 263 | *.nuget.props 264 | *.nuget.targets 265 | 266 | # Microsoft Azure Build Output 267 | csx/ 268 | *.build.csdef 269 | 270 | # Microsoft Azure Emulator 271 | ecf/ 272 | rcf/ 273 | 274 | # Windows Store app package directories and files 275 | AppPackages/ 276 | BundleArtifacts/ 277 | Package.StoreAssociation.xml 278 | _pkginfo.txt 279 | *.appx 280 | *.appxbundle 281 | *.appxupload 282 | 283 | # Visual Studio cache files 284 | # files ending in .cache can be ignored 285 | *.[Cc]ache 286 | # but keep track of directories ending in .cache 287 | !?*.[Cc]ache/ 288 | 289 | # Others 290 | ClientBin/ 291 | ~$* 292 | *~ 293 | *.dbmdl 294 | *.dbproj.schemaview 295 | *.jfm 296 | *.pfx 297 | *.publishsettings 298 | orleans.codegen.cs 299 | 300 | # Including strong name files can present a security risk 301 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 302 | #*.snk 303 | 304 | # Since there are multiple workflows, uncomment next line to ignore bower_components 305 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 306 | #bower_components/ 307 | 308 | # RIA/Silverlight projects 309 | Generated_Code/ 310 | 311 | # Backup & report files from converting an old project file 312 | # to a newer Visual Studio version. Backup files are not needed, 313 | # because we have git ;-) 314 | _UpgradeReport_Files/ 315 | Backup*/ 316 | UpgradeLog*.XML 317 | UpgradeLog*.htm 318 | ServiceFabricBackup/ 319 | *.rptproj.bak 320 | 321 | # SQL Server files 322 | *.mdf 323 | *.ldf 324 | *.ndf 325 | 326 | # Business Intelligence projects 327 | *.rdl.data 328 | *.bim.layout 329 | *.bim_*.settings 330 | *.rptproj.rsuser 331 | *- [Bb]ackup.rdl 332 | *- [Bb]ackup ([0-9]).rdl 333 | *- [Bb]ackup ([0-9][0-9]).rdl 334 | 335 | # Microsoft Fakes 336 | FakesAssemblies/ 337 | 338 | # GhostDoc plugin setting file 339 | *.GhostDoc.xml 340 | 341 | # Node.js Tools for Visual Studio 342 | .ntvs_analysis.dat 343 | node_modules/ 344 | 345 | # Visual Studio 6 build log 346 | *.plg 347 | 348 | # Visual Studio 6 workspace options file 349 | *.opt 350 | 351 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 352 | *.vbw 353 | 354 | # Visual Studio LightSwitch build output 355 | **/*.HTMLClient/GeneratedArtifacts 356 | **/*.DesktopClient/GeneratedArtifacts 357 | **/*.DesktopClient/ModelManifest.xml 358 | **/*.Server/GeneratedArtifacts 359 | **/*.Server/ModelManifest.xml 360 | _Pvt_Extensions 361 | 362 | # Paket dependency manager 363 | .paket/paket.exe 364 | paket-files/ 365 | 366 | # FAKE - F# Make 367 | .fake/ 368 | 369 | # CodeRush personal settings 370 | .cr/personal 371 | 372 | # Python Tools for Visual Studio (PTVS) 373 | __pycache__/ 374 | *.pyc 375 | 376 | # Cake - Uncomment if you are using it 377 | # tools/** 378 | # !tools/packages.config 379 | 380 | # Tabs Studio 381 | *.tss 382 | 383 | # Telerik's JustMock configuration file 384 | *.jmconfig 385 | 386 | # BizTalk build output 387 | *.btp.cs 388 | *.btm.cs 389 | *.odx.cs 390 | *.xsd.cs 391 | 392 | # OpenCover UI analysis results 393 | OpenCover/ 394 | 395 | # Azure Stream Analytics local run output 396 | ASALocalRun/ 397 | 398 | # MSBuild Binary and Structured Log 399 | *.binlog 400 | 401 | # NVidia Nsight GPU debugger configuration file 402 | *.nvuser 403 | 404 | # MFractors (Xamarin productivity tool) working folder 405 | .mfractor/ 406 | 407 | # Local History for Visual Studio 408 | .localhistory/ 409 | 410 | # BeatPulse healthcheck temp database 411 | healthchecksdb 412 | 413 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 414 | MigrationBackup/ 415 | 416 | # Ionide (cross platform F# VS Code tools) working folder 417 | .ionide/ 418 | 419 | # Fody - auto-generated XML schema 420 | FodyWeavers.xsd 421 | 422 | .vscode/ 423 | 424 | abiproxy/build/ 425 | abiproxy/.ninja_deps 426 | abiproxy/.ninja_log 427 | abiproxy/build.ninja 428 | lib/cocos_old/ 429 | lib/Dobby_old/ 430 | sign.bat 431 | sign.sh 432 | jarsign.sh 433 | 434 | # Android NDK, build-tools 435 | /ndk/ 436 | /abt/ 437 | com.aniplex.magireco.arm8.apk 438 | test/ 439 | -------------------------------------------------------------------------------- /patches/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Navigate home 4 | Navigate up 5 | More options 6 | Done 7 | See all 8 | Choose an app 9 | OFF 10 | ON 11 | sans-serif 12 | sans-serif-medium 13 | sans-serif-medium 14 | sans-serif 15 | sans-serif 16 | sans-serif 17 | sans-serif 18 | sans-serif-light 19 | sans-serif 20 | sans-serif 21 | sans-serif 22 | sans-serif-medium 23 | Alt+ 24 | Ctrl+ 25 | delete 26 | enter 27 | Function+ 28 | Meta+ 29 | Shift+ 30 | space 31 | Sym+ 32 | Menu+ 33 | Search… 34 | Clear query 35 | Search query 36 | Search 37 | Submit query 38 | Voice search 39 | Share with 40 | Share with %s 41 | Collapse 42 | Magia Record JP 43 | APP 44 | RELEASE 45 | Ad 46 | Casting to %1$s 47 | Closed captions 48 | Closed captions unavailable 49 | Connecting to %1$s 50 | Disconnect 51 | Image displayed while the cast receiver is playing an ad 52 | Ad in progress… 53 | Album art 54 | Live stream 55 | Loading… 56 | Skip 57 | Forward 58 | Forward ten seconds 59 | Forward thirty seconds 60 | @android:string/ok 61 | --:-- 62 | --:-- 63 | Mute 64 | Connected to %1$s 65 | Connecting to %1$s 66 | Cast 67 | Disconnect 68 | Pause 69 | Play 70 | Rewind 71 | Rewind ten seconds 72 | Rewind thirty seconds 73 | Playback seek control 74 | Skip to next item 75 | Skip to previous item 76 | Stop 77 | Stop live stream 78 | Audio 79 | Cancel 80 | Closed Captions 81 | Track %1$d 82 | None 83 | OK 84 | Subtitles 85 | Unmute 86 | Enable 87 | "%1$s won't work unless you enable Google Play services." 88 | Enable Google Play services 89 | Install 90 | "%1$s won't run without Google Play services, which are missing from your device." 91 | Get Google Play services 92 | Google Play services availability 93 | Google Play services error 94 | %1$s is having trouble with Google Play services. Please try again. 95 | "%1$s won't run without Google Play services, which are not supported by your device." 96 | Update 97 | "%1$s won't run unless you update Google Play services." 98 | Update Google Play services 99 | "%1$s won't run without Google Play services, which are currently updating." 100 | New version of Google Play services needed. It will update itself shortly. 101 | Open on phone 102 | Sign in 103 | Sign in with Google 104 | 48695978581-l3g2vae3su7td5tilbunf8fpb1875np4.apps.googleusercontent.com 105 | android_channel_id 106 | Miscellaneous 107 | https://kamihama-io.firebaseio.com 108 | 48695978581 109 | Miscellaneous 110 | AIzaSyBlHoCTAsEa1zsIhKfE8nvMVewd0ULYmkw 111 | 1:48695978581:android:bc7cc007c2de2d0e79c2dc 112 | AIzaSyBlHoCTAsEa1zsIhKfE8nvMVewd0ULYmkw 113 | kamihama-io.appspot.com 114 | Cast button 115 | Cast button. Connected 116 | Cast button. Connecting 117 | Cast button. Disconnected 118 | Finding devices 119 | Cast to 120 | Album art 121 | Casting screen 122 | Close 123 | Collapse 124 | Disconnect 125 | Expand 126 | No info available 127 | No media selected 128 | Pause 129 | Play 130 | Stop 131 | Stop casting 132 | Volume slider 133 | System 134 | Devices 135 | Clear search 136 | Search 137 | magireco-754ac 138 | Save image 139 | Allow Ad to store image in Picture gallery? 140 | Accept 141 | Decline 142 | Create calendar event 143 | Allow Ad to create a calendar event? 144 | Test Ad 145 | Search 146 | PRODUCT 147 | 999+ 148 | Continue 149 | Preview mode updated. 150 | Preview 151 | Buy with Google 152 | androidx.startup 153 | 154 | -------------------------------------------------------------------------------- /smali/MagiaNative/app/src/main/java/io/kamihama/magianative/RestClient.smali: -------------------------------------------------------------------------------- 1 | .class public Lio/kamihama/magianative/RestClient; 2 | .super Ljava/lang/Object; 3 | .source "RestClient.java" 4 | 5 | 6 | # static fields 7 | .field private static final JSON:Lokhttp3/MediaType; 8 | 9 | 10 | # instance fields 11 | .field private final Endpoint:Ljava/lang/String; 12 | 13 | .field private final LogTag:Ljava/lang/String; 14 | 15 | .field private UserAgent:Ljava/lang/String; 16 | 17 | .field private client:Lokhttp3/OkHttpClient; 18 | 19 | 20 | # direct methods 21 | .method static constructor ()V 22 | .registers 1 23 | 24 | .prologue 25 | .line 47 26 | const-string v0, "application/json; charset=utf-8" 27 | 28 | .line 48 29 | invoke-static {v0}, Lokhttp3/MediaType;->parse(Ljava/lang/String;)Lokhttp3/MediaType; 30 | 31 | move-result-object v0 32 | 33 | sput-object v0, Lio/kamihama/magianative/RestClient;->JSON:Lokhttp3/MediaType; 34 | 35 | .line 47 36 | return-void 37 | .end method 38 | 39 | .method public constructor ()V 40 | .registers 3 41 | 42 | .prologue 43 | .line 24 44 | invoke-direct {p0}, Ljava/lang/Object;->()V 45 | 46 | .line 25 47 | const-string v0, "https://walpurgisnacht.rayshift.io" 48 | 49 | iput-object v0, p0, Lio/kamihama/magianative/RestClient;->Endpoint:Ljava/lang/String; 50 | 51 | .line 26 52 | const-string v0, "MagiaClientJNI" 53 | 54 | iput-object v0, p0, Lio/kamihama/magianative/RestClient;->LogTag:Ljava/lang/String; 55 | 56 | .line 27 57 | new-instance v0, Ljava/lang/StringBuilder; 58 | 59 | invoke-direct {v0}, Ljava/lang/StringBuilder;->()V 60 | 61 | const-string v1, "okhttp3 " 62 | 63 | invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 64 | 65 | move-result-object v0 66 | 67 | const-string v1, "http.agent" 68 | 69 | invoke-static {v1}, Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String; 70 | 71 | move-result-object v1 72 | 73 | invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 74 | 75 | move-result-object v0 76 | 77 | invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 78 | 79 | move-result-object v0 80 | 81 | iput-object v0, p0, Lio/kamihama/magianative/RestClient;->UserAgent:Ljava/lang/String; 82 | 83 | .line 50 84 | invoke-static {}, Lio/kamihama/magianative/RestClient;->getUnsafeOkHttpClient()Lokhttp3/OkHttpClient; 85 | 86 | move-result-object v0 87 | 88 | iput-object v0, p0, Lio/kamihama/magianative/RestClient;->client:Lokhttp3/OkHttpClient; 89 | 90 | return-void 91 | .end method 92 | 93 | .method private static getUnsafeOkHttpClient()Lokhttp3/OkHttpClient; 94 | .registers 8 95 | 96 | .prologue 97 | .line 85 98 | const/4 v6, 0x1 99 | 100 | :try_start_1 101 | new-array v5, v6, [Ljavax/net/ssl/TrustManager; 102 | 103 | const/4 v6, 0x0 104 | 105 | new-instance v7, Lio/kamihama/magianative/RestClient$1; 106 | 107 | invoke-direct {v7}, Lio/kamihama/magianative/RestClient$1;->()V 108 | 109 | aput-object v7, v5, v6 110 | 111 | .line 103 112 | .local v5, "trustAllCerts":[Ljavax/net/ssl/TrustManager; 113 | const-string v6, "SSL" 114 | 115 | invoke-static {v6}, Ljavax/net/ssl/SSLContext;->getInstance(Ljava/lang/String;)Ljavax/net/ssl/SSLContext; 116 | 117 | move-result-object v3 118 | 119 | .line 104 120 | .local v3, "sslContext":Ljavax/net/ssl/SSLContext; 121 | const/4 v6, 0x0 122 | 123 | new-instance v7, Ljava/security/SecureRandom; 124 | 125 | invoke-direct {v7}, Ljava/security/SecureRandom;->()V 126 | 127 | invoke-virtual {v3, v6, v5, v7}, Ljavax/net/ssl/SSLContext;->init([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;Ljava/security/SecureRandom;)V 128 | 129 | .line 106 130 | invoke-virtual {v3}, Ljavax/net/ssl/SSLContext;->getSocketFactory()Ljavax/net/ssl/SSLSocketFactory; 131 | 132 | move-result-object v4 133 | 134 | .line 108 135 | .local v4, "sslSocketFactory":Ljavax/net/ssl/SSLSocketFactory; 136 | new-instance v0, Lokhttp3/OkHttpClient$Builder; 137 | 138 | invoke-direct {v0}, Lokhttp3/OkHttpClient$Builder;->()V 139 | 140 | .line 109 141 | .local v0, "builder":Lokhttp3/OkHttpClient$Builder; 142 | const/4 v6, 0x0 143 | 144 | aget-object v6, v5, v6 145 | 146 | check-cast v6, Ljavax/net/ssl/X509TrustManager; 147 | 148 | invoke-virtual {v0, v4, v6}, Lokhttp3/OkHttpClient$Builder;->sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 149 | 150 | .line 110 151 | new-instance v6, Lio/kamihama/magianative/RestClient$2; 152 | 153 | invoke-direct {v6}, Lio/kamihama/magianative/RestClient$2;->()V 154 | 155 | invoke-virtual {v0, v6}, Lokhttp3/OkHttpClient$Builder;->hostnameVerifier(Ljavax/net/ssl/HostnameVerifier;)Lokhttp3/OkHttpClient$Builder; 156 | 157 | .line 117 158 | invoke-virtual {v0}, Lokhttp3/OkHttpClient$Builder;->build()Lokhttp3/OkHttpClient; 159 | :try_end_36 160 | .catch Ljava/lang/Exception; {:try_start_1 .. :try_end_36} :catch_38 161 | 162 | move-result-object v2 163 | 164 | .line 118 165 | .local v2, "okHttpClient":Lokhttp3/OkHttpClient; 166 | return-object v2 167 | 168 | .line 119 169 | .end local v0 # "builder":Lokhttp3/OkHttpClient$Builder; 170 | .end local v2 # "okHttpClient":Lokhttp3/OkHttpClient; 171 | .end local v3 # "sslContext":Ljavax/net/ssl/SSLContext; 172 | .end local v4 # "sslSocketFactory":Ljavax/net/ssl/SSLSocketFactory; 173 | :catch_38 174 | move-exception v1 175 | 176 | .line 120 177 | .local v1, "e":Ljava/lang/Exception; 178 | new-instance v6, Ljava/lang/RuntimeException; 179 | 180 | invoke-direct {v6, v1}, Ljava/lang/RuntimeException;->(Ljava/lang/Throwable;)V 181 | 182 | throw v6 183 | .end method 184 | 185 | .method private postRequest(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 186 | .registers 11 187 | .param p1, "url" # Ljava/lang/String; 188 | .param p2, "json" # Ljava/lang/String; 189 | .annotation system Ldalvik/annotation/Throws; 190 | value = { 191 | Ljava/io/IOException; 192 | } 193 | .end annotation 194 | 195 | .prologue 196 | .line 53 197 | sget-object v5, Lio/kamihama/magianative/RestClient;->JSON:Lokhttp3/MediaType; 198 | 199 | invoke-static {v5, p2}, Lokhttp3/RequestBody;->create(Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody; 200 | 201 | move-result-object v0 202 | 203 | .line 55 204 | .local v0, "body":Lokhttp3/RequestBody; 205 | new-instance v5, Lokhttp3/Request$Builder; 206 | 207 | invoke-direct {v5}, Lokhttp3/Request$Builder;->()V 208 | 209 | .line 56 210 | invoke-virtual {v5, p1}, Lokhttp3/Request$Builder;->url(Ljava/lang/String;)Lokhttp3/Request$Builder; 211 | 212 | move-result-object v5 213 | 214 | .line 57 215 | invoke-virtual {v5, v0}, Lokhttp3/Request$Builder;->post(Lokhttp3/RequestBody;)Lokhttp3/Request$Builder; 216 | 217 | move-result-object v5 218 | 219 | const-string v6, "User-Agent" 220 | 221 | .line 58 222 | invoke-virtual {v5, v6}, Lokhttp3/Request$Builder;->removeHeader(Ljava/lang/String;)Lokhttp3/Request$Builder; 223 | 224 | move-result-object v5 225 | 226 | const-string v6, "User-Agent" 227 | 228 | iget-object v7, p0, Lio/kamihama/magianative/RestClient;->UserAgent:Ljava/lang/String; 229 | 230 | .line 59 231 | invoke-virtual {v5, v6, v7}, Lokhttp3/Request$Builder;->addHeader(Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder; 232 | 233 | move-result-object v5 234 | 235 | .line 60 236 | invoke-virtual {v5}, Lokhttp3/Request$Builder;->build()Lokhttp3/Request; 237 | 238 | move-result-object v3 239 | 240 | .line 62 241 | .local v3, "request":Lokhttp3/Request; 242 | iget-object v5, p0, Lio/kamihama/magianative/RestClient;->client:Lokhttp3/OkHttpClient; 243 | 244 | invoke-virtual {v5, v3}, Lokhttp3/OkHttpClient;->newCall(Lokhttp3/Request;)Lokhttp3/Call; 245 | 246 | move-result-object v5 247 | 248 | invoke-interface {v5}, Lokhttp3/Call;->execute()Lokhttp3/Response; 249 | 250 | move-result-object v4 251 | 252 | .line 65 253 | .local v4, "response":Lokhttp3/Response; 254 | invoke-virtual {v4}, Lokhttp3/Response;->code()I 255 | 256 | move-result v5 257 | 258 | const/16 v6, 0x133 259 | 260 | if-eq v5, v6, :cond_3f 261 | 262 | invoke-virtual {v4}, Lokhttp3/Response;->code()I 263 | 264 | move-result v5 265 | 266 | const/16 v6, 0x134 267 | 268 | if-ne v5, v6, :cond_81 269 | 270 | .line 66 271 | :cond_3f 272 | const-string v5, "Location" 273 | 274 | invoke-virtual {v4, v5}, Lokhttp3/Response;->header(Ljava/lang/String;)Ljava/lang/String; 275 | 276 | move-result-object v1 277 | 278 | .line 67 279 | .local v1, "location":Ljava/lang/String; 280 | if-eqz v1, :cond_81 281 | 282 | .line 68 283 | invoke-virtual {v3}, Lokhttp3/Request;->newBuilder()Lokhttp3/Request$Builder; 284 | 285 | move-result-object v5 286 | 287 | .line 69 288 | invoke-virtual {v5, v1}, Lokhttp3/Request$Builder;->url(Ljava/lang/String;)Lokhttp3/Request$Builder; 289 | 290 | move-result-object v5 291 | 292 | .line 70 293 | invoke-virtual {v5, v0}, Lokhttp3/Request$Builder;->post(Lokhttp3/RequestBody;)Lokhttp3/Request$Builder; 294 | 295 | move-result-object v5 296 | 297 | const-string v6, "User-Agent" 298 | 299 | .line 71 300 | invoke-virtual {v5, v6}, Lokhttp3/Request$Builder;->removeHeader(Ljava/lang/String;)Lokhttp3/Request$Builder; 301 | 302 | move-result-object v5 303 | 304 | const-string v6, "User-Agent" 305 | 306 | iget-object v7, p0, Lio/kamihama/magianative/RestClient;->UserAgent:Ljava/lang/String; 307 | 308 | .line 72 309 | invoke-virtual {v5, v6, v7}, Lokhttp3/Request$Builder;->addHeader(Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder; 310 | 311 | move-result-object v5 312 | 313 | .line 73 314 | invoke-virtual {v5}, Lokhttp3/Request$Builder;->build()Lokhttp3/Request; 315 | 316 | move-result-object v3 317 | 318 | .line 75 319 | iget-object v5, p0, Lio/kamihama/magianative/RestClient;->client:Lokhttp3/OkHttpClient; 320 | 321 | invoke-virtual {v5, v3}, Lokhttp3/OkHttpClient;->newCall(Lokhttp3/Request;)Lokhttp3/Call; 322 | 323 | move-result-object v5 324 | 325 | invoke-interface {v5}, Lokhttp3/Call;->execute()Lokhttp3/Response; 326 | 327 | move-result-object v2 328 | 329 | .line 76 330 | .local v2, "newResponse":Lokhttp3/Response; 331 | invoke-virtual {v2}, Lokhttp3/Response;->body()Lokhttp3/ResponseBody; 332 | 333 | move-result-object v5 334 | 335 | if-eqz v5, :cond_7e 336 | 337 | invoke-virtual {v2}, Lokhttp3/Response;->body()Lokhttp3/ResponseBody; 338 | 339 | move-result-object v5 340 | 341 | invoke-virtual {v5}, Lokhttp3/ResponseBody;->string()Ljava/lang/String; 342 | 343 | move-result-object v5 344 | 345 | .line 79 346 | .end local v1 # "location":Ljava/lang/String; 347 | .end local v2 # "newResponse":Lokhttp3/Response; 348 | :goto_7d 349 | return-object v5 350 | 351 | .line 76 352 | .restart local v1 # "location":Ljava/lang/String; 353 | .restart local v2 # "newResponse":Lokhttp3/Response; 354 | :cond_7e 355 | const-string v5, "" 356 | 357 | goto :goto_7d 358 | 359 | .line 79 360 | .end local v1 # "location":Ljava/lang/String; 361 | .end local v2 # "newResponse":Lokhttp3/Response; 362 | :cond_81 363 | invoke-virtual {v4}, Lokhttp3/Response;->body()Lokhttp3/ResponseBody; 364 | 365 | move-result-object v5 366 | 367 | if-eqz v5, :cond_90 368 | 369 | invoke-virtual {v4}, Lokhttp3/Response;->body()Lokhttp3/ResponseBody; 370 | 371 | move-result-object v5 372 | 373 | invoke-virtual {v5}, Lokhttp3/ResponseBody;->string()Ljava/lang/String; 374 | 375 | move-result-object v5 376 | 377 | goto :goto_7d 378 | 379 | :cond_90 380 | const-string v5, "" 381 | 382 | goto :goto_7d 383 | .end method 384 | 385 | 386 | # virtual methods 387 | .method public GetEndpoint(I)Ljava/lang/String; 388 | .registers 7 389 | .param p1, "version" # I 390 | 391 | .prologue 392 | .line 30 393 | new-instance v1, Lorg/json/JSONObject; 394 | 395 | invoke-direct {v1}, Lorg/json/JSONObject;->()V 396 | 397 | .line 33 398 | .local v1, "jsonString":Lorg/json/JSONObject; 399 | :try_start_5 400 | const-string v2, "version" 401 | 402 | invoke-virtual {v1, v2, p1}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject; 403 | :try_end_a 404 | .catch Lorg/json/JSONException; {:try_start_5 .. :try_end_a} :catch_15 405 | 406 | .line 40 407 | :try_start_a 408 | const-string v2, "https://walpurgisnacht.rayshift.io/api/v1/endpoint" 409 | 410 | invoke-virtual {v1}, Lorg/json/JSONObject;->toString()Ljava/lang/String; 411 | 412 | move-result-object v3 413 | 414 | invoke-direct {p0, v2, v3}, Lio/kamihama/magianative/RestClient;->postRequest(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 415 | :try_end_13 416 | .catch Ljava/io/IOException; {:try_start_a .. :try_end_13} :catch_35 417 | 418 | move-result-object v2 419 | 420 | .line 43 421 | :goto_14 422 | return-object v2 423 | 424 | .line 34 425 | :catch_15 426 | move-exception v0 427 | 428 | .line 35 429 | .local v0, "e":Lorg/json/JSONException; 430 | const-string v2, "MagiaClientJNI" 431 | 432 | new-instance v3, Ljava/lang/StringBuilder; 433 | 434 | invoke-direct {v3}, Ljava/lang/StringBuilder;->()V 435 | 436 | const-string v4, "Error adding version: " 437 | 438 | invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 439 | 440 | move-result-object v3 441 | 442 | invoke-virtual {v0}, Lorg/json/JSONException;->toString()Ljava/lang/String; 443 | 444 | move-result-object v4 445 | 446 | invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 447 | 448 | move-result-object v3 449 | 450 | invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 451 | 452 | move-result-object v3 453 | 454 | invoke-static {v2, v3}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I 455 | 456 | .line 36 457 | const-string v2, "" 458 | 459 | goto :goto_14 460 | 461 | .line 41 462 | .end local v0 # "e":Lorg/json/JSONException; 463 | :catch_35 464 | move-exception v0 465 | 466 | .line 42 467 | .local v0, "e":Ljava/io/IOException; 468 | const-string v2, "MagiaClientJNI" 469 | 470 | new-instance v3, Ljava/lang/StringBuilder; 471 | 472 | invoke-direct {v3}, Ljava/lang/StringBuilder;->()V 473 | 474 | const-string v4, "Error with request: " 475 | 476 | invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 477 | 478 | move-result-object v3 479 | 480 | invoke-virtual {v0}, Ljava/io/IOException;->toString()Ljava/lang/String; 481 | 482 | move-result-object v4 483 | 484 | invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 485 | 486 | move-result-object v3 487 | 488 | invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 489 | 490 | move-result-object v3 491 | 492 | invoke-static {v2, v3}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I 493 | 494 | .line 43 495 | const-string v2, "" 496 | 497 | goto :goto_14 498 | .end method 499 | -------------------------------------------------------------------------------- /src/MagiaClient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Utils.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Config.h" 16 | #include 17 | #include "libmadomagi.h" 18 | #include "rest/MagiaRest.h" 19 | 20 | #define RT_FAILED -1 21 | #define RS_SUCCESS 0 22 | 23 | const char* libName = "libmadomagi_native.so"; 24 | const char* hookName = "libuwasa.so"; 25 | 26 | JavaVM* gJvm = nullptr; 27 | static jobject gClassLoader; 28 | static jmethodID gFindClassMethod; 29 | uintptr_t libBase = 0; 30 | 31 | //namespace fs = std::filesystem; ndk 22+ required 32 | 33 | struct hook_loop_args { 34 | std::string libso; 35 | }; 36 | 37 | uintptr_t storyMessageUnitStartOffset = 0; 38 | uintptr_t storyMessageUnitCreateMessageAreaOffset = 0; 39 | uintptr_t storyLogUnitAddMessageOffset = 0; 40 | uintptr_t storyLogUnitAddNarrationOffset = 0; 41 | uintptr_t storyCharaUnitonTextHomeOffset = 0; 42 | uintptr_t storyNarrationUnitCreateLabelOffset = 0; 43 | uintptr_t initCenterWidthOutline = 0; 44 | 45 | int max_threads = 10; 46 | 47 | void* openMessageBoxPtr = nullptr; 48 | uintptr_t* resourceUrlPtr = nullptr; 49 | 50 | bool initialized = false; 51 | 52 | const std::string assetBase = "/magica/resource"; 53 | const std::string assetTrunk = "/download/asset/master"; 54 | const std::string assetScenario = "/resource/scenario"; 55 | std::vector> urlEndpoints(3); 56 | 57 | const std::string koruriFont("fonts/koruri-semibold.ttf"); 58 | 59 | typedef int *(*setUrlType)(int *); 60 | typedef int *(*setResourceType)(int *, unsigned int *); 61 | typedef uintptr_t *(*UrlConfigImplResourceType)(uintptr_t &a1, int a2, unsigned int a3, int a4); 62 | 63 | // Hooked functions 64 | void *(*setPositionHooked)(uintptr_t label, cocos2d::Vec2 const& position); 65 | void *(*setMaxLineWidthHooked)(uintptr_t label, float length); 66 | void *(*setDimensionsHooked)(uintptr_t label, float width, float a3); 67 | 68 | const std::string* (*urlConfigResourceHooked)(void* a1, UrlConfigResourceType type); // There is also api, chat, web, etc for other endpoints 69 | 70 | //void* urlConfig_ImplObj = nullptr; 71 | 72 | // Cocos functions 73 | typedef cocos2d::Director* (*director_type)(void* dummy); 74 | typedef const cocos2d::Size& (*get_win_size_type)(cocos2d::Director* director); 75 | typedef cocos2d::Vec2 (*get_visible_origin_type)(cocos2d::Director* director); 76 | 77 | director_type getDirector; 78 | get_win_size_type getWinSize; 79 | get_visible_origin_type getVisibleOrigin; 80 | 81 | /* BROKEN 82 | void testDialogue() { 83 | if (openMessageBoxPtr != nullptr) { 84 | auto x = DialogueBoxProxy(); 85 | x.DialogueBox(openMessageBoxPtr); 86 | } 87 | }*/ 88 | jclass findClass(JNIEnv* env, const char* name) { 89 | return static_cast(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name))); 90 | } 91 | 92 | void displayMessage(const std::string& title, const std::string& description) { 93 | auto env = getEnv(gJvm); 94 | const char* cocosHelper = "org/cocos2dx/lib/Cocos2dxHelper"; 95 | const char* showDialogueBox = "showDialog"; 96 | std::string signature = "(" + std::string(getJNISignature(title, description)) + ")V"; 97 | 98 | jclass klass = findClass(env, cocosHelper); 99 | if (klass == nullptr) { 100 | LOGE("No Cocos2dxHelper found."); 101 | return; 102 | } 103 | 104 | jmethodID mid = env->GetStaticMethodID(klass, showDialogueBox, signature.c_str()); 105 | if (mid == nullptr) { 106 | LOGE("No showDialog found."); 107 | return; 108 | } 109 | jstring str1 = env->NewStringUTF(title.c_str()); 110 | jstring str2 = env->NewStringUTF(description.c_str()); 111 | env->CallStaticObjectMethod(klass, mid, str1, str2); 112 | } 113 | 114 | int *(*sceneLayerManagerCreateSceneLayerOld)(uintptr_t *sceneLayerManager, BaseSceneLayerInfo* sceneLayerInfo); 115 | 116 | int *sceneLayerManagerCreateSceneLayer(uintptr_t *sceneLayerManager, BaseSceneLayerInfo* sceneLayerInfo) { 117 | auto sceneType = sceneLayerInfo->layerType; 118 | if (sceneType >= BaseSceneLayerType::BaseSceneLayerTypeMaxValue || sceneType < 0) { 119 | LOGW("Unknown scene triggered. %d", sceneType); 120 | } 121 | else { 122 | LOGI("Scene layer changed to: %s", BaseSceneLayerTypeStrings[sceneType]); 123 | } 124 | 125 | if (!initialized && sceneType == BaseSceneLayerType::WebSceneLayer) { // Set up everything here 126 | auto rest = MagiaRest(gJvm); 127 | switch(rest.Endpoint()) { 128 | case MAGIAREST_EMPTY: 129 | { 130 | 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)", 131 | rest.EndpointStringLength()); 132 | 133 | displayMessage("MagiaTranslate Error", emptyMessage.c_str()); 134 | break; 135 | } 136 | case MAGIAREST_ERROR: 137 | { 138 | 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()); 139 | displayMessage("MagiaTranslate Error", errorMessage.c_str()); 140 | break; 141 | } 142 | case MAGIAREST_SUCCESS: 143 | { 144 | auto ver = rest.GetEndpointVersion(); 145 | if (MT_VERSION < ver) { 146 | LOGI("Version update required."); 147 | 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", 148 | MT_VERSION, ver); 149 | displayMessage("MagiaTranslate Update", updateMessage.c_str()); 150 | } 151 | auto endpointUrl = rest.GetEndpointUrl(); 152 | if (endpointUrl.empty()) { 153 | LOGW("Empty endpoint URL."); 154 | displayMessage("MagiaTranslate Error", "Error 115 has occurred, the returned translate endpoint URL is empty, please try again later."); 155 | break; 156 | } 157 | 158 | // Set max download threads 159 | auto mt = rest.GetMaxThreads(); 160 | if (mt > 0) { 161 | LOGD("Set maximum threads from API, value %d.", mt); 162 | max_threads = mt; 163 | } 164 | 165 | const std::string assetNameBase = endpointUrl + assetBase; 166 | const std::string assetNameFull = endpointUrl + assetBase + assetTrunk; 167 | const std::string assetNameScript = endpointUrl + assetBase + assetTrunk + assetScenario; 168 | 169 | //std::string assetNameBaseProxy(assetNameBase.c_str()); 170 | //std::string assetNameFullProxy(assetNameFull.c_str()); 171 | //std::string assetNameScriptProxy(assetNameScript.c_str()); 172 | 173 | LOGD("Setting endpoint URLs."); 174 | LOGD("%s", assetNameScript.c_str()); 175 | 176 | urlEndpoints.at(UrlConfigResourceType::BaseUrl) = std::make_shared(assetNameBase); 177 | urlEndpoints.at(UrlConfigResourceType::TrunkUrl) = std::make_shared(assetNameFull); 178 | urlEndpoints.at(UrlConfigResourceType::ScenarioUrl) = std::make_shared(assetNameScript); 179 | LOGD("Finished setting endpoint URLs."); 180 | break; 181 | } 182 | } 183 | 184 | initialized = true; 185 | auto y = sceneLayerManagerCreateSceneLayerOld(sceneLayerManager, sceneLayerInfo); 186 | return y; 187 | } 188 | 189 | return sceneLayerManagerCreateSceneLayerOld(sceneLayerManager, sceneLayerInfo); 190 | 191 | } 192 | 193 | // Change function to fetch resource URLs 194 | const std::string* urlConfigResource(void* a1, UrlConfigResourceType type) { 195 | LOGD("Fetching URL config resource %d", (int)type); 196 | if (type < UrlConfigResourceType::UrlConfigResourceTypeMaxValue) { 197 | try { 198 | if (urlEndpoints.at(type) != nullptr && urlEndpoints.at(type).get() != nullptr) { 199 | auto url = (urlEndpoints.at(type)).get(); 200 | LOGD("URL: %s", url->c_str()); 201 | return url; 202 | } 203 | else { 204 | LOGW("Empty endpoint found for endpoint type %d!", (int)type); 205 | } 206 | } 207 | catch (std::out_of_range const& exc) { 208 | LOGW("Out of range for endpoint type %d!", (int)type); 209 | } 210 | } 211 | return urlConfigResourceHooked(a1, type); 212 | } 213 | 214 | 215 | void* (*cocosCreateLabelHooked)(const uintptr_t* textPtr, const std::string &fontPtr, float textSize, cocos2d::Size const& cocosSize, cocos2d::TextHAlignment hAlign, cocos2d::TextVAlignment vAlign); 216 | void* cocosCreateLabel(const uintptr_t* textPtr, const std::string &fontPtr, float textSize, cocos2d::Size const& cocosSize, cocos2d::TextHAlignment hAlign, cocos2d::TextVAlignment vAlign) { 217 | uintptr_t addr = reinterpret_cast(__builtin_extract_return_addr(__builtin_return_address(0))); 218 | LOGD("Label created at %p (%p), size %.1f.", (void*) addr, (void*)(addr - libBase), textSize); 219 | if (storyMessageUnitCreateMessageAreaOffset != 0 && addr >= storyMessageUnitCreateMessageAreaOffset) { 220 | uintptr_t difference = addr - storyMessageUnitCreateMessageAreaOffset; 221 | if (difference <= 0x200) { 222 | LOGD("Setting new text font for main story text."); 223 | if (textSize == 27.0) { 224 | textSize = 30.0; 225 | } 226 | return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign); 227 | } 228 | } 229 | if (storyNarrationUnitCreateLabelOffset != 0 && addr >= storyNarrationUnitCreateLabelOffset) { 230 | uintptr_t difference = addr - storyNarrationUnitCreateLabelOffset; 231 | 232 | if (difference <= 0x200) { // 0x8e 233 | LOGD("Setting new narration text font. Difference: %p", (void*)difference); 234 | return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign); 235 | } 236 | } 237 | if (initCenterWidthOutline != 0 && addr >= initCenterWidthOutline) { 238 | uintptr_t difference = addr - initCenterWidthOutline; 239 | 240 | if (difference <= 0x200) { 241 | LOGD("Setting new home text font and size."); 242 | textSize = textSize * 0.85; 243 | return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign); 244 | } 245 | } 246 | if (storyLogUnitAddMessageOffset != 0 && addr >= storyLogUnitAddMessageOffset) { 247 | uintptr_t difference = addr - storyLogUnitAddMessageOffset; 248 | 249 | if (difference <= 0x640) { // 0xec, 0x54e 250 | LOGD("Setting new log text font. Difference: %p", (void*)difference); 251 | return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign); 252 | } 253 | } 254 | if (storyLogUnitAddNarrationOffset != 0 && addr >= storyLogUnitAddNarrationOffset) { 255 | uintptr_t difference = addr - storyLogUnitAddNarrationOffset; 256 | if (difference <= 0x640) { // 0x43e, 0x5b0 257 | LOGD("Setting new log text font (narration). Difference: %p", (void*)difference); 258 | return cocosCreateLabelHooked(textPtr, koruriFont, textSize, cocosSize, hAlign, vAlign); 259 | } 260 | } 261 | return cocosCreateLabelHooked(textPtr, fontPtr, textSize, cocosSize, hAlign, vAlign); 262 | 263 | } 264 | 265 | // New functions 266 | void *setPositionNew(uintptr_t label, cocos2d::Vec2 const& position) { 267 | uintptr_t addr = reinterpret_cast(__builtin_extract_return_addr(__builtin_return_address(0))); 268 | //LOGI("Move at %p to x: %.2f, y: %.2f", (addr - libBase), position.x, position.y); 269 | 270 | // Story message boxes 271 | if (storyMessageUnitStartOffset != 0 && addr >= storyMessageUnitStartOffset) { 272 | uintptr_t difference = addr - storyMessageUnitStartOffset; 273 | 274 | // Alignment of main text in box 275 | if (difference <= 0x200) { // Offset = 0x76 as of 2.15 276 | LOGD("Moving story text."); 277 | //LOGI("Difference: %p", difference); 278 | //LOGD("old [message] x: %.2f, y: %.2f", position.x, position.y); 279 | 280 | // Move text to the left and up a bit 281 | if (position.x == -222.0 && position.y == 20.0) { 282 | cocos2d::Vec2 newPosition = cocos2d::Vec2(-368.0, 25.0); 283 | //LOGD("new 1 x: %.2f, y: %.2f", newPosition.x, newPosition.y); 284 | return setPositionHooked(label, newPosition); 285 | } 286 | else if (position.x == -207.0 && position.y == 30.0) { 287 | cocos2d::Vec2 newPosition = cocos2d::Vec2(-360.0, 40.0); 288 | //LOGD("new 2 x: %.2f, y: %.2f", newPosition.x, newPosition.y); 289 | return setPositionHooked(label, newPosition); 290 | } 291 | } 292 | 293 | } 294 | 295 | 296 | // Names alignment 297 | if (storyMessageUnitCreateMessageAreaOffset != 0 && addr >= storyMessageUnitCreateMessageAreaOffset) { 298 | 299 | uintptr_t difference = addr - storyMessageUnitCreateMessageAreaOffset; 300 | //LOGI("Difference: %p", difference); 301 | 302 | // Move names into the right place 303 | if (difference <= 0x600) { // Offset = 0x35e as of 2.15 304 | //LOGD("old [name] x: %.2f, y: %.2f", position.x, position.y); 305 | LOGD("Moving story name."); 306 | if (position.x == -215.0 && position.y == 57.0) { // Left names 307 | cocos2d::Vec2 newPosition = cocos2d::Vec2(-320.0, 55.0); 308 | //LOGD("new l-1 x: %.2f, y: %.2f", newPosition.x, newPosition.y); 309 | return setPositionHooked(label, newPosition); 310 | } 311 | else if (position.x == -55.0 && position.y == 57.0) { // Center names 312 | cocos2d::Vec2 newPosition = cocos2d::Vec2(30.0, 55.0); 313 | //LOGD("new m-1 x: %.2f, y: %.2f", newPosition.x, newPosition.y); 314 | return setPositionHooked(label, newPosition); 315 | } 316 | else if (position.x == 215.0 && position.y == 57.0) { // Right names 317 | cocos2d::Vec2 newPosition = cocos2d::Vec2(320.0, 55.0); 318 | //LOGD("new r-1 x: %.2f, y: %.2f", newPosition.x, newPosition.y); 319 | return setPositionHooked(label, newPosition); 320 | } 321 | } 322 | 323 | } 324 | 325 | // History 326 | if (storyLogUnitAddMessageOffset != 0 && addr >= storyLogUnitAddMessageOffset) { 327 | uintptr_t difference = addr - storyLogUnitAddMessageOffset; 328 | LOGD("LOG MESSAGE: %.2f %.2f", position.x, position.y); 329 | LOGD("Difference: %p", (void *)difference); 330 | //if (position.y >= 55.50 && position.y <= 56.50 && difference <= 0x1000) { 331 | if ((position.y >= 37.5 && position.y <= 39.5 && difference <= 0x1300) 332 | || (position.y >= 55.00 && position.y <= 56.00 && difference <= 0x1300)) { 333 | float newPosX = position.x + 125.0; 334 | LOGD("Moved log text from %.2f to %.2f", position.x, newPosX); 335 | cocos2d::Vec2 newPosition = cocos2d::Vec2(newPosX, 66.50); 336 | return setPositionHooked(label, newPosition); 337 | } 338 | else if (difference <= 0x400 && position.x == 70.00) { 339 | LOGD("Moving left-aligned name down in the log."); 340 | auto newY = position.y; 341 | cocos2d::Vec2 newPosition = cocos2d::Vec2(position.x, newY); 342 | return setPositionHooked(label, newPosition); 343 | } 344 | else if (difference <= 0x400 && position.x == 500.00) { // Names on the right 345 | LOGD("Moving name further to the right in log."); 346 | auto newX = position.x + 200; 347 | //auto newY = position.y - 15.0; 348 | auto newY = position.y; 349 | cocos2d::Vec2 newPosition = cocos2d::Vec2(newX, newY); 350 | return setPositionHooked(label, newPosition); 351 | } 352 | else if (difference <= 0x400 && position.x == 280.00) { // Names in the center, 280.00? 353 | LOGD("Moving center name to the left in the log."); 354 | auto newX = 71.50; 355 | auto newY = position.y; 356 | cocos2d::Vec2 newPosition = cocos2d::Vec2(newX, newY); 357 | return setPositionHooked(label, newPosition); 358 | } 359 | } 360 | return setPositionHooked(label, position); 361 | } 362 | 363 | void *setMaxLineWidthNew(uintptr_t label, float length) { 364 | //LOGI("Hook triggered - line length"); 365 | uintptr_t addr = reinterpret_cast(__builtin_extract_return_addr(__builtin_return_address(0))); 366 | //LOGI("%p", (addr - libBase)); 367 | 368 | if (storyMessageUnitCreateMessageAreaOffset != 0 && addr >= storyMessageUnitCreateMessageAreaOffset) { 369 | uintptr_t difference = addr - storyMessageUnitCreateMessageAreaOffset; 370 | //LOGI("Difference: %p", difference); 371 | 372 | // Make lines longer 373 | if (difference <= 0x244 && length == 410.0) { // Offset = 0x144 as of 2.15 374 | LOGD("Set line length from 410.0 to 810.0"); 375 | length = 810.0; 376 | } 377 | } 378 | return setMaxLineWidthHooked(label, length); 379 | } 380 | 381 | void *setDimensionsNew(uintptr_t label, float width, float height) { 382 | uintptr_t addr = reinterpret_cast(__builtin_extract_return_addr(__builtin_return_address(0))); 383 | uintptr_t difference = addr - storyLogUnitAddMessageOffset; 384 | //LOGI("Difference [dimensions]: %p, addr: %p", difference, addr); 385 | 386 | if (storyLogUnitAddMessageOffset != 0 && addr >= storyLogUnitAddMessageOffset) { 387 | //LOGI("%p, %.2f, %.2f", label, width, a3); 388 | if (difference <= 0x900 && width == 410.0) { // Offset = 0x5ac as of 2.15 389 | LOGD("Set dimensions for log from 410.0 to 710.0."); 390 | return setDimensionsHooked(label, 710.0, height); 391 | } 392 | } 393 | //LOGD("Dimensions: %f, %f", width, height); 394 | return setDimensionsHooked(label, width, height); 395 | } 396 | 397 | // Fix the position of homeText under live2d girls 398 | cocos2d::Size (*lbGetViewPositionHooked)(float x, float y); 399 | cocos2d::Size lbGetViewPositionNew(float x, float y) { 400 | uintptr_t addr = reinterpret_cast(__builtin_extract_return_addr(__builtin_return_address(0))); 401 | uintptr_t difference = addr - storyCharaUnitonTextHomeOffset; 402 | //LOGD("Difference (viewPos): %p, addr: %p", (void*)difference, (void*)addr); 403 | 404 | if (storyCharaUnitonTextHomeOffset != 0 && addr >= storyCharaUnitonTextHomeOffset) { 405 | if (difference <= 0x1300) { 406 | auto oldx = x; 407 | auto oldy = y; 408 | if (x > -100.0) { 409 | x = -120.0; 410 | } 411 | if (x == -100.0) { 412 | x = -250.0; 413 | } 414 | x = x - 30.0; 415 | y = y - 30.0; 416 | LOGD("Set live2d subtitle dimensions from (%f, %f) to (%f, %f).", oldx, oldy, x, y); 417 | } 418 | } 419 | 420 | #if defined(__aarch64__) 421 | return lbGetViewPositionHooked(x, y); 422 | #endif 423 | //LOGI("Size 1: %f, size 2: %f", sizes.width, sizes.height); 424 | 425 | // Reimplement from scratch because arm is bugged (also this segfaults on arm64) 426 | auto director = getDirector((void *)0x00); 427 | //LOGI("Obtained director at %p.", (void*) director); 428 | 429 | auto dirSize = getWinSize(director); 430 | //LOGI("Obtained winsize, %f %f.", dirSize.width, dirSize.height); 431 | auto origin = getVisibleOrigin(director); 432 | //LOGI("Obtained visible origin, %f %f.", origin.x, origin.y); 433 | auto sizes = cocos2d::Size(); 434 | sizes.width = origin.x + x + (float)dirSize.width * 0.5; 435 | sizes.height = origin.y + y + (float)dirSize.height * 0.5; 436 | //sizes.width = origin.x + x + (float)dirSize.width * 0.5; 437 | //sizes.height = origin.y + y + (float)dirSize.height * 0.5; 438 | 439 | //LOGI("NEW size 1: %f, size 2: %f", sizes.width, sizes.height); 440 | return sizes; 441 | } 442 | 443 | 444 | pthread_mutex_t *(*setUriDebugOld)(uintptr_t a1, const std::string &st); 445 | pthread_mutex_t *setUriDebug(uintptr_t a1, const std::string &stri) { 446 | auto mut = setUriDebugOld(a1, stri); 447 | 448 | auto outstr = stri.c_str(); 449 | LOGI("Uri base set: %s", outstr); 450 | return mut; 451 | } 452 | 453 | pthread_mutex_t *(*http2SessionSetMaxConnectionNumOld)(uintptr_t *session, int max); 454 | 455 | pthread_mutex_t *http2SessionSetMaxConnectionNum(uintptr_t *session, int max) { 456 | if (max == 4) { 457 | max = max_threads; 458 | } 459 | LOGD("Set max number of connections to %d.", max); 460 | return http2SessionSetMaxConnectionNumOld(session, max); 461 | } 462 | 463 | #if defined(MAGIA_TRANSLATE_AUDIOFIX_3_0_1) 464 | uint32_t (*criNcv_GetHardwareSamplingRate_ANDROID_Hooked)(); 465 | 466 | uint32_t criNcv_GetHardwareSamplingRate_ANDROID() { 467 | auto orig = criNcv_GetHardwareSamplingRate_ANDROID_Hooked(); 468 | auto value = orig; 469 | value = 48000; 470 | LOGI("using hardware sample rate: %d (orig: %d)", value, orig); 471 | return value; 472 | } 473 | 474 | void *(*criNcv_SetHardwareSamplingRate_ANDROID_Hooked)(uint32_t value); 475 | 476 | void criNcv_SetHardwareSamplingRate_ANDROID(uint32_t value) { 477 | LOGI("set cached hardware sample rate to %d", value); 478 | criNcv_SetHardwareSamplingRate_ANDROID_Hooked(value); 479 | } 480 | #endif 481 | 482 | void initialization_error(const char* error) { 483 | LOGE("%s", error); 484 | 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); 485 | displayMessage("MagiaTranslate Error", errorMsg); 486 | } 487 | 488 | // Hook loop function. We run this in a separate thread so it doesn't block the main thread. 489 | void *hook_loop(void *arguments) { 490 | std::unique_ptr args((struct hook_loop_args *)arguments); 491 | auto libLocation = (args->libso).c_str(); 492 | 493 | LOGI("Library location: %s", libLocation); 494 | 495 | while(libBase == 0) { 496 | libBase = get_libBase(libName); 497 | } 498 | LOGI("Base address: %p", (void*)libBase); 499 | 500 | // Hook resource endpoint 501 | void *resourceHook = lookup_symbol(libLocation, "_ZNK9UrlConfig8resourceENS_8Resource4TypeE"); // UrlConfig::resource(UrlConfig::Resource::Type)const 502 | if (DobbyHook(resourceHook, (void *)urlConfigResource, (void **)&urlConfigResourceHooked) == RS_SUCCESS) { 503 | LOGI("Successfully hooked UrlConfig::resource."); 504 | } 505 | else { 506 | initialization_error("Failed to hook UrlConfig::resource."); 507 | pthread_exit(NULL); 508 | } 509 | 510 | 511 | // Hook scene creator 512 | void *sceneHook = lookup_symbol(libLocation, "_ZN17SceneLayerManager16createSceneLayerEP18BaseSceneLayerInfo"); //_DWORD __fastcall SceneLayerManager::createSceneLayer(SceneLayerManager *__hidden this, BaseSceneLayerInfo *) 513 | if (DobbyHook(sceneHook, (void *)sceneLayerManagerCreateSceneLayer, (void **)&sceneLayerManagerCreateSceneLayerOld) == RS_SUCCESS) { 514 | LOGI("Successfully hooked SceneLayerManager::createSceneLayer."); 515 | } 516 | else { 517 | initialization_error("Failed to hook SceneLayerManager::createSceneLayer."); 518 | pthread_exit(NULL); 519 | } 520 | 521 | // Speed up downloads 522 | void *maxDlHook = lookup_symbol(libLocation, "_ZN5http212Http2Session19setMaxConnectionNumEi"); //_DWORD __fastcall http2::Http2Session::setMaxConnectionNum(http2::Http2Session *__hidden this, int) 523 | if (DobbyHook(maxDlHook, (void *)http2SessionSetMaxConnectionNum, (void **)&http2SessionSetMaxConnectionNumOld) == RS_SUCCESS) { 524 | LOGI("Successfully hooked http2::Http2Session::setMaxConnectionNum."); 525 | } 526 | else { 527 | LOGW("Failed to hook http2::Http2Session::setMaxConnectionNum."); 528 | } 529 | 530 | 531 | //openMessageBoxPtr = lookup_symbol(libLocation, "_ZN10MessageBox4openEPKcS1_S1_RKSt8functionIFvPN7cocos2d3RefEEEb"); 532 | //LOGI("Set openMessageBox ptr to %p", openMessageBoxPtr); 533 | 534 | // For debugging 535 | //DobbyHook(lookup_symbol(libLocation, "_ZN5http212Http2Session6setURIERKSs"), (void *)setUriDebug, (void **)&setUriDebugOld); - crashes arm32 now. 536 | 537 | #if defined(MAGIA_TRANSLATE_AUDIOFIX_3_0_1) 538 | // audio pitch & speed fix 539 | void *getHWSampleRate = lookup_symbol(libLocation, "criNcv_GetHardwareSamplingRate_ANDROID"); 540 | 541 | if (getHWSampleRate != nullptr) { 542 | LOGD("Found criNcv_GetHardwareSamplingRate_ANDROID at %p.", (void *)getHWSampleRate); 543 | if (DobbyHook(getHWSampleRate, (void *)criNcv_GetHardwareSamplingRate_ANDROID, (void **)&criNcv_GetHardwareSamplingRate_ANDROID_Hooked) == RS_SUCCESS) { 544 | LOGI("Successfully hooked criNcv_GetHardwareSamplingRate_ANDROID."); 545 | } 546 | else { 547 | initialization_error("Unable to hook criNcv_GetHardwareSamplingRate_ANDROID."); 548 | pthread_exit(NULL); 549 | } 550 | } 551 | else { 552 | initialization_error("Unable to hook criNcv_GetHardwareSamplingRate_ANDROID."); 553 | pthread_exit(NULL); 554 | } 555 | 556 | void *setHWSampleRate = lookup_symbol(libLocation, "criNcv_SetHardwareSamplingRate_ANDROID"); 557 | 558 | if (setHWSampleRate != nullptr) { 559 | LOGD("Found criNcv_SetHardwareSamplingRate_ANDROID at %p.", (void *)setHWSampleRate); 560 | if (DobbyHook(setHWSampleRate, (void *)criNcv_SetHardwareSamplingRate_ANDROID, (void **)&criNcv_SetHardwareSamplingRate_ANDROID_Hooked) == RS_SUCCESS) { 561 | LOGI("Successfully hooked criNcv_SetHardwareSamplingRate_ANDROID."); 562 | } 563 | else { 564 | initialization_error("Unable to hook criNcv_SetHardwareSamplingRate_ANDROID."); 565 | pthread_exit(NULL); 566 | } 567 | } 568 | else { 569 | initialization_error("Unable to hook criNcv_SetHardwareSamplingRate_ANDROID."); 570 | pthread_exit(NULL); 571 | } 572 | #endif 573 | 574 | // Hooks 575 | void *cocos2dnodeSetPosition = lookup_symbol(libLocation, "_ZN7cocos2d4Node11setPositionERKNS_4Vec2E"); 576 | // cocos2d::Node::setPosition(cocos2d::Vec2 const&) 577 | if (cocos2dnodeSetPosition != nullptr) { 578 | LOGD("Found cocos2d::Node::setPosition at %p.", (void *)cocos2dnodeSetPosition); 579 | if (DobbyHook(cocos2dnodeSetPosition, (void *)setPositionNew, (void **)&setPositionHooked) == RS_SUCCESS) { 580 | LOGI("Successfully hooked cocos2d::Node::setPosition."); 581 | } 582 | else { 583 | initialization_error("Unable to hook cocos2d::Node::setPosition."); 584 | pthread_exit(NULL); 585 | } 586 | } 587 | else { 588 | initialization_error("Unable to hook cocos2d::Node::setPosition."); 589 | pthread_exit(NULL); 590 | } 591 | 592 | void *cocos2dlineLength = lookup_symbol(libLocation, "_ZN7cocos2d5Label15setMaxLineWidthEf"); 593 | // cocos2d::Label::setMaxLineWidth(float) 594 | if (cocos2dlineLength != nullptr) { 595 | LOGD("Found cocos2d::Label::setMaxLineWidth at %p.", (void *)cocos2dlineLength); 596 | if (DobbyHook(cocos2dlineLength, (void *)setMaxLineWidthNew, (void **)&setMaxLineWidthHooked) == RS_SUCCESS) { 597 | LOGI("Successfully hooked cocos2d::Label::setMaxLineWidth."); 598 | } 599 | else { 600 | initialization_error("Unable to hook cocos2d::Label::setMaxLineWidth."); 601 | pthread_exit(NULL); 602 | } 603 | } 604 | else { 605 | initialization_error("Unable to hook cocos2d::Label::setMaxLineWidth."); 606 | pthread_exit(NULL); 607 | } 608 | 609 | void *cocos2dsetDimensions = lookup_symbol(libLocation, "_ZN7cocos2d5Label13setDimensionsEff"); 610 | //_DWORD __fastcall cocos2d::Label::setDimensions(cocos2d::Label *__hidden this, float, float) 611 | if (cocos2dsetDimensions != nullptr) { 612 | LOGD("Found cocos2d::Label::setDimensions at %p.", (void *)cocos2dsetDimensions); 613 | if (DobbyHook(cocos2dsetDimensions, (void *)setDimensionsNew, (void **)&setDimensionsHooked) == RS_SUCCESS) { 614 | LOGI("Successfully hooked cocos2d::Label::setDimensions."); 615 | } 616 | else { 617 | initialization_error("Unable to hook cocos2d::Label::setDimensions."); 618 | pthread_exit(NULL); 619 | } 620 | } 621 | else { 622 | initialization_error("Unable to hook cocos2d::Label::setDimensions."); 623 | pthread_exit(NULL); 624 | } 625 | 626 | // For moving live2d subtitles 627 | void *lbGetViewPosition = lookup_symbol(libLocation, "_ZN9LbUtility15getViewPositionEff"); 628 | //_DWORD __fastcall LbUtility::getViewPosition(LbUtility *__hidden this, float, float) 629 | if (lbGetViewPosition != nullptr) { 630 | LOGD("Found LbUtility::getViewPosition at %p.", (void *)lbGetViewPosition); 631 | if (DobbyHook(lbGetViewPosition, (void *)lbGetViewPositionNew, (void **)&lbGetViewPositionHooked) == RS_SUCCESS) { 632 | LOGI("Successfully hooked LbUtility::getViewPosition."); 633 | } 634 | else { 635 | initialization_error("Unable to hook LbUtility::getViewPosition."); 636 | pthread_exit(NULL); 637 | } 638 | } 639 | else { 640 | initialization_error("Unable to hook LbUtility::getViewPosition."); 641 | pthread_exit(NULL); 642 | } 643 | 644 | // Change font 645 | void *cocosCreateLabelPtr = lookup_symbol(libLocation, "_ZN7cocos2d5Label13createWithTTFERKNSt6__ndk112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_fRKNS_4SizeENS_14TextHAlignmentENS_14TextVAlignmentE"); 646 | 647 | if (cocosCreateLabelPtr != nullptr) { 648 | LOGD("Found cocos2d::Label::createWithTTF at %p.", (void *)cocosCreateLabel); 649 | if (DobbyHook(cocosCreateLabelPtr, (void*) cocosCreateLabel, (void **)& cocosCreateLabelHooked) == RS_SUCCESS) { 650 | LOGI("Successfully hooked cocos2d::Label::createWithTTF."); 651 | } 652 | else { 653 | initialization_error("Unable to hook cocos2d::Label::createWithTTF."); 654 | pthread_exit(NULL); 655 | } 656 | } 657 | else { 658 | initialization_error("Unable to hook cocos2d::Label::createWithTTF."); 659 | pthread_exit(NULL); 660 | } 661 | 662 | 663 | // Find key functions, TODO: Tidy up into 1 nice loop 664 | 665 | void *storyMessageUnitTextStart = lookup_symbol(libLocation, "_ZN16StoryMessageUnit9textStartENS_11TextPosType13TextPosType__E"); 666 | // StoryMessageUnit::textStart(StoryMessageUnit::TextPosType::TextPosType__) 667 | if (storyMessageUnitTextStart == nullptr) { 668 | initialization_error("Unable to find a pointer for StoryMessageUnit::textStart."); 669 | pthread_exit(NULL); 670 | } 671 | storyMessageUnitStartOffset = reinterpret_cast(storyMessageUnitTextStart); 672 | 673 | void *storyMessageUnitCreateMessageArea = lookup_symbol(libLocation, "_ZN16StoryMessageUnit17createMessageAreaENS_11TextPosType13TextPosType__E"); 674 | // StoryMessageUnit::createMessageArea(StoryMessageUnit::TextPosType::TextPosType__) 675 | if (storyMessageUnitCreateMessageArea == nullptr) { 676 | initialization_error("Unable to find a pointer for StoryMessageUnit::createMessageArea."); 677 | pthread_exit(NULL); 678 | } 679 | storyMessageUnitCreateMessageAreaOffset = reinterpret_cast(storyMessageUnitCreateMessageArea); 680 | 681 | void *storyLogUnitAddMessage = lookup_symbol(libLocation, "_ZN12StoryLogUnit10addMessageENS_11MessageType13MessageType__ERKNSt6__ndk112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"); 682 | 683 | if (storyLogUnitAddMessage == nullptr) { 684 | initialization_error("Unable to find a pointer for StoryLogUnit::addMessage."); 685 | pthread_exit(NULL); 686 | } 687 | storyLogUnitAddMessageOffset = reinterpret_cast(storyLogUnitAddMessage); 688 | 689 | void *storyLogUnitAddNarrationOffsetPtr = lookup_symbol(libLocation, "_ZN12StoryLogUnit19addNarrationMessageERKNSt6__ndk112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE"); 690 | 691 | if (storyLogUnitAddNarrationOffsetPtr == nullptr) { 692 | initialization_error("Unable to find a pointer for StoryLogUnit::addNarrationMessage."); 693 | pthread_exit(NULL); 694 | } 695 | 696 | storyLogUnitAddNarrationOffset = reinterpret_cast(storyLogUnitAddNarrationOffsetPtr); 697 | 698 | void *storyCharaUnitonTextHome = lookup_symbol(libLocation, "_ZN14StoryCharaUnit10onTextHomeENSt6__ndk110shared_ptrI16StoryTurnCommandEEb"); 699 | // StoryCharaUnit::onTextHome(std::shared_ptr, bool) 700 | 701 | if (storyCharaUnitonTextHome == nullptr) { 702 | initialization_error("Unable to find a pointer for StoryCharaUnit::onTextHome."); 703 | pthread_exit(NULL); 704 | } 705 | storyCharaUnitonTextHomeOffset = reinterpret_cast(storyCharaUnitonTextHome); 706 | 707 | void *initLabelCWO = lookup_symbol(libLocation, "_ZN9LbUtility27initLabelCenterWidthOutlineEPN7cocos2d4NodeERPNS0_5LabelEPKcfNS0_4Vec2EiNS0_4SizeEiNS0_7Color4BEiSA_"); 708 | // LbUtility::initLabelCenterWidthOutline(cocos2d::Node *, cocos2d::Label *&, char const*, float, cocos2d::Vec2, int, cocos2d::Size, int, cocos2d::Color4B, int, cocos2d::Color4B) 709 | 710 | if (initLabelCWO == nullptr) { 711 | initialization_error("Unable to find a pointer for LbUtility::initLabelCenterWidthOutline."); 712 | pthread_exit(NULL); 713 | } 714 | initCenterWidthOutline = reinterpret_cast(initLabelCWO); 715 | 716 | void* storyNarrationUnitCreateLabelPtr = lookup_symbol(libLocation, "_ZN18StoryNarrationUnit11createLabelEv"); 717 | 718 | if (storyNarrationUnitCreateLabelPtr == nullptr) { 719 | initialization_error("Unable to find a pointer for StoryNarrationUnit::createLabel."); 720 | pthread_exit(NULL); 721 | } 722 | 723 | storyNarrationUnitCreateLabelOffset = reinterpret_cast(storyNarrationUnitCreateLabelPtr); 724 | 725 | // Cocos functions 726 | void *directorPtr = lookup_symbol(libLocation, "_ZN7cocos2d8Director11getInstanceEv"); 727 | if (directorPtr == nullptr) { 728 | initialization_error("Unable to find a pointer for cocos2d::Director::getInstance()"); 729 | pthread_exit(NULL); 730 | } 731 | getDirector = (director_type) directorPtr; 732 | 733 | void* getWinSizePtr = lookup_symbol(libLocation, "_ZNK7cocos2d8Director10getWinSizeEv"); 734 | if (getWinSizePtr == nullptr) { 735 | initialization_error("Unable to find a pointer for cocos2d::Director::getWinSize()"); 736 | pthread_exit(NULL); 737 | } 738 | getWinSize = (get_win_size_type) getWinSizePtr; 739 | 740 | void* getVisibleOriginPtr = lookup_symbol(libLocation, "_ZNK7cocos2d8Director16getVisibleOriginEv"); 741 | if (getVisibleOriginPtr == nullptr) { 742 | initialization_error("Unable to find a pointer for cocos2d::Director::getVisibleOrigin()"); 743 | pthread_exit(NULL); 744 | } 745 | getVisibleOrigin = (get_visible_origin_type) getVisibleOriginPtr; 746 | 747 | 748 | 749 | LOGI("Exiting hook thread."); 750 | pthread_exit(NULL); 751 | } 752 | 753 | __attribute__((constructor)) 754 | void hook_main() { 755 | 756 | } 757 | 758 | extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { 759 | LOGI("Starting MagiaHook."); 760 | 761 | gJvm = vm; // cache the JavaVM pointer 762 | Dl_info dlInfo; 763 | JNIEnv* env; 764 | if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) 765 | { 766 | return -1; 767 | } 768 | 769 | // https://stackoverflow.com/a/16302771/9665729 770 | // Cache ClassLoader object due to threading bug 771 | auto randomClass = env->FindClass("org/cocos2dx/lib/Cocos2dxHelper"); 772 | jclass classClass = env->GetObjectClass(randomClass); 773 | auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); 774 | auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", 775 | "()Ljava/lang/ClassLoader;"); 776 | gClassLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod)); 777 | gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", 778 | "(Ljava/lang/String;)Ljava/lang/Class;"); 779 | 780 | 781 | if (dladdr((const void*) hook_main, &dlInfo)) 782 | { 783 | //fs::path ilso = fs::path(dlInfo.dli_fname).remove_filename(); ndk 22+ required 784 | //ilso /= "il2cpp.so"; ndk 22+ required 785 | hook_loop_args* args = new hook_loop_args; 786 | auto ilso = (std::string) dirname((char *)dlInfo.dli_fname); 787 | 788 | ilso += "/"; 789 | ilso += libName; 790 | 791 | //if (fs::exists(ilso)) { ndk 22+ required 792 | if (!file_exists(ilso)) { 793 | // Fix for some devices 794 | auto this_ilso = get_libFoldername(hookName); 795 | if (this_ilso.empty()) { 796 | LOGE("Failed to locate shared library %s. Checked: %s", libName, ilso.c_str()); 797 | return JNI_VERSION_1_6; 798 | } 799 | 800 | auto ilso2 = this_ilso.substr(0, this_ilso.length() - strlen(hookName) - 1); 801 | ilso2 += libName; 802 | if (!file_exists(ilso2)) { 803 | LOGE("Failed to load shared library %s. Checked: %s", libName, ilso2.c_str()); 804 | return JNI_VERSION_1_6; 805 | } 806 | args->libso = ilso2; 807 | } 808 | else { 809 | args->libso = ilso; 810 | } 811 | pthread_t ptid; 812 | if (pthread_create(&ptid, NULL, &hook_loop, args) != 0) { 813 | LOGE("Hooking thread failed to start."); 814 | return JNI_VERSION_1_6; 815 | } 816 | } 817 | return JNI_VERSION_1_6; 818 | } --------------------------------------------------------------------------------