9 Commits

Author SHA1 Message Date
matthias a2fb95e57b Add Windows cross-compilation via MinGW, and fix Docker build permission
build / build (push) Successful in 3m5s
issues on vboxsf-mounted checkouts

New `make dist-win`/`package-win` targets (folded into `dist`/`package`
alongside the renamed `dist-linux`/`package-linux`) cross-compile
systemshock.exe via MinGW, using prebuilt SDL2/SDL2_mixer/GLEW/
fluidsynth-lite baked into the build-image - no Windows machine or Wine
needed to build it, confirmed working and playable on a real Windows
machine. dist-win/ ships DLLs flat alongside the exe plus a new
res/run.bat launcher, packaged into a .zip the same way dist/ becomes a
.tar.gz.

Also fixes three build-image bugs hit while testing on a VirtualBox
vboxsf-mounted checkout: build-engine.sh/docker-entrypoint.sh losing
their execute bit (chmod +x on restrictive source perms), the container
user missing access to /workspace's supplementary vboxsf group, and
cp -a failing on symlink/hard-link creation (vboxsf doesn't support
either) - now falls back to dereferencing copies when detected. Also
adds Docker/zip/etc. prerequisites to the README for both the desktop
and Quest builds.
2026-08-16 06:27:31 +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 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
ml 8712269b51 Adding a logo and NodeJS to the build image
build / build (push) Successful in 42s
2026-07-19 16:50:36 +02:00
ml a5ec8db5f7 Vendor the Shockolate engine and build it via a Docker image with every
build / build (push) Failing after 5s
dependency (SDL2, SDL2_mixer, fluidsynth-lite, a MIDI soundfont)
prebuilt, so compiling the engine needs no network access - just the
image and the engine source. Add res/assets/extract_assets.sh to pull
the game's data files out of a purchased GOG installer, and a Makefile
that assembles a runnable dist/ from the two.

Also add `make package`, which builds a redistributable tarball that
omits the proprietary game assets (shipping res/GET_ASSETS.txt instead)
plus license information for both the MIT tooling and the GPLv3 engine,
and a Gitea Actions workflow that builds and publishes it to
dl.ladkau.de on every push.
2026-07-19 15:43:38 +02:00