[Scummvm-git-logs] scummvm master -> 84e0355924871a666140137e0518e1bfb50c4d04
lephilousophe
noreply at scummvm.org
Sat Oct 24 16:48:58 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
84e0355924 ANDROID: Reset the global variables while restarting
Commit: 84e0355924871a666140137e0518e1bfb50c4d04
https://github.com/scummvm/scummvm/commit/84e0355924871a666140137e0518e1bfb50c4d04
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2026-10-24T18:48:52+02:00
Commit Message:
ANDROID: Reset the global variables while restarting
This fixes various crashes and oddities related to the fact that global
variables were not reset when restarting the ScummVM application.
To do that, a loader shared object is interposed between Java and the
real ScummVM library.
It is in charge of loading the shared object and reloading it at
restart.
It also resolves the library path by parsing /proc/self/maps.
Changed paths:
A backends/platform/android/loader.cpp
backends/platform/android/android.mk
backends/platform/android/fatbundle.mk
backends/platform/android/org/scummvm/scummvm/ScummVM.java
backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk
index 94353176c9c..f5b1636e044 100644
--- a/backends/platform/android/android.mk
+++ b/backends/platform/android/android.mk
@@ -9,6 +9,7 @@ PATH_BUILD_GRADLE = $(PATH_BUILD)/gradle/.timestamp $(PATH_BUILD)/gradlew $(PATH
PATH_BUILD_ASSETS = $(PATH_BUILD)/mainAssets/src/main/assets
PATH_BUILD_LIB = $(PATH_BUILD)/lib/$(ABI)
PATH_BUILD_LIBSCUMMVM = $(PATH_BUILD)/lib/$(ABI)/libscummvm.so
+PATH_BUILD_LOADER = $(PATH_BUILD)/lib/$(ABI)/libloader.so
APK_MAIN = ScummVM-debug.apk
APK_MAIN_RELEASE = ScummVM-release-unsigned.apk
@@ -16,6 +17,9 @@ AAB_MAIN_RELEASE = ScummVM-release.aab
DIST_FILES_PLATFORM = $(PATH_DIST)/android-help.zip $(PATH_DIST)/gamepad.svg
+libloader.so: $(srcdir)/backends/platform/android/loader.cpp
+ +$(QUIET_LINK)$(CXX) $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS) $^ -o $@ -ldl -llog
+
$(PATH_BUILD):
$(MKDIR) $(PATH_BUILD)
@@ -62,17 +66,21 @@ endif
$(PATH_BUILD_LIBSCUMMVM): libscummvm.so | $(PATH_BUILD)
$(INSTALL) -d $(PATH_BUILD_LIB)
- $(INSTALL) -c -m 644 libscummvm.so $(PATH_BUILD_LIBSCUMMVM)
+ $(INSTALL) -c -m 644 $< $@
+
+$(PATH_BUILD_LOADER): libloader.so | $(PATH_BUILD)
+ $(INSTALL) -d $(PATH_BUILD_LIB)
+ $(INSTALL) -c -m 644 $< $@
-$(APK_MAIN): $(PATH_BUILD_GRADLE) $(PATH_BUILD_ASSETS)/MD5SUMS $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD)
+$(APK_MAIN): $(PATH_BUILD_GRADLE) $(PATH_BUILD_ASSETS)/MD5SUMS $(PATH_BUILD_LIBSCUMMVM) $(PATH_BUILD_LOADER) | $(PATH_BUILD)
(cd $(PATH_BUILD); ./gradlew assembleDebug)
$(CP) $(PATH_BUILD)/build/outputs/apk/debug/$(APK_MAIN) $@
-$(APK_MAIN_RELEASE): $(PATH_BUILD_GRADLE) $(PATH_BUILD_ASSETS)/MD5SUMS $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD)
+$(APK_MAIN_RELEASE): $(PATH_BUILD_GRADLE) $(PATH_BUILD_ASSETS)/MD5SUMS $(PATH_BUILD_LIBSCUMMVM) $(PATH_BUILD_LOADER) | $(PATH_BUILD)
(cd $(PATH_BUILD); ./gradlew assembleRelease)
$(CP) $(PATH_BUILD)/build/outputs/apk/release/$(APK_MAIN_RELEASE) $@
-$(AAB_MAIN_RELEASE): $(PATH_BUILD_GRADLE) $(PATH_BUILD_ASSETS)/MD5SUMS $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD)
+$(AAB_MAIN_RELEASE): $(PATH_BUILD_GRADLE) $(PATH_BUILD_ASSETS)/MD5SUMS $(PATH_BUILD_LIBSCUMMVM) $(PATH_BUILD_LOADER) | $(PATH_BUILD)
(cd $(PATH_BUILD); ./gradlew bundleRelease -PsplitAssets)
$(CP) $(PATH_BUILD)/build/outputs/bundle/release/$(AAB_MAIN_RELEASE) $@
diff --git a/backends/platform/android/fatbundle.mk b/backends/platform/android/fatbundle.mk
index 9ad00b27534..b585ede8429 100644
--- a/backends/platform/android/fatbundle.mk
+++ b/backends/platform/android/fatbundle.mk
@@ -2,12 +2,15 @@ ALL_ABIS = armeabi-v7a arm64-v8a x86 x86_64
OTHER_ABIS = $(filter-out $(ABI), $(ALL_ABIS))
PATH_BUILD_LIBSSCUMMVM = $(foreach abi, $(OTHER_ABIS), $(PATH_BUILD)/lib/$(abi)/libscummvm.so)
+PATH_BUILD_LIBSLOADER = $(foreach abi, $(OTHER_ABIS), $(PATH_BUILD)/lib/$(abi)/libloader.so)
ANDROID_CONFIGURE_PATH := $(realpath $(srcdir)/configure)
ANDROID_CONFIGFLAGS := $(filter-out --host=android-%, $(SAVED_CONFIGFLAGS))
define BUILD_ANDROID
SUBPATH_BUILD_LIBSCUMMVM_abi := ./build-android$(1)/libscummvm.so
PATH_BUILD_LIBSCUMMVM_abi := $(PATH_BUILD)/lib/$(1)/libscummvm.so
+SUBPATH_BUILD_LIBLOADER_abi := ./build-android$(1)/libloader.so
+PATH_BUILD_LIBLOADER_abi := $(PATH_BUILD)/lib/$(1)/libloader.so
SUBPATH_BUILDS += ./build-android$(1)
@@ -19,17 +22,26 @@ $$(SUBPATH_BUILD_LIBSCUMMVM_abi): config.mk $$(EXECUTABLE)
"$$(ANDROID_CONFIGURE_PATH)" --host=android-$(1) $$(ANDROID_CONFIGFLAGS))
$$(MAKE) -C "$$(SUBPATH_BUILD)" $$(EXECUTABLE)
+$$(SUBPATH_BUILD_LIBLOADER_abi): SUBPATH_BUILD=./build-android$(1)
+$$(SUBPATH_BUILD_LIBLOADER_abi): $$(SUBPATH_BUILD_LIBSCUMMVM_abi)
+ $$(MAKE) -C "$$(SUBPATH_BUILD)" libloader.so
+
$$(PATH_BUILD_LIBSCUMMVM_abi): PATH_BUILD_LIB=$(PATH_BUILD)/lib/$(1)
$$(PATH_BUILD_LIBSCUMMVM_abi): $$(SUBPATH_BUILD_LIBSCUMMVM_abi)
$$(INSTALL) -d "$$(PATH_BUILD_LIB)"
$$(INSTALL) -c -m 644 "$$<" "$$@"
+$$(PATH_BUILD_LIBLOADER_abi): PATH_BUILD_LIB=$(PATH_BUILD)/lib/$(1)
+$$(PATH_BUILD_LIBLOADER_abi): $$(SUBPATH_BUILD_LIBLOADER_abi)
+ $$(INSTALL) -d "$$(PATH_BUILD_LIB)"
+ $$(INSTALL) -c -m 644 "$$<" "$$@"
+
endef
SUBPATH_BUILDS :=
$(foreach abi,$(OTHER_ABIS),$(eval $(call BUILD_ANDROID,$(abi))))
-androidfatall $(subst android,androidfat,$(ANDROID_BUILD_RULES)): androidfat%: $(PATH_BUILD_LIBSSCUMMVM)
+androidfatall $(subst android,androidfat,$(ANDROID_BUILD_RULES)): androidfat%: $(PATH_BUILD_LIBSSCUMMVM) $(PATH_BUILD_LIBSLOADER)
$(MAKE) $(if $(filter all,$*),$*,android$*)
.PHONY: androidfatall $(subst android,androidfat,$(ANDROID_BUILD_RULES))
diff --git a/backends/platform/android/loader.cpp b/backends/platform/android/loader.cpp
new file mode 100644
index 00000000000..58cfdd77d5e
--- /dev/null
+++ b/backends/platform/android/loader.cpp
@@ -0,0 +1,310 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This translation unit is built into an indepent shared object
+ *
+ * It acts as a proxy between Java and the real ScummVM code to let us unload
+ * ScummVM and clean up global variables when restarting.
+ * As JNI doesn't support unloading libraries, we need to resort to such stunts
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include <jni.h>
+#include <dlfcn.h>
+
+#include <android/log.h>
+
+static const char *android_log_tag = "ScummVM";
+
+#define _ANDROID_LOG(prio, fmt, args...) __android_log_print(prio, android_log_tag, fmt, ## args)
+#define LOGD(fmt, args...) _ANDROID_LOG(ANDROID_LOG_DEBUG, fmt, ##args)
+#define LOGI(fmt, args...) _ANDROID_LOG(ANDROID_LOG_INFO, fmt, ##args)
+#define LOGW(fmt, args...) _ANDROID_LOG(ANDROID_LOG_WARN, fmt, ##args)
+#define LOGE(fmt, args...) _ANDROID_LOG(ANDROID_LOG_ERROR, fmt, ##args)
+
+typedef jint JNICALL (*onLoadFunction)(JavaVM *vm, void *);
+typedef void JNICALL (*onUnloadFunction)(JavaVM *vm, void *);
+
+static JavaVM *vm;
+static void *reserved;
+
+static void *scummvm_so;
+static onLoadFunction scummvm_onLoad;
+static onUnloadFunction scummvm_onUnload;
+static bool dirty;
+
+static jboolean scummvm_reload(JNIEnv *env, jclass clazz);
+
+static const JNINativeMethod reloader_natives[] = {
+ { "reload", "()Z", (void *)scummvm_reload },
+};
+
+static const char *scummvm_so_path = "libscummvm.so";
+static char *full_scummvm_so_path;
+
+char *parse_maps_line(char *line, size_t sz) {
+ char *end, *p;
+
+ // Start address
+ p = (char *)memchr(line, '-', sz);
+ if (p == nullptr) {
+ return nullptr;
+ }
+ *p = '\0';
+ unsigned long int start_addr = strtoul(line, &end, 16);
+ if (end != p) {
+ return nullptr;
+ }
+ p++;
+
+ sz -= p - line;
+ line = p;
+
+ // End address
+ p = (char *)memchr(line, ' ', sz);
+ if (p == nullptr) {
+ return nullptr;
+ }
+ *p = '\0';
+ unsigned long int end_addr = strtoul(line, &end, 16);
+ if (end != p) {
+ return nullptr;
+ }
+ p++;
+
+ if (!((intptr_t)start_addr <= (intptr_t)parse_maps_line && (intptr_t)parse_maps_line < (intptr_t)end_addr)) {
+ // Not our library
+ return nullptr;
+ }
+
+ sz -= p - line;
+ line = p;
+
+ // Flags, pgoff, dev, inode
+ for(int count = 4; count > 0; count--) {
+ sz -= p - line;
+ line = p;
+
+ p = (char *)memchr(line, ' ', sz);
+ if (p == nullptr) {
+ return nullptr;
+ }
+ p++;
+ }
+ if (sz == 0) {
+ return nullptr;
+ }
+
+ // padding
+ for(; sz > 0 && *p == ' '; p++, sz--)
+ ;
+ if (sz == 0) {
+ return nullptr;
+ }
+
+ line = p;
+
+ // file path
+ for (p = line + sz - 1; p > line && *p != '/'; p--)
+ ;
+ if (p == line) {
+ return nullptr;
+ }
+
+ *p = '\0';
+
+ int len = snprintf(nullptr, 0, "%s/%s", line, scummvm_so_path);
+ if (len < 0) {
+ return nullptr;
+ }
+ len++;
+
+ char *path = (char *)malloc(len);
+ if (!path) {
+ return nullptr;
+ }
+
+ len = snprintf(path, len, "%s/%s", line, scummvm_so_path);
+ if (len < 0) {
+ free(path);
+ return nullptr;
+ }
+
+ return path;
+}
+
+char *find_lib_path() {
+ FILE *maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ return nullptr;
+ }
+
+ size_t lineBufferSize = 256;
+ char *lineBuffer = (char *)malloc(lineBufferSize * sizeof(*lineBuffer));
+ if (!lineBuffer) {
+ fclose(maps);
+ return nullptr;
+ }
+ char *lineWPtr = lineBuffer;
+
+ while (1) {
+ size_t readLength = fread(lineWPtr, sizeof(*lineWPtr), lineBufferSize - (lineWPtr - lineBuffer), maps);
+ lineWPtr += readLength;
+ char *nlPtr = (char *)memchr(lineBuffer, '\n', lineWPtr - lineBuffer);
+
+ if (nlPtr == nullptr) {
+ if (lineBufferSize - (lineWPtr - lineBuffer) != 0) {
+ break; // We still have room: we went till end of stream
+ }
+
+ char *newlineBuffer = (char *)realloc(lineBuffer, lineBufferSize * 2);
+ if (!newlineBuffer) {
+ free(lineBuffer);
+ fclose(maps);
+ return nullptr;
+ }
+ lineBuffer = newlineBuffer;
+ lineWPtr = lineBuffer + lineBufferSize;
+ lineBufferSize *= 2;
+
+ // Let's read new content in our larger buffer
+ continue;
+ }
+
+ char *lib_path;
+ if ((lib_path = parse_maps_line(lineBuffer, nlPtr - lineBuffer))) {
+ free(lineBuffer);
+ fclose(maps);
+ return lib_path;
+ }
+
+ nlPtr++;
+ memmove(lineBuffer, nlPtr, lineWPtr - nlPtr);
+ lineWPtr -= nlPtr - lineBuffer;
+ }
+
+ free(lineBuffer);
+ fclose(maps);
+ return nullptr;
+}
+
+__attribute__ ((visibility("default")))
+jint JNICALL JNI_OnLoad(JavaVM *vm_, void *reserved_) {
+ vm = vm_;
+ reserved = reserved_;
+
+ scummvm_so = dlopen(scummvm_so_path, RTLD_NOW | RTLD_LOCAL);
+ if (!scummvm_so) {
+ LOGW("Failed to load libscummvm.so: %s", dlerror());
+ full_scummvm_so_path = find_lib_path();
+ if (full_scummvm_so_path) {
+ scummvm_so = dlopen(full_scummvm_so_path, RTLD_NOW | RTLD_LOCAL);
+ }
+
+ }
+ if (!scummvm_so) {
+ __android_log_assert(nullptr, android_log_tag, "Failed to load libscummvm.so or %s: %s", full_scummvm_so_path, dlerror());
+ return JNI_ERR;
+ }
+ scummvm_onLoad = (onLoadFunction)dlsym(scummvm_so, "JNI_OnLoad");
+ if (!scummvm_onLoad) {
+ LOGD("Didn't find JNI_OnLoad");
+ }
+ scummvm_onUnload = (onUnloadFunction)dlsym(scummvm_so, "JNI_OnUnload");
+ if (!scummvm_onUnload) {
+ LOGD("Didn't find JNI_OnUnload");
+ }
+
+ dirty = false;
+
+ JNIEnv *env;
+
+ if (vm->GetEnv((void **)&env, JNI_VERSION_1_2)) {
+ LOGE("Failed to GetEnv");
+ return JNI_ERR;
+ }
+
+ jclass cls = env->FindClass("org/scummvm/scummvm/ScummVM$Reloader");
+ if (cls == 0) {
+ return JNI_ERR;
+ }
+
+ if (env->RegisterNatives(cls, reloader_natives, sizeof(reloader_natives) / sizeof(*reloader_natives)) < 0) {
+ return JNI_ERR;
+ }
+
+ env->DeleteLocalRef(cls);
+
+ if (scummvm_onLoad) {
+ LOGD("Running ScummVM onLoad");
+ return scummvm_onLoad(vm, reserved);
+ } else {
+ // JNI_OnLoad is optional
+ return JNI_VERSION_1_2;
+ }
+}
+
+__attribute__ ((visibility("default")))
+void JNICALL JNI_OnUnload(JavaVM *vm_, void *reserved_) {
+ if (!scummvm_so) {
+ return;
+ }
+ if (scummvm_onUnload) {
+ LOGD("Running ScummVM onUnload");
+ scummvm_onUnload(vm_, reserved_);
+ }
+ dlclose(scummvm_so);
+ free(full_scummvm_so_path);
+}
+
+static jboolean scummvm_reload(JNIEnv *env, jclass clazz) {
+ if (!dirty) {
+ // Ignore first reload request: we loaded a fresh instance at startup
+ dirty = true;
+ return true;
+ }
+
+ LOGI("Reloading ScummVM");
+ if (scummvm_so) {
+ if (scummvm_onUnload) {
+ LOGD("Running ScummVM onUnload");
+ scummvm_onUnload(vm, reserved);
+ }
+ dlclose(scummvm_so);
+ }
+
+ scummvm_so = dlopen(full_scummvm_so_path ? full_scummvm_so_path : scummvm_so_path, RTLD_NOW | RTLD_LOCAL);
+ if (!scummvm_so) {
+ __android_log_assert(nullptr, android_log_tag, "Failed to load libscummvm.so or %s: %s", full_scummvm_so_path, dlerror());
+ return false;
+ }
+
+ scummvm_onLoad = (onLoadFunction)dlsym(scummvm_so, "JNI_OnLoad");
+ scummvm_onUnload = (onUnloadFunction)dlsym(scummvm_so, "JNI_OnUnload");
+
+ if (!scummvm_onLoad) {
+ return true;
+ }
+ LOGD("Running ScummVM onLoad");
+ return scummvm_onLoad(vm, reserved) != JNI_ERR;
+}
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index 37c223f04d7..3e27a10248d 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -574,16 +574,24 @@ public abstract class ScummVM implements SurfaceHolder.Callback,
return res;
}
- static {
-// // For grabbing with gdb...
-// final boolean sleep_for_debugger = false;
-// if (sleep_for_debugger) {
-// try {
-// Thread.sleep(20 * 1000);
-// } catch (InterruptedException ignored) {
-// }
-// }
-
- System.loadLibrary("scummvm");
- }
+ public static class Reloader {
+ public native static boolean reload();
+
+ static {
+ // // For grabbing with gdb...
+ // final boolean sleep_for_debugger = false;
+ // if (sleep_for_debugger) {
+ // try {
+ // Thread.sleep(20 * 1000);
+ // } catch (InterruptedException ignored) {
+ // }
+ // }
+
+ // The loader will load the real libscummvm.so and resolve the Reloader native
+ // This will allow us to reset ScummVM global variables
+
+ Log.i(LOG_TAG, "Loading ScummVM loader");
+ System.loadLibrary("loader");
+ }
+ };
}
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 9ba500428f1..a5f021c9bb0 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -1135,6 +1135,10 @@ public class ScummVMActivity extends Activity {
super.onCreate(savedInstanceState);
+ if (!ScummVM.Reloader.reload()) {
+ throw new RuntimeException("Can't reload ScummVM");
+ }
+
setLogFile();
safSyncObject = new Object();
More information about the Scummvm-git-logs
mailing list