diff --git a/AGENTS.md b/AGENTS.md index f7d99ef..e9e0dd7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,7 @@ a cross-platform port of the original engine, in an Android app that: - shows a menu and an on-screen keyboard as separate OpenXR quads, operated by a laser pointer from either controller; - maps controller inputs to game actions through a configurable binding layer; -- persists settings in `/sdcard/questshock/questshock.config.json`. +- persists settings in `/sdcard/QuestShock/questshock.config.json`. A desktop (Linux/Windows) build of the same engine exists for development without a headset; it has none of the Android/OpenXR code. @@ -94,13 +94,14 @@ not print it or commit it. | `xr_input.c/.h` | Controller input: OpenXR actions, raw-input layer, action bindings, capture-mode rebinding, key injection into the engine, free view, cyberspace scaling, snap-turn/smooth-move of the player's position, pointer rays. The largest file. | | `xr_mouse.c/.h` | Injects mouse motion and clicks into the engine. | | `qs_config.c/.h` | Configuration: JSON parse, apply, write; screen profiles; menu-driven requests; cheats; menu-triggered pause. | +| `qs_splash.c/.h` | Startup splash: `qs_splash_show()` fades the image (`res/splash.png`, decoded by Java and passed in through `nativeSetSplashImage`) in and out on the game screen. Called from engine patch 27. | | `questshock_native.c` | JNI exports and small native helpers (`nativeChdir`, `nativeSendPrintableChar`, ...). | ### 4.2 Java classes | Class | Role | |---|---| -| `QuestShockActivity` | `SDLActivity` subclass. Creates `/sdcard/questshock/`, extracts bundled files, blocks engine start until the game data exists. | +| `QuestShockActivity` | `SDLActivity` subclass. Creates `/sdcard/QuestShock/`, extracts bundled files, blocks engine start until the game data exists. | | `OverlayPanel` | Base for the off-screen panels: builds the view tree, renders it to a texture, turns ray hits into touch events. | | `MenuOverlay` | The menu: Main Menu, Video Options, Controller Options and Cheats tabs. Singleton. | | `KeyboardOverlay` | The on-screen keyboard. | @@ -116,6 +117,10 @@ not print it or commit it. first-party code log under the tag `QuestShock`. - Bindings, config and menu state are owned natively. Java only displays and requests. +- `gScreenWide`/`gScreenHigh` are only set by the engine's + `ChangeScreenSize()`, so they are 0 until its first mode change. Code that + runs during startup (such as `qs_splash_show()`) takes the screen size from + `xr_get_game_swapchain_size()` instead. ### 4.4 Threads and cross-thread requests @@ -314,15 +319,19 @@ toggles. ## 10. Bundled files on the device -`QuestShockActivity` extracts these into `/sdcard/questshock/`: +`QuestShockActivity` extracts these into `/sdcard/QuestShock/`: - shaders, the default soundfont and `GET_ASSETS_QUEST.txt` (once); - `questshock.config.default.json` and `controller_profiles/` (every launch, overwriting only the bundled filenames so user-added profiles survive). -`res/controller_profiles/` and the default config are staged into APK assets by -`build-image/prepare-android-project.sh`; new staged directories must also be -declared as `inputs.dir(...)` of `stageEngine` in `android/app/build.gradle`. +`res/splash.png` stays in the APK assets and is not extracted to the device; +`QuestShockActivity` decodes it and hands the pixels to `qs_splash.c`. + +`res/controller_profiles/`, `res/splash.png` and the default config are staged +into APK assets by `build-image/prepare-android-project.sh`; every staged file +or directory must also be declared as `inputs.file(...)`/`inputs.dir(...)` of +`stageEngine` in `android/app/build.gradle`. The proprietary game data (`res/data/`, `res/sound/`) is never bundled or committed. The app refuses to start the engine until it is present. diff --git a/CONFIG.md b/CONFIG.md index a38284b..f8f553a 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -3,7 +3,7 @@ QuestShock can load a persistent, human-editable configuration file at startup, in addition to (and independent of) the engine's own `prefs.txt`. -- **Location**: `/sdcard/questshock/questshock.config.json` - the same +- **Location**: `/sdcard/QuestShock/questshock.config.json` - the same folder you already copy `res/data`/`res/sound` into (see README.md section 5.1). - **Format**: plain JSON, grouped by concern. Each top-level key is a @@ -421,7 +421,7 @@ profile is active. The Controller Options tab shows a labeled illustration of whichever controller is actually active, driven by `res/controller_profiles/*.json` -(bundled with the app, copied to `/sdcard/questshock/controller_profiles/` +(bundled with the app, copied to `/sdcard/QuestShock/controller_profiles/` on every launch, refreshing the known bundled files without touching any extra ones you add there yourself). Each file: diff --git a/README.md b/README.md index a3b5c92..1e8409c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ sideloaded Android app, and needs your own copy of the game data. - `res/questshock.config.default.json` - the bundled default configuration (see [CONFIG.md](CONFIG.md)); `res/controller_profiles/` - the per-controller illustrations and labels shown in the Controller Options tab (see - [CONFIG.md](CONFIG.md)). Both are copied into the APK. + [CONFIG.md](CONFIG.md)). Both are copied into the APK, as is + `res/splash.png`, the startup splash image. - `Makefile` - targets: - `all` (default) - alias for `dist`. - `build-image` - builds the Docker build-image (`./build-image.sh`). @@ -293,17 +294,17 @@ debugging enabled.) 1. Install the APK with [SideQuest](https://sidequestvr.com/) (or `adb install`). 2. Launch it once. It'll ask for storage permission, then create - `/sdcard/questshock/` and extract its own bundled files (shaders, a + `/sdcard/QuestShock/` and extract its own bundled files (shaders, a default MIDI soundfont) there - `res/data/` and `res/sound/` are deliberately left missing, since that's the proprietary game data. 3. With the headset connected to a PC, use SideQuest's file browser (or any MTP file manager) to copy your own `res/data/` and `res/sound/` - (see `/sdcard/questshock/GET_ASSETS_QUEST.txt`, extracted in step 2, + (see `/sdcard/QuestShock/GET_ASSETS_QUEST.txt`, extracted in step 2, for exactly what's needed and where it comes from) into - `/sdcard/questshock/res/`. + `/sdcard/QuestShock/res/`. 4. Launch it again. -`/sdcard/questshock/` also holds an optional persistent settings file, +`/sdcard/QuestShock/` also holds an optional persistent settings file, `questshock.config.json`, loaded automatically at startup and saved/loaded from the in-headset menu - see [CONFIG.md](CONFIG.md) for the documented options. Every launch also refreshes `questshock.config.default.json` (the @@ -314,6 +315,9 @@ alone. #### 5.1.1. Menu, controls and cheats +At startup the QuestShock title (`res/splash.png`) fades in and out on the +virtual screen for a few seconds, before the game's own intro screens. + The controller's menu button (left controller) opens and closes the menu. It can't be reassigned. Opening it pauses a running game; closing it resumes the game, unless it was already paused. Aim with either hand's ray and pull the @@ -411,7 +415,7 @@ the same as any other Android Studio project: Android Studio builds, installs, and launches the app on the headset directly. No manual `adb install` or SideQuest step needed for this loop. -4. `/sdcard/questshock/` (game assets, extracted shaders/soundfont) +4. `/sdcard/QuestShock/` (game assets, extracted shaders/soundfont) persists across reinstalls from Android Studio, so this loop doesn't require re-copying `res/data/`/`res/sound/` on every iteration - only a full uninstall or wiping that directory clears it. @@ -611,6 +615,9 @@ each one does: - **`26-android-cheat-refill.patch`** - adds `qs_cheat_refill()` to `hkeyfunc.c`, which tops up health, energy, ammo and items (grenades and drugs the player already holds) for the Cheats tab's unlimited switches. +- **`27-android-startup-splash.patch`** - `setup.c`'s `splash_draw()` calls + `qs_splash_show()` first, which fades the QuestShock title in and out on the + game screen before the engine's own splash screens. `android/gl4es-patches/`: diff --git a/android/app/build.gradle b/android/app/build.gradle index 08b1802..9598859 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,6 +107,7 @@ def stageEngineTask = tasks.register("stageEngine", Exec) { // Staged into assets/ by prepare-android-project.sh. inputs.file("${repoRoot}/res/questshock.config.default.json") inputs.dir("${repoRoot}/res/controller_profiles") + inputs.file("${repoRoot}/res/splash.png") outputs.dir("${repoRoot}/build/android-engine") outputs.dir("${repoRoot}/build/android-gl4es") outputs.dir("${repoRoot}/build/android-prebuilt") @@ -138,7 +139,7 @@ android { // 29, not compileSdk's 34, is deliberate - see build-image/Dockerfile's // comment on ANDROID_PLATFORM_VERSION: paired with // requestLegacyExternalStorage in the manifest, this keeps - // /sdcard/questshock/ a plain, unrestricted shared folder instead of + // /sdcard/QuestShock/ a plain, unrestricted shared folder instead of // Android 11+ scoped storage, on the Quest 2/3/3S/Pro headsets this // targets - not just whatever's the current API level today. minSdkVersion 24 @@ -199,7 +200,7 @@ android { "-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;${projectDir}/src/main/cpp/xr_session.c;${projectDir}/src/main/cpp/xr_input.c;${projectDir}/src/main/cpp/xr_overlay.c;${projectDir}/src/main/cpp/xr_swapchain.c;${projectDir}/src/main/cpp/xr_mouse.c;${projectDir}/src/main/cpp/qs_config.c" + "-DANDROID_EXTRA_SOURCES=${projectDir}/src/main/cpp/questshock_native.c;${projectDir}/src/main/cpp/xr_session.c;${projectDir}/src/main/cpp/xr_input.c;${projectDir}/src/main/cpp/xr_overlay.c;${projectDir}/src/main/cpp/xr_swapchain.c;${projectDir}/src/main/cpp/xr_mouse.c;${projectDir}/src/main/cpp/qs_config.c;${projectDir}/src/main/cpp/qs_splash.c" abiFilters 'arm64-v8a' } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2b8b7e2..292048e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -38,6 +38,11 @@ OpenXR session to actually start. --> + + + + immersive OpenXR entry point, plus Meta's own VR + category, which Horizon OS looks for to treat the app as + a VR app. Other runtimes ignore the Meta one. --> + diff --git a/android/app/src/main/cpp/qs_config.h b/android/app/src/main/cpp/qs_config.h index 06f6b7b..3dae568 100644 --- a/android/app/src/main/cpp/qs_config.h +++ b/android/app/src/main/cpp/qs_config.h @@ -10,7 +10,7 @@ extern "C" { // Persistent, human-editable JSON config file - see CONFIG.md at the repo // root for the documented schema. Lives at a plain relative path // ("questshock.config.json") since the whole engine already runs with its -// cwd chdir()'d to the user-facing /sdcard/questshock/ folder (see +// cwd chdir()'d to the user-facing /sdcard/QuestShock/ folder (see // QuestShockActivity.java's nativeChdir()) before main() ever runs - the // same way prefs.txt/keybinds.txt (Prefs.c) already do. // diff --git a/android/app/src/main/cpp/qs_splash.c b/android/app/src/main/cpp/qs_splash.c new file mode 100644 index 0000000..452e117 --- /dev/null +++ b/android/app/src/main/cpp/qs_splash.c @@ -0,0 +1,110 @@ +#include "qs_splash.h" + +#include +#include +#include +#include + +#include "xr_session.h" + +#define QS_SPLASH_HOLD_MS 2500 +#define QS_SPLASH_FADE_MS 500 +#define QS_SPLASH_TOTAL_MS (QS_SPLASH_HOLD_MS + 2 * QS_SPLASH_FADE_MS) +// The XR session only starts once frames are polled, so the display time is +// counted from the first submitted frame; this bounds the wait for it. +#define QS_SPLASH_SESSION_TIMEOUT_MS 5000 + +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "QuestShock", __VA_ARGS__) + +extern void pump_events(void); +// OpenGL.cc (engine patch 11): draws a software frame into the XR game swapchain. +extern void android_composite_software_frame(SDL_Surface *ui); + +static SDL_Surface *s_image = NULL; + +void qs_splash_set_image(const uint32_t *argb, int width, int height) { + if (s_image != NULL) { + SDL_FreeSurface(s_image); + s_image = NULL; + } + if (argb == NULL || width <= 0 || height <= 0) + return; + s_image = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_ARGB8888); + if (s_image == NULL) + return; + for (int y = 0; y < height; y++) + memcpy((uint8_t *)s_image->pixels + (size_t)y * s_image->pitch, argb + (size_t)y * width, + (size_t)width * sizeof(uint32_t)); + SDL_SetSurfaceBlendMode(s_image, SDL_BLENDMODE_BLEND); +} + +// 0-255 brightness at `elapsed` ms after the first frame. +static int qs_splash_level(uint32_t elapsed) { + uint32_t edge = elapsed < QS_SPLASH_TOTAL_MS - elapsed ? elapsed : QS_SPLASH_TOTAL_MS - elapsed; + if (edge >= QS_SPLASH_FADE_MS) + return 255; + return (int)(255 * edge / QS_SPLASH_FADE_MS); +} + +void qs_splash_show(void) { + if (s_image == NULL) { + LOGI("Startup splash: no image"); + return; + } + int width, height; + xr_get_game_swapchain_size(&width, &height); + if (width <= 0 || height <= 0) { + LOGI("Startup splash: no XR game swapchain"); + return; + } + + // The image is stretched over the whole swapchain image, so it is + // letterboxed into a canvas of that size to keep its aspect ratio. + SDL_Surface *canvas = + SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGBA32); + if (canvas == NULL) + return; + const float scale = SDL_min((float)width / s_image->w, (float)height / s_image->h); + SDL_Rect dst; + dst.w = (int)(s_image->w * scale); + dst.h = (int)(s_image->h * scale); + dst.x = (width - dst.w) / 2; + dst.y = (height - dst.h) / 2; + + const uint32_t waitStart = SDL_GetTicks(); + uint32_t shownStart = 0; + uint32_t elapsed = 0; + int drawnLevel = -1; + bool done = false; + + while (!done) { + pump_events(); + xr_poll_events(); + if (xr_frame_begin()) { + if (shownStart == 0) + shownStart = SDL_GetTicks(); + elapsed = SDL_GetTicks() - shownStart; + if (elapsed >= QS_SPLASH_TOTAL_MS) { + elapsed = QS_SPLASH_TOTAL_MS; + done = true; + } + const int level = qs_splash_level(elapsed); + if (level != drawnLevel) { + drawnLevel = level; + SDL_FillRect(canvas, NULL, SDL_MapRGB(canvas->format, 0, 0, 0)); + SDL_SetSurfaceColorMod(s_image, level, level, level); + SDL_BlitScaled(s_image, NULL, canvas, &dst); + } + android_composite_software_frame(canvas); + } else { + SDL_Delay(10); + if (shownStart == 0 && SDL_GetTicks() - waitStart > QS_SPLASH_SESSION_TIMEOUT_MS) + done = true; + } + xr_frame_end(); + } + + LOGI("Startup splash: %s after %u ms", shownStart != 0 ? "shown" : "XR session did not start", + (unsigned)(SDL_GetTicks() - waitStart)); + SDL_FreeSurface(canvas); +} diff --git a/android/app/src/main/cpp/qs_splash.h b/android/app/src/main/cpp/qs_splash.h new file mode 100644 index 0000000..ef8c64a --- /dev/null +++ b/android/app/src/main/cpp/qs_splash.h @@ -0,0 +1,15 @@ +// Startup splash: the QuestShock title, shown on the game screen before the +// engine's own splash sequence. +#pragma once + +#include + +// Stores a copy of the splash image (straight-alpha ARGB, one int per pixel). +// Called from JNI before the engine starts; replaces any earlier image. Without +// an image qs_splash_show() does nothing. +void qs_splash_set_image(const uint32_t *argb, int width, int height); + +// Engine thread only (called from engine patch 27). Fades the image in and out +// on the game screen and returns when done, or after a few seconds if the XR +// session never starts. +void qs_splash_show(void); diff --git a/android/app/src/main/cpp/questshock_native.c b/android/app/src/main/cpp/questshock_native.c index 35dc2b0..ebd5c72 100644 --- a/android/app/src/main/cpp/questshock_native.c +++ b/android/app/src/main/cpp/questshock_native.c @@ -11,6 +11,7 @@ #include #include "qs_config.h" +#include "qs_splash.h" #include "xr_input.h" #include "xr_overlay.h" #include "xr_session.h" @@ -22,6 +23,15 @@ Java_de_ladkau_questshock_QuestShockActivity_nativeChdir(JNIEnv *env, jclass cla (*env)->ReleaseStringUTFChars(env, path, cpath); } +JNIEXPORT void JNICALL Java_de_ladkau_questshock_QuestShockActivity_nativeSetSplashImage( + JNIEnv *env, jclass clazz, jintArray pixels, jint width, jint height) { + jint *data = (*env)->GetIntArrayElements(env, pixels, NULL); + if (data == NULL) + return; + qs_splash_set_image((const uint32_t *)data, width, height); + (*env)->ReleaseIntArrayElements(env, pixels, data, JNI_ABORT); +} + // KeyboardOverlay's hand-built on-screen keyboard (see // KeyboardOverlay.handleCharKeyDown()/handleCharKeyUp()) has no real // Android IME session behind it, so printable characters can't reach the diff --git a/android/app/src/main/cpp/xr_session.c b/android/app/src/main/cpp/xr_session.c index d10056f..5eb63c0 100644 --- a/android/app/src/main/cpp/xr_session.c +++ b/android/app/src/main/cpp/xr_session.c @@ -871,6 +871,11 @@ void xr_get_game_quad_extent(float *distance_m, float *half_width_m, float *half } } +void xr_get_game_swapchain_size(int *width, int *height) { + *width = g_game_swapchain.width; + *height = g_game_swapchain.height; +} + void xr_get_game_resolution(int *width, int *height) { // Not g_game_swapchain.width/height: that's the fixed physical size of // the swapchain image the game is composited into (see xr_init()) - diff --git a/android/app/src/main/cpp/xr_session.h b/android/app/src/main/cpp/xr_session.h index 9f56d89..2840b2e 100644 --- a/android/app/src/main/cpp/xr_session.h +++ b/android/app/src/main/cpp/xr_session.h @@ -102,6 +102,12 @@ void xr_session_set_game_screen_shape(float width_m, float curve_deg, float dist // swapchain. void xr_get_game_resolution(int *width, int *height); +// Pixel size of the game quad's swapchain image, fixed at launch (0, 0 before +// xr_init()). The quad's aspect ratio is this size, so full-quad content +// drawn outside the engine's own frame path (the startup splash) is laid out +// against it. +void xr_get_game_swapchain_size(int *width, int *height); + // Exponentially-smoothed frame rate/frame time, updated once per real // rendered frame in xr_frame_end() - read by MenuOverlay's header (see // questshock_native.c's nativeGetFps()/nativeGetFrameTimeMs()). 0 before diff --git a/android/app/src/main/java/de/ladkau/questshock/QuestShockActivity.java b/android/app/src/main/java/de/ladkau/questshock/QuestShockActivity.java index 6c41201..09627d9 100644 --- a/android/app/src/main/java/de/ladkau/questshock/QuestShockActivity.java +++ b/android/app/src/main/java/de/ladkau/questshock/QuestShockActivity.java @@ -5,6 +5,8 @@ import android.app.AlertDialog; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; @@ -18,7 +20,7 @@ import org.libsdl.app.SDLActivity; import org.libsdl.app.SDLSurface; /** - * Prepares /sdcard/questshock/ the way Shockolate's plain relative-path + * 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 @@ -40,7 +42,7 @@ public class QuestShockActivity extends SDLActivity { private static final String TAG = "QuestShock"; private static final int PERMISSION_REQUEST_STORAGE = 1; // Package-visible: MenuOverlay reads controller_profiles/ from here. - static final String GAME_DIR = "/sdcard/questshock"; + static final String GAME_DIR = "/sdcard/QuestShock"; // Loaded here (rather than waiting for SDLActivity's own // getLibraries()/loadLibraries(), which only runs from super.onCreate()) @@ -58,6 +60,9 @@ public class QuestShockActivity extends SDLActivity { // has no public chdir() (confirmed against the actual API 34 stub jar). private static native void nativeChdir(String path); + // Straight-alpha ARGB pixels of the startup splash (qs_splash.c). + private static native void nativeSetSplashImage(int[] pixels, int width, int height); + @Override protected String[] getLibraries() { return new String[] { @@ -187,11 +192,31 @@ public class QuestShockActivity extends SDLActivity { // before clearing mBrokenLibraries below so the engine can never // start pre-chdir. nativeChdir(GAME_DIR); + loadSplashImage(); // Assets confirmed present - safe to let the native engine start now. SDLActivity.mBrokenLibraries = false; } + // The engine shows this at startup (qs_splash.c); without it there is no splash. + private void loadSplashImage() { + try (InputStream in = getAssets().open("splash.png")) { + Bitmap bitmap = BitmapFactory.decodeStream(in); + if (bitmap == null) { + Log.e(TAG, "Could not decode splash.png"); + return; + } + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + bitmap.recycle(); + nativeSetSplashImage(pixels, width, height); + } catch (IOException e) { + Log.e(TAG, "Could not read splash.png", e); + } + } + private static boolean isNonEmptyDir(File dir) { String[] entries = dir.list(); return entries != null && entries.length > 0; @@ -219,7 +244,7 @@ public class QuestShockActivity extends SDLActivity { + "merging or extraction required.\n\n" + "Then relaunch.\n\n" + "(These instructions are also in GET_ASSETS_QUEST.txt in the " - + "questshock folder on this device, if you need to read them again.)") + + "QuestShock folder on this device, if you need to read them again.)") .setCancelable(false) .setPositiveButton("Exit", (dialog, which) -> finish()) .create() diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index bdf578d..0b03f14 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - System Shock + QuestShock diff --git a/android/engine-patches/27-android-startup-splash.patch b/android/engine-patches/27-android-startup-splash.patch new file mode 100644 index 0000000..e87fa5f --- /dev/null +++ b/android/engine-patches/27-android-startup-splash.patch @@ -0,0 +1,24 @@ +--- a/src/GameSrc/setup.c 2026-09-20 16:04:46.197310679 +0200 ++++ b/src/GameSrc/setup.c 2026-09-20 16:05:08.400330327 +0200 +@@ -58,6 +58,9 @@ + #include "gamestrn.h" + + #include "mainloop.h" ++#ifdef __ANDROID__ ++#include "qs_splash.h" ++#endif + #include "tools.h" + #include "input.h" + #include "game_screen.h" +@@ -1300,6 +1303,11 @@ + if (!show_splash) + return; + ++#ifdef __ANDROID__ ++ // The QuestShock title, before the engine's own splash screens. ++ qs_splash_show(); ++#endif ++ + // Need to load the splash palette file + + INFO("Loading splshpal.res"); diff --git a/build-image/prepare-android-project.sh b/build-image/prepare-android-project.sh index 8ae235c..0503c16 100755 --- a/build-image/prepare-android-project.sh +++ b/build-image/prepare-android-project.sh @@ -93,6 +93,7 @@ cp "/opt/prebuilt/soundfont/default.sf2" "$ASSETS_DIR/res/soundfont.sf2" cp "$REPO_ROOT/res/assets/GET_ASSETS_QUEST.txt" "$ASSETS_DIR/GET_ASSETS_QUEST.txt" cp "$REPO_ROOT/res/questshock.config.default.json" "$ASSETS_DIR/questshock.config.default.json" cp -a "$REPO_ROOT/res/controller_profiles" "$ASSETS_DIR/controller_profiles" +cp "$REPO_ROOT/res/splash.png" "$ASSETS_DIR/splash.png" echo "== Staging prebuilt Android SDL2/SDL2_mixer/fluidsynth-lite/openxr .so's into jniLibs ==" # gl4es is deliberately absent here - it's no longer prebuilt, it's compiled diff --git a/res/assets/GET_ASSETS_QUEST.txt b/res/assets/GET_ASSETS_QUEST.txt index bd49f8e..070f98e 100644 --- a/res/assets/GET_ASSETS_QUEST.txt +++ b/res/assets/GET_ASSETS_QUEST.txt @@ -5,8 +5,8 @@ installed (on Windows, or via Wine/Proton on Linux). Using SideQuest (or any MTP file browser) with your Quest connected, copy its res/data/ and res/sound/ folders directly into: - /sdcard/questshock/res/data/ - /sdcard/questshock/res/sound/ + /sdcard/QuestShock/res/data/ + /sdcard/QuestShock/res/sound/ They already contain everything needed, in the right layout - no merging or extraction required. diff --git a/res/splash.png b/res/splash.png new file mode 100644 index 0000000..ade9f85 Binary files /dev/null and b/res/splash.png differ