Skip to content

Commit 0b83238

Browse files
committed
feat: implement new mod loading logic
1 parent 2f7dcc1 commit 0b83238

File tree

8 files changed

+218
-201
lines changed

8 files changed

+218
-201
lines changed

src/pl/Hook.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
1-
#include <cstring>
2-
#include <memory>
1+
#include "Hook.h"
2+
3+
#include <cstdint>
4+
#include <string>
5+
#include <vector>
6+
7+
#ifndef RT_SUCCESS
8+
#define RT_SUCCESS 0
9+
#endif
10+
311
#include <mutex>
412
#include <set>
13+
#include <type_traits>
514
#include <unordered_map>
615

716
#include <dlfcn.h>
17+
818
typedef enum {
9-
SHADOWHOOK_MODE_SHARED = 0,
10-
SHADOWHOOK_MODE_UNIQUE = 1
19+
SHADOWHOOK_MODE_SHARED = 0, // a function can be hooked multiple times
20+
SHADOWHOOK_MODE_UNIQUE = 1 // a function can only be hooked once, and hooking
21+
// again will report an error
1122
} shadowhook_mode_t;
1223

1324
static int (*shadowhook_init)(shadowhook_mode_t mode, bool debuggable);
1425
static void *(*shadowhook_hook_func_addr)(void *func_addr, void *new_addr,
1526
void **orig_addr);
1627
static int (*shadowhook_unhook)(void *stub);
17-
1828
namespace pl::hook {
1929

20-
using FuncPtr = void *;
21-
2230
struct HookElement {
2331
FuncPtr detour{};
2432
FuncPtr *originalFunc{};
@@ -71,8 +79,9 @@ std::unordered_map<FuncPtr, std::shared_ptr<HookData>> &getHooks() {
7179
static std::mutex hooksMutex{};
7280

7381
int pl_hook(FuncPtr target, FuncPtr detour, FuncPtr *originalFunc,
74-
int priority) {
82+
Priority priority) {
7583
std::lock_guard lock(hooksMutex);
84+
7685
static bool init = false;
7786

7887
if (!init) {
@@ -91,7 +100,6 @@ int pl_hook(FuncPtr target, FuncPtr detour, FuncPtr *originalFunc,
91100
init = true;
92101
}
93102
}
94-
95103
auto it = getHooks().find(target);
96104
if (it != getHooks().end()) {
97105
auto hookData = it->second;
@@ -107,9 +115,9 @@ int pl_hook(FuncPtr target, FuncPtr detour, FuncPtr *originalFunc,
107115
hookData->target = target;
108116
hookData->origin = nullptr;
109117
hookData->start = detour;
118+
hookData->stub = shadowhook_hook_func_addr(target, detour, &hookData->origin);
110119
hookData->hooks.insert(
111120
{detour, originalFunc, priority, hookData->incrementHookId()});
112-
hookData->stub = shadowhook_hook_func_addr(target, detour, &hookData->origin);
113121
if (!hookData->stub) {
114122
return -1;
115123
}

src/pl/PreLoader.cpp

Lines changed: 14 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,211 +1,36 @@
1-
#include "nlohmann/json.hpp"
1+
#include "internal/AndroidUtils.h"
2+
#include "internal/Logger.h"
3+
#include "internal/ModManager.h"
24
#include <android/native_activity.h>
35
#include <dlfcn.h>
4-
#include <filesystem>
5-
#include <fstream>
6-
#include <jni.h>
7-
#include <string>
8-
#include <string_view>
9-
10-
#include "internal/Logger.h"
11-
#include "internal/StringUtils.h"
12-
13-
JNIEnv *env = nullptr;
14-
15-
jobject getGlobalContext(JNIEnv *env) {
16-
jclass activity_thread = env->FindClass("android/app/ActivityThread");
17-
if (!activity_thread)
18-
return nullptr;
19-
jmethodID current_activity_thread =
20-
env->GetStaticMethodID(activity_thread, "currentActivityThread",
21-
"()Landroid/app/ActivityThread;");
22-
if (!current_activity_thread) {
23-
env->DeleteLocalRef(activity_thread);
24-
return nullptr;
25-
}
26-
jobject at =
27-
env->CallStaticObjectMethod(activity_thread, current_activity_thread);
28-
if (!at || env->ExceptionCheck()) {
29-
env->ExceptionClear();
30-
env->DeleteLocalRef(activity_thread);
31-
return nullptr;
32-
}
33-
jmethodID get_application = env->GetMethodID(
34-
activity_thread, "getApplication", "()Landroid/app/Application;");
35-
jobject context = env->CallObjectMethod(at, get_application);
36-
if (env->ExceptionCheck())
37-
env->ExceptionClear();
38-
39-
env->DeleteLocalRef(activity_thread);
40-
env->DeleteLocalRef(at);
41-
return context;
42-
}
436

44-
std::string getAbsolutePath(JNIEnv *env, jobject file) {
45-
jclass file_class = env->GetObjectClass(file);
46-
jmethodID get_abs_path =
47-
env->GetMethodID(file_class, "getAbsolutePath", "()Ljava/lang/String;");
48-
jstring jstr = (jstring)env->CallObjectMethod(file, get_abs_path);
49-
std::string result;
50-
51-
if (env->ExceptionCheck() || !jstr) {
52-
env->ExceptionClear();
53-
env->DeleteLocalRef(file_class);
54-
return result;
55-
}
56-
const char *cstr = env->GetStringUTFChars(jstr, nullptr);
57-
result = cstr;
58-
env->ReleaseStringUTFChars(jstr, cstr);
59-
env->DeleteLocalRef(jstr);
60-
env->DeleteLocalRef(file_class);
61-
return result;
62-
}
7+
JavaVM *g_vm = nullptr;
638

64-
std::string getSelectedModsDir(JNIEnv *env, jobject context) {
65-
jclass versionManagerClass =
66-
env->FindClass("org/levimc/launcher/core/versions/VersionManager");
67-
if (!versionManagerClass)
68-
return "";
69-
70-
jmethodID getSelectedModsDirMethod =
71-
env->GetStaticMethodID(versionManagerClass, "getSelectedModsDir",
72-
"(Landroid/content/Context;)Ljava/lang/String;");
73-
if (!getSelectedModsDirMethod) {
74-
env->DeleteLocalRef(versionManagerClass);
75-
return "";
76-
}
77-
78-
jstring modsDirPathJstr = (jstring)env->CallStaticObjectMethod(
79-
versionManagerClass, getSelectedModsDirMethod, context);
80-
if (!modsDirPathJstr) {
81-
env->DeleteLocalRef(versionManagerClass);
82-
return "";
83-
}
84-
85-
const char *chars = env->GetStringUTFChars(modsDirPathJstr, nullptr);
86-
std::string modsDirPath = chars ? chars : "";
87-
env->ReleaseStringUTFChars(modsDirPathJstr, chars);
88-
89-
env->DeleteLocalRef(modsDirPathJstr);
90-
env->DeleteLocalRef(versionManagerClass);
91-
92-
return modsDirPath;
93-
}
94-
95-
namespace pl {
96-
namespace fs = std::filesystem;
97-
98-
static bool endsWithSo(const std::string &filename) {
99-
if (filename.length() >= 3) {
100-
return (0 == filename.compare(filename.length() - 3, 3, ".so"));
101-
}
102-
return false;
103-
}
104-
105-
static bool isModEnabled(const fs::path &json_path,
106-
const std::string &mod_filename) {
107-
if (!fs::exists(json_path))
108-
return false;
109-
std::ifstream json_file(json_path);
110-
if (!json_file)
111-
return false;
112-
nlohmann::json mods_config;
113-
try {
114-
json_file >> mods_config;
115-
} catch (...) {
116-
return false;
117-
}
118-
auto it = mods_config.find(mod_filename);
119-
if (it != mods_config.end())
120-
return it.value().get<bool>();
121-
return false;
122-
}
123-
124-
void loadPreloadNativeMods() {
125-
jobject appContext = getGlobalContext(env);
126-
if (!appContext)
127-
return;
128-
jclass contextClass = env->GetObjectClass(appContext);
129-
jmethodID get_files_dir =
130-
env->GetMethodID(contextClass, "getFilesDir", "()Ljava/io/File;");
131-
jobject filesDir = env->CallObjectMethod(appContext, get_files_dir);
132-
std::string dataDir = getAbsolutePath(env, filesDir);
133-
if (!dataDir.empty() && filesDir)
134-
env->DeleteLocalRef(filesDir);
135-
if (contextClass)
136-
env->DeleteLocalRef(contextClass);
137-
138-
auto dirStr = getSelectedModsDir(env, appContext);
139-
if (appContext)
140-
env->DeleteLocalRef(appContext);
141-
fs::path modsDir(dirStr);
142-
fs::path configPath = modsDir / "mods_config.json";
143-
144-
if (!fs::exists(modsDir))
145-
return;
146-
147-
for (const auto &entry : fs::directory_iterator(modsDir)) {
148-
if (entry.is_regular_file()) {
149-
auto fname = entry.path().filename().string();
150-
if (!endsWithSo(fname))
151-
continue;
152-
153-
std::string libName =
154-
pl::utils::u8str2str(entry.path().stem().u8string()) + ".so";
155-
if (!isModEnabled(configPath, libName)) {
156-
pl::log::Info("{} is not enabled, skip loading.", libName);
157-
continue;
158-
}
159-
fs::path destPath = fs::path(dataDir) / entry.path().filename();
160-
try {
161-
fs::copy_file(entry, destPath, fs::copy_options::overwrite_existing);
162-
fs::permissions(destPath, fs::perms::owner_all | fs::perms::group_all);
163-
} catch (std::exception &e) {
164-
pl::log::Error("File operation error: {}", e.what());
165-
continue;
166-
}
167-
if (void *handle = dlopen(destPath.c_str(), RTLD_NOW)) {
168-
pl::log::Info("{} Loaded.", libName);
169-
} else {
170-
pl::log::Error("Can't load {}", libName);
171-
pl::log::Error("%s", dlerror());
172-
}
173-
}
174-
}
175-
}
176-
177-
void init() { loadPreloadNativeMods(); }
178-
179-
} // namespace pl
180-
181-
static void (*android_main_minecraft)(struct android_app *app) = nullptr;
1829
static void (*ANativeActivity_onCreate_minecraft)(ANativeActivity *, void *,
18310
size_t) = nullptr;
18411

185-
extern "C" void android_main(struct android_app *app) {
186-
if (android_main_minecraft)
187-
android_main_minecraft(app);
188-
pl::init();
189-
}
190-
19112
extern "C" void ANativeActivity_onCreate(ANativeActivity *activity,
19213
void *savedState,
19314
size_t savedStateSize) {
194-
if (ANativeActivity_onCreate_minecraft)
15+
if (ANativeActivity_onCreate_minecraft) {
19516
ANativeActivity_onCreate_minecraft(activity, savedState, savedStateSize);
17+
}
19618
}
19719

19820
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
21+
g_vm = vm;
22+
JNIEnv *env = nullptr;
23+
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
24+
return JNI_VERSION_1_4;
25+
26+
auto paths = AndroidUtils::FetchContextPaths(env);
27+
ModManager::LoadAndInitializeEnabledMods(paths.modsDir, paths.cacheDir, g_vm);
28+
19929
void *handle = dlopen("libminecraftpe.so", RTLD_LAZY);
20030
if (handle) {
201-
android_main_minecraft =
202-
(void (*)(struct android_app *))dlsym(handle, "android_main");
20331
ANativeActivity_onCreate_minecraft =
20432
(void (*)(ANativeActivity *, void *, size_t))dlsym(
20533
handle, "ANativeActivity_onCreate");
20634
}
207-
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK) {
208-
return JNI_VERSION_1_4;
209-
}
21035
return JNI_VERSION_1_4;
21136
}

src/pl/PreLoader.h

Whitespace-only changes.

src/pl/internal/AndroidUtils.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include "AndroidUtils.h"
2+
3+
jobject AndroidUtils::GetGlobalContext(JNIEnv *env) {
4+
jclass activity_thread = env->FindClass("android/app/ActivityThread");
5+
if (!activity_thread)
6+
return nullptr;
7+
jmethodID current_activity_thread =
8+
env->GetStaticMethodID(activity_thread, "currentActivityThread",
9+
"()Landroid/app/ActivityThread;");
10+
if (!current_activity_thread) {
11+
env->DeleteLocalRef(activity_thread);
12+
return nullptr;
13+
}
14+
jobject at =
15+
env->CallStaticObjectMethod(activity_thread, current_activity_thread);
16+
if (!at || env->ExceptionCheck()) {
17+
env->ExceptionClear();
18+
env->DeleteLocalRef(activity_thread);
19+
return nullptr;
20+
}
21+
jmethodID get_application = env->GetMethodID(
22+
activity_thread, "getApplication", "()Landroid/app/Application;");
23+
jobject context = env->CallObjectMethod(at, get_application);
24+
if (env->ExceptionCheck())
25+
env->ExceptionClear();
26+
env->DeleteLocalRef(activity_thread);
27+
env->DeleteLocalRef(at);
28+
return context;
29+
}
30+
31+
std::string AndroidUtils::GetAbsolutePath(JNIEnv *env, jobject file) {
32+
jclass file_class = env->GetObjectClass(file);
33+
jmethodID get_abs_path =
34+
env->GetMethodID(file_class, "getAbsolutePath", "()Ljava/lang/String;");
35+
jstring jstr = (jstring)env->CallObjectMethod(file, get_abs_path);
36+
std::string result;
37+
if (env->ExceptionCheck() || !jstr) {
38+
env->ExceptionClear();
39+
env->DeleteLocalRef(file_class);
40+
return result;
41+
}
42+
const char *cstr = env->GetStringUTFChars(jstr, nullptr);
43+
result = cstr;
44+
env->ReleaseStringUTFChars(jstr, cstr);
45+
env->DeleteLocalRef(jstr);
46+
env->DeleteLocalRef(file_class);
47+
return result;
48+
}
49+
50+
std::string AndroidUtils::GetSelectedModsDir(JNIEnv *env, jobject context) {
51+
jclass versionManagerClass =
52+
env->FindClass("org/levimc/launcher/core/versions/VersionManager");
53+
if (!versionManagerClass)
54+
return "";
55+
jmethodID getSelectedModsDirMethod =
56+
env->GetStaticMethodID(versionManagerClass, "getSelectedModsDir",
57+
"(Landroid/content/Context;)Ljava/lang/String;");
58+
if (!getSelectedModsDirMethod) {
59+
env->DeleteLocalRef(versionManagerClass);
60+
return "";
61+
}
62+
jstring modsDirPathJstr = (jstring)env->CallStaticObjectMethod(
63+
versionManagerClass, getSelectedModsDirMethod, context);
64+
if (!modsDirPathJstr) {
65+
env->DeleteLocalRef(versionManagerClass);
66+
return "";
67+
}
68+
const char *chars = env->GetStringUTFChars(modsDirPathJstr, nullptr);
69+
std::string modsDirPath = chars ? chars : "";
70+
env->ReleaseStringUTFChars(modsDirPathJstr, chars);
71+
env->DeleteLocalRef(modsDirPathJstr);
72+
env->DeleteLocalRef(versionManagerClass);
73+
return modsDirPath;
74+
}
75+
76+
AndroidContextPaths AndroidUtils::FetchContextPaths(JNIEnv *env) {
77+
AndroidContextPaths paths;
78+
jobject appContext = GetGlobalContext(env);
79+
if (!appContext)
80+
return paths;
81+
82+
jclass contextClass = env->GetObjectClass(appContext);
83+
84+
jmethodID get_cache_dir =
85+
env->GetMethodID(contextClass, "getCacheDir", "()Ljava/io/File;");
86+
jobject cacheDir = env->CallObjectMethod(appContext, get_cache_dir);
87+
if (cacheDir) {
88+
paths.cacheDir = GetAbsolutePath(env, cacheDir);
89+
env->DeleteLocalRef(cacheDir);
90+
}
91+
92+
paths.modsDir = GetSelectedModsDir(env, appContext);
93+
94+
env->DeleteLocalRef(contextClass);
95+
env->DeleteLocalRef(appContext);
96+
97+
return paths;
98+
}

0 commit comments

Comments
 (0)