Astro Bot draws its scene one eye at a time (1200x1280 at preset 5), so
the side-by-side gate never matched it and the map only reached other
wide passes. known_title now publishes the scene's eye size; exactly
that pass gets a single-eye map.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue #16 (and seen on the PC): at the end of level 2-1 the hero stood in the air above the
last mound and ran into a wall that was not there, the island below had the same; its
collisions were most of a block above what was drawn.
The game moves the collision bodies of what moves by sending them to a place: the physics
library gives a body the speed that takes it there in one step, distance / time step, with
the time step of the step last taken, and the step that follows moves it by speed x the new
time step. On the console both are a sixtieth of a second. With the time step the emulator
gives the game, which follows what frames take, the body lands off by its way times the
difference between the two. What moves all the time is put right a frame later. What is sent
once is not: a level sleeps by sections, and a section that wakes sends all its bodies in one
frame from where they slept to where the level has moved since. The last two sections of 2-1
are woken at the top of a tree that carried them 17 units up while they slept, in a frame
that is long because of all that is new in it.
The function that has the library take a step wrote the new time step down and then stepped.
It is changed in place, in both versions of the game, to step with the time step that is
written down, the one every body was sent with, and to write the new one down after
(SHADPS4_TITLE_PHYSICS_STEP=0 leaves the game's own). The places are checked for what the
game has there before anything is written.
SHADPS4_TITLE_PHYSICS_WATCH=1 has the log tell every ten seconds how far the bodies sent
ended from where they were sent. On the PC, the scripted walk through the first level at
about 40 frames a second: with the game's own step, off by the way times the difference of
the two time steps (0.003 for a body sent 0.16 where they were 2% apart, 0.085 for one sent
0.19 across a hitch); with the change 0.0000 over 110,000 sends, in 1.00 and in 1.04.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The session after the last fix played from the PC's speakers again, with the headset
connected from the start: Virtual Desktop's sound device was there, but Windows preferred
the speakers (which the test of the last commit had caused: it chose them by hand, and
Windows keeps to the device chosen last), and Virtual Desktop's runtime names the device
Windows prefers, not its own.
- The device whose name ends with "(Virtual Desktop Audio)" is the headset's while it is in
the system, which it is while Virtual Desktop streams to a headset: whatever Windows
prefers and whatever the runtime in use (SteamVR names none). Its microphone likewise,
while the playback device is there. SHADPS4_XR_STREAM_AUDIO names another ending.
- tools/audio-endpoint.ps1 preferred says which device Windows would play on if all were
there, and pc-audio-default-test.sh makes that one the preferred one again when it is
done, also where it is not there during the test.
Tried against a simulated headset with the monitor's device named as the streaming
program's: the game plays on it while Windows plays on the speakers. Not tried: with
Virtual Desktop itself, which needs the headset worn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A session on the PC ended with "Unhandled Exception code 0xc0000005" in
SDL_GetAudioStreamDataAdjustGain, on the playback thread of Virtual Desktop's sound device,
a moment after the emulator had moved its ports "back" to the PC's speakers.
- Why the speakers: Virtual Desktop has its sound device only while it streams to the
headset. Asked before that, its runtime names what Windows plays on, and the emulator asked
once, at the start; it then held on to the speakers by name and took the sound back to them
three seconds after Windows had moved it to the headset. The runtime is asked again now
whenever a sound device comes or goes (OpenXrHost::AudioDevicesChanged, on the frame
thread), and the ports go where it then says.
- Why that stopped the game: ports opened on "the default device" are moved by SDL when the
system changes its default, and SDL 3.5.0 (SDL_DefaultAudioDeviceChanged) puts each moved
port at the front of the new device's list without setting the back link of the one
behind it; closing a moved port afterwards leaves the list pointing at freed memory, which
the device's playback thread reads. The ports no longer play on "the default device":
each is opened on the device the system plays on at that moment, as the device it is, and
FollowDevice moves it when the system chooses another (after the three seconds it already
waits for devices that come back).
tools/audio-endpoint.ps1 can say and set the device Windows plays on;
tools/pc-audio-default-test.sh changes it while the game plays, and back.
Tried: with the build before, that test stops the emulator (access violation); with this one
it plays on, the ports that are left to the system on the new device and back, the ones
named for a device staying there. The device test of 0.13 (a named device, and the default
device itself, taken out of Windows and put back: the level moves to the other device and
back). Against a simulated headset, whose runtime names another device once one went away:
the runtime is asked again and the ports follow. With SDL's dummy driver, as the headset's
build has it, the ports open as before. Not tried: Virtual Desktop's own device coming and
going, which needs the headset worn, and the Quest app (the headset was in use).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the project's thread on Reddit and the comments under the videos about it, all written
before 0.18. What 0.18 had not answered:
- The PC launcher asked for the Visual C++ runtime again and again, however often it was
installed (one player cut the check out of the launcher to play). Started from a 32-bit
program, "powershell" is the 32-bit one, to which Windows shows the 32-bit system folder
under the 64-bit one's name, and vcruntime140_1.dll only exists in 64 bits. The launcher
hands over to the 64-bit PowerShell first (which also puts the OpenXR runtime's registry
entry where it looks), looks for the files where a 64-bit program finds them and next to
the emulator, names the ones it misses, and its question can be answered with "start the
game all the same". When the emulator ends with "DLL not found" it says what that is.
- The game was in English whatever the console's language should have been: the emulator's
console was set to English and nothing offered another. SHADPS4_CONSOLE_LANGUAGE takes a
language tag ("fr-FR", "pt_BR", "zh-Hans-CN") or the console's number
(Common::ConsoleLanguageFromTag). The PC launcher passes the language Windows is shown in,
or the one chosen in its window from the game's 28 (language=); the Quest app passes the
headset's (language= for another).
- On the PC the headset's controllers did nothing while a gamepad was connected, even one
that only lay there. Whichever was used last plays (Input::PadSource): a button or a stick
of the headset's controllers takes over once the gamepad has said nothing for a quarter of
a second, which leaves a gamepad that a streaming program makes of those controllers
playing as before; the gamepad takes back with its first button, stick or touch. While the
headset's controllers play, GameController leaves the gamepad's input aside, and the
runtime believes a host that says where the controller is over a gamepad's sensors.
- Turning the view by steps, asked for by a player who cannot turn round where they sit:
Runtime::TurnView turns the seat about the head (the head keeps its place in the title's
world, the controller comes round, a reset of the view undoes it). Asked for with a button
the game has no use for in play, held, and the right stick flicked to a side
(Input::ViewTurn): L1 on a gamepad, the grip of the hand that does not hold the controller
on the headset's own; from the Quest app as two new flags of the pad pose message. 30
degrees a step (SHADPS4_VR_TURN, turn=). The Quest app's card names it.
- "Save Migration", a question in a box behind the game's window at the first start
(reported from Linux, where the package's ready-made folder was not used): only asked
where an older shadPS4 left something to move.
README: common questions (getting the game, headsets, controllers, SteamVR, why the picture
looks reprojected, language, Linux), and a player's recipe for Proton, as reported.
Tried: the unit tests (console_language_test, pad_source_test, pad_router_test with the
turn, launcher-test under 64-bit and 32-bit PowerShell, the launcher's hand-over from a
32-bit one); the game on the PC in French, Japanese and Arabic, and in English for a
language it lacks; against a simulated headset with a test gamepad and test presses of the
headset's controllers (SHADPS4_TEST_VIRTUAL_PRESS/_TURN, SHADPS4_XR_TEST_PRESS/_TURN): each
takes over from the other, the view turns by either, the picture turns and the head stays in
place; on a Quest 3 without wearing it: the language reaches the game, the self-test's new
HOST_TURN turns the view with a tracked and with an untracked controller alike, the card
shows its new line. Not tried: any of it worn, and Virtual Desktop's own gamepad emulation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launcher took a package for the game's update by three of the marks an update can
carry; PATCHGO (0x00200000) is the fourth. By ODevStudio, who reduced their pull request to
this once main had the rest of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first worn try of the Quest app with the headset's own controllers ended at the
catapult of the first level: the right stick pulled back and let go did not shoot it. And
nothing told the player that the pad is pressed (the water cannon) by pressing that stick in.
Why the catapult failed: the game reads the pad once for every frame it draws (the newest of
the states it is handed), and takes the place it first finds the finger at for the start of
a pull. A stick is at its end within a few hundredths of a second, so the finger that
followed it there had, for the game, begun where it ended. By a model of the game's rule
(tools/tests/touch_game_model.h: read at 0xc04cc3, y = 1 - raw / 950, the catapult of
0x436fc0 wants more than 0.25 s and more than 0.4 of the pad) one fast pull in four failed
at 60 frames a second and two in three at 30, which is what the reports of Issue #2 say and
what the headset draws.
- Input::MovedFinger: a finger that stays where it lands until the game has seen it there,
goes no faster than a finger flicks and counts as arrived once it was seen at its end.
- Input::StickFinger is built on it: the finger comes down behind the pad's middle, on the
side the stick comes from, and is dragged to where the stick points.
- Input::PadGestures: what the game asks of the pad, done by buttons it has no use for in
play (it only asks about them in a few menus, and is still told of them): the pad pressed
while a button is held, a swipe forward for each press of another, a pull back that is
held and let go with a third. On the headset's own controllers the trigger and the grip of
the hand that holds the controller in the game and the other hand's trigger; on a gamepad
without a touchpad R2, R1 and L2. In the Quest app's router, the PC host's code for the
headset's controllers and the PC's gamepads.
- The Quest app shows a card over the game that names those buttons, when the game starts
and whenever OPTIONS is pressed (the game's pause).
- SHADPS4_PAD_TOUCH_TRACE=1 has the log say what the game was given of the touchpad at each
of its reads; input scripts can press the gesture buttons (gesture=press|swipe|pull); the
Quest app's dry start pulls the right stick and presses R1.
Tried: the tests (stick_finger_test and pad_gestures_test against the model at 26 to 60
frames a second, pad_router_test); with the trace, in the game on the PC (62 reads a
second) and on a Quest 3 without wearing it (48 in its sandbox, 36 through the whole app):
a pull is given as 0.74 to 0.76 of the pad over 0.34 s and more, a swipe as 0.67 over a
dozen reads, a press for as long as it is held. Not tried: the catapult itself going off
(a script did not get through the level to it), and all of it worn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app was made for a DualSense and took everything else for a lesser one of those. Players
without one could play on the PC, with the headset's controllers, and not on the headset.
The headset's own controllers: the app reads them through the headset's runtime (OpenXR
actions, the layout the PC host has: A cross, B square, X circle, Y triangle and both
together to blow, grips L1/R1, triggers L2/R2, right stick the finger on the touchpad, left
menu button OPTIONS, both sticks in to reset the view), the right one is the controller in
the game with where it is and how it points, both shake with the game's rumble. The gamepad
the system makes of them for other apps is no longer taken for a gamepad: it says neither
where it is nor whether anybody holds it.
Which of the two: a small router (quest-host/cpp/pad_router.h) between the activity, which
hears of gamepads, and the session, which reads the controllers. The game is played with the
one used last. From a gamepad only a button under a thumb or a stick takes over, never a
trigger or a grip: while a gamepad is held it is hands the headset sees. What buttons mean
about the view besides (the first press of X takes the seat, OPTIONS held resets the view)
moved there from the activity, so that it is the same for both. The start-up menu takes
either; what confirmed it is not pressed in the game.
Gamepads that are not a PlayStation's:
- The right stick as the touchpad's finger is Input::StickFinger now, as on the PC: the
finger lifts where the stick was let go, which the catapult at the end of a level needs.
A button that presses the touchpad touches it in its middle.
- A gamepad without motion sensors followed the hands to where they were and kept pointing
straight ahead. Now the hands turn and tilt it (quest-host/cpp/held_pad.h): the line
between the palms is its sideways axis, the way the hands point is its tilt. The app says
so with a new flag of the pad pose message (HeldInHands), and the emulator lets go of a
controller that was a tracked device once it no longer is (it stayed where that device
was last seen for as long as no motion sensor of its own spoke up).
- Motion sensors are only taken from PlayStation controllers: which way is up for one is
each maker's own affair.
- A right stick on RX/RY, triggers that are only buttons, a Back button the system takes
for its own "back".
For trying a build on a headset whose owner plays with the installed one:
quest-host/build.sh --test-package builds the app under another name, which installs next to
it; the test tools take ASTRO_PACKAGE. The self-test can report a controller the way a
session does (HOST_PAD) and keep pictures of it (HOST_SHOTS).
Tried on a Quest 3 without wearing it: the runtime takes the bindings, the app starts and
plays with a made-up gamepad, and in the game's picture the controller is where and how the
self-test says a tracked controller or hands around a gamepad are, and comes back when they
are lost. tools/tests/pad_router_test.cpp and held_pad_test.cpp try the rest on the PC.
Not tried: any of it worn, and no gamepad other than a DualSense.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep the maintainer's 1.04 build support, calibration and documentation from main. Retain the missing PATCHGO flag check and a regression fixture that is larger than the base package.
- A copy of the game at 1.04 is no longer warned about; other versions are told what they
miss (the game's own speed and the larger pictures), and the emulator's line naming the
build it found is shown. The check for a path that is too long ran only for copies that
were not warned about: it runs for all.
- A package that is an update of the game holds the files the update changed and no more.
It was unpacked as if it were the game when it was the only package there. The game's own
package is now taken before any update, whatever their sizes, and an update alone is
turned down with what to do about it; so is a folder that has only an update in it.
- What to do in the headset names the headset, not only an Index, for SteamVR, and says how
to move the gamepad in the game where nothing tracks it (PS + D-pad) and how the right
stick shoots as a touchpad.
Issues #1, #4, #7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ASTRO BOT's catapult shoots for a finger that was down for more than a quarter of a
second and has been dragged over more than four tenths of the pad when it lifts (its
SlingShot settings, and the code that reads them). A finger that comes down in the
middle of the pad has half the pad to go: with the stick's full push worth 0.45 of it,
anything short of a full push was not enough. The finger is now at the edge with the
stick four fifths out, and a touch lasts 0.3 seconds at least, so a quick pull and
release shoots as well.
Issue #2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The right stick (of a gamepad without a touchpad, and of the headset's own controllers)
moved a finger that was wherever the stick pointed. A stick that is let go flies back to
its centre, so the finger went back to where it had come from before it lifted: the
catapult at the end of every level, which shoots by how far the finger was dragged when
it lets go, had nothing pulled, and getting it to shoot took many tries.
The finger now stays where the stick was before it started back and lifts from there
once the stick has come to rest; it comes down in the middle of the pad, so a push is a
swipe over all of its way; a stick can be moved through its centre without the finger
lifting (a drag from one edge of the pad to the other); and a stick that never rests
near its centre, a worn one that drifts, moves no finger at all. That last one matters
on a controller with a real touchpad too: the stick's finger stayed on the pad for good
there, and a real finger's swipes counted for nothing beside it. A real finger coming
down now also lifts the stick's first, as a touch of its own.
Input::StickFinger, tried by tools/tests/stick_finger_test.cpp at the rates a gamepad
and a headset's controllers are read at.
Issues #2, #12.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From its first update on, the game asks the tracker to find the controller anew on the
screen that has the player sit inside a silhouette, and waits there until it has seen the
controller's status go from calibrating to anything else. sceVrTrackerRecalibrate was a
stub: the status never changed, and the updated game stayed on that screen for good ("the
controller with the red light bar should also be in view"). The tracker now says what a
console's does while it finds a device anew.
The executable of 1.04 is laid out differently from the disc's, each part moved by an
amount of its own. Where its time step, its sizes and the memory for them are is in
known_title_builds.h, as found and tried by Clodo76 (issue #1) and checked here against
both executables. A build is told from the others by what its image holds when it is
loaded, before anything is written to it; tools/tests/known_title_builds_test.cpp tries
that on images made up for it and on real executables.
Issues #1, #3, #5, #7, #10.
Co-authored-by: Clodo76 <1511345+Clodo76@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Virtual Desktop takes its playback device out of Windows whenever the headset is not
streamed to; a stream opened on it then stayed silent until the emulator was restarted.
The SDL backend now moves a port to the system's default device when its own is gone and
back when it returns, and plays on "the default" where the wanted device is the default.
Also: sound devices coming and going are logged, an unhandled exception logs its call
stack, and tools to take a playback device out of Windows and measure what devices play.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tools/tests/launcher-test.ps1 loads the launcher's functions and tries them on made-up folder
layouts; tools/ui-drive.ps1 waits for the launcher's windows, saves pictures of them and
presses their buttons, which is how the first start of a release was gone through.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Until now the launcher wanted an unpacked game at games\CUSA12392\eboot.bin and pointed at a
text file otherwise. Now:
- it looks through the games folder, three folders down, for an unpacked game (a folder with
eboot.bin in it; the one whose param.sfo says CUSA12392 if there are several);
- failing that, for a .pkg there, which it offers to unpack into games\<serial> with PkgTool
(LibOrbisPkg, LGPL-3.0, put into the PC package by make-release.sh): into a folder of its
own first, so that an unpacking that was cancelled or failed leaves nothing that could be
taken for the game; the sce_sys entries the package keeps outside its file system
(param.sfo, pictures, trophies) are added; the result is the same, byte for byte, as the
copy the project was developed with;
- failing that, a window asks where the game is (its eboot.bin or its .pkg), and what is
chosen outside the games folder is written to settings.txt as game=;
- a game folder whose path is too long for the emulator to open all of the game's files is
refused with the reason, not started;
- a missing Visual C++ runtime is said, with Microsoft's download;
- a warning when Windows has less memory left to hand out than the emulator asks for at once
(about 14 GB), which is what makes it stop as it starts now and then;
- the launcher's windows open in front and at their normal size even when it was started
minimized.
The Quest app is unchanged; its version follows the release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>