Files
mitch030504--AstroQuest-Frame/quest-host/java/com/astrobotquest/vrhost/RuntimeInstaller.java
T
bigmak94andClaude Opus 5.5 b5f400bbbd AstroQuest 0.11: ASTRO BOT Rescue Mission in VR on Meta Quest 3 and PC VR
On top of the shadps4-arm64 core imported in the previous commit:

- PlayStation VR emulation: headset, tracker, reprojection, camera and social screen; the
  player's seat and view reset; head velocity for head-butts; field of view settings
- Sound: a native Ngs2 engine (samplers, submixers, effects), the Audio3d spatializer, virtual
  surround for headset speakers, microphone pass-through; libSceJson2; libSceFiber on FEX
- Fixes for CUSA12392 under emulation: real-time step, pace and resolution governor, larger
  render sizes on the PC, save loading at start
- Renderer: geometry shaders fed with fans/polygons/loops, cube-array layers of light probes,
  sample-count mismatches on Adreno, timeline semaphore hang, FXAA in place of the MSAA
  resolve, sharpening, VR frame export
- quest-host: the Quest app (OpenXR host that runs the glibc core as a child process, frames
  through AHardwareBuffers, DualSense mapping, hand-tracked controller placement, KGSL shim)
- PC: in-process OpenXR host (Virtual Desktop or any OpenXR runtime), Touch controllers as the
  gamepad, launcher with a settings window; vendored OpenXR SDK 1.1.63
- Builds keep the build machine's folders out of the binaries (-ffile-prefix-map); machine
  settings (Python, headset serial, signing key) live in git-ignored tools/*.local files
- tools: build, packaging and test scripts for Windows and the headset
- README with installation, license (GPL-2.0-or-later), third-party notices and thanks

No game files are part of the repository; .gitignore only lets listed paths in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:00:43 +02:00

83 lines
3.1 KiB
Java

// SPDX-License-Identifier: GPL-2.0-or-later
package com.astrobotquest.vrhost;
import android.content.Context;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Unpacks the Linux runtime the emulator runs in (its libraries, the GPU driver, the emulator
* core itself) from the app's assets into the app's files folder. The assets carry a stamp of
* what they hold; a folder with the same stamp is left alone.
*/
final class RuntimeInstaller {
private RuntimeInstaller() {}
/** Returns true when the runtime had to be unpacked, false when it was there already. */
static boolean install(Context context, File runtime) throws IOException {
String stamp;
try (InputStream in = context.getAssets().open("runtime.stamp")) {
stamp = new String(readAll(in), StandardCharsets.UTF_8).trim();
}
File stampFile = new File(runtime, ".stamp");
if (stampFile.isFile()
&& new String(Files.readAllBytes(stampFile.toPath()), StandardCharsets.UTF_8)
.trim().equals(stamp)) {
return false;
}
deleteRecursively(runtime);
runtime.mkdirs();
String root = runtime.getCanonicalPath() + File.separator;
byte[] buffer = new byte[1 << 16];
try (ZipInputStream zip = new ZipInputStream(context.getAssets().open("runtime.zip"))) {
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
File target = new File(runtime, entry.getName());
if (!target.getCanonicalPath().startsWith(root)) {
throw new IOException("bad runtime entry " + entry.getName());
}
if (entry.isDirectory()) {
target.mkdirs();
continue;
}
target.getParentFile().mkdirs();
try (FileOutputStream out = new FileOutputStream(target)) {
for (int count = zip.read(buffer); count > 0; count = zip.read(buffer)) {
out.write(buffer, 0, count);
}
}
target.setExecutable(true, true);
}
}
Files.write(stampFile.toPath(), stamp.getBytes(StandardCharsets.UTF_8));
return true;
}
private static byte[] readAll(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
for (int count = in.read(buffer); count > 0; count = in.read(buffer)) {
out.write(buffer, 0, count);
}
return out.toByteArray();
}
static void deleteRecursively(File file) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursively(child);
}
}
file.delete();
}
}