add plugin init progress

This commit is contained in:
chinosk 2024-07-03 20:29:11 +08:00
parent c28e05e271
commit c810a27ba2
Signed by: chinosk
GPG Key ID: 00610B08C1BF7BE9
5 changed files with 283 additions and 3 deletions

View File

@ -47,6 +47,18 @@
#include "../../GakumasLocalify/Log.h" #include "../../GakumasLocalify/Log.h"
#include "../../GakumasLocalify/Misc.hpp" #include "../../GakumasLocalify/Misc.hpp"
class UnityResolveProgress final {
public:
struct Progress {
long current = 0;
long total = 1;
};
static bool startInit;
static Progress assembliesProgress;
static Progress classProgress;
};
class UnityResolve final { class UnityResolve final {
public: public:
struct Assembly; struct Assembly;
@ -284,9 +296,11 @@ public:
hmodule_ = hmodule; hmodule_ = hmodule;
if (mode_ == Mode::Il2Cpp) { if (mode_ == Mode::Il2Cpp) {
UnityResolveProgress::startInit = true;
pDomain = Invoke<void*>("il2cpp_domain_get"); pDomain = Invoke<void*>("il2cpp_domain_get");
Invoke<void*>("il2cpp_thread_attach", pDomain); Invoke<void*>("il2cpp_thread_attach", pDomain);
ForeachAssembly(); ForeachAssembly();
UnityResolveProgress::startInit = false;
} }
else { else {
pDomain = Invoke<void*>("mono_get_root_domain"); pDomain = Invoke<void*>("mono_get_root_domain");
@ -561,7 +575,11 @@ private:
if (mode_ == Mode::Il2Cpp) { if (mode_ == Mode::Il2Cpp) {
size_t nrofassemblies = 0; size_t nrofassemblies = 0;
const auto assemblies = Invoke<void**>("il2cpp_domain_get_assemblies", pDomain, &nrofassemblies); const auto assemblies = Invoke<void**>("il2cpp_domain_get_assemblies", pDomain, &nrofassemblies);
UnityResolveProgress::assembliesProgress.total = nrofassemblies;
for (auto i = 0; i < nrofassemblies; i++) { for (auto i = 0; i < nrofassemblies; i++) {
UnityResolveProgress::assembliesProgress.current = i + 1;
const auto ptr = assemblies[i]; const auto ptr = assemblies[i];
if (ptr == nullptr) continue; if (ptr == nullptr) continue;
auto assembly = new Assembly{ .address = ptr }; auto assembly = new Assembly{ .address = ptr };
@ -594,7 +612,9 @@ private:
// 遍历类 // 遍历类
if (mode_ == Mode::Il2Cpp) { if (mode_ == Mode::Il2Cpp) {
const auto count = Invoke<int>("il2cpp_image_get_class_count", image); const auto count = Invoke<int>("il2cpp_image_get_class_count", image);
UnityResolveProgress::classProgress.total = count;
for (auto i = 0; i < count; i++) { for (auto i = 0; i < count; i++) {
UnityResolveProgress::classProgress.current = i + 1;
const auto pClass = Invoke<void*>("il2cpp_image_get_class", image, i); const auto pClass = Invoke<void*>("il2cpp_image_get_class", image, i);
if (pClass == nullptr) continue; if (pClass == nullptr) continue;
const auto pAClass = new Class(); const auto pAClass = new Class();

View File

@ -15,6 +15,10 @@ JavaVM* g_javaVM = nullptr;
jclass g_gakumasHookMainClass = nullptr; jclass g_gakumasHookMainClass = nullptr;
jmethodID showToastMethodId = nullptr; jmethodID showToastMethodId = nullptr;
bool UnityResolveProgress::startInit = false;
UnityResolveProgress::Progress UnityResolveProgress::assembliesProgress{};
UnityResolveProgress::Progress UnityResolveProgress::classProgress{};
namespace namespace
{ {
class AndroidHookInstaller : public GakumasLocal::HookInstaller class AndroidHookInstaller : public GakumasLocal::HookInstaller
@ -114,8 +118,37 @@ Java_io_github_chinosk_gakumas_localify_GakumasHookMain_loadConfig(JNIEnv *env,
} }
extern "C" extern "C"
JNIEXPORT void JNICALL JNIEXPORT jint JNICALL
Java_io_github_chinosk_gakumas_localify_GakumasHookMain_pluginCallbackLooper(JNIEnv *env, Java_io_github_chinosk_gakumas_localify_GakumasHookMain_pluginCallbackLooper(JNIEnv *env,
jclass clazz) { jclass clazz) {
GakumasLocal::Log::ToastLoop(env, clazz); GakumasLocal::Log::ToastLoop(env, clazz);
if (UnityResolveProgress::startInit) {
return 9;
}
return 0;
}
extern "C"
JNIEXPORT void JNICALL
Java_io_github_chinosk_gakumas_localify_models_NativeInitProgress_pluginInitProgressLooper(
JNIEnv *env, jclass clazz, jobject progress) {
// jclass progressClass = env->GetObjectClass(progress);
static jfieldID startInitFieldID = env->GetStaticFieldID(clazz, "startInit", "Z");
static jmethodID setAssembliesProgressDataMethodID = env->GetMethodID(clazz, "setAssembliesProgressData", "(JJ)V");
static jmethodID setClassProgressDataMethodID = env->GetMethodID(clazz, "setClassProgressData", "(JJ)V");
// jboolean startInit = env->GetStaticBooleanField(clazz, startInitFieldID);
env->SetStaticBooleanField(clazz, startInitFieldID, UnityResolveProgress::startInit);
env->CallVoidMethod(progress, setAssembliesProgressDataMethodID,
UnityResolveProgress::assembliesProgress.current, UnityResolveProgress::assembliesProgress.total);
env->CallVoidMethod(progress, setClassProgressDataMethodID,
UnityResolveProgress::classProgress.current, UnityResolveProgress::classProgress.total);
} }

View File

@ -34,7 +34,9 @@ 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.FileHotUpdater
import io.github.chinosk.gakumas.localify.mainUtils.json 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.models.ProgramConfig
import io.github.chinosk.gakumas.localify.ui.game_attach.InitProgressUI
val TAG = "GakumasLocalify" val TAG = "GakumasLocalify"
@ -49,6 +51,7 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
private var getConfigError: Exception? = null private var getConfigError: Exception? = null
private var externalFilesChecked: Boolean = false private var externalFilesChecked: Boolean = false
private var gameActivity: Activity? = null
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
// if (lpparam.packageName == "io.github.chinosk.gakumas.localify") { // if (lpparam.packageName == "io.github.chinosk.gakumas.localify") {
@ -135,6 +138,7 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
super.beforeHookedMethod(param) super.beforeHookedMethod(param)
Log.d(TAG, "onStart") Log.d(TAG, "onStart")
val currActivity = param.thisObject as Activity val currActivity = param.thisObject as Activity
gameActivity = currActivity
if (getConfigError != null) { if (getConfigError != null) {
showGetConfigFailed(currActivity) showGetConfigFailed(currActivity)
} }
@ -148,6 +152,7 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
override fun beforeHookedMethod(param: MethodHookParam) { override fun beforeHookedMethod(param: MethodHookParam) {
Log.d(TAG, "onResume") Log.d(TAG, "onResume")
val currActivity = param.thisObject as Activity val currActivity = param.thisObject as Activity
gameActivity = currActivity
if (getConfigError != null) { if (getConfigError != null) {
showGetConfigFailed(currActivity) showGetConfigFailed(currActivity)
} }
@ -206,9 +211,30 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
private fun startLoop() { private fun startLoop() {
GlobalScope.launch { GlobalScope.launch {
val interval = 1000L / 30 val interval = 1000L / 30
var lastFrameStartInit = NativeInitProgress.startInit
val initProgressUI = InitProgressUI()
while (isActive) { while (isActive) {
val timeTaken = measureTimeMillis { val timeTaken = measureTimeMillis {
pluginCallbackLooper() val returnValue = pluginCallbackLooper() // plugin main thread loop
if (returnValue == 9) {
NativeInitProgress.startInit = true
}
if (NativeInitProgress.startInit) { // if init, update data
NativeInitProgress.pluginInitProgressLooper(NativeInitProgress)
gameActivity?.let { initProgressUI.updateData(it) }
}
if ((gameActivity != null) && (lastFrameStartInit != NativeInitProgress.startInit)) { // change status
if (NativeInitProgress.startInit) {
initProgressUI.createView(gameActivity!!)
}
else {
initProgressUI.finishLoad(gameActivity!!)
}
}
lastFrameStartInit = NativeInitProgress.startInit
} }
delay(interval - timeTaken) delay(interval - timeTaken)
} }
@ -413,7 +439,7 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
} }
@JvmStatic @JvmStatic
external fun pluginCallbackLooper() external fun pluginCallbackLooper(): Int
} }
init { init {

View File

@ -0,0 +1,25 @@
package io.github.chinosk.gakumas.localify.models
data class ProgressData(
var current: Long = 0,
var total: Long = 1
)
object NativeInitProgress {
var assembliesProgress = ProgressData()
var classProgress = ProgressData()
var startInit: Boolean = false
fun setAssembliesProgressData(current: Long, total: Long) {
assembliesProgress.current = current
assembliesProgress.total = total
}
fun setClassProgressData(current: Long, total: Long) {
classProgress.current = current
classProgress.total = total
}
@JvmStatic
external fun pluginInitProgressLooper(progress: NativeInitProgress)
}

View File

@ -0,0 +1,176 @@
package io.github.chinosk.gakumas.localify.ui.game_attach
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RectShape
import android.util.Log
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import io.github.chinosk.gakumas.localify.TAG
import io.github.chinosk.gakumas.localify.models.NativeInitProgress
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class InitProgressUI {
private var uiCreated = false
private lateinit var rootView: ViewGroup
private lateinit var container: LinearLayout
private lateinit var assembliesProgressBar: ProgressBar
private lateinit var classProgressBar: ProgressBar
private lateinit var titleText: TextView
private lateinit var assembliesProgressText: TextView
private lateinit var classProgressText: TextView
@SuppressLint("SetTextI18n")
fun createView(context: Context) {
if (uiCreated) return
uiCreated = true
val activity = context as? Activity ?: return
rootView = activity.findViewById<ViewGroup>(android.R.id.content)
container = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
gravity = Gravity.TOP or Gravity.END
marginEnd = 20
marginStart = 20
topMargin = 100
}
setBackgroundColor(Color.WHITE)
setPadding(20, 20, 20, 20)
}
// Set up the container layout
assembliesProgressBar = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
topMargin = 20
}
max = 100
}
// Set up the class progress bar
classProgressBar = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
topMargin = 20
}
max = 100
}
assembliesProgressBar.progressTintList = ColorStateList.valueOf(Color.parseColor("#FFF89400"))
classProgressBar.progressTintList = ColorStateList.valueOf(Color.parseColor("#FFF89400"))
// Set up the text views
titleText = TextView(context).apply {
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
topMargin = 20
gravity = Gravity.CENTER_HORIZONTAL
}
setTextColor(Color.BLACK)
text = "Initializing Plugins"
textSize = 20f
setTypeface(typeface, Typeface.BOLD)
}
val textLayout = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
topMargin = 20
}
assembliesProgressText = TextView(context).apply {
layoutParams = textLayout
setTextColor(Color.BLACK)
}
classProgressText = TextView(context).apply {
layoutParams = textLayout
setTextColor(Color.BLACK)
}
// Add container to the root view
context.runOnUiThread {
// Add views to the container
container.addView(titleText)
container.addView(assembliesProgressText)
container.addView(assembliesProgressBar)
container.addView(classProgressText)
container.addView(classProgressBar)
rootView.addView(container)
}
}
@SuppressLint("SetTextI18n")
@OptIn(DelicateCoroutinesApi::class)
fun finishLoad(context: Activity) {
if (!uiCreated) return
uiCreated = false
GlobalScope.launch {
context.runOnUiThread {
assembliesProgressBar.progressTintList = ColorStateList.valueOf(Color.parseColor("#FF28B463"))
classProgressBar.progressTintList = ColorStateList.valueOf(Color.parseColor("#FF28B463"))
titleText.text = "Finished"
}
delay(1500L)
context.runOnUiThread {
rootView.removeView(container)
}
}
}
fun removeView(context: Activity) {
if (!uiCreated) return
uiCreated = false
context.runOnUiThread {
rootView.removeView(container)
}
}
@SuppressLint("SetTextI18n")
fun updateData(context: Activity) {
if (!uiCreated) return
//return
context.runOnUiThread {
val assembliesProgress = NativeInitProgress.assembliesProgress
val classProgress = NativeInitProgress.classProgress
assembliesProgressText.text = "${assembliesProgress.current}/${assembliesProgress.total}"
classProgressText.text = "${classProgress.current}/${classProgress.total}"
assembliesProgressBar.setProgress((assembliesProgress.current * 100 / assembliesProgress.total).toInt(), true)
classProgressBar.setProgress((classProgress.current * 100 / classProgress.total).toInt(), true)
}
}
}