forked from chinosk/gkms-local
Compare commits
15 Commits
v3.0.0Beta
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
cabb902a41 | |
|
|
04b467992f | |
|
|
ca3fb1b05a | |
|
|
cbc571cf7a | |
|
|
1256b12d9a | |
|
|
6fc1873b27 | |
|
|
3df995e3bc | |
|
|
a12651d60f | |
|
|
048827feee | |
|
|
c7af3e41a5 | |
|
|
87c4f366df | |
|
|
a39b024b58 | |
|
|
3110cc7e49 | |
|
|
b70376dcef | |
|
|
e859589125 |
|
|
@ -3,6 +3,8 @@
|
||||||
- 学园偶像大师 本地化插件
|
- 学园偶像大师 本地化插件
|
||||||
- **开发中**
|
- **开发中**
|
||||||
|
|
||||||
|
- 下游更改:将API版本提升至101并暂时禁用Patcher
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ android {
|
||||||
minSdk 29
|
minSdk 29
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 12
|
versionCode 12
|
||||||
versionName "v3.0.0"
|
versionName "v3.2.0"
|
||||||
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
@ -130,6 +130,6 @@ dependencies {
|
||||||
|
|
||||||
implementation(libs.xdl)
|
implementation(libs.xdl)
|
||||||
implementation(libs.shadowhook)
|
implementation(libs.shadowhook)
|
||||||
compileOnly(libs.xposed.api)
|
compileOnly(libs.libxposed.api)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -2,33 +2,33 @@ import os
|
||||||
|
|
||||||
|
|
||||||
logs = """
|
logs = """
|
||||||
Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules listenablefuture-1.0.jar -> listenablefuture-1.0 (com.google.guava:listenablefuture:1.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules listenablefuture-1.0.jar -> listenablefuture-1.0 (com.google.guava:listenablefuture:1.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.CanIgnoreReturnValue found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.CanIgnoreReturnValue found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.CheckReturnValue found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.CheckReturnValue found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.CompatibleWith found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.CompatibleWith found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.CompileTimeConstant found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.CompileTimeConstant found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.DoNotCall found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.DoNotCall found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.DoNotMock found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.DoNotMock found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.ForOverride found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.ForOverride found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.FormatMethod found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.FormatMethod found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.FormatString found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.FormatString found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.Immutable found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.Immutable found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.IncompatibleModifiers found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.IncompatibleModifiers found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.InlineMe found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.InlineMe found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.InlineMeValidationDisabled found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.InlineMeValidationDisabled found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.Keep found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.Keep found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.Modifier found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.Modifier found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.MustBeClosed found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.MustBeClosed found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.NoAllocation found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.NoAllocation found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.RequiredModifiers found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.RequiredModifiers found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.RestrictedApi found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.RestrictedApi found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.SuppressPackageLocation found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.SuppressPackageLocation found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.Var found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.Var found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.concurrent.GuardedBy found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.concurrent.GuardedBy found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.concurrent.LazyInit found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.concurrent.LazyInit found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.concurrent.LockMethod found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.concurrent.LockMethod found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
Duplicate class com.google.errorprone.annotations.concurrent.UnlockMethod found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch.jar -> lspatch (lspatch.jar)
|
Duplicate class com.google.errorprone.annotations.concurrent.UnlockMethod found in modules error_prone_annotations-2.15.0.jar -> error_prone_annotations-2.15.0 (com.google.errorprone:error_prone_annotations:2.15.0) and lspatch_cleaned.jar -> lspatch_cleaned (lspatch_cleaned.jar)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for i in logs.split("\n"):
|
for i in logs.split("\n"):
|
||||||
|
|
|
||||||
|
|
@ -21,23 +21,6 @@
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="xposedmodule"
|
|
||||||
android:value="true" />
|
|
||||||
<meta-data
|
|
||||||
android:name="xposeddescription"
|
|
||||||
android:value="IDOLM@STER Gakuen localify" />
|
|
||||||
<meta-data
|
|
||||||
android:name="xposedminversion"
|
|
||||||
android:value="53" />
|
|
||||||
<meta-data
|
|
||||||
android:name="xposedsharedprefs"
|
|
||||||
android:value="true" />
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="xposedscope"
|
|
||||||
android:value="com.bandainamcoent.idolmaster_gakuen" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +0,0 @@
|
||||||
io.github.chinosk.gakumas.localify.GakumasHookMain
|
|
||||||
|
|
@ -68,4 +68,8 @@ target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||||
log
|
log
|
||||||
fmt)
|
fmt)
|
||||||
|
|
||||||
|
if (ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 31)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} icu)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE cxx_std_23)
|
target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE cxx_std_23)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Local.h"
|
#include "Local.h"
|
||||||
#include "MasterLocal.h"
|
#include "MasterLocal.h"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <algorithm>
|
||||||
#include "camera/camera.hpp"
|
#include "camera/camera.hpp"
|
||||||
#include "config/Config.hpp"
|
#include "config/Config.hpp"
|
||||||
// #include <jni.h>
|
// #include <jni.h>
|
||||||
|
|
@ -99,6 +100,18 @@ namespace GakumasLocal::HookMain {
|
||||||
return GetResolution->Invoke<Il2cppUtils::Resolution_t>();
|
return GetResolution->Invoke<Il2cppUtils::Resolution_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Il2cppString* ToJsonStr(void* object) {
|
||||||
|
static Il2cppString* (*toJsonStr)(void*) = nullptr;
|
||||||
|
if (!toJsonStr) {
|
||||||
|
toJsonStr = reinterpret_cast<Il2cppString * (*)(void*)>(Il2cppUtils::GetMethodPointer("Newtonsoft.Json.dll", "Newtonsoft.Json",
|
||||||
|
"JsonConvert", "SerializeObject", { "*" }));
|
||||||
|
}
|
||||||
|
if (!toJsonStr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return toJsonStr(object);
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_HOOK(void, Unity_set_fieldOfView, (UnityResolve::UnityType::Camera* self, float value)) {
|
DEFINE_HOOK(void, Unity_set_fieldOfView, (UnityResolve::UnityType::Camera* self, float value)) {
|
||||||
if (Config::enableFreeCamera) {
|
if (Config::enableFreeCamera) {
|
||||||
if (self == mainCameraCache) {
|
if (self == mainCameraCache) {
|
||||||
|
|
@ -454,6 +467,8 @@ namespace GakumasLocal::HookMain {
|
||||||
"TMPro", "TMP_Text", "get_font");
|
"TMPro", "TMP_Text", "get_font");
|
||||||
static auto set_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll",
|
static auto set_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll",
|
||||||
"TMPro", "TMP_Text", "set_font");
|
"TMPro", "TMP_Text", "set_font");
|
||||||
|
static auto get_name = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll",
|
||||||
|
"UnityEngine", "Object", "get_name");
|
||||||
// static auto set_fontMaterial = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll",
|
// static auto set_fontMaterial = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll",
|
||||||
// "TMPro", "TMP_Text", "set_fontMaterial");
|
// "TMPro", "TMP_Text", "set_fontMaterial");
|
||||||
// static auto ForceMeshUpdate = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll",
|
// static auto ForceMeshUpdate = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll",
|
||||||
|
|
@ -467,21 +482,32 @@ namespace GakumasLocal::HookMain {
|
||||||
static auto UpdateFontAssetData = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro",
|
static auto UpdateFontAssetData = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro",
|
||||||
"TMP_FontAsset", "UpdateFontAssetData");
|
"TMP_FontAsset", "UpdateFontAssetData");
|
||||||
|
|
||||||
|
auto fontAsset = get_font->Invoke<void*>(TMP_Textself);
|
||||||
|
if (!fontAsset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查字体名称,跳过 CampusAlphanumeric 系列字体
|
||||||
|
auto fontAssetName = get_name->Invoke<Il2cppString*>(fontAsset);
|
||||||
|
if (fontAssetName) {
|
||||||
|
std::string fontName = fontAssetName->ToString();
|
||||||
|
std::transform(fontName.begin(), fontName.end(), fontName.begin(),
|
||||||
|
[](unsigned char c) { return std::tolower(c); });
|
||||||
|
if (fontName.find("campusalphanumeric") != std::string::npos) {
|
||||||
|
return; // 保持原版数字字体
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto newFont = GetReplaceFont();
|
auto newFont = GetReplaceFont();
|
||||||
if (!newFont) return;
|
if (!newFont) return;
|
||||||
|
|
||||||
auto fontAsset = get_font->Invoke<void*>(TMP_Textself);
|
set_sourceFontFile->Invoke<void>(fontAsset, newFont);
|
||||||
if (fontAsset) {
|
if (!updatedFontPtrs.contains(fontAsset)) {
|
||||||
set_sourceFontFile->Invoke<void>(fontAsset, newFont);
|
updatedFontPtrs.emplace(fontAsset);
|
||||||
if (!updatedFontPtrs.contains(fontAsset)) {
|
UpdateFontAssetData->Invoke<void>(fontAsset);
|
||||||
updatedFontPtrs.emplace(fontAsset);
|
|
||||||
UpdateFontAssetData->Invoke<void>(fontAsset);
|
|
||||||
}
|
|
||||||
if (updatedFontPtrs.size() > 200) updatedFontPtrs.clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log::Error("UpdateFont: fontAsset is null.");
|
|
||||||
}
|
}
|
||||||
|
if (updatedFontPtrs.size() > 200) updatedFontPtrs.clear();
|
||||||
|
|
||||||
set_font->Invoke<void>(TMP_Textself, fontAsset);
|
set_font->Invoke<void>(TMP_Textself, fontAsset);
|
||||||
|
|
||||||
// auto fontMaterial = get_material->Invoke<void*>(fontAsset);
|
// auto fontMaterial = get_material->Invoke<void*>(fontAsset);
|
||||||
|
|
@ -514,6 +540,66 @@ namespace GakumasLocal::HookMain {
|
||||||
UpdateFont(self);
|
UpdateFont(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_HOOK(void, TMP_Text_set_text, (void* self, Il2cppString* value, void* mtd)) {
|
||||||
|
if (!value) {
|
||||||
|
return TMP_Text_set_text_Orig(self, value, mtd);
|
||||||
|
}
|
||||||
|
const std::string origText = value->ToString();
|
||||||
|
std::string transText;
|
||||||
|
if (Local::GetGenericText(origText, &transText)) {
|
||||||
|
const auto newText = UnityResolve::UnityType::String::New(transText);
|
||||||
|
UpdateFont(self);
|
||||||
|
return TMP_Text_set_text_Orig(self, newText, mtd);
|
||||||
|
}
|
||||||
|
if (Config::textTest) {
|
||||||
|
TMP_Text_set_text_Orig(self, UnityResolve::UnityType::String::New("[TT]" + origText), mtd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TMP_Text_set_text_Orig(self, value, mtd);
|
||||||
|
}
|
||||||
|
UpdateFont(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_HOOK(void, TMP_Text_SetText_1, (void* self, Il2cppString* sourceText, void* mtd)) {
|
||||||
|
if (!sourceText) {
|
||||||
|
return TMP_Text_SetText_1_Orig(self, sourceText, mtd);
|
||||||
|
}
|
||||||
|
const std::string origText = sourceText->ToString();
|
||||||
|
std::string transText;
|
||||||
|
if (Local::GetGenericText(origText, &transText)) {
|
||||||
|
const auto newText = UnityResolve::UnityType::String::New(transText);
|
||||||
|
UpdateFont(self);
|
||||||
|
return TMP_Text_SetText_1_Orig(self, newText, mtd);
|
||||||
|
}
|
||||||
|
if (Config::textTest) {
|
||||||
|
TMP_Text_SetText_1_Orig(self, UnityResolve::UnityType::String::New("[T1]" + origText), mtd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TMP_Text_SetText_1_Orig(self, sourceText, mtd);
|
||||||
|
}
|
||||||
|
UpdateFont(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_HOOK(void, TMP_Text_SetText_2, (void* self, Il2cppString* sourceText, bool syncTextInputBox, void* mtd)) {
|
||||||
|
if (!sourceText) {
|
||||||
|
return TMP_Text_SetText_2_Orig(self, sourceText, syncTextInputBox, mtd);
|
||||||
|
}
|
||||||
|
const std::string origText = sourceText->ToString();
|
||||||
|
std::string transText;
|
||||||
|
if (Local::GetGenericText(origText, &transText)) {
|
||||||
|
const auto newText = UnityResolve::UnityType::String::New(transText);
|
||||||
|
UpdateFont(self);
|
||||||
|
return TMP_Text_SetText_2_Orig(self, newText, syncTextInputBox, mtd);
|
||||||
|
}
|
||||||
|
if (Config::textTest) {
|
||||||
|
TMP_Text_SetText_2_Orig(self, UnityResolve::UnityType::String::New("[TS]" + sourceText->ToString()), syncTextInputBox, mtd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TMP_Text_SetText_2_Orig(self, sourceText, syncTextInputBox, mtd);
|
||||||
|
}
|
||||||
|
UpdateFont(self);
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_HOOK(void, TextMeshProUGUI_Awake, (void* self, void* method)) {
|
DEFINE_HOOK(void, TextMeshProUGUI_Awake, (void* self, void* method)) {
|
||||||
// Log::InfoFmt("TextMeshProUGUI_Awake at %p, self at %p", TextMeshProUGUI_Awake_Orig, self);
|
// Log::InfoFmt("TextMeshProUGUI_Awake at %p, self at %p", TextMeshProUGUI_Awake_Orig, self);
|
||||||
|
|
||||||
|
|
@ -540,9 +626,64 @@ namespace GakumasLocal::HookMain {
|
||||||
TextMeshProUGUI_Awake_Orig(self, method);
|
TextMeshProUGUI_Awake_Orig(self, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 文本未hook完整
|
// Legacy UnityEngine.UI.Text hook(礼物/邮件等非TMP界面)
|
||||||
|
DEFINE_HOOK(void, UIText_set_text, (void* self, Il2cppString* value)) {
|
||||||
|
if (!value) {
|
||||||
|
return UIText_set_text_Orig(self, value);
|
||||||
|
}
|
||||||
|
const std::string origText = value->ToString();
|
||||||
|
std::string transText;
|
||||||
|
if (Local::GetGenericText(origText, &transText)) {
|
||||||
|
const auto newText = UnityResolve::UnityType::String::New(transText);
|
||||||
|
return UIText_set_text_Orig(self, newText);
|
||||||
|
}
|
||||||
|
if (Config::textTest) {
|
||||||
|
UIText_set_text_Orig(self, UnityResolve::UnityType::String::New("[UI]" + origText));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UIText_set_text_Orig(self, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMP_Text.SetCharArray(char[], int, int) — 礼物/邮件描述文字通过此路径设置
|
||||||
|
DEFINE_HOOK(void, TMP_Text_SetCharArray, (void* self, void* charArray, int start, int count, void* mtd)) {
|
||||||
|
if (charArray && start >= 0 && count > 0) {
|
||||||
|
// IL2CPP char[] elements are uint16_t (UTF-16)
|
||||||
|
auto arr = reinterpret_cast<UnityResolve::UnityType::Array<uint16_t>*>(charArray);
|
||||||
|
// 边界检查:确保 start+count 不超出数组长度
|
||||||
|
if (static_cast<uintptr_t>(start + count) <= arr->max_length) {
|
||||||
|
auto rawData = arr->GetData();
|
||||||
|
if (rawData) {
|
||||||
|
// rawData 是 uintptr_t(字节地址),每个 char16_t 占 2 字节
|
||||||
|
// 必须用 start * sizeof(char16_t) 而非直接 + start(否则偏移量减半)
|
||||||
|
const std::u16string u16(
|
||||||
|
reinterpret_cast<const char16_t*>(rawData + static_cast<uintptr_t>(start) * sizeof(char16_t)),
|
||||||
|
static_cast<size_t>(count));
|
||||||
|
const std::string origText = Misc::ToUTF8(u16);
|
||||||
|
std::string transText;
|
||||||
|
if (Local::GetGenericText(origText, &transText)) {
|
||||||
|
UpdateFont(self);
|
||||||
|
TMP_Text_set_text_Orig(self, Il2cppString::New(transText), nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Config::textTest) {
|
||||||
|
UpdateFont(self);
|
||||||
|
TMP_Text_set_text_Orig(self, Il2cppString::New("[CA]" + origText), nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TMP_Text_SetCharArray_Orig(self, charArray, start, count, mtd);
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_HOOK(void, TextField_set_value, (void* self, Il2cppString* value)) {
|
DEFINE_HOOK(void, TextField_set_value, (void* self, Il2cppString* value)) {
|
||||||
Log::DebugFmt("TextField_set_value: %s", value->ToString().c_str());
|
if (value) {
|
||||||
|
std::string transText;
|
||||||
|
if (Local::GetGenericText(value->ToString(), &transText)) {
|
||||||
|
return TextField_set_value_Orig(self, UnityResolve::UnityType::String::New(transText));
|
||||||
|
}
|
||||||
|
}
|
||||||
TextField_set_value_Orig(self, value);
|
TextField_set_value_Orig(self, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,24 +824,24 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
#ifdef GKMS_WINDOWS
|
||||||
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* retstr, void* self, void* liveData, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* retstr, void* self, void* liveData, Il2cppString* characterId, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
||||||
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
||||||
if (Config::dbgMode && Config::unlockAllLive) {
|
if (Config::dbgMode && Config::unlockAllLive) {
|
||||||
isUnlocked = true;
|
isUnlocked = true;
|
||||||
isReleased = true;
|
isReleased = true;
|
||||||
hasLiveSkin = true;
|
hasLiveSkin = true;
|
||||||
}
|
}
|
||||||
PictureBookLiveThumbnailView_SetDataAsync_Orig(retstr, self, liveData, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
PictureBookLiveThumbnailView_SetDataAsync_Orig(retstr, self, liveData, characterId, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* self, void* liveData, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
DEFINE_HOOK(void, PictureBookLiveThumbnailView_SetDataAsync, (void* self, void* liveData, Il2cppString* characterId, bool isReleased, bool isUnlocked, bool isNew, bool hasLiveSkin, void* ct, void* mtd)) {
|
||||||
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
// Log::DebugFmt("PictureBookLiveThumbnailView_SetDataAsync: isReleased: %d, isUnlocked: %d, isNew: %d, hasLiveSkin: %d", isReleased, isUnlocked, isNew, hasLiveSkin);
|
||||||
if (Config::dbgMode && Config::unlockAllLive) {
|
if (Config::dbgMode && Config::unlockAllLive) {
|
||||||
isUnlocked = true;
|
isUnlocked = true;
|
||||||
isReleased = true;
|
isReleased = true;
|
||||||
hasLiveSkin = true;
|
hasLiveSkin = true;
|
||||||
}
|
}
|
||||||
PictureBookLiveThumbnailView_SetDataAsync_Orig(self, liveData, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
PictureBookLiveThumbnailView_SetDataAsync_Orig(self, liveData, characterId, isReleased, isUnlocked, isNew, hasLiveSkin, ct, mtd);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -845,6 +986,34 @@ namespace GakumasLocal::HookMain {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* getCompletedUniTask() {
|
||||||
|
static auto unitask_klass = Il2cppUtils::GetClass("UniTask.dll", "Cysharp.Threading.Tasks", "UniTask");
|
||||||
|
static auto CompletedTask_field = unitask_klass->Get<UnityResolve::Field>("CompletedTask");
|
||||||
|
auto ret = UnityResolve::Invoke<void*>("il2cpp_object_new", unitask_klass->address);
|
||||||
|
UnityResolve::Invoke<void>("il2cpp_field_static_get_value", CompletedTask_field->address, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GKMS_WINDOWS
|
||||||
|
// 绕过切歌时的等待以及网络请求
|
||||||
|
DEFINE_HOOK(void*, Produce_ViewPictureBookLiveAsync, (void* retstr, Il2cppString* musicId, Il2cppString* characterId,
|
||||||
|
void* ct, void* callOption, void* errorHandlerIl, Il2cppString* requestIdForResponseCache, void* mtd)) {
|
||||||
|
|
||||||
|
// Log::DebugFmt("Produce_ViewPictureBookLiveAsync: %s - %s", musicId->ToString().c_str(), characterId->ToString().c_str());
|
||||||
|
if (Config::unlockAllLive) return getCompletedUniTask();
|
||||||
|
return Produce_ViewPictureBookLiveAsync_Orig(retstr, musicId, characterId, ct, callOption, errorHandlerIl, requestIdForResponseCache, mtd);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
DEFINE_HOOK(void*, Produce_ViewPictureBookLiveAsync, (void* retstr, void* musicId, void* characterId,
|
||||||
|
void* ct, void* callOption, void* errorHandlerIl, void* requestIdForResponseCache, void* mtd, void* wenhao)) {
|
||||||
|
|
||||||
|
// Log::DebugFmt("Produce_ViewPictureBookLiveAsync: %s - %s", musicId->ToString().c_str(), characterId->ToString().c_str());
|
||||||
|
if (Config::unlockAllLive) return getCompletedUniTask();
|
||||||
|
return Produce_ViewPictureBookLiveAsync_Orig(retstr, musicId, characterId, ct, callOption, errorHandlerIl, requestIdForResponseCache, mtd, wenhao);
|
||||||
|
}
|
||||||
|
#endif // GKMS_WINDOWS
|
||||||
|
|
||||||
|
|
||||||
void* PictureBookWindowPresenter_instance = nullptr;
|
void* PictureBookWindowPresenter_instance = nullptr;
|
||||||
std::string PictureBookWindowPresenter_charaId;
|
std::string PictureBookWindowPresenter_charaId;
|
||||||
DEFINE_HOOK(void*, PictureBookWindowPresenter_GetLiveMusics, (void* self, Il2cppString* charaId, void* mtd)) {
|
DEFINE_HOOK(void*, PictureBookWindowPresenter_GetLiveMusics, (void* self, Il2cppString* charaId, void* mtd)) {
|
||||||
|
|
@ -857,27 +1026,59 @@ namespace GakumasLocal::HookMain {
|
||||||
static auto PictureBookWindowPresenter_klass = Il2cppUtils::GetClass("Assembly-CSharp.dll", "Campus.OutGame",
|
static auto PictureBookWindowPresenter_klass = Il2cppUtils::GetClass("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookWindowPresenter");
|
"PictureBookWindowPresenter");
|
||||||
static auto existsMusicIds_field = PictureBookWindowPresenter_klass->Get<UnityResolve::Field>("_existsMusicIds");
|
static auto existsMusicIds_field = PictureBookWindowPresenter_klass->Get<UnityResolve::Field>("_existsMusicIds");
|
||||||
auto existsMusicIds = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::List<Il2cppString*>*>(self, existsMusicIds_field);
|
// auto existsMusicIds = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::List<Il2cppString*>*>(self, existsMusicIds_field);
|
||||||
|
auto existsMusicIds = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::Dictionary<Il2cppString*, UnityResolve::UnityType::List<Il2cppString*>>*>(self, existsMusicIds_field);
|
||||||
|
|
||||||
if (!existsMusicIds) {
|
if (!existsMusicIds) {
|
||||||
|
static auto Dict_List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
||||||
|
"System.Collections.Generic.Dictionary`2[System.String, System.Collections.Generic.List`1[System.String]]");
|
||||||
static auto List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
static auto List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
|
||||||
"System.Collections.Generic.List`1[System.String]");
|
"System.Collections.Generic.List`1[System.String]");
|
||||||
static auto List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(List_String_klass, ".ctor", 0);
|
static auto List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(List_String_klass, ".ctor", 0);
|
||||||
static auto List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(List_String_ctor_mtd->methodPointer);
|
static auto List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(List_String_ctor_mtd->methodPointer);
|
||||||
|
|
||||||
auto newList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
|
||||||
List_String_ctor(newList, List_String_ctor_mtd);
|
|
||||||
Il2cppUtils::Tools::CSListEditor<Il2cppString*> newListEditor(newList);
|
|
||||||
|
|
||||||
auto fullIds = GetIdolMusicIdAll();
|
auto fullIds = GetIdolMusicIdAll();
|
||||||
|
|
||||||
|
static auto Dict_List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(Dict_List_String_klass, ".ctor", 0);
|
||||||
|
static auto Dict_List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(Dict_List_String_ctor_mtd->methodPointer);
|
||||||
|
|
||||||
|
auto newDict = UnityResolve::Invoke<void*>("il2cpp_object_new", Dict_List_String_klass);
|
||||||
|
Dict_List_String_ctor(newDict, Dict_List_String_ctor_mtd);
|
||||||
|
Il2cppUtils::Tools::CSDictEditor<Il2cppString*, void*> newDictEditor(newDict, Dict_List_String_klass);
|
||||||
|
|
||||||
|
// auto fullIds = GetIdolMusicIdAll();
|
||||||
|
|
||||||
for (auto& i : fullIds) {
|
for (auto& i : fullIds) {
|
||||||
// Log::DebugFmt("GetLiveMusics - Add: %s", i.c_str());
|
// Log::DebugFmt("GetLiveMusics - Add: %s", i.c_str()); // eg. music-all-amao-001, music-char-hski-001
|
||||||
newListEditor.Add(Il2cppString::New(i));
|
//newListEditor.Add(Il2cppString::New(i));
|
||||||
|
auto newList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
||||||
|
List_String_ctor(newList, List_String_ctor_mtd);
|
||||||
|
newDictEditor.Add(Il2cppString::New(i), newList);
|
||||||
}
|
}
|
||||||
Il2cppUtils::ClassSetFieldValue(self, existsMusicIds_field, newList);
|
Il2cppUtils::ClassSetFieldValue(self, existsMusicIds_field, newDict);
|
||||||
|
existsMusicIds = reinterpret_cast<decltype(existsMusicIds)>(newDict);
|
||||||
// Log::DebugFmt("GetLiveMusics - set end: %d", fullIds.size());
|
// Log::DebugFmt("GetLiveMusics - set end: %d", fullIds.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Il2cppUtils::Tools::CSDictEditor<Il2cppString*, void*> dicCheckEditor(existsMusicIds, Dict_List_String_klass);
|
||||||
|
for (auto& i : fullIds) {
|
||||||
|
auto currKeyStr = Il2cppString::New(i);
|
||||||
|
void* currList;
|
||||||
|
if (dicCheckEditor.ContainsKey(currKeyStr)) {
|
||||||
|
currList = dicCheckEditor.get_Item(currKeyStr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
|
||||||
|
List_String_ctor(currList, List_String_ctor_mtd);
|
||||||
|
}
|
||||||
|
Il2cppUtils::Tools::CSListEditor<Il2cppString*> currListEditor(currList);
|
||||||
|
if (!currListEditor.Contains(charaId)) {
|
||||||
|
currListEditor.Add(charaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PictureBookWindowPresenter_GetLiveMusics_Orig(self, charaId, mtd);
|
return PictureBookWindowPresenter_GetLiveMusics_Orig(self, charaId, mtd);
|
||||||
|
|
@ -886,7 +1087,7 @@ namespace GakumasLocal::HookMain {
|
||||||
DEFINE_HOOK(void, PictureBookLiveSelectScreenModel_ctor, (void* self, void* transitionParam, UnityResolve::UnityType::List<void*>* musics, void* mtd)) {
|
DEFINE_HOOK(void, PictureBookLiveSelectScreenModel_ctor, (void* self, void* transitionParam, UnityResolve::UnityType::List<void*>* musics, void* mtd)) {
|
||||||
// Log::DebugFmt("PictureBookLiveSelectScreenModel_ctor");
|
// Log::DebugFmt("PictureBookLiveSelectScreenModel_ctor");
|
||||||
|
|
||||||
if (Config::unlockAllLive) {
|
if (Config::dbgMode && Config::unlockAllLive) {
|
||||||
static auto GetLiveMusics = Il2cppUtils::GetMethod("Assembly-CSharp.dll", "Campus.OutGame",
|
static auto GetLiveMusics = Il2cppUtils::GetMethod("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookWindowPresenter", "GetLiveMusics");
|
"PictureBookWindowPresenter", "GetLiveMusics");
|
||||||
if (PictureBookWindowPresenter_instance && !PictureBookWindowPresenter_charaId.empty()) {
|
if (PictureBookWindowPresenter_instance && !PictureBookWindowPresenter_charaId.empty()) {
|
||||||
|
|
@ -900,17 +1101,16 @@ namespace GakumasLocal::HookMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needRestoreHides = false;
|
bool needRestoreHides = false;
|
||||||
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* self, void* produceLive,
|
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* self, void* produceLive, bool isPlayCharacterFocusCamera, void* mtd)) {
|
||||||
Il2cppString* characterId, Il2cppString* idolCardId, Il2cppString* costumeId, Il2cppString* costumeHeadId, void* mtd)) {
|
|
||||||
needRestoreHides = false;
|
needRestoreHides = false;
|
||||||
Log::InfoFmt("MoveLiveScene: characterId: %s, idolCardId: %s, costumeId: %s, costumeHeadId: %s,",
|
// Log::InfoFmt("MoveLiveScene: characterId: %s, idolCardId: %s, costumeId: %s, costumeHeadId: %s,",
|
||||||
characterId->ToString().c_str(), idolCardId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
|
// characterId->ToString().c_str(), idolCardId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
characterId: hski, costumeId: hski-cstm-0002, costumeHeadId: costume_head_hski-cstm-0002,
|
characterId: hski, costumeId: hski-cstm-0002, costumeHeadId: costume_head_hski-cstm-0002,
|
||||||
characterId: shro, costumeId: shro-cstm-0006, costumeHeadId: costume_head_shro-cstm-0006,
|
characterId: shro, costumeId: shro-cstm-0006, costumeHeadId: costume_head_shro-cstm-0006,
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
if (Config::dbgMode && Config::enableLiveCustomeDress) {
|
if (Config::dbgMode && Config::enableLiveCustomeDress) {
|
||||||
// 修改 LiveFixedData_GetCharacter 可以更改 Loading 角色和演唱者名字,而不变更实际登台人
|
// 修改 LiveFixedData_GetCharacter 可以更改 Loading 角色和演唱者名字,而不变更实际登台人
|
||||||
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId,
|
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId,
|
||||||
|
|
@ -918,8 +1118,9 @@ namespace GakumasLocal::HookMain {
|
||||||
Config::liveCustomeHeadId.empty() ? costumeHeadId : Il2cppString::New(Config::liveCustomeHeadId),
|
Config::liveCustomeHeadId.empty() ? costumeHeadId : Il2cppString::New(Config::liveCustomeHeadId),
|
||||||
mtd);
|
mtd);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId, costumeId, costumeHeadId, mtd);
|
// return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId, costumeId, costumeHeadId, mtd);
|
||||||
|
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, isPlayCharacterFocusCamera, mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::string lastMusicId;
|
// std::string lastMusicId;
|
||||||
|
|
@ -1240,6 +1441,70 @@ namespace GakumasLocal::HookMain {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef GKMS_WINDOWS
|
||||||
|
// DMM Only
|
||||||
|
DEFINE_HOOK(void*, WindowHandle_SetWindowLong, (int32_t nIndex, intptr_t dwNewLong, void* mtd)) {
|
||||||
|
if (GakumasLocal::Config::dmmUnlockSize) {
|
||||||
|
// Log::DebugFmt("WindowHandle_SetWindowLong: %d, %p\n", nIndex, dwNewLong);
|
||||||
|
|
||||||
|
if (nIndex == GWLP_WNDPROC) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WindowHandle_SetWindowLong_Orig(nIndex, dwNewLong, mtd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DMM Only
|
||||||
|
void SetResolution(int width, int height, bool fullscreen) {
|
||||||
|
static auto Screen_SetResolution = reinterpret_cast<void (*)(UINT, UINT, UINT, void*)>(
|
||||||
|
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::SetResolution_Injected(System.Int32,System.Int32,UnityEngine.FullScreenMode,UnityEngine.RefreshRate&)"));
|
||||||
|
|
||||||
|
int64_t v8[3];
|
||||||
|
v8[0] = 0x100000000LL;
|
||||||
|
Screen_SetResolution(width, height, 2 * !fullscreen + 1, v8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DMM Only
|
||||||
|
DEFINE_HOOK(void, WindowManager_ApplyOrientationSettings, (int orientation, void* method)) {
|
||||||
|
if (!GakumasLocal::Config::dmmUnlockSize) return WindowManager_ApplyOrientationSettings_Orig(orientation, method);
|
||||||
|
|
||||||
|
static auto get_Height = reinterpret_cast<int (*)()>(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_height()"));
|
||||||
|
static auto get_Width = reinterpret_cast<int (*)()>(Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Screen::get_width()"));
|
||||||
|
|
||||||
|
static auto lastWidth = -1;
|
||||||
|
static auto lastHeight = -1;
|
||||||
|
|
||||||
|
const auto currWidth = get_Width();
|
||||||
|
const auto currHeight = get_Height();
|
||||||
|
|
||||||
|
if (lastWidth == -1) {
|
||||||
|
lastWidth = currWidth;
|
||||||
|
lastHeight = currHeight;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool lastIsPortrait = lastWidth < lastHeight;
|
||||||
|
const bool currIsPortrait = currWidth < currHeight;
|
||||||
|
if (lastIsPortrait == currIsPortrait) {
|
||||||
|
lastWidth = currWidth;
|
||||||
|
lastHeight = currHeight;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetResolution(lastWidth, lastHeight, false);
|
||||||
|
lastWidth = currWidth;
|
||||||
|
lastHeight = currHeight;
|
||||||
|
|
||||||
|
Log::DebugFmt("WindowManager_ApplyOrientationSettings: %d (%d, %d)\n", orientation, get_Width(), get_Height());
|
||||||
|
}
|
||||||
|
|
||||||
|
// DMM Only
|
||||||
|
DEFINE_HOOK(void, AspectRatioHandler_NudgeWindow, (void* method)) {
|
||||||
|
if (!GakumasLocal::Config::dmmUnlockSize) return AspectRatioHandler_NudgeWindow_Orig(method);
|
||||||
|
// printf("AspectRatioHandler_NudgeWindow\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void UpdateSwingBreastBonesData(void* initializeData) {
|
void UpdateSwingBreastBonesData(void* initializeData) {
|
||||||
if (!Config::enableBreastParam) return;
|
if (!Config::enableBreastParam) return;
|
||||||
|
|
@ -1441,12 +1706,35 @@ namespace GakumasLocal::HookMain {
|
||||||
ADD_HOOK(TextMeshProUGUI_Awake, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
ADD_HOOK(TextMeshProUGUI_Awake, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
"TextMeshProUGUI", "Awake"));
|
"TextMeshProUGUI", "Awake"));
|
||||||
|
|
||||||
|
ADD_HOOK(TMP_Text_set_text, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
|
"TMP_Text", "set_text"));
|
||||||
|
ADD_HOOK(TMP_Text_SetText_1, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
|
"TMP_Text", "SetText",
|
||||||
|
{"System.String"}));
|
||||||
ADD_HOOK(TMP_Text_PopulateTextBackingArray, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
ADD_HOOK(TMP_Text_PopulateTextBackingArray, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
"TMP_Text", "PopulateTextBackingArray",
|
"TMP_Text", "PopulateTextBackingArray",
|
||||||
{"System.String", "System.Int32", "System.Int32"}));
|
{"System.String", "System.Int32", "System.Int32"}));
|
||||||
|
ADD_HOOK(TMP_Text_SetText_2, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
|
"TMP_Text", "SetText",
|
||||||
|
{ "System.String", "System.Boolean" }));
|
||||||
|
|
||||||
ADD_HOOK(TextField_set_value, Il2cppUtils::GetMethodPointer("UnityEngine.UIElementsModule.dll", "UnityEngine.UIElements",
|
ADD_HOOK(TextField_set_value, Il2cppUtils::GetMethodPointer("UnityEngine.UIElementsModule.dll", "UnityEngine.UIElements",
|
||||||
"TextField", "set_value"));
|
"TextField", "set_value"));
|
||||||
|
|
||||||
|
// Legacy UnityEngine.UI.Text hook
|
||||||
|
{
|
||||||
|
auto uiTextPtr = Il2cppUtils::GetMethodPointer("UnityEngine.UI.dll", "UnityEngine.UI",
|
||||||
|
"Text", "set_text");
|
||||||
|
if (uiTextPtr) {
|
||||||
|
ADD_HOOK(UIText_set_text, uiTextPtr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log::InfoFmt("UIText_set_text: method not found, legacy UI.Text hook skipped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_HOOK(TMP_Text_SetCharArray, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro",
|
||||||
|
"TMP_Text", "SetCharArray", {"System.Char[]", "System.Int32", "System.Int32"}));
|
||||||
/* SQL 查询相关函数,不好用
|
/* SQL 查询相关函数,不好用
|
||||||
// 下面是 byte[] u8 string 转 std::string 的例子
|
// 下面是 byte[] u8 string 转 std::string 的例子
|
||||||
auto query = reinterpret_cast<UnityResolve::UnityType::Array<UnityResolve::UnityType::Byte>*>(mtd);
|
auto query = reinterpret_cast<UnityResolve::UnityType::Array<UnityResolve::UnityType::Byte>*>(mtd);
|
||||||
|
|
@ -1537,6 +1825,13 @@ namespace GakumasLocal::HookMain {
|
||||||
ADD_HOOK(PictureBookWindowPresenter_GetLiveMusics,
|
ADD_HOOK(PictureBookWindowPresenter_GetLiveMusics,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookWindowPresenter", "GetLiveMusics"));
|
"PictureBookWindowPresenter", "GetLiveMusics"));
|
||||||
|
|
||||||
|
#ifdef GKMS_WINDOWS
|
||||||
|
// 跳过切歌Loading,安卓端会崩溃
|
||||||
|
ADD_HOOK(Produce_ViewPictureBookLiveAsync,
|
||||||
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "",
|
||||||
|
"Produce", "ViewPictureBookLiveAsync"));
|
||||||
|
#endif
|
||||||
ADD_HOOK(PictureBookLiveSelectScreenModel_ctor,
|
ADD_HOOK(PictureBookLiveSelectScreenModel_ctor,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
"PictureBookLiveSelectScreenModel", ".ctor"));
|
"PictureBookLiveSelectScreenModel", ".ctor"));
|
||||||
|
|
@ -1634,6 +1929,32 @@ namespace GakumasLocal::HookMain {
|
||||||
"RenderPipeline", "EndCameraRendering"));
|
"RenderPipeline", "EndCameraRendering"));
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
#ifdef GKMS_WINDOWS
|
||||||
|
ADD_HOOK(WindowHandle_SetWindowLong, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
"WindowHandle", "SetWindowLong"));
|
||||||
|
//ADD_HOOK(WindowHandle_SetWindowLong32, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
// "WindowHandle", "SetWindowLong32"));
|
||||||
|
//ADD_HOOK(WindowHandle_SetWindowLongPtr64, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
// "WindowHandle", "SetWindowLongPtr64"));
|
||||||
|
//ADD_HOOK(WindowSizeUtility_RestoreWindowSize, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
// "WindowSizeUtility", "RestoreWindowSize"));
|
||||||
|
ADD_HOOK(WindowManager_ApplyOrientationSettings, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
"WindowManager", "ApplyOrientationSettings"));
|
||||||
|
ADD_HOOK(AspectRatioHandler_NudgeWindow, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
"AspectRatioHandler", "NudgeWindow"));
|
||||||
|
//ADD_HOOK(AspectRatioHandler_WindowProc, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||||
|
// "AspectRatioHandler", "WindowProc"));
|
||||||
|
|
||||||
|
if (GakumasLocal::Config::dmmUnlockSize) {
|
||||||
|
std::thread([]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
auto hWnd = FindWindowW(L"UnityWndClass", L"gakumas");
|
||||||
|
// 添加可调整大小的边框和最大化按钮
|
||||||
|
LONG style = GetWindowLong(hWnd, GWL_STYLE);
|
||||||
|
style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
||||||
|
SetWindowLong(hWnd, GWL_STYLE, style);
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
g_extra_assetbundle_paths.push_back((gakumasLocalPath / "local-files/gakumasassets").string());
|
g_extra_assetbundle_paths.push_back((gakumasLocalPath / "local-files/gakumasassets").string());
|
||||||
LoadExtraAssetBundle();
|
LoadExtraAssetBundle();
|
||||||
GkmsResourceUpdate::CheckUpdateFromAPI(false);
|
GkmsResourceUpdate::CheckUpdateFromAPI(false);
|
||||||
|
|
|
||||||
|
|
@ -337,17 +337,23 @@ namespace Il2cppUtils {
|
||||||
lst_get_Item_method = il2cpp_class_get_method_from_name(list_klass, "get_Item", 1);
|
lst_get_Item_method = il2cpp_class_get_method_from_name(list_klass, "get_Item", 1);
|
||||||
lst_set_Item_method = il2cpp_class_get_method_from_name(list_klass, "set_Item", 2);
|
lst_set_Item_method = il2cpp_class_get_method_from_name(list_klass, "set_Item", 2);
|
||||||
lst_Add_method = il2cpp_class_get_method_from_name(list_klass, "Add", 1);
|
lst_Add_method = il2cpp_class_get_method_from_name(list_klass, "Add", 1);
|
||||||
|
lst_Contains_method = il2cpp_class_get_method_from_name(list_klass, "Contains", 1);
|
||||||
|
|
||||||
lst_get_Count = reinterpret_cast<lst_get_Count_t>(lst_get_Count_method->methodPointer);
|
lst_get_Count = reinterpret_cast<lst_get_Count_t>(lst_get_Count_method->methodPointer);
|
||||||
lst_get_Item = reinterpret_cast<lst_get_Item_t>(lst_get_Item_method->methodPointer);
|
lst_get_Item = reinterpret_cast<lst_get_Item_t>(lst_get_Item_method->methodPointer);
|
||||||
lst_set_Item = reinterpret_cast<lst_set_Item_t>(lst_set_Item_method->methodPointer);
|
lst_set_Item = reinterpret_cast<lst_set_Item_t>(lst_set_Item_method->methodPointer);
|
||||||
lst_Add = reinterpret_cast<lst_Add_t>(lst_Add_method->methodPointer);
|
lst_Add = reinterpret_cast<lst_Add_t>(lst_Add_method->methodPointer);
|
||||||
|
lst_Contains = reinterpret_cast<lst_Contains_t>(lst_Contains_method->methodPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Add(T value) {
|
void Add(T value) {
|
||||||
lst_Add(lst, value, lst_Add_method);
|
lst_Add(lst, value, lst_Add_method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Contains(T value) {
|
||||||
|
return lst_Contains(lst, value, lst_Contains_method);
|
||||||
|
}
|
||||||
|
|
||||||
T get_Item(int index) {
|
T get_Item(int index) {
|
||||||
return lst_get_Item(lst, index, lst_get_Item_method);
|
return lst_get_Item(lst, index, lst_get_Item_method);
|
||||||
}
|
}
|
||||||
|
|
@ -401,16 +407,86 @@ namespace Il2cppUtils {
|
||||||
typedef void(*lst_Add_t)(void*, T, void* mtd);
|
typedef void(*lst_Add_t)(void*, T, void* mtd);
|
||||||
typedef void(*lst_set_Item_t)(void*, int, T, void* mtd);
|
typedef void(*lst_set_Item_t)(void*, int, T, void* mtd);
|
||||||
typedef int(*lst_get_Count_t)(void*, void* mtd);
|
typedef int(*lst_get_Count_t)(void*, void* mtd);
|
||||||
|
typedef bool(*lst_Contains_t)(void*, T, void* mtd);
|
||||||
|
|
||||||
MethodInfo* lst_get_Item_method;
|
MethodInfo* lst_get_Item_method;
|
||||||
MethodInfo* lst_Add_method;
|
MethodInfo* lst_Add_method;
|
||||||
MethodInfo* lst_get_Count_method;
|
MethodInfo* lst_get_Count_method;
|
||||||
MethodInfo* lst_set_Item_method;
|
MethodInfo* lst_set_Item_method;
|
||||||
|
MethodInfo* lst_Contains_method;
|
||||||
|
|
||||||
lst_get_Item_t lst_get_Item;
|
lst_get_Item_t lst_get_Item;
|
||||||
lst_set_Item_t lst_set_Item;
|
lst_set_Item_t lst_set_Item;
|
||||||
lst_Add_t lst_Add;
|
lst_Add_t lst_Add;
|
||||||
lst_get_Count_t lst_get_Count;
|
lst_get_Count_t lst_get_Count;
|
||||||
|
lst_Contains_t lst_Contains;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename KT = void*, typename VT = void*>
|
||||||
|
class CSDictEditor {
|
||||||
|
public:
|
||||||
|
// @param dict: Dictionary instance.
|
||||||
|
// @param dictTypeStr: Reflection type. eg: "System.Collections.Generic.Dictionary`2[System.Int32, System.Int32]"
|
||||||
|
CSDictEditor(void* dict, const char* dictTypeStr) {
|
||||||
|
dic_klass = Il2cppUtils::get_system_class_from_reflection_type_str(dictTypeStr);
|
||||||
|
initDict(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSDictEditor(void* dict) {
|
||||||
|
dic_klass = get_class_from_instance(dict);
|
||||||
|
initDict(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSDictEditor(void* dict, void* dicClass) {
|
||||||
|
dic_klass = dicClass;
|
||||||
|
initDict(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(KT key, VT value) {
|
||||||
|
dic_Add(dict, key, value, Add_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContainsKey(KT key) {
|
||||||
|
return dic_containsKey(dict, key, ContainsKey_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT get_Item(KT key) {
|
||||||
|
return dic_get_Item(dict, key, get_Item_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT operator[] (KT key) {
|
||||||
|
return get_Item(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* dict;
|
||||||
|
void* dic_klass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initDict(void* dict) {
|
||||||
|
// dic_klass = dicClass;
|
||||||
|
this->dict = dict;
|
||||||
|
|
||||||
|
get_Item_method = il2cpp_class_get_method_from_name(dic_klass, "get_Item", 1);
|
||||||
|
Add_method = il2cpp_class_get_method_from_name(dic_klass, "Add", 2);
|
||||||
|
ContainsKey_method = il2cpp_class_get_method_from_name(dic_klass, "ContainsKey", 1);
|
||||||
|
|
||||||
|
dic_get_Item = (dic_get_Item_t)get_Item_method->methodPointer;
|
||||||
|
dic_Add = (dic_Add_t)Add_method->methodPointer;
|
||||||
|
dic_containsKey = (dic_containsKey_t)ContainsKey_method->methodPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef VT(*dic_get_Item_t)(void*, KT, void* mtd);
|
||||||
|
typedef VT(*dic_Add_t)(void*, KT, VT, void* mtd);
|
||||||
|
typedef VT(*dic_containsKey_t)(void*, KT, void* mtd);
|
||||||
|
|
||||||
|
CSDictEditor();
|
||||||
|
MethodInfo* get_Item_method;
|
||||||
|
MethodInfo* Add_method;
|
||||||
|
MethodInfo* ContainsKey_method;
|
||||||
|
dic_get_Item_t dic_get_Item;
|
||||||
|
dic_Add_t dic_Add;
|
||||||
|
dic_containsKey_t dic_containsKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
#include "BaseDefine.h"
|
#include "BaseDefine.h"
|
||||||
#include "string_parser/StringParser.hpp"
|
#include "string_parser/StringParser.hpp"
|
||||||
|
|
||||||
|
// #include "cpprest/details/http_helpers.h"
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal::Local {
|
namespace GakumasLocal::Local {
|
||||||
std::unordered_map<std::string, std::string> i18nData{};
|
std::unordered_map<std::string, std::string> i18nData{};
|
||||||
|
|
@ -37,6 +39,12 @@ namespace GakumasLocal::Local {
|
||||||
return Plugin::GetInstance().GetHookInstaller()->localizationFilesDir;
|
return Plugin::GetInstance().GetHookInstaller()->localizationFilesDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAllSpace(const std::string& str) {
|
||||||
|
return std::all_of(str.begin(), str.end(), [](unsigned char c) {
|
||||||
|
return std::isspace(c);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::string trim(const std::string& str) {
|
std::string trim(const std::string& str) {
|
||||||
auto is_not_space = [](char ch) { return !std::isspace(ch); };
|
auto is_not_space = [](char ch) { return !std::isspace(ch); };
|
||||||
auto start = std::ranges::find_if(str, is_not_space);
|
auto start = std::ranges::find_if(str, is_not_space);
|
||||||
|
|
@ -249,7 +257,7 @@ namespace GakumasLocal::Local {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetSplitTagsTranslation(const std::string& origText, std::string* newText, std::vector<std::string>& unTransResultRet) {
|
bool GetSplitTagsTranslation(const std::string& origText, std::string* newText, std::vector<std::string>& unTransResultRet) {
|
||||||
if (!origText.contains(L'<')) return false;
|
if (!origText.contains('<')) return false;
|
||||||
const auto splitResult = SplitByTags(origText);
|
const auto splitResult = SplitByTags(origText);
|
||||||
if (splitResult.empty()) return false;
|
if (splitResult.empty()) return false;
|
||||||
|
|
||||||
|
|
@ -289,10 +297,18 @@ namespace GakumasLocal::Local {
|
||||||
|
|
||||||
std::u16string currentWaitingReplaceText;
|
std::u16string currentWaitingReplaceText;
|
||||||
|
|
||||||
|
#ifdef GKMS_WINDOWS
|
||||||
|
#define checkCurrentWaitingReplaceTextAndClear() \
|
||||||
|
if (!currentWaitingReplaceText.empty()) { \
|
||||||
|
auto trimmed = trim(Misc::ToUTF8(currentWaitingReplaceText)); \
|
||||||
|
waitingReplaceTexts.push_back(trimmed); \
|
||||||
|
currentWaitingReplaceText.clear(); }
|
||||||
|
#else
|
||||||
#define checkCurrentWaitingReplaceTextAndClear() \
|
#define checkCurrentWaitingReplaceTextAndClear() \
|
||||||
if (!currentWaitingReplaceText.empty()) { \
|
if (!currentWaitingReplaceText.empty()) { \
|
||||||
waitingReplaceTexts.push_back(Misc::ToUTF8(currentWaitingReplaceText)); \
|
waitingReplaceTexts.push_back(Misc::ToUTF8(currentWaitingReplaceText)); \
|
||||||
currentWaitingReplaceText.clear(); }
|
currentWaitingReplaceText.clear(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
for (char16_t currChar : origText) {
|
for (char16_t currChar : origText) {
|
||||||
if (currChar == u'<') {
|
if (currChar == u'<') {
|
||||||
|
|
@ -333,6 +349,7 @@ namespace GakumasLocal::Local {
|
||||||
bool hasNotTrans = false;
|
bool hasNotTrans = false;
|
||||||
if (!waitingReplaceTexts.empty()) {
|
if (!waitingReplaceTexts.empty()) {
|
||||||
for (const auto& i : waitingReplaceTexts) {
|
for (const auto& i : waitingReplaceTexts) {
|
||||||
|
if (isAllSpace(i)) continue;
|
||||||
std::string searchResult = findInMapIgnoreSpace(i, genericSplitText);
|
std::string searchResult = findInMapIgnoreSpace(i, genericSplitText);
|
||||||
if (!searchResult.empty()) {
|
if (!searchResult.empty()) {
|
||||||
ReplaceNumberComma(&searchResult);
|
ReplaceNumberComma(&searchResult);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
#include "Misc.hpp"
|
#include "Misc.hpp"
|
||||||
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
|
||||||
#include "fmt/core.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
#ifndef GKMS_WINDOWS
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#if defined(__ANDROID__) && __ANDROID_API__ >= 31
|
||||||
|
#include <unicode/ustring.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
extern JavaVM* g_javaVM;
|
extern JavaVM* g_javaVM;
|
||||||
#else
|
#else
|
||||||
|
|
@ -14,20 +15,155 @@
|
||||||
|
|
||||||
|
|
||||||
namespace GakumasLocal::Misc {
|
namespace GakumasLocal::Misc {
|
||||||
std::u16string ToUTF16(const std::string_view& str) {
|
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
|
|
||||||
return utf16conv.from_bytes(str.data(), str.data() + str.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ToUTF8(const std::u16string_view& str) {
|
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
|
|
||||||
return utf16conv.to_bytes(str.data(), str.data() + str.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GKMS_WINDOWS
|
#ifdef GKMS_WINDOWS
|
||||||
std::string ToUTF8(const std::wstring_view& str) {
|
std::string ToUTF8(const std::wstring_view& str) {
|
||||||
return utility::conversions::to_utf8string(str.data());
|
return utility::conversions::to_utf8string(str.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::u16string ToUTF16(const std::string_view& str) {
|
||||||
|
std::string input(str);
|
||||||
|
std::wstring wstr = utility::conversions::utf8_to_utf16(input);
|
||||||
|
return std::u16string(wstr.begin(), wstr.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToUTF8(const std::u16string_view& str) {
|
||||||
|
std::u16string u16(str);
|
||||||
|
std::wstring wstr(u16.begin(), u16.end());
|
||||||
|
return utility::conversions::utf16_to_utf8(wstr);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
namespace {
|
||||||
|
jclass GetStringClass(JNIEnv* env) {
|
||||||
|
static jclass stringClass = nullptr;
|
||||||
|
if (stringClass) return stringClass;
|
||||||
|
|
||||||
|
jclass localClass = env->FindClass("java/lang/String");
|
||||||
|
if (!localClass) return nullptr;
|
||||||
|
stringClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
|
||||||
|
env->DeleteLocalRef(localClass);
|
||||||
|
return stringClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring GetUtf8CharsetName(JNIEnv* env) {
|
||||||
|
static jstring utf8Charset = nullptr;
|
||||||
|
if (utf8Charset) return utf8Charset;
|
||||||
|
|
||||||
|
jstring localUtf8 = env->NewStringUTF("UTF-8");
|
||||||
|
if (!localUtf8) return nullptr;
|
||||||
|
utf8Charset = reinterpret_cast<jstring>(env->NewGlobalRef(localUtf8));
|
||||||
|
env->DeleteLocalRef(localUtf8);
|
||||||
|
return utf8Charset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u16string ToUTF16(const std::string_view& str) {
|
||||||
|
#if defined(__ANDROID__) && __ANDROID_API__ >= 31
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
int32_t outLen = 0;
|
||||||
|
u_strFromUTF8(nullptr, 0, &outLen, str.data(), static_cast<int32_t>(str.size()), &status);
|
||||||
|
if (status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(status)) return {};
|
||||||
|
|
||||||
|
status = U_ZERO_ERROR;
|
||||||
|
std::u16string out(outLen, u'\0');
|
||||||
|
u_strFromUTF8(
|
||||||
|
reinterpret_cast<UChar*>(out.data()),
|
||||||
|
outLen,
|
||||||
|
&outLen,
|
||||||
|
str.data(),
|
||||||
|
static_cast<int32_t>(str.size()),
|
||||||
|
&status);
|
||||||
|
if (U_FAILURE(status)) return {};
|
||||||
|
out.resize(outLen);
|
||||||
|
return out;
|
||||||
|
#else
|
||||||
|
JNIEnv* env = GetJNIEnv();
|
||||||
|
if (!env) return {};
|
||||||
|
|
||||||
|
jclass stringClass = GetStringClass(env);
|
||||||
|
jstring utf8Charset = GetUtf8CharsetName(env);
|
||||||
|
if (!stringClass || !utf8Charset) return {};
|
||||||
|
|
||||||
|
jmethodID ctor = env->GetMethodID(stringClass, "<init>", "([BLjava/lang/String;)V");
|
||||||
|
if (!ctor) return {};
|
||||||
|
|
||||||
|
jbyteArray bytes = env->NewByteArray(static_cast<jsize>(str.size()));
|
||||||
|
if (!bytes) return {};
|
||||||
|
env->SetByteArrayRegion(bytes, 0, static_cast<jsize>(str.size()), reinterpret_cast<const jbyte*>(str.data()));
|
||||||
|
|
||||||
|
jstring jstr = reinterpret_cast<jstring>(env->NewObject(stringClass, ctor, bytes, utf8Charset));
|
||||||
|
env->DeleteLocalRef(bytes);
|
||||||
|
if (!jstr || env->ExceptionCheck()) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
if (jstr) env->DeleteLocalRef(jstr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize len = env->GetStringLength(jstr);
|
||||||
|
const jchar* chars = env->GetStringChars(jstr, nullptr);
|
||||||
|
if (!chars) {
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u16string out(reinterpret_cast<const char16_t*>(chars), reinterpret_cast<const char16_t*>(chars) + len);
|
||||||
|
env->ReleaseStringChars(jstr, chars);
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
return out;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToUTF8(const std::u16string_view& str) {
|
||||||
|
#if defined(__ANDROID__) && __ANDROID_API__ >= 31
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
int32_t outLen = 0;
|
||||||
|
u_strToUTF8(nullptr, 0, &outLen, reinterpret_cast<const UChar*>(str.data()), static_cast<int32_t>(str.size()), &status);
|
||||||
|
if (status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(status)) return {};
|
||||||
|
|
||||||
|
status = U_ZERO_ERROR;
|
||||||
|
std::string out(outLen, '\0');
|
||||||
|
u_strToUTF8(
|
||||||
|
out.data(),
|
||||||
|
outLen,
|
||||||
|
&outLen,
|
||||||
|
reinterpret_cast<const UChar*>(str.data()),
|
||||||
|
static_cast<int32_t>(str.size()),
|
||||||
|
&status);
|
||||||
|
if (U_FAILURE(status)) return {};
|
||||||
|
out.resize(outLen);
|
||||||
|
return out;
|
||||||
|
#else
|
||||||
|
JNIEnv* env = GetJNIEnv();
|
||||||
|
if (!env) return {};
|
||||||
|
|
||||||
|
jclass stringClass = GetStringClass(env);
|
||||||
|
jstring utf8Charset = GetUtf8CharsetName(env);
|
||||||
|
if (!stringClass || !utf8Charset) return {};
|
||||||
|
|
||||||
|
jstring jstr = env->NewString(reinterpret_cast<const jchar*>(str.data()), static_cast<jsize>(str.size()));
|
||||||
|
if (!jstr) return {};
|
||||||
|
|
||||||
|
jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
|
||||||
|
if (!getBytes) {
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray bytes = reinterpret_cast<jbyteArray>(env->CallObjectMethod(jstr, getBytes, utf8Charset));
|
||||||
|
env->DeleteLocalRef(jstr);
|
||||||
|
if (!bytes || env->ExceptionCheck()) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
if (bytes) env->DeleteLocalRef(bytes);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize len = env->GetArrayLength(bytes);
|
||||||
|
std::string out(static_cast<size_t>(len), '\0');
|
||||||
|
env->GetByteArrayRegion(bytes, 0, len, reinterpret_cast<jbyte*>(out.data()));
|
||||||
|
env->DeleteLocalRef(bytes);
|
||||||
|
return out;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GKMS_WINDOWS
|
#ifndef GKMS_WINDOWS
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ namespace GakumasLocal::Config {
|
||||||
float bLimitZx = 1.0f;
|
float bLimitZx = 1.0f;
|
||||||
float bLimitZy = 1.0f;
|
float bLimitZy = 1.0f;
|
||||||
|
|
||||||
|
bool dmmUnlockSize = false;
|
||||||
|
|
||||||
void LoadConfig(const std::string& configStr) {
|
void LoadConfig(const std::string& configStr) {
|
||||||
try {
|
try {
|
||||||
const auto config = nlohmann::json::parse(configStr);
|
const auto config = nlohmann::json::parse(configStr);
|
||||||
|
|
@ -102,6 +104,7 @@ namespace GakumasLocal::Config {
|
||||||
GetConfigItem(bLimitYy);
|
GetConfigItem(bLimitYy);
|
||||||
GetConfigItem(bLimitZx);
|
GetConfigItem(bLimitZx);
|
||||||
GetConfigItem(bLimitZy);
|
GetConfigItem(bLimitZy);
|
||||||
|
GetConfigItem(dmmUnlockSize);
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
catch (std::exception& e) {
|
||||||
Log::ErrorFmt("LoadConfig error: %s", e.what());
|
Log::ErrorFmt("LoadConfig error: %s", e.what());
|
||||||
|
|
@ -157,6 +160,7 @@ namespace GakumasLocal::Config {
|
||||||
SetConfigItem(bLimitYy);
|
SetConfigItem(bLimitYy);
|
||||||
SetConfigItem(bLimitZx);
|
SetConfigItem(bLimitZx);
|
||||||
SetConfigItem(bLimitZy);
|
SetConfigItem(bLimitZy);
|
||||||
|
SetConfigItem(dmmUnlockSize);
|
||||||
|
|
||||||
std::ofstream out(configPath);
|
std::ofstream out(configPath);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ namespace GakumasLocal::Config {
|
||||||
extern float bLimitZx;
|
extern float bLimitZx;
|
||||||
extern float bLimitZy;
|
extern float bLimitZy;
|
||||||
|
|
||||||
|
extern bool dmmUnlockSize;
|
||||||
|
|
||||||
void LoadConfig(const std::string& configStr);
|
void LoadConfig(const std::string& configStr);
|
||||||
void SaveConfig(const std::string& configPath);
|
void SaveConfig(const std::string& configPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -565,10 +565,10 @@ public:
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
std::cout << funcName << " Invoke Error\n";
|
std::cout << funcName << " Invoke Error\n";
|
||||||
Return();
|
return Return();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Return();
|
return Return();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static std::vector<Assembly*> assembly;
|
inline static std::vector<Assembly*> assembly;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package io.github.chinosk.gakumas.localify
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.AndroidAppHelper
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
|
@ -17,34 +17,32 @@ import android.view.MotionEvent
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.bytedance.shadowhook.ShadowHook
|
import com.bytedance.shadowhook.ShadowHook
|
||||||
import com.bytedance.shadowhook.ShadowHook.ConfigBuilder
|
import com.bytedance.shadowhook.ShadowHook.ConfigBuilder
|
||||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
import io.github.chinosk.gakumas.localify.hookUtils.FileHotUpdater
|
||||||
import de.robv.android.xposed.IXposedHookZygoteInit
|
|
||||||
import de.robv.android.xposed.XC_MethodHook
|
|
||||||
import de.robv.android.xposed.XposedBridge
|
|
||||||
import de.robv.android.xposed.XposedHelpers
|
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
|
||||||
import io.github.chinosk.gakumas.localify.hookUtils.FilesChecker
|
import io.github.chinosk.gakumas.localify.hookUtils.FilesChecker
|
||||||
|
import io.github.chinosk.gakumas.localify.hookUtils.FilesChecker.localizationFilesDir
|
||||||
|
import io.github.chinosk.gakumas.localify.mainUtils.json
|
||||||
import io.github.chinosk.gakumas.localify.models.GakumasConfig
|
import io.github.chinosk.gakumas.localify.models.GakumasConfig
|
||||||
|
import io.github.chinosk.gakumas.localify.models.NativeInitProgress
|
||||||
|
import io.github.chinosk.gakumas.localify.models.ProgramConfig
|
||||||
|
import io.github.chinosk.gakumas.localify.ui.game_attach.InitProgressUI
|
||||||
|
import io.github.libxposed.api.XposedInterface
|
||||||
|
import io.github.libxposed.api.XposedModule
|
||||||
|
import io.github.libxposed.api.XposedModuleInterface
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.reflect.Method
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
import io.github.chinosk.gakumas.localify.hookUtils.FileHotUpdater
|
|
||||||
import io.github.chinosk.gakumas.localify.hookUtils.FilesChecker.localizationFilesDir
|
|
||||||
import io.github.chinosk.gakumas.localify.mainUtils.json
|
|
||||||
import io.github.chinosk.gakumas.localify.models.NativeInitProgress
|
|
||||||
import io.github.chinosk.gakumas.localify.models.ProgramConfig
|
|
||||||
import io.github.chinosk.gakumas.localify.ui.game_attach.InitProgressUI
|
|
||||||
|
|
||||||
val TAG = "GakumasLocalify"
|
val TAG = "GakumasLocalify"
|
||||||
|
|
||||||
class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
class GakumasHookMain : XposedModule() {
|
||||||
private lateinit var modulePath: String
|
private var modulePath: String = ""
|
||||||
private var nativeLibLoadSuccess: Boolean
|
private var nativeLibLoadSuccess: Boolean = false
|
||||||
private var alreadyInitialized = false
|
private var alreadyInitialized = false
|
||||||
private val targetPackageName = "com.bandainamcoent.idolmaster_gakuen"
|
private val targetPackageName = "com.bandainamcoent.idolmaster_gakuen"
|
||||||
private val nativeLibName = "MarryKotone"
|
private val nativeLibName = "MarryKotone"
|
||||||
|
|
@ -55,160 +53,197 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
private var externalFilesChecked: Boolean = false
|
private var externalFilesChecked: Boolean = false
|
||||||
private var gameActivity: Activity? = null
|
private var gameActivity: Activity? = null
|
||||||
|
|
||||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
override fun onModuleLoaded(param: XposedModuleInterface.ModuleLoadedParam) {
|
||||||
// if (lpparam.packageName == "io.github.chinosk.gakumas.localify") {
|
modulePath = getModuleApplicationInfo().sourceDir
|
||||||
// XposedHelpers.findAndHookMethod(
|
|
||||||
// "io.github.chinosk.gakumas.localify.MainActivity",
|
|
||||||
// lpparam.classLoader,
|
|
||||||
// "showToast",
|
|
||||||
// String::class.java,
|
|
||||||
// object : XC_MethodHook() {
|
|
||||||
// override fun beforeHookedMethod(param: MethodHookParam) {
|
|
||||||
// Log.d(TAG, "beforeHookedMethod hooked: ${param.args}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (lpparam.packageName != targetPackageName) {
|
ShadowHook.init(
|
||||||
|
ConfigBuilder()
|
||||||
|
.setMode(ShadowHook.Mode.UNIQUE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
nativeLibLoadSuccess = try {
|
||||||
|
System.loadLibrary(nativeLibName)
|
||||||
|
true
|
||||||
|
} catch (_: UnsatisfiedLinkError) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPackageReady(param: XposedModuleInterface.PackageReadyParam) {
|
||||||
|
if (param.packageName != targetPackageName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
XposedHelpers.findAndHookMethod(
|
val classLoader = param.classLoader
|
||||||
"android.app.Activity",
|
|
||||||
lpparam.classLoader,
|
hookMethod(
|
||||||
"dispatchKeyEvent",
|
classLoader = classLoader,
|
||||||
KeyEvent::class.java,
|
className = "android.app.Activity",
|
||||||
object : XC_MethodHook() {
|
methodName = "dispatchKeyEvent",
|
||||||
override fun beforeHookedMethod(param: MethodHookParam) {
|
parameterTypes = arrayOf(KeyEvent::class.java),
|
||||||
val keyEvent = param.args[0] as KeyEvent
|
before = { chain ->
|
||||||
val keyCode = keyEvent.keyCode
|
val keyEvent = chain.getArg(0) as KeyEvent
|
||||||
val action = keyEvent.action
|
keyboardEvent(keyEvent.keyCode, keyEvent.action)
|
||||||
// Log.d(TAG, "Key event: keyCode=$keyCode, action=$action")
|
|
||||||
keyboardEvent(keyCode, action)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
XposedHelpers.findAndHookMethod(
|
hookMethod(
|
||||||
"android.app.Activity",
|
classLoader = classLoader,
|
||||||
lpparam.classLoader,
|
className = "android.app.Activity",
|
||||||
"dispatchGenericMotionEvent",
|
methodName = "dispatchGenericMotionEvent",
|
||||||
MotionEvent::class.java,
|
parameterTypes = arrayOf(MotionEvent::class.java),
|
||||||
object : XC_MethodHook() {
|
before = { chain ->
|
||||||
override fun beforeHookedMethod(param: MethodHookParam) {
|
val motionEvent = chain.getArg(0) as MotionEvent
|
||||||
val motionEvent = param.args[0] as MotionEvent
|
val action = motionEvent.action
|
||||||
val action = motionEvent.action
|
|
||||||
|
|
||||||
// 左摇杆的X和Y轴
|
// 左摇杆的X和Y轴
|
||||||
val leftStickX = motionEvent.getAxisValue(MotionEvent.AXIS_X)
|
val leftStickX = motionEvent.getAxisValue(MotionEvent.AXIS_X)
|
||||||
val leftStickY = motionEvent.getAxisValue(MotionEvent.AXIS_Y)
|
val leftStickY = motionEvent.getAxisValue(MotionEvent.AXIS_Y)
|
||||||
|
|
||||||
// 右摇杆的X和Y轴
|
// 右摇杆的X和Y轴
|
||||||
val rightStickX = motionEvent.getAxisValue(MotionEvent.AXIS_Z)
|
val rightStickX = motionEvent.getAxisValue(MotionEvent.AXIS_Z)
|
||||||
val rightStickY = motionEvent.getAxisValue(MotionEvent.AXIS_RZ)
|
val rightStickY = motionEvent.getAxisValue(MotionEvent.AXIS_RZ)
|
||||||
|
|
||||||
// 左扳机
|
// 左扳机
|
||||||
val leftTrigger = motionEvent.getAxisValue(MotionEvent.AXIS_LTRIGGER)
|
val leftTrigger = motionEvent.getAxisValue(MotionEvent.AXIS_LTRIGGER)
|
||||||
|
|
||||||
// 右扳机
|
// 右扳机
|
||||||
val rightTrigger = motionEvent.getAxisValue(MotionEvent.AXIS_RTRIGGER)
|
val rightTrigger = motionEvent.getAxisValue(MotionEvent.AXIS_RTRIGGER)
|
||||||
|
|
||||||
// 十字键
|
// 十字键
|
||||||
val hatX = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X)
|
val hatX = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X)
|
||||||
val hatY = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y)
|
val hatY = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y)
|
||||||
|
|
||||||
// 处理摇杆和扳机事件
|
// 处理摇杆和扳机事件
|
||||||
joystickEvent(
|
joystickEvent(
|
||||||
action,
|
action,
|
||||||
leftStickX,
|
leftStickX,
|
||||||
leftStickY,
|
leftStickY,
|
||||||
rightStickX,
|
rightStickX,
|
||||||
rightStickY,
|
rightStickY,
|
||||||
leftTrigger,
|
leftTrigger,
|
||||||
rightTrigger,
|
rightTrigger,
|
||||||
hatX,
|
hatX,
|
||||||
hatY
|
hatY
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
val appActivityClass = XposedHelpers.findClass("android.app.Activity", lpparam.classLoader)
|
val activityClass = classLoader.loadClass("android.app.Activity")
|
||||||
XposedBridge.hookAllMethods(appActivityClass, "onStart", object : XC_MethodHook() {
|
hookAllMethods(activityClass, "onStart") { chain ->
|
||||||
override fun beforeHookedMethod(param: MethodHookParam) {
|
Log.d(TAG, "onStart")
|
||||||
super.beforeHookedMethod(param)
|
val currActivity = chain.thisObject as Activity
|
||||||
Log.d(TAG, "onStart")
|
gameActivity = currActivity
|
||||||
val currActivity = param.thisObject as Activity
|
if (getConfigError != null) {
|
||||||
gameActivity = currActivity
|
showGetConfigFailed(currActivity)
|
||||||
if (getConfigError != null) {
|
} else {
|
||||||
showGetConfigFailed(currActivity)
|
initGkmsConfig(currActivity)
|
||||||
}
|
|
||||||
else {
|
|
||||||
initGkmsConfig(currActivity)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
chain.proceed()
|
||||||
|
}
|
||||||
|
|
||||||
XposedBridge.hookAllMethods(appActivityClass, "onResume", object : XC_MethodHook() {
|
hookAllMethods(activityClass, "onResume") { chain ->
|
||||||
override fun beforeHookedMethod(param: MethodHookParam) {
|
Log.d(TAG, "onResume")
|
||||||
Log.d(TAG, "onResume")
|
val currActivity = chain.thisObject as Activity
|
||||||
val currActivity = param.thisObject as Activity
|
gameActivity = currActivity
|
||||||
gameActivity = currActivity
|
if (getConfigError != null) {
|
||||||
if (getConfigError != null) {
|
showGetConfigFailed(currActivity)
|
||||||
showGetConfigFailed(currActivity)
|
} else {
|
||||||
}
|
initGkmsConfig(currActivity)
|
||||||
else {
|
|
||||||
initGkmsConfig(currActivity)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
chain.proceed()
|
||||||
|
}
|
||||||
|
|
||||||
val cls = lpparam.classLoader.loadClass("com.unity3d.player.UnityPlayer")
|
val unityPlayerClass = classLoader.loadClass("com.unity3d.player.UnityPlayer")
|
||||||
XposedHelpers.findAndHookMethod(
|
val loadNativeMethod = unityPlayerClass.getDeclaredMethod("loadNative", String::class.java)
|
||||||
cls,
|
|
||||||
"loadNative",
|
|
||||||
String::class.java,
|
|
||||||
object : XC_MethodHook() {
|
|
||||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
|
||||||
override fun afterHookedMethod(param: MethodHookParam) {
|
|
||||||
super.afterHookedMethod(param)
|
|
||||||
|
|
||||||
Log.i(TAG, "UnityPlayer.loadNative")
|
hook(loadNativeMethod).intercept { chain ->
|
||||||
|
val result = chain.proceed()
|
||||||
if (alreadyInitialized) {
|
onUnityLoadNativeAfterHook()
|
||||||
return
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
val app = AndroidAppHelper.currentApplication()
|
|
||||||
if (nativeLibLoadSuccess) {
|
|
||||||
showToast("lib$nativeLibName.so loaded.")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
showToast("Load native library lib$nativeLibName.so failed.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gkmsDataInited) {
|
|
||||||
requestConfig(app.applicationContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
FilesChecker.initDir(app.filesDir, modulePath)
|
|
||||||
initHook(
|
|
||||||
"${app.applicationInfo.nativeLibraryDir}/libil2cpp.so",
|
|
||||||
File(
|
|
||||||
app.filesDir.absolutePath,
|
|
||||||
FilesChecker.localizationFilesDir
|
|
||||||
).absolutePath
|
|
||||||
)
|
|
||||||
|
|
||||||
alreadyInitialized = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
startLoop()
|
startLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hookMethod(
|
||||||
|
classLoader: ClassLoader,
|
||||||
|
className: String,
|
||||||
|
methodName: String,
|
||||||
|
parameterTypes: Array<Class<*>>,
|
||||||
|
before: ((XposedInterface.Chain) -> Unit)? = null,
|
||||||
|
after: ((XposedInterface.Chain, Any?) -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
val clazz = classLoader.loadClass(className)
|
||||||
|
val method = clazz.getDeclaredMethod(methodName, *parameterTypes)
|
||||||
|
hook(method).intercept { chain ->
|
||||||
|
before?.invoke(chain)
|
||||||
|
val result = chain.proceed()
|
||||||
|
after?.invoke(chain, result)
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hookAllMethods(clazz: Class<*>, methodName: String, interceptor: (XposedInterface.Chain) -> Any?) {
|
||||||
|
val allMethods = (clazz.declaredMethods.asSequence() + clazz.methods.asSequence())
|
||||||
|
.filter { it.name == methodName }
|
||||||
|
.distinctBy(Method::toGenericString)
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
allMethods.forEach { method ->
|
||||||
|
hook(method).intercept { chain -> interceptor(chain) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||||
|
private fun onUnityLoadNativeAfterHook() {
|
||||||
|
Log.i(TAG, "UnityPlayer.loadNative")
|
||||||
|
|
||||||
|
if (alreadyInitialized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val app = getCurrentApplication()
|
||||||
|
if (app == null) {
|
||||||
|
Log.e(TAG, "currentApplication is null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nativeLibLoadSuccess) {
|
||||||
|
showToast("lib$nativeLibName.so loaded.")
|
||||||
|
} else {
|
||||||
|
showToast("Load native library lib$nativeLibName.so failed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gkmsDataInited) {
|
||||||
|
requestConfig(app.applicationContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
FilesChecker.initDir(app.filesDir, modulePath)
|
||||||
|
initHook(
|
||||||
|
"${app.applicationInfo.nativeLibraryDir}/libil2cpp.so",
|
||||||
|
File(
|
||||||
|
app.filesDir.absolutePath,
|
||||||
|
FilesChecker.localizationFilesDir
|
||||||
|
).absolutePath
|
||||||
|
)
|
||||||
|
|
||||||
|
alreadyInitialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCurrentApplication(): Application? {
|
||||||
|
return try {
|
||||||
|
val activityThreadClass = Class.forName("android.app.ActivityThread")
|
||||||
|
val method = activityThreadClass.getDeclaredMethod("currentApplication")
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(null) as? Application
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
private fun startLoop() {
|
private fun startLoop() {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
|
|
@ -231,8 +266,7 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
if ((gameActivity != null) && (lastFrameStartInit != NativeInitProgress.startInit)) { // change status
|
if ((gameActivity != null) && (lastFrameStartInit != NativeInitProgress.startInit)) { // change status
|
||||||
if (NativeInitProgress.startInit) {
|
if (NativeInitProgress.startInit) {
|
||||||
initProgressUI.createView(gameActivity!!)
|
initProgressUI.createView(gameActivity!!)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
initProgressUI.finishLoad(gameActivity!!)
|
initProgressUI.finishLoad(gameActivity!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,7 +320,6 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
|
|
||||||
// 使用热更新文件
|
// 使用热更新文件
|
||||||
if ((programConfig?.useRemoteAssets == true) || (programConfig?.useAPIAssets == true)) {
|
if ((programConfig?.useRemoteAssets == true) || (programConfig?.useAPIAssets == true)) {
|
||||||
// val dataUri = intent.data
|
|
||||||
val dataUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
val dataUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
intent.getParcelableExtra("resource_file", Uri::class.java)
|
intent.getParcelableExtra("resource_file", Uri::class.java)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -297,7 +330,6 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
if (dataUri != null) {
|
if (dataUri != null) {
|
||||||
if (!externalFilesChecked) {
|
if (!externalFilesChecked) {
|
||||||
externalFilesChecked = true
|
externalFilesChecked = true
|
||||||
// Log.d(TAG, "dataUri: $dataUri")
|
|
||||||
FileHotUpdater.updateFilesFromZip(activity, dataUri, activity.filesDir,
|
FileHotUpdater.updateFilesFromZip(activity, dataUri, activity.filesDir,
|
||||||
programConfig.delRemoteAfterUpdate)
|
programConfig.delRemoteAfterUpdate)
|
||||||
}
|
}
|
||||||
|
|
@ -444,10 +476,6 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
|
|
||||||
modulePath = startupParam.modulePath
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun initHook(targetLibraryPath: String, localizationFilesDir: String)
|
external fun initHook(targetLibraryPath: String, localizationFilesDir: String)
|
||||||
|
|
@ -473,7 +501,15 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun showToast(message: String) {
|
fun showToast(message: String) {
|
||||||
val app = AndroidAppHelper.currentApplication()
|
val app = try {
|
||||||
|
val activityThreadClass = Class.forName("android.app.ActivityThread")
|
||||||
|
val method = activityThreadClass.getDeclaredMethod("currentApplication")
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(null) as? Application
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val context = app?.applicationContext
|
val context = app?.applicationContext
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
val handler = Handler(Looper.getMainLooper())
|
val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
@ -494,19 +530,4 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun pluginCallbackLooper(): Int
|
external fun pluginCallbackLooper(): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
ShadowHook.init(
|
|
||||||
ConfigBuilder()
|
|
||||||
.setMode(ShadowHook.Mode.UNIQUE)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
|
|
||||||
nativeLibLoadSuccess = try {
|
|
||||||
System.loadLibrary(nativeLibName)
|
|
||||||
true
|
|
||||||
} catch (e: UnsatisfiedLinkError) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -48,8 +48,11 @@ class MainActivity : ComponentActivity(), ConfigUpdateListener, IConfigurableAct
|
||||||
}
|
}
|
||||||
|
|
||||||
fun gotoPatchActivity() {
|
fun gotoPatchActivity() {
|
||||||
val intent = Intent(this, PatchActivity::class.java)
|
mainUIConfirmStatUpdate(
|
||||||
startActivity(intent)
|
isShow = true,
|
||||||
|
title = getString(R.string.patcher_unavailable_title),
|
||||||
|
content = getString(R.string.patcher_unavailable_content)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveConfig() {
|
override fun saveConfig() {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
package io.github.chinosk.gakumas.localify.hookUtils
|
package io.github.chinosk.gakumas.localify.hookUtils
|
||||||
|
|
||||||
import android.content.res.XModuleResources
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
object FilesChecker {
|
object FilesChecker {
|
||||||
|
private const val MODULE_ASSETS_PREFIX = "assets/"
|
||||||
|
|
||||||
lateinit var filesDir: File
|
lateinit var filesDir: File
|
||||||
lateinit var modulePath: String
|
lateinit var modulePath: String
|
||||||
val localizationFilesDir = "gakumas-local"
|
val localizationFilesDir = "gakumas-local"
|
||||||
|
|
@ -17,7 +18,6 @@ object FilesChecker {
|
||||||
|
|
||||||
fun initAndCheck(fileDir: File, modulePath: String) {
|
fun initAndCheck(fileDir: File, modulePath: String) {
|
||||||
initDir(fileDir, modulePath)
|
initDir(fileDir, modulePath)
|
||||||
|
|
||||||
checkFiles()
|
checkFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,31 +46,28 @@ object FilesChecker {
|
||||||
pluginBasePath.mkdirs()
|
pluginBasePath.mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
val assets = XModuleResources.createInstance(modulePath, null).assets
|
val rootAssetDir = moduleAssetPath(localizationFilesDir).trimEnd('/') + "/"
|
||||||
fun forAllAssetFiles(
|
ZipFile(modulePath).use { zipFile ->
|
||||||
basePath: String,
|
val entries = zipFile.entries()
|
||||||
action: (String, InputStream?) -> Unit
|
while (entries.hasMoreElements()) {
|
||||||
) {
|
val entry = entries.nextElement()
|
||||||
val assetFiles = assets.list(basePath)!!
|
val name = entry.name
|
||||||
for (file in assetFiles) {
|
if (!name.startsWith(rootAssetDir)) continue
|
||||||
try {
|
|
||||||
assets.open("$basePath/$file")
|
val relativePath = name.removePrefix(MODULE_ASSETS_PREFIX)
|
||||||
} catch (e: IOException) {
|
if (relativePath.isBlank()) continue
|
||||||
action("$basePath/$file", null)
|
|
||||||
forAllAssetFiles("$basePath/$file", action)
|
val outFile = File(filesDir, relativePath)
|
||||||
|
if (entry.isDirectory) {
|
||||||
|
outFile.mkdirs()
|
||||||
continue
|
continue
|
||||||
}.use {
|
|
||||||
action("$basePath/$file", it)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
outFile.parentFile?.mkdirs()
|
||||||
forAllAssetFiles(localizationFilesDir) { path, file ->
|
zipFile.getInputStream(entry).use { input ->
|
||||||
val outFile = File(filesDir, path)
|
outFile.outputStream().use { output ->
|
||||||
if (file == null) {
|
input.copyTo(output)
|
||||||
outFile.mkdirs()
|
}
|
||||||
} else {
|
|
||||||
outFile.outputStream().use { out ->
|
|
||||||
file.copyTo(out)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -79,15 +76,13 @@ object FilesChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPluginVersion(): String {
|
fun getPluginVersion(): String {
|
||||||
val assets = XModuleResources.createInstance(modulePath, null).assets
|
val versionAssetPath = moduleAssetPath("$localizationFilesDir/version.txt")
|
||||||
|
ZipFile(modulePath).use { zipFile ->
|
||||||
for (i in assets.list(localizationFilesDir)!!) {
|
val entry = zipFile.getEntry(versionAssetPath) ?: return "0.0"
|
||||||
if (i.toString() == "version.txt") {
|
zipFile.getInputStream(entry).use { stream ->
|
||||||
val stream = assets.open("$localizationFilesDir/$i")
|
|
||||||
return convertToString(stream).trim()
|
return convertToString(stream).trim()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "0.0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInstalledVersion(): String {
|
fun getInstalledVersion(): String {
|
||||||
|
|
@ -100,26 +95,25 @@ object FilesChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convertToString(inputStream: InputStream?): String {
|
fun convertToString(inputStream: InputStream?): String {
|
||||||
val stringBuilder = StringBuilder()
|
if (inputStream == null) return ""
|
||||||
var reader: BufferedReader? = null
|
return try {
|
||||||
try {
|
BufferedReader(InputStreamReader(inputStream)).use { reader ->
|
||||||
reader = BufferedReader(InputStreamReader(inputStream))
|
buildString {
|
||||||
var line: String?
|
var line: String?
|
||||||
while (reader.readLine().also { line = it } != null) {
|
while (reader.readLine().also { line = it } != null) {
|
||||||
stringBuilder.append(line)
|
append(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
} finally {
|
""
|
||||||
if (reader != null) {
|
|
||||||
try {
|
|
||||||
reader.close()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return stringBuilder.toString()
|
}
|
||||||
|
|
||||||
|
private fun moduleAssetPath(path: String): String {
|
||||||
|
val cleanPath = path.trimStart('/')
|
||||||
|
return "$MODULE_ASSETS_PREFIX$cleanPath"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteRecursively(file: File): Boolean {
|
private fun deleteRecursively(file: File): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,8 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
|
||||||
checked = config.value.unlockAllLiveCostume) {
|
checked = config.value.unlockAllLiveCostume) {
|
||||||
v -> context?.onUnlockAllLiveCostumeChanged(v)
|
v -> context?.onUnlockAllLiveCostumeChanged(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
thickness = 1.dp,
|
thickness = 1.dp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
|
||||||
|
|
@ -389,7 +391,7 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
|
||||||
value = config.value.liveCustomeCostumeId,
|
value = config.value.liveCustomeCostumeId,
|
||||||
onValueChange = { c -> context?.onLiveCustomeCostumeIdChanged(c, 0, 0, 0)},
|
onValueChange = { c -> context?.onLiveCustomeCostumeIdChanged(c, 0, 0, 0)},
|
||||||
label = { Text(stringResource(R.string.live_custome_dress_id)) }
|
label = { Text(stringResource(R.string.live_custome_dress_id)) }
|
||||||
)
|
)*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,8 @@ Xposed スコープは再パッチなしで動的に変更が可能です。
|
||||||
<string name="patch_uninstall_confirm">アンインストールをしてもよろしいですか?</string>
|
<string name="patch_uninstall_confirm">アンインストールをしてもよろしいですか?</string>
|
||||||
<string name="patch_uninstall_text">"署名が異なるため、パッチをインストールする前に元となるアプリをアンインストールする必要があります。
|
<string name="patch_uninstall_text">"署名が異なるため、パッチをインストールする前に元となるアプリをアンインストールする必要があります。
|
||||||
個人データのバックアップを設定済みであることを確認してください。"</string>
|
個人データのバックアップを設定済みであることを確認してください。"</string>
|
||||||
|
<string name="patcher_unavailable_title">Patcher は利用できません</string>
|
||||||
|
<string name="patcher_unavailable_content">GKMSPatchを使用して、root権限なしでご利用ください</string>
|
||||||
<string name="path_password_eye">M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z</string>
|
<string name="path_password_eye">M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z</string>
|
||||||
<string name="path_password_eye_mask_strike_through">M2,4.27 L19.73,22 L22.27,19.46 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
|
<string name="path_password_eye_mask_strike_through">M2,4.27 L19.73,22 L22.27,19.46 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
|
||||||
<string name="path_password_eye_mask_visible">M2,4.27 L2,4.27 L4.54,1.73 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
|
<string name="path_password_eye_mask_visible">M2,4.27 L2,4.27 L4.54,1.73 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,8 @@
|
||||||
<string name="patch_uninstall_text">由于签名不同,安装修补的应用前需要先卸载原应用。\n确保您已备份好个人数据。</string>
|
<string name="patch_uninstall_text">由于签名不同,安装修补的应用前需要先卸载原应用。\n确保您已备份好个人数据。</string>
|
||||||
<string name="patch_uninstall_confirm">您确定要卸载吗</string>
|
<string name="patch_uninstall_confirm">您确定要卸载吗</string>
|
||||||
<string name="patch_finished">修补完成,是否开始安装?</string>
|
<string name="patch_finished">修补完成,是否开始安装?</string>
|
||||||
|
<string name="patcher_unavailable_title">Patcher 暂不可用</string>
|
||||||
|
<string name="patcher_unavailable_content">请使用GKMSPatch进行免Root使用</string>
|
||||||
|
|
||||||
<string name="about_contributors_asset_file">about_contributors_zh_cn.json</string>
|
<string name="about_contributors_asset_file">about_contributors_zh_cn.json</string>
|
||||||
<string name="default_assets_check_api">https://uma.chinosk6.cn/api/gkms_trans_data</string>
|
<string name="default_assets_check_api">https://uma.chinosk6.cn/api/gkms_trans_data</string>
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
<string name="axisx_y">axisX.y</string>
|
<string name="axisx_y">axisX.y</string>
|
||||||
<string name="axisy_y">axisY.y</string>
|
<string name="axisy_y">axisY.y</string>
|
||||||
<string name="axisz_y">axisZ.y</string>
|
<string name="axisz_y">axisZ.y</string>
|
||||||
<string name="basic_settings">Basic Ssettings</string>
|
<string name="basic_settings">Basic Settings</string>
|
||||||
<string name="graphic_settings">Graphic Settings</string>
|
<string name="graphic_settings">Graphic Settings</string>
|
||||||
<string name="camera_settings">Camera Settings</string>
|
<string name="camera_settings">Camera Settings</string>
|
||||||
<string name="test_mode_live">Test Mode - LIVE</string>
|
<string name="test_mode_live">Test Mode - LIVE</string>
|
||||||
|
|
@ -102,6 +102,8 @@
|
||||||
<string name="patch_uninstall_text">Due to different signatures, you need to uninstall the original app before installing the patched one.\nMake sure you have backed up personal data.</string>
|
<string name="patch_uninstall_text">Due to different signatures, you need to uninstall the original app before installing the patched one.\nMake sure you have backed up personal data.</string>
|
||||||
<string name="patch_uninstall_confirm">Are you sure you want to uninstall?</string>
|
<string name="patch_uninstall_confirm">Are you sure you want to uninstall?</string>
|
||||||
<string name="patch_finished">Patch finished. Start installing?</string>
|
<string name="patch_finished">Patch finished. Start installing?</string>
|
||||||
|
<string name="patcher_unavailable_title">Patcher Unavailable</string>
|
||||||
|
<string name="patcher_unavailable_content">Please use GKMSPatch to use this module rootlessly</string>
|
||||||
|
|
||||||
<string name="about_contributors_asset_file">about_contributors_en.json</string>
|
<string name="about_contributors_asset_file">about_contributors_en.json</string>
|
||||||
<string name="default_assets_check_api">https://api.github.com/repos/NatsumeLS/Gakumas-Translation-Data-EN/releases/latest</string>
|
<string name="default_assets_check_api">https://api.github.com/repos/NatsumeLS/Gakumas-Translation-Data-EN/releases/latest</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
io.github.chinosk.gakumas.localify.GakumasHookMain
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
minApiVersion=101
|
||||||
|
targetApiVersion=101
|
||||||
|
staticScope=true
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.bandainamcoent.idolmaster_gakuen
|
||||||
|
|
@ -6,7 +6,7 @@ shizukuApi = "12.1.0"
|
||||||
hiddenapi-refine = "4.3.0"
|
hiddenapi-refine = "4.3.0"
|
||||||
hiddenapi-stub = "4.2.0"
|
hiddenapi-stub = "4.2.0"
|
||||||
okhttpBom = "4.12.0"
|
okhttpBom = "4.12.0"
|
||||||
xposedApi = "82"
|
libxposedApi = "101.0.0"
|
||||||
appcompat = "1.7.0"
|
appcompat = "1.7.0"
|
||||||
coil = "2.6.0"
|
coil = "2.6.0"
|
||||||
composeBom = "2024.06.00"
|
composeBom = "2024.06.00"
|
||||||
|
|
@ -50,7 +50,7 @@ rikka-hidden-stub = { module = "dev.rikka.hidden:stub", version.ref = "hiddenapi
|
||||||
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor" }
|
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor" }
|
||||||
okhttp = { module = "com.squareup.okhttp3:okhttp" }
|
okhttp = { module = "com.squareup.okhttp3:okhttp" }
|
||||||
okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttpBom" }
|
okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttpBom" }
|
||||||
xposed-api = { module = "de.robv.android.xposed:api", version.ref = "xposedApi" }
|
libxposed-api = { module = "io.github.libxposed:api", version.ref = "libxposedApi" }
|
||||||
coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
|
coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
|
||||||
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
|
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
|
||||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ dependencyResolutionManagement {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
maven { url "https://api.xposed.info/" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue