2 Commits

Author SHA1 Message Date
ml 0f59898bb6 - AndroidManifest.xml: add android:screenOrientation="landscape" and a
build / build (push) Successful in 2m15s
<layout> defaultWidth/defaultHeight/minWidth/minHeight/gravity hint -
  Quest's Home shell otherwise defaults a freshly-launched 2D panel to a
  portrait shape, cropping the game's 4:3 640x480 content down to a
  sliver.
- The actual root cause of the remaining crop (game rendering into one
  corner of an otherwise correctly-sized panel), found via on-device
  logcat and the new diagnostics below rather than guesswork:
  ChangeScreenSize() (engine/src/MacSrc/ShockBitmap.c) calls
  SDL_SetWindowSize() whenever the game sets its video mode. That's a
  desktop-only operation in effect - Android has no SetWindowSize driver
  hook, so SDL's generic layer instead overwrites its own cached window
  size to the game's internal resolution (640x480) and synthesizes a
  resize event from that, desyncing SDL's notion of the window size from
  the real, unchanged Android surface (e.g. 1600x1200). Both SDL's own
  renderer viewport and the engine's custom GL viewport then scale
  against that corrupted cached size. 10-android-no-window-resize.patch
  skips the desktop-only SDL_SetWindowFullscreen/SetWindowSize/
  SetWindowPosition calls on Android, keeping the legitimate
  SDL_RenderSetLogicalSize/offscreen-bitmap setup.
- 06-android-resize-event.patch: also react to SDL_WINDOWEVENT_RESIZED
  in the engine's event loop, not just SIZE_CHANGED - Android's SDL video
  backend never sends SIZE_CHANGED for surface-driven resizes, only
  RESIZED, an independent gap worth closing regardless of the bug above.
- 08/09-android-logcat-*.patch: route the engine's existing log.c output
  (previously plain fprintf(stderr,...), never actually captured by
  logcat on this build) through __android_log_vprint instead, so every
  existing INFO/DEBUG/WARN/ERROR call site becomes visible for on-device
  debugging. This is what made the diagnostic below (and everything
  since) observable at all.
- 07-android-size-diagnostics.patch: one-time startup log comparing
  SDL_GetWindowSize/SDL_GL_GetDrawableSize/SDL_GetRendererOutputSize -
  the evidence that actually pinned down the SDL_SetWindowSize bug above.
- QuestShockActivity.java: add a diagnostic onSizeChanged() log on
  GameSurface, used to rule out a later Android-side panel relayout as
  the cause before finding the real one.
2026-07-24 06:31:18 +02:00
ml 4e47e0a989 Fix missing-assets detection race, 16 KB page alignment, and Android audio backend
build / build (push) Successful in 2m11s
- QuestShockActivity now actually blocks the native engine from starting
  when game data is missing, closing three gaps found via on-device
  testing: super.onCreate() must run unconditionally first (Android
  throws SuperNotCalledException otherwise); SDLActivity.mBrokenLibraries
  is now set provisionally before the storage-permission check, since
  onWindowFocusChanged() closing the permission dialog could otherwise
  start the engine before the async onRequestPermissionsResult() callback
  ran; and a new GameSurface (SDLSurface subclass) closes the actual gap
  that let the init_popups NULL-deref crash through even with
  mBrokenLibraries set - SDLSurface.surfaceChanged() starts the native
  thread directly without ever checking that flag.
- Force Android to use SDL2's openslES audio backend instead of AAudio
  (android/engine-patches/05-android-audio-driver.patch): AAudio only
  allows one open playback device at a time, but the engine opens two
  (cutscene audio via SDL_OpenAudioDevice, SFX/MIDI via Mix_OpenAudio),
  hitting an assertion failure on real hardware.
- Add 16 KB ELF page-size alignment (-Wl,-z,max-page-size=16384) to every
  Android shared library - the four prebuilts (SDL2, SDL2_mixer,
  fluidsynth-lite, gl4es, in build-image/Dockerfile) and the engine's own
  libmain.so (build.gradle) - matching Google's Play Store requirement
  for Android 15+ and clearing Android Studio's compatibility warning.
- Add a stageEngine Gradle task that automatically re-stages the patched
  engine/ copy and prebuilt libraries before any Android Studio build
  (hooked into preBuild, with proper up-to-date checking), so source/
  patch changes can't silently go stale in the build/android-engine
  scratch copy - previously a manual, easy-to-forget step. Skips
  automatically inside the build-image container so make apk/CI are
  unaffected.
2026-07-23 19:20:22 +02:00
16 changed files with 324 additions and 15 deletions
+49
View File
@@ -27,6 +27,49 @@ if (prebuiltDir == null) {
def questshockVersionName = project.hasProperty('questshockVersionName') ? project.property('questshockVersionName') : '0.0.0-dev'
def questshockVersionCode = project.hasProperty('questshockVersionCode') ? project.property('questshockVersionCode').toInteger() : 1
// Auto-refreshes the staged, patched engine/ copy and prebuilt libraries
// (see build-image/prepare-android-project.sh --host-paths, and the
// engineDir/prebuiltDir properties read above) so a plain Android Studio
// build/run can never silently compile against a stale scratch copy after
// engine/ or android/engine-patches/ change - previously a manual, easy to
// forget re-run. inputs/outputs are declared so Gradle skips the (Docker-
// invoking, not free) step entirely when nothing relevant actually changed,
// keeping pure-Java edit/run cycles fast.
def repoRoot = file("${projectDir}/../..")
def stageEngineTask = tasks.register("stageEngine", Exec) {
group = "build setup"
description = "Refreshes the patched engine/ scratch copy and prebuilt libraries for a native Android Studio build (build-image/prepare-android-project.sh --host-paths)."
workingDir repoRoot
commandLine "./run-image.sh", "bash", "build-image/prepare-android-project.sh", "--host-paths"
// /opt/prebuilt/android only ever exists inside the build-image
// container itself (baked in at image-build time, never on the host -
// see build-image/Dockerfile). Its presence means this build is
// build-apk.sh's own gradlew call, running INSIDE that container, which
// already staged everything itself in container mode before invoking
// gradlew - re-running this task there would try to `docker run` from
// inside a container with no docker socket, breaking make apk/CI. Only
// a genuine host-side Android Studio build (where this path is absent)
// needs this task.
onlyIf { !file('/opt/prebuilt/android').isDirectory() }
inputs.dir("${repoRoot}/engine")
inputs.dir("${projectDir}/../engine-patches")
inputs.file("${repoRoot}/build-image/Dockerfile")
outputs.dir("${repoRoot}/build/android-engine")
outputs.dir("${repoRoot}/build/android-prebuilt")
outputs.file("${projectDir}/../engine.properties")
}
// preBuild is what every variant's compile/native-build tasks already
// transitively depend on, regardless of AGP version's exact CMake task
// naming - the simplest reliable hook to run before any of them.
afterEvaluate {
tasks.named("preBuild").configure {
dependsOn stageEngineTask
}
}
android {
namespace "de.ladkau.questshock"
// compileSdk/buildToolsVersion/ndkVersion must all match what
@@ -67,6 +110,11 @@ android {
// which would otherwise miss our custom-installed SDL2 (see
// build-image/Dockerfile's SDL2_mixer build, which hit the
// exact same thing).
// -Wl,-z,max-page-size=16384: NDK 26 doesn't 16 KB-align ELF
// LOAD segments by default (only automatic in NDK 28+) - see
// build-image/Dockerfile's ANDROID_16KB_LDFLAGS, which does
// the same for the prebuilt SDL2/SDL2_mixer/fluidsynth-lite/
// gl4es .so's this links against.
arguments "-DENABLE_SDL2=ON", "-DENABLE_SOUND=BUNDLED", "-DENABLE_FLUIDSYNTH=BUNDLED", \
"-DANDROID_PREBUILT_DIR=${prebuiltDir}", \
"-DCMAKE_PREFIX_PATH=${prebuiltDir}/sdl2", \
@@ -74,6 +122,7 @@ android {
"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH", \
"-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH", \
"-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH", \
"-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-z,max-page-size=16384", \
"-DANDROID_EXTRA_SOURCES=${projectDir}/src/main/cpp/questshock_native.c"
abiFilters 'arm64-v8a'
}
+14
View File
@@ -34,6 +34,7 @@
android:launchMode="singleInstance"
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
android:preferMinimalPostProcessing="true"
android:screenOrientation="landscape"
android:exported="true"
>
<intent-filter>
@@ -43,6 +44,19 @@
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<!-- Without this, Quest's Home shell picks its own default 2D
panel shape for this activity - a tall portrait rectangle,
which crops a 4:3 landscape game like System Shock's classic
640x480 down to a sliver. defaultWidth/defaultHeight (the
standard Android multi-window sizing hint, which Quest's
panel system honors for 2D apps) requests a properly
landscape, 4:3-ish panel instead; minWidth/minHeight keep it
from being resized below the game's native resolution. -->
<layout android:defaultWidth="1280dp"
android:defaultHeight="960dp"
android:minWidth="640dp"
android:minHeight="480dp"
android:gravity="center" />
</activity>
</application>
@@ -2,10 +2,14 @@ package de.ladkau.questshock;
import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.SurfaceHolder;
import androidx.core.app.ActivityCompat;
import java.io.File;
import java.io.FileOutputStream;
@@ -13,16 +17,26 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.libsdl.app.SDLActivity;
import org.libsdl.app.SDLSurface;
/**
* Everything here runs before super.onCreate() (which is what loads the
* native libraries and eventually calls Shockolate's own main()) - see
* android/engine-patches/ for why: engine/ itself is never modified, so
* this Java-side setup is the only place left to prepare
* /sdcard/questshock/ the way Shockolate's plain relative-path file I/O
* ("res/data/...", confirmed via grep - none of it goes through SDL_RWops,
* so Android's asset-manager fallback for SDL_RWFromFile doesn't apply
* here) expects to find it.
* Prepares /sdcard/questshock/ the way Shockolate's plain relative-path
* file I/O ("res/data/...", confirmed via grep - none of it goes through
* SDL_RWops, so Android's asset-manager fallback for SDL_RWFromFile
* doesn't apply here) expects to find it - see android/engine-patches/ for
* why this is done from Java instead of patching engine/ itself, which is
* never modified.
*
* super.onCreate() (SDLActivity's) must always run first and unconditionally
* - Android throws SuperNotCalledException otherwise, checked right after
* onCreate() returns, regardless of what this subclass does afterwards.
* SDLActivity's own onCreate() only loads libraries, sets up JNI and the
* surface - the actual native SDL_main thread doesn't start until later, from
* one of several lifecycle paths (onResume(), onWindowFocusChanged(), and
* SDLSurface.surfaceChanged() - see GameSurface below for why that last one
* needs its own fix) - so it's safe to do our own checks (and set
* SDLActivity.mBrokenLibraries, which most - but not all - of those paths
* gate on) afterwards.
*/
public class QuestShockActivity extends SDLActivity {
private static final String TAG = "QuestShock";
@@ -45,8 +59,6 @@ public class QuestShockActivity extends SDLActivity {
// has no public chdir() (confirmed against the actual API 34 stub jar).
private static native void nativeChdir(String path);
private Bundle mSavedInstanceState;
@Override
protected String[] getLibraries() {
return new String[] {
@@ -57,9 +69,66 @@ public class QuestShockActivity extends SDLActivity {
};
}
// SDLSurface.surfaceChanged() (org/libsdl/app/, vendored from SDL2's own
// template) starts the native SDL thread directly - unlike onResume()/
// onWindowFocusChanged(), it never checks SDLActivity.mBrokenLibraries
// first. Since surfaceChanged() fires on essentially every launch
// regardless of that flag, setting mBrokenLibraries alone (see onCreate()/
// setUpGameDirAndContinue()) does NOT actually stop the engine from
// starting - confirmed on-device: the missing-assets crash still happened
// with mBrokenLibraries set, from exactly this path. Route through a
// subclass that adds the missing check instead of patching the vendored
// file directly.
private static class GameSurface extends SDLSurface {
GameSurface(Context context) {
super(context);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (SDLActivity.mBrokenLibraries) {
return;
}
super.surfaceChanged(holder, format, width, height);
}
// Diagnostic only (see OpenGL.cc's matching log) - Android's real,
// legitimate signal for "this View's laid-out size actually
// changed" is onSizeChanged(), fired by the framework itself, not
// something we have to poll or guess about. If Quest's Home shell
// settles a freshly-launched panel into its final <layout> size via
// a genuine later layout pass, this is where that would show up -
// confirming whether a real event exists to hook, before writing
// any fix around it.
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.i(TAG, "GameSurface.onSizeChanged() " + oldw + "x" + oldh + " -> " + w + "x" + h);
}
}
@Override
protected SDLSurface createSDLSurface(Context context) {
return new GameSurface(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
mSavedInstanceState = savedInstanceState;
super.onCreate(savedInstanceState);
if (SDLActivity.mBrokenLibraries) {
// SDLActivity's own onCreate() already showed its "SDL Error"
// dialog for this - nothing left for us to do.
return;
}
// Provisionally block the native engine from starting - cleared only
// once setUpGameDirAndContinue() confirms game data is present. Must
// happen before requestPermissions() below: the storage-permission
// dialog closing can fire onWindowFocusChanged(true) - which starts
// the native SDLThread - before the async onRequestPermissionsResult()
// callback (which is what actually calls setUpGameDirAndContinue())
// gets a chance to run, so mBrokenLibraries has to already be true
// going into that race, not set afterwards.
SDLActivity.mBrokenLibraries = true;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
@@ -102,19 +171,28 @@ public class QuestShockActivity extends SDLActivity {
// and, on a fresh install with no game data copied in yet, that
// fails deep inside startup (init_popups(), which doesn't NULL-check
// the load) as a hard native crash instead of a message. Catch the
// missing-data case here instead, before super.onCreate() ever
// starts Shockolate's native main().
// missing-data case here instead, before Shockolate's native
// SDL_main ever starts.
if (!isNonEmptyDir(new File(gameDir, "res/data")) || !isNonEmptyDir(new File(gameDir, "res/sound"))) {
// mBrokenLibraries is already true (set in onCreate()) - leave it
// that way. Every path that starts the native SDL_main thread
// (onWindowFocusChanged(), resumeNativeThread(), etc.) already
// checks this flag before doing anything native, so this
// reliably prevents the engine from starting without having to
// duplicate all of SDLActivity's own lifecycle guards ourselves.
showMissingAssetsDialog();
return;
}
// chdir() is process-wide, not per-thread - already in effect for
// every thread (including the one that will run Shockolate's own
// SDL_main) by the time super.onCreate() below starts it.
// SDL_main) by the time the native thread actually starts. Done
// before clearing mBrokenLibraries below so the engine can never
// start pre-chdir.
nativeChdir(GAME_DIR);
super.onCreate(mSavedInstanceState);
// Assets confirmed present - safe to let the native engine start now.
SDLActivity.mBrokenLibraries = false;
}
private static boolean isNonEmptyDir(File dir) {
@@ -0,0 +1,20 @@
--- a/src/MacSrc/Shock.c
+++ b/src/MacSrc/Shock.c
@@ -162,6 +162,17 @@
void InitSDL() {
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
+#ifdef __ANDROID__
+ // SDL2 2.28.5's AAudio backend (Android's default since API 26) only
+ // ever allows one open non-capture (playback) device at a time - it
+ // keeps a single static handle and asserts on a second open
+ // ('SDL_assert((audioDevice == NULL) || iscapture)' in
+ // src/audio/aaudio/SDL_aaudio.c). SDLSound.c opens two: one directly via
+ // SDL_OpenAudioDevice() for cutscene audio, one via Mix_OpenAudio() for
+ // SFX/MIDI. The older OpenSL ES backend has no such limitation, so force
+ // it instead of AAudio.
+ SDL_SetHint(SDL_HINT_AUDIODRIVER, "openslES");
+#endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) {
DEBUG("%s: Init failed", __FUNCTION__);
}
@@ -0,0 +1,27 @@
--- a/src/Libraries/INPUT/Source/sdl_events.c
+++ b/src/Libraries/INPUT/Source/sdl_events.c
@@ -737,7 +737,24 @@
break;
case SDL_WINDOWEVENT_MOVED:
+ break;
+
case SDL_WINDOWEVENT_RESIZED:
+#ifdef __ANDROID__
+ // Android's SDL video backend (Android_SendResize() in
+ // src/video/android/SDL_androidvideo.c) only ever sends
+ // RESIZED for surface-driven resizes - e.g. the Quest Home
+ // shell settling the 2D panel into its requested <layout>
+ // defaultWidth/defaultHeight after activity launch - never
+ // SIZE_CHANGED (unlike desktop platforms, where an
+ // app-driven SDL_SetWindowSize() triggers both, handled
+ // above). Without this, opengl_resize() never re-runs after
+ // that late resize, leaving the GL viewport stuck at
+ // whatever (smaller) size the window first reported,
+ // rendered into one corner of the now-larger surface.
+ if (can_use_opengl())
+ opengl_resize(ev.window.data1, ev.window.data2);
+#endif
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
@@ -0,0 +1,24 @@
--- a/src/MacSrc/OpenGL.cc
+++ b/src/MacSrc/OpenGL.cc
@@ -332,6 +332,21 @@
int width, height;
SDL_GetWindowSize(window, &width, &height);
+#ifdef __ANDROID__
+ // Temporary diagnostic: the reported panel/surface size is correct
+ // (confirmed via SDLSurface's own "Window size" log and a Java-side
+ // onSizeChanged() probe showing no later relayout), but the rendered
+ // content still only fills a small corner of it. That means the
+ // divergence is somewhere below the Java/Activity layer - between what
+ // SDL_GetWindowSize() (the value used for opengl_resize() below) reports
+ // and what SDL/EGL/gl4es actually believe the live drawable size is.
+ // Compare all three directly instead of guessing further.
+ int drawable_w, drawable_h, output_w, output_h;
+ SDL_GL_GetDrawableSize(window, &drawable_w, &drawable_h);
+ SDL_GetRendererOutputSize(renderer, &output_w, &output_h);
+ INFO("Android size diag: SDL_GetWindowSize=%dx%d SDL_GL_GetDrawableSize=%dx%d SDL_GetRendererOutputSize=%dx%d",
+ width, height, drawable_w, drawable_h, output_w, output_h);
+#endif
opengl_resize(width, height);
// Now make the palettes
@@ -0,0 +1,10 @@
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -440,6 +440,7 @@
${FLUIDSYNTH_LIBRARIES}
${OPENGL_LIBRARIES}
${ALSA_LIBRARIES}
+ $<$<BOOL:${ANDROID}>:log>
)
# Turn on address sanitizing if wanted (desktop only - not meaningful for
@@ -0,0 +1,45 @@
--- a/src/Libraries/LG/Source/LOG/src/log.c
+++ b/src/Libraries/LG/Source/LOG/src/log.c
@@ -28,6 +28,10 @@
#include "log.h"
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
static struct {
void *udata;
log_LockFn lock;
@@ -99,6 +103,23 @@
time_t t = time(NULL);
struct tm *lt = localtime(&t);
+#ifdef __ANDROID__
+ /* Plain stdout/stderr isn't captured by logcat on this platform (unlike
+ e.g. gl4es's own "LIBGL"-tagged logging, which goes through this same
+ API directly) - route there instead so every existing INFO/DEBUG/WARN/
+ ERROR call site in the engine becomes visible for on-device debugging,
+ with no changes needed at those call sites. */
+ if (!L.quiet) {
+ static const int android_priority[] = {
+ ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL
+ };
+ va_list args;
+ va_start(args, fmt);
+ __android_log_vprint(android_priority[level], "QuestShock", fmt, args);
+ va_end(args);
+ }
+#else
/* Log to stderr */
if (!L.quiet) {
va_list args;
@@ -116,6 +137,7 @@
va_end(args);
fprintf(stderr, "\n");
}
+#endif
/* Log to file */
if (L.fp) {
@@ -0,0 +1,31 @@
--- a/src/MacSrc/ShockBitmap.c
+++ b/src/MacSrc/ShockBitmap.c
@@ -42,11 +42,28 @@
SDL_RenderClear(renderer);
+#ifndef __ANDROID__
+ // On Android there's exactly one OS-controlled-size surface - no
+ // desktop-style window to resize, move, or toggle fullscreen on.
+ // SDL_SetWindowSize() still "succeeds" there (Android has no
+ // SetWindowSize driver hook, so SDL's generic layer just overwrites its
+ // own cached window->w/h to the requested game resolution, e.g.
+ // 640x480, and synthesizes a resize event from that) - which desyncs
+ // SDL's own notion of the window size from the real, unchanged Android
+ // surface size (e.g. 1600x1200), and that desync is exactly what then
+ // makes both SDL's internal renderer viewport and this engine's own
+ // custom GL viewport (OpenGL.cc's opengl_resize(), driven by that same
+ // now-wrong cached size) shrink to the game's resolution instead of
+ // filling the real surface - confirmed on-device via added logcat
+ // diagnostics (see android/engine-patches/07-09) showing the correct
+ // 1600x1200 size at startup, then this exact call sequence collapsing
+ // it to 640x480 the moment the splash screen sets its video mode.
extern bool fullscreenActive;
SDL_SetWindowFullscreen(window, fullscreenActive ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+#endif
SDL_RenderSetLogicalSize(renderer, width, height);
+11
View File
@@ -46,6 +46,13 @@ ARG ANDROID_COMPILE_SDK_VERSION=34
ARG ANDROID_BUILD_TOOLS_VERSION=34.0.0
ARG ANDROID_NDK_VERSION=26.1.10909125
ARG ANDROID_CMAKE_VERSION=3.22.1
# NDK 26 doesn't yet default to 16 KB-aligned ELF LOAD segments (that only
# became automatic in NDK 28+) - Google requires this for Play Store
# submissions targeting Android 15+ as of Nov 2025, and it's cheap/harmless
# to do regardless of Play Store status. Passed to every Android shared-lib
# CMake configure below, and to the engine's own build via
# android/app/build.gradle's externalNativeBuild.cmake.arguments.
ARG ANDROID_16KB_LDFLAGS="-Wl,-z,max-page-size=16384"
# GL4ES (https://github.com/ptitSeb/gl4es) - translates the engine's
# desktop-style immediate-mode OpenGL calls into real GLES/EGL calls, so
# engine/src/MacSrc/OpenGL.cc needs no immediate-mode rewrite on Android.
@@ -182,6 +189,7 @@ RUN curl -sSLO "https://www.libsdl.org/release/SDL2-${ANDROID_SDL2_VERSION}.tar.
-DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM="android-${ANDROID_PLATFORM_VERSION}" \
-DCMAKE_INSTALL_PREFIX=/opt/prebuilt/android/sdl2 -DBUILD_SHARED_LIBS=ON \
-DSDL_STATIC=OFF \
-DCMAKE_SHARED_LINKER_FLAGS="${ANDROID_16KB_LDFLAGS}" \
&& cmake --build build-sdl2-android -j"$(nproc)" \
&& cmake --install build-sdl2-android \
&& rm -rf "SDL2-${ANDROID_SDL2_VERSION}" "SDL2-${ANDROID_SDL2_VERSION}.tar.gz" build-sdl2-android
@@ -207,6 +215,7 @@ RUN curl -sSLO "https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${A
-DSDL2MIXER_FLAC=OFF -DSDL2MIXER_GME=OFF -DSDL2MIXER_MOD=OFF \
-DSDL2MIXER_MP3=OFF -DSDL2MIXER_MIDI=OFF -DSDL2MIXER_OPUS=OFF -DSDL2MIXER_VORBIS=OFF \
-DSDL2MIXER_WAVPACK=OFF \
-DCMAKE_SHARED_LINKER_FLAGS="${ANDROID_16KB_LDFLAGS}" \
&& cmake --build build-sdl2mixer-android -j"$(nproc)" \
&& cmake --install build-sdl2mixer-android \
&& rm -rf "SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}" "SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}.tar.gz" build-sdl2mixer-android
@@ -222,6 +231,7 @@ RUN git clone https://github.com/EtherTyper/fluidsynth-lite.git fluidsynth-lite-
&& cmake -S fluidsynth-lite-android -B build-fluidsynth-android \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_TOOLCHAIN}" \
-DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM="android-${ANDROID_PLATFORM_VERSION}" \
-DCMAKE_SHARED_LINKER_FLAGS="${ANDROID_16KB_LDFLAGS}" \
&& cmake --build build-fluidsynth-android -j"$(nproc)" \
&& mkdir -p /opt/prebuilt/android/fluidsynth-lite/lib /opt/prebuilt/android/fluidsynth-lite/include \
&& cp -a build-fluidsynth-android/src/libfluidsynth.so* /opt/prebuilt/android/fluidsynth-lite/lib/ \
@@ -254,6 +264,7 @@ RUN git clone --branch "v${ANDROID_GL4ES_VERSION}" --depth 1 \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_TOOLCHAIN}" \
-DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM="android-${ANDROID_PLATFORM_VERSION}" \
-DANDROID=ON -DUSE_ANDROID_LOG=ON -DSTATICLIB=OFF \
-DCMAKE_SHARED_LINKER_FLAGS="${ANDROID_16KB_LDFLAGS}" \
&& cmake --build build-gl4es-android -j"$(nproc)" \
&& mkdir -p /opt/prebuilt/android/gl4es/lib /opt/prebuilt/android/gl4es/include \
&& cp -a gl4es-android/lib/libGL.so.1 /opt/prebuilt/android/gl4es/lib/libGL.so \
Binary file not shown.

After

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB