Commit Graph

25 Commits

Author SHA1 Message Date
ml d8324d409e Add free view and a separate cyberspace action set
build / build (push) Successful in 3m24s
- Free view: hold a mapped input (default: left grip) and that thumbstick
  drives the engine's mouse look, with the stick's normal bindings suspended
  while held. Also available for the right stick, unbound by default.
- Cyberspace actions: eleven thrust/climb/dive/bank/roll actions with their
  own bindings, active only in cyberspace. The normal movement actions are
  inactive there, and capture exclusivity only compares actions that can be
  active together, so one input can be bound in both sets.
- Cyberspace pitch and bank scale with thumbstick deflection instead of
  running at full strength.
- Remove the toggle_freelook action; bind the automap view to the right
  stick click by default.
- Engine patches: 24 exposes whether the player is in cyberspace, 25 lets
  the controller code scale cyberspace pitch and bank.
2026-09-20 07:24:44 +02:00
ml c24b7f15cb Add remappable controller controls with a Controller Options tab
build / build (push) Successful in 2m14s
Add a player_controls config group that maps game actions to controller
inputs, and a Controller Options menu tab to rebind them in-headset.

- 45 actions: movement, click/double-click, jump, fire, and the engine's own
  keyboard hotkeys (posture, save/load, weapon/MFD select, etc.), sent to the
  engine as synthetic key events. Cheat and dead hotkeys are not exposed.
- Press-to-capture rebinding with per-input exclusivity, Reset to Defaults
  (built-in defaults, then questshock.config.default.json), and new defaults:
  left stick moves/strafes, right stick turns.
- Controller profiles for Touch, Vive and generic OpenXR
  (res/controller_profiles/), matched to the active interaction profile at
  runtime, with a fatal dialog if a profile's bindings are rejected at
  startup.
- The menu's thumbstick locomotion is disabled while the Controller Options
  tab is open or a capture is active. A scrollable list with a tap-to-jump
  scroll bar handles the long action list.
- Opening the menu pauses a running game and resumes it on close.
- Reset Position now keeps the player's facing instead of turning the screen
  to the current gaze.
- Play the intro video in the game's language (engine patch 23), falling back
  to English.
- Watchdog and shared-key handling for stuck movement keys, plus key-edge
  logging.
2026-09-20 06:08:50 +02:00
ml e1e6a006d4 Add configurable game/initial screen width and distance, with a bundled first-run config
build / build (push) Successful in 2m41s
- Video Options tab offers width and distance presets independently for
  the Initial Screen (splash/intro/menu) and Game Screen (gameplay)
  profiles, with their preset step/offset configurable via
  questshock.config.json.
- Ships res/questshock.config.default.json as the initial config on
  first run (or whenever questshock.config.json doesn't exist yet),
  refreshed from the APK on every launch without touching a user's own
  saved config.
2026-09-06 09:08:54 +02:00
ml 5fe4cdf7f8 Bump AGP to 8.5.2 and Gradle to 8.7 for 16 KB page-size support
build / build (push) Successful in 2m21s
Every .so this project builds was already ELF-segment-aligned for 16 KB
pages (build-image/Dockerfile's ANDROID_16KB_LDFLAGS, app/build.gradle's
own CMAKE_SHARED_LINKER_FLAGS), but AGP 8.1.1 doesn't align native
libraries' zip entries within the APK itself - that only became automatic
in AGP 8.5.1, which is what Android Studio's "not compatible with 16 KB
devices" warning was actually flagging. No build-image changes needed:
Gradle/AGP are resolved over the network at build time, not baked into
the image.
2026-09-05 16:18:29 +02:00
ml 369555efc7 Making the menu slightly larger 2026-09-05 15:53:15 +02:00
ml 360a1f7e3d Add VR locomotion: move/turn with thumbsticks while the menu is open
build / build (push) Successful in 2m25s
- Left thumbstick snap-turns, right thumbstick smooth-moves, letting the
  player line themselves up with the fixed game quad ("virtual screen")
  without needing physical room-scale space to do it in. Implemented via
  a second OpenXR reference space (a "play space") derived from the real
  LOCAL space by the inverse of the player's transform, so every existing
  quad/hit-testing/rendering call site just needed g_local_space swapped
  for it - no manual coordinate-transform math required elsewhere
- Generalized the ray/quad hit-testing in xr_input.c to a proper
  oriented-plane intersection (needed once quads can rotate); verified it
  degenerates to the old axis-aligned formula when unrotated, so the
  keyboard and non-recentered menu are unaffected
- The menu launcher now re-centers in front of the player, correctly
  oriented to face them, each time it's freshly reopened
- "Reset Position" (Main Menu tab) recenters using the player's current
  real head pose - not a plain reset-to-origin, which only undid
  thumbstick movement and ignored physical drift since the session
  started. The same recenter runs once automatically at startup too, as a
  fallback for when there's no complete saved position to use instead
- Position/orientation persists in questshock.config.json's new
  player_position group (independent of savegames), including new
  numeric-value support in the config file's hand-rolled JSON parser
  (previously string-enums only)
2026-09-05 15:47:54 +02:00
ml c0619a55ab Add a language option to the VR menu, and fix a crash switching to it
build / build (push) Successful in 2m22s
- New "Language" row on the Main Menu tab (English/French/German),
  backed by a new "general" group in questshock.config.json
- Fixes a device-reproduced crash: switching language used to call
  language_dealfunc(), whose render_run()/opanel_redraw() repaint
  assumes the game's own in-game options panel is on screen - crashes
  (SIGSEGV) when triggered from our independent VR overlay instead.
  Now calls language_change() directly, which is all that's actually
  needed to reload resources and refresh the inventory/MFD/side-icon UI
- Fixes a second crash: this data set ships frnstrng.res/gerstrng.res
  but not mfdfrn.res/mfdger.res, and language_change() closes the
  current resource files before discovering the new ones don't exist -
  leaving no valid resource file open and crashing on the next lookup.
  Now checks both files exist before ever switching, refusing the
  change (with an in-headset popup) instead of corrupting resource state
- extract_assets.sh can now also pull mfdfrn.res/mfdger.res out of a
  System Shock: Classic installer (which includes them, unlike Enhanced
  Edition) if one is present in res/assets/ - entirely optional
- Documented all of the above in README.md/CONFIG.md
2026-09-05 08:09:12 +02:00
ml 09280dd410 Overhaul the VR menu UI: header, tabs, live video options, hover/click feedback, i18n, and dragging
build / build (push) Successful in 2m25s
- Header with title, git-tag-derived version, and a live FPS/frame-time/
  CPU/GPU-utilization/free-memory stats ribbon below the tab bar (FPS via
  a new per-frame tracker in xr_session.c; CPU/GPU via the OpenXR
  XR_META_performance_metrics extension, gracefully unavailable on
  runtimes that don't support it)
- Bottom tab bar with a live-editable Video Options tab (renderer/
  resolution), applying changes immediately via qs_config.c
- Hover and press feedback on every menu/keyboard button via a
  StateListDrawable (Android's default ripple never animates on this
  permanently unattached view tree), with a lighter default button color
  for visibility
- i18n for all menu/keyboard chrome text, following the game's own
  language setting (Strings.java)
- The main menu is now draggable via its header, matching the keyboard;
  xr_input.c's drag handling is generalized to per-overlay state instead
  of being keyboard-only
- The keyboard's title bar now matches the menu header's style
2026-09-05 07:02:30 +02:00
ml c4fd4eb3dd Adding support for a configuration file 2026-09-05 05:30:00 +02:00
ml e0a2e1f4bf Fix persistent GL_INVALID_OPERATION spam and resolution-change bugs on Android
build / build (push) Successful in 2m44s
- opengl_end_frame() bound framebuffer 0 on Android, which has no valid
  target there (real presentation goes through the OpenXR swapchain's own
  FBOs) - gl4es reported this every frame. Skip it there.
- render_hack_cameras() re-entered opengl_start_frame()/opengl_end_frame()
  for camera-monitor renders that never touch the GL screen; excluded them
  to match use_opengl()'s existing behavior. Also drains a stray error left
  by xrPollEvent().
- ChangeScreenSize() never called opengl_resize() on Android, freezing the
  3D render target at launch size after an in-game resolution change; and
  xr_get_game_resolution() used the swapchain's fixed size instead of the
  current screen resolution, misplacing the laser-pointer cursor after such
  a change.
- Removes all temporary bisection diagnostics added while tracking down the
  GL_INVALID_OPERATION source.
2026-09-04 16:34:52 +02:00
ml 504ed3e1cb Fix SFX and MIDI music not playing on Android
snd_start_digital() opened two separate SDL audio devices - a raw one
for cutscene/audiolog audio and a second via Mix_OpenAudio() for
SFX/MIDI - but Android's audio backends only ever allow one open
non-capture device, so the second open always failed silently.

- Consolidate onto a single Mix_OpenAudio() device; cutscene/audiolog
  PCM is now mixed into its output via Mix_SetPostMix() instead of a
  dedicated device (16-android-audio-single-device.patch)
- Add SDL_GetError() to the failure log so a future open failure is
  actually diagnosable (15-android-audio-error-detail.patch)
- Make NativeMidiInit() report failure on platforms with no real
  implementation (Android included), instead of silently claiming
  success without opening anything (17-android-native-midi-unsupported.patch)
- Strengthen the OpenSL ES driver hint to SDL_HINT_OVERRIDE and log the
  active audio driver as a standing diagnostic (05-android-audio-driver.patch)
2026-08-30 07:09:16 +02:00
ml d6e438a541 Fix 3D texture corruption and stuck/dead-key input bugs
build / build (push) Successful in 2m13s
Stale GL_UNPACK state in gl4es and a missing pre-release GPU sync
caused diagonal-banded corruption in the 3D view. Synthetic key
press/release must track the real controller button or touch down/up
edges, not fire together in one call - fixes a typed movement key
sticking forever, then a follow-up that had silently broken A-button
jump and on-screen keyboard arrow keys. Also adds force-rebuild.sh for
the known stageEngine stale-cache issue.
2026-08-29 19:26:53 +02:00
ml 581af95e7b Drive the engine's mouse cursor from the VR aim ray on the game quad
build / build (push) Successful in 1m44s
The game quad's laser beam was purely visual - aiming at the game's own
UI (inventory, menus, dialogs) had no way to interact with it. Add a
real mouse pointer: absolute cursor position plus a single left click,
mirroring a plain point-and-click mouse (no mouselook, no right-click,
no drag).

New xr_mouse.c/h bridges the aim-ray hit-test to the vendored engine's
own public mouse API - mouse_put_xy() for position, a synthetic
SDL_MOUSEBUTTONDOWN/UP (SDL_BUTTON_LEFT) via SDL_PushEvent for clicks,
the same technique nativeSendPrintableChar() already uses for
synthetic keyboard input. No engine patch needed. Deliberately bypasses
SDLActivity.onNativeMouse()/SDL_MOUSEMOTION/SetMouseXY(), whose
physical-window-size scaling is unreliable in this headless immersive
build.

xr_input_try_game_quad() now reports the hit u,v; a new
g_game_touch_active[] tracks a held click on the game quad so it
survives the ray straying onto the keyboard/menu before release,
gating those overlays' claims the same way pending overlay touches
already do.
2026-08-14 08:36:09 +02:00
ml 824b4a2506 Give the game quad the same laser-beam pointer as the overlays
Aiming looked and behaved differently depending on whether the
keyboard/menu was open: xr_input_try_overlay() claimed a hand's ray for
its overlay whenever the overlay was merely visible, regardless of
whether the ray was actually pointed at it - so with the keyboard open,
aiming at the main game screen still showed a beam terminating at the
keyboard's fixed depth, while closing the keyboard swapped in the old
flat cross-shaped reticle at whatever the ray was really pointing at.

Only claim an overlay when the ray is actually relevant this frame - a
real hit, or a touch/drag begun on a previous frame still pending
release - so a visible-but-unaimed-at overlay now falls through to the
next-priority target. Give the game quad its own hit-test
(xr_input_try_game_quad()) and the same billboarded laser-beam
treatment as the overlays instead of the old flat reticle, so aiming
looks and behaves identically everywhere - overlay open or not. The
old reticle GL program/shaders are now fully dead and removed. No
click/touch dispatch is added to the game quad itself; that's future
work.
2026-08-14 07:59:09 +02:00
ml 920842a465 Show a laser-beam pointer while aiming at the menu/keyboard
build / build (push) Successful in 1m40s
The flat cross-shaped reticle used while aiming at the game quad is
drawn directly into the game quad's own texture, so it only ever made
sense there - while the menu launcher or keyboard was open, there was
no visual feedback at all until the ray was precisely on target,
making both panels hard to aim at.

Add a second, thin XrCompositionLayerQuad per hand: a billboarded
"laser beam" ribbon from the controller to wherever that hand's ray
currently crosses the plane of whichever overlay (keyboard, then menu)
claims it that frame, oriented via a view-space head pose so it reads
as a line from the viewer's eye regardless of angle, colored per hand
(cyan left, amber right) and clamped to 2m so grazing angles don't
produce an absurdly long beam. Built from data xr_input.c's existing
per-hand hit-testing already computes; the game-quad reticle itself is
untouched.
2026-08-14 07:30:13 +02:00
ml 3be310ade6 Split the keyboard onto its own translucent OpenXR quad
build / build (push) Successful in 1m52s
Expand the on-screen keyboard to a full US layout (letters, digits,
punctuation, Shift layer, Tab, arrows, F1-F12, one-shot Ctrl/Alt
modifiers), make it a persistent, movable, closeable panel via a
title-bar drag handle, and lower its opacity.

Previously the keyboard was just a second panel swapped into the same
quad as the menu launcher, which made the launcher translucent too and
made the keyboard and menu mutually exclusive. Extract the shared
swapchain/JNI/touch-dispatch plumbing into two generic, reusable
modules - xr_swapchain.c (swapchain + per-image FBO setup) and
xr_overlay.c (an off-screen Android View rendered into its own OpenXR
quad, hit-tested via a laser pointer) - and make the menu launcher and
keyboard two independent XrOverlay instances instead of one. The
controller's menu button now only opens/closes the launcher; the
launcher's "Keyboard" button only opens the keyboard - so the keyboard
can stay up while picking something from the menu, and only the
keyboard's own Close button hides it.

xr_menu.c/.h are gone, fully absorbed into xr_overlay.c. On the Java
side, OverlayPanel.java carries the shared off-screen-render/touch/
cursor plumbing that MenuOverlay and the new KeyboardOverlay both
subclass.
2026-08-14 06:43:44 +02:00
ml 2d8fb49bf5 Add hand-built on-screen keyboard to the OpenXR menu quad
build / build (push) Successful in 1m45s
MenuOverlay's key grid forwards every press to the game as real input:
non-printable keys (Esc/Enter/Backspace) as an SDLActivity.onNativeKeyDown()/
onNativeKeyUp() pair, and printable keys (letters, Space) as a synthesized
SDL_TEXTINPUT event pushed directly from native code, since the engine's
pump_events() only picks up printable ASCII from that event type, not from
SDL_KEYDOWN. Confirmed on-device, including that Space now correctly skips
the intro cutscene alongside Esc/Enter.
2026-08-05 08:11:16 +02:00
ml 1be0bebda2 Split the menu quad back onto its own independent OpenXR swapchain
build / build (push) Successful in 1m43s
Replaces the single shared, side-by-side swapchain (game + menu content
packed into one image, needing viewport/scissor juggling to keep the
menu's own glClear from bleeding into the game's region) with two fully
independent XrSwapchainState instances, each sized to exactly its own
content and acquired/released on its own within the same
xrBeginFrame/xrEndFrame pair - an ordinary multi-layer OpenXR setup.

The shared-swapchain design was originally adopted to work around what
looked like a Horizon OS compositor limitation with independent
swapchains, but that was diagnosed before the real root cause of the
"menu shows the game's content" bug was found (gl4es's fpe.c
unconditionally substituting its own shader onto the menu's draw call -
see README's Debugging notes). Since the actual fix routes the menu's
blit through a real, non-gl4es glBlitFramebuffer() call - orthogonal to
how many swapchains exist - splitting back onto two swapchains works
fine and removes the GL_SCISSOR_TEST workaround and sub-rectangle
offset math entirely. Confirmed on-device: both quads render correctly,
hit-testing and 90 FPS unaffected.

Also adds a visible cursor to the menu quad itself: xr_input.c now
tracks whichever hand's aim ray hits the menu each frame and forwards it
to a new MenuOverlay.nativeUpdateCursor(), which draws a small dot at
that position (composited through the same Bitmap the menu's own
content already goes through) - previously there was no visual feedback
at all showing where you were pointing before pulling the trigger.
2026-08-02 08:01:14 +02:00
ml d2dc58391b Add controller input and a laser-pointer-driven menu quad
build / build (push) Successful in 2m26s
- xr_input.c/h: an OpenXR action set (aim pose, trigger/select click, a
  menu-toggle button) plus ray/quad hit-testing, and a visible
  laser-pointer line drawn from the controller toward the hit point.
- xr_menu.c/h + MenuOverlay.java: a second composition-layer quad,
  toggled by a controller button, showing an off-screen, never-attached
  Android View tree (one "Keyboard" button so far) driven by synthetic
  touch events computed from the laser ray's hit UV.
- Migrate gl4es from a prebuilt binary baked into the Docker build-image
  to a vendored source snapshot (android/gl4es-src/) compiled from
  source at APK build time via CMake add_subdirectory(), patched through
  a new android/gl4es-patches/ (mirrors the existing engine-patches/
  pattern). This was needed to track down and fix a bug hit while
  building the menu: gl4es's fixed-pipeline emulation (fpe.c) was
  unconditionally substituting its own shader onto the menu's draw call
  (fpe_ReleventState() always sets alphafunc to a nonzero sentinel, so
  its fpe_IsEmpty() check could never see the state as empty), making
  the menu quad show the game's own rendering instead of its own
  content. Fixed by routing the menu's blit through a real
  glBlitFramebuffer() call instead of a shader-based draw, which gl4es's
  fpe.c has nothing to intercept - documented in README.md's new
  "Debugging notes" section.
- Renumber android/engine-patches/ to close the gap left by removing an
  unrelated diagnostic-only patch, and strip investigation-journal
  comments and dead diagnostic code (temporary tracing, env-var probes)
  left over from finding the bug above.
- Restructure README.md into numbered sections, document every
  engine/gl4es patch, add Android Studio dev/testing-cycle instructions,
  and generalize Quest-specific wording to any OpenXR headset. Update
  NOTICE.txt to match (gl4es is now vendored/patched source, not a
  prebuilt binary; add the OpenXR-SDK loader).
2026-08-02 06:27:49 +02:00
ml 2300d081c3 Add OpenXR bring-up: render the game as a floating quad in an immersive Quest session
build / build (push) Successful in 2m12s
- New android/app/src/main/cpp/xr_session.c (+.h): owns the OpenXR
  instance/session/local-space/swapchain and the per-frame
  xrWaitFrame/xrBeginFrame/xrEndFrame loop, submitting the game's
  existing flat render as a single head-tracked XrCompositionLayerQuad.
  No stereo rendering or controller input yet - that's steps B/C/D of
  the plan.
- 11-android-openxr-cmake.patch: links the OpenXR loader as a build
  dependency, staged into jniLibs the same way as gl4es/SDL2.
  build-image/Dockerfile and prepare-android-project.sh build and stage
  it.
- 12-android-openxr-present.patch: redirects OpenGL.cc's final present
  step (opengl_swap_and_restore / SDLDraw's software path) into the XR
  swapchain FBO instead of the window, on Android only. Two real gl4es
  bugs had to be worked around to get pixels on screen at all:
    - gl4es's own glBindFramebuffer errors on an FBO id it didn't create
      itself, even though the real driver-level bind succeeds - fixed by
      creating the swapchain FBO container via gl4es's own
      glGenFramebuffers/glBindFramebuffer, and only using the real
      (dlsym'd) driver call for attaching OpenXR's foreign swapchain
      texture, which gl4es's own attach can't handle.
    - gl4es's glBegin/glVertexAttrib/glVertex3f immediate-mode emulation
      only captures a fresh per-vertex value for attribute 0 (position,
      driven directly by glVertex3f) - custom attributes like this
      shader's texcoords/light are GLES2's *constant*-attribute API and
      applied once for the whole draw, not per vertex, silently
      collapsing the UI-overlay quad to a single sampled texel. Fixed by
      switching that one draw call to real vertex arrays
      (glVertexAttribPointer/glDrawArrays).
  Also flips the V texcoord to match SDL's top-down row order against
  GL's texture convention, and reuses a persistent texture object
  instead of a fresh gen/upload/delete every frame.
- AndroidManifest.xml: declares the immersive-HMD intent category and
  focus-aware metadata, drops the 2D-panel layout hint.
- README: documents the new OpenXR immersive mode.
2026-07-25 07:25:27 +02:00
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
ml ac640762d7 Fix Quest launch crashes, version the APK like the tarball, and support native builds in Android Studio
build / build (push) Successful in 2m13s
- patchelf gl4es's embedded SONAME to libGL.so so AGP's jniLibs packaging
  (which drops any file not literally named "*.so") and the dynamic
  linker's NEEDED-entry resolution (by embedded SONAME, not filename)
  finally agree - fixes the "library \"libGL.so.1\" not found" crash seen
  on real Quest hardware.
- QuestShockActivity now checks that res/data and res/sound exist before
  starting the native engine, showing an explanatory dialog instead of
  crashing on init_popups' unchecked NULL resource load when a fresh
  install has no game data copied in yet.
- dist/questshock-<version>-android-arm64.apk is now versioned from the
  same git-tag-or-dev-placeholder scheme as the desktop tarball
  (build-image/version.sh, shared by both via the Makefile and
  build-apk.sh).
- build-image/prepare-android-project.sh (prep logic extracted out of
  build-apk.sh) can now stage android/ for a native build directly in
  Android Studio (--host-paths), exporting the prebuilt SDL2/SDL2_mixer/
  fluidsynth-lite/gl4es libraries and writing host-resolvable paths,
  instead of only ever building inside the Docker image.
- Corrected GET_ASSETS.txt/GET_ASSETS_QUEST.txt, which wrongly described
  merging res/pc/hd and res/pc/cdrom trees out of the raw installer's
  sshock.kpf - an already-installed copy's res/data res/sound can just
  be copied directly, with extract_assets.sh only needed from the raw
  installer.
2026-07-23 06:42:45 +02:00
ml 66f02a0c61 The Android/Quest CMakeLists.txt patch previously reimplemented desktop
build / build (push) Successful in 1m8s
immediate-mode GL (glBegin/glVertex3f/glEnd and friends) from scratch on
top of GLES, since GLES has none of it. That code compiled but was never
actually exercised - no on-screen verification was possible without real
Quest hardware - making it the biggest unverified risk in the port.

Replace it with GL4ES (MIT-licensed, prebuilt into the build image),
a mature desktop-GL-on-GLES translation library used by other
Quest/Android game ports for exactly this problem. This cuts
04-android-opengl-es-render.patch by half, deleting the hand-written
vertex-accumulation emulation entirely; alpha test, point sprites, and
point size stay on their existing GLES-native fixes rather than trusting
GL4ES's shakier custom-shader interop for those.

Bump build-image/VERSION to 3 - a fresh image with GL4ES cross-compiled
for arm64-v8a was built and make apk verified successfully against it.
2026-07-20 12:52:34 +02:00
ml 9e2c9ffbe6 Adding first APK build 2026-07-20 09:45:08 +02:00