Routing platform_unix.c's JNI thread-local storage through rthreads made
it depend on the sthread_tls_* API, which is gated behind
HAVE_THREAD_STORAGE in both rthreads.h and rthreads.c. The griffin
ndk-build (pkg/android/phoenix-common/jni/Android.mk) never defined that
flag -- the previous raw pthread_key_* code did not need it -- so the
whole TLS API compiled out and the Android build failed:
platform_unix.c:125: unknown type name 'sthread_tls_t'
platform_unix.c:425: call to undeclared function 'sthread_tls_set'
platform_unix.c:573: call to undeclared function 'sthread_tls_create_with_dtor'
Android/Bionic fully supports pthread thread-local storage (including
pthread_key_create destructors), so declare the capability alongside
-DHAVE_THREADS. This also activates retroarch.c's rarch_tls, which uses
the same API and is purely additive (no #else path). The configure-based
Linux/Unix builds autodetect HAVE_THREAD_STORAGE and were unaffected.
The DDS loader landed with only the core wiring (Makefile.common,
griffin, qb). Enable it everywhere the WebP loader (HAVE_RWEBP) is
enabled, so DDS decodes on every target rather than only default builds.
- platform Makefiles: mirror every HAVE_RWEBP toggle and inline
-DHAVE_RWEBP with HAVE_RDDS (ctr, dingux, dos, emscripten, griffin,
lfx000, libnx, miyoo, ngc, openpandora, orbis, ps2, psl1ght, psp1,
retrofw, rg350, rg350_odbeta, rs90, switch, vita, webos, wii, wiiu,
msvc).
- MSVC .vcxproj (2010-2022, 360, UWP) and the Android NativeActivity
project: add the HAVE_RDDS preprocessor define next to HAVE_RWEBP.
- Apple .xcodeproj (OSX107, PPC, iOS6-11 incl. Metal/static): add
-DHAVE_RDDS to OTHER_CFLAGS next to -DHAVE_RWEBP.
- pkg/android Android.mk: add -DHAVE_RDDS.
- feature reporting: SUPPORTS_RDDS (config.features.h), the retroarch
--features string, the menu Core Information image-format list, and
the imageviewer core extension list (adds |dds).
All these targets build the image loaders through the griffin unity TU,
which already includes rdds.c under HAVE_RDDS, so defining the flag is
sufficient to compile and enable the loader.
The WebP decoder was only enabled through the qb configure default, so
platforms with static build files never compiled it. Wire HAVE_RWEBP
everywhere HAVE_RPNG is wired, following each file's existing style:
- Variable assignments (routed through Makefile.common's define/object
block or Makefile.griffin's CFLAGS emit): dingux, dos, emscripten,
lfx000, libnx, miyoo, msvc, ngc, retrofw, rg350, rg350_odbeta, rs90,
webos, wii, and every per-platform block in Makefile.griffin, which
also gains the matching CFLAGS emit.
- Platforms with both a griffin branch (inline -D defines) and a
default Makefile.common branch (variable assignments) get both:
ps2, ctr, switch, orbis, wiiu.
- Continuation-style define lists: psl1ght, psp1, vita, openpandora,
and the Android NDK build.
- MSVC project files (griffin-based, PreprocessorDefinitions):
msvc-2010 through msvc-2022, UWP, Xbox 360, and the legacy Android
NativeActivity project.
- Xcode projects (griffin-based, -D flag lists): all eleven, including
the PPC and legacy iOS projects.
The decoder itself is C89-clean scalar C on platforms without SSE2 or
NEON (the SIMD paths are compile-time gated), so no platform needs
anything beyond the define. The salamander Makefiles are deliberately
left alone: the loaders do not decode images beyond their existing
boot path. Console targets are not buildable in this environment; the
edits mechanically mirror the existing HAVE_RPNG wiring in each file,
and the Makefile.common route they rely on is the same one exercised
by the regular Linux build.
The mixer internals and the build files were moved onto
HAVE_RFLAC/HAVE_RVORBIS/HAVE_RMP3 (matching HAVE_RWAV), but the
extension-registration and file-type sites in audio_driver.c,
msg_hash.c, retroarch.c and the menu still gated on the pre-rename
HAVE_STB_VORBIS / HAVE_DR_MP3 / HAVE_DR_FLAC. Those are now defined in
no build file (vorbis, mp3) or only some (flac), so ogg and mp3 were
silently dropped as loadable mixer formats and flac hung off a stale
flag. Rename the remaining sites to the unified flags, drop the dead
HAVE_DR_FLAC define from Android.mk, and switch Makefile.orbis to
HAVE_RFLAC.
The rflac and rvorbis extractions regressed targets that were not
exercised when they landed:
- rvorbis.c included <alloca.h> unconditionally, which does not exist
on DJGPP; alloca is declared there in <stdlib.h> (already included).
Skip the include on __DJGPP__.
- libchdr_flac.c is the CHD FLAC decoder and uses the rflac API
unconditionally, yet its rflac.h include was gated on HAVE_RFLAC.
On builds that decode CHD FLAC without defining HAVE_RFLAC (Android,
griffin without the audio mixer) the rflac types went undeclared.
Include rflac.h unconditionally: the file is only compiled under CHD
and always needs the decoder.
- griffin pulled in the rflac implementation only under
HAVE_AUDIOMIXER && HAVE_RFLAC, leaving CHD-only builds without it.
Include rflac.c whenever either the mixer (HAVE_RFLAC) or CHD
(HAVE_CHD) needs it, once, ahead of both consumers.
- Makefile.common added rflac.o only via HAVE_BUILTINFLAC; a system
libFLAC + CHD build compiled libchdr_flac.o without the decoder.
Add rflac.o alongside libchdr_flac.o when the built-in FLAC library
is not in use.
- Android.mk carried the FLAC feature flag but never the decoder flag;
add -DHAVE_RFLAC so mixer FLAC and rflac.c are actually compiled.
Carve the vendored dr_mp3 single-header decoder into a first-party in-tree
decoder (rmp3), wire it under the codec-agnostic audio_transfer layer as the
AUDIO_TYPE_MP3 backend, repoint the audio mixer's MP3 voice onto
audio_transfer, and remove deps/dr/dr_mp3.h (deps/dr is now empty).
rmp3 is the whole dr_mp3 decoder with every exported symbol namespaced from
drmp3/DRMP3/dr_mp3 to rmp3/RMP3, so a statically-linked console core
vendoring its own dr_mp3 cannot collide at link with RetroArch's copy: the
object exports rmp3_* symbols only. dr_mp3 is already memory-only (no file
I/O to strip) and self-includes its CRT headers, so the impl wrapper is just
RMP3_IMPLEMENTATION plus the include.
dr_mp3 exposes only a float decode path (rmp3_read_f32) and no integer API,
so the backend reads f32 natively and quantises to s16 at the boundary via
the shared audio_transfer_f32_to_s16 helper (round-half-away, clamp) that
now also serves the Vorbis backend. Unlike FLAC/Vorbis the drmp3 handle is
embedded in the transfer context by value (dr_mp3 initialises in place), so
validity is tracked with a flag. The mixer's MP3 voice holds an opaque
audio_transfer context and drives it through
new/start/info/read_f32/seek/free.
Build: HAVE_DR_MP3 is renamed HAVE_RMP3 across the platform makefiles, the qb
knob and Android.mk. The existing Makefile.common HAVE_RMP3 branch gains
formats/mp3/rmp3.o (and audio_transfer.o once, guarded against the FLAC and
Vorbis branches). griffin includes rmp3.c under HAVE_RMP3 and shares
audio_transfer.c across all three backends.
Carve the vendored stb_vorbis single-header decoder into a first-party
in-tree decoder (rvorbis), wire it under the codec-agnostic audio_transfer
layer as the AUDIO_TYPE_VORBIS backend, repoint the audio mixer's OGG voice
onto audio_transfer, and remove deps/stb/stb_vorbis.h.
rvorbis is the whole stb_vorbis decoder with file I/O stripped
(RVORBIS_NO_STDIO) and every exported symbol namespaced from
stb_vorbis/STB_VORBIS/STBVorbis/VORBIS_ to rvorbis/RVORBIS/RVorbis/RVORBIS_,
so a statically-linked console core vendoring its own stb_vorbis cannot
collide at link with RetroArch's copy: the object exports rvorbis_* symbols
only. The header carries its own <stdint.h>/<stddef.h> and keeps the
fixed-width types the vendored copy already used.
Vorbis is a float-internal codec with no integer decode API, so the backend
reads f32 natively and quantises to s16 at the boundary (round-half-away,
clamp) rather than round-tripping. The mixer's OGG voice holds an opaque
audio_transfer context and drives it through
new/start/info/read_f32/seek/free; loop-to-start routes through
rvorbis_seek_start, since stb_vorbis's seek(0) does not rewind.
Build: HAVE_STB_VORBIS is renamed HAVE_RVORBIS across the platform
makefiles, the qb knob and Android.mk. Makefile.common gains a HAVE_RVORBIS
branch adding formats/vorbis/rvorbis.o (and audio_transfer.o once, guarded
against the FLAC branch). griffin includes rvorbis.c under HAVE_RVORBIS and
shares audio_transfer.c across both backends.
Android's HID driver exposes only a single vibrator for USB-connected DS4 and
DualSense controllers, causing both motors to receive the same amplitude value
and defeating the strong/weak distinction RetroArch maintains internally.
This fix adds a USB HID raw-output path that sends controller-specific HID
output reports (SET_REPORT via controlTransfer) directly through UsbManager,
bypassing the HID driver stack entirely. The native rumble path attempts USB
first and falls back to VibratorManager if the device is not supported, not yet
permitted, or the transfer fails.
Changes:
android_joypad.c
- Widen `id` parameter in android_input_set_rumble_internal from int8_t to int
to avoid truncation of InputDevice IDs above 127.
- Add USB HID raw-output attempt before the VibratorManager path. Calls
doVibrateUSB() and returns early on success; falls through on failure so
existing rumble behaviour is fully preserved.
platform_unix.h / platform_unix.c
- Add `doVibrateUSB` jmethodID to struct android_app and look it up at init.
- Add CALL_BOOLEAN_METHOD_PARAM macro for variadic JNI boolean calls.
RetroActivityCommon.java
- Implement doVibrateUSB(): resolves InputDevice ID → UsbDevice by VID/PID,
manages a pooled UsbDeviceConnection per device, and dispatches to either
sendDs4Rumble() (report 0x05, 48 bytes) or sendDualSenseRumble() (report
0x02, 48 bytes).
- Register InputManager.InputDeviceListener to request USB permission as soon
as a supported controller is connected, not on first vibration.
- Override onResume() to scan already-connected devices at startup and on
activity resume, covering cases where onInputDeviceAdded() never fires.
- BroadcastReceiver clears the in-flight permission flag on user response,
preventing duplicate permission dialogs.
- Connections are cached and closed on device removal or activity destroy.
menu_setting.c
- Fix inverted conditions in get_string_representation_input_device_index()
and get_string_representation_input_mouse_index() that prevented "Disabled"
from ever being shown in the input device menu entries.
Supported devices: DS4 v1 (054C:05C4), DS4 v2 (054C:09CC),
DualSense (054C:0CE6). Unknown VID/PID falls through to existing path.
A refactor in d6b974c replaced string_is_empty(s) calls with equivalent
hand-written checks throughout the file. In two places the replacement
was inverted:
get_string_representation_input_device_index()
get_string_representation_input_mouse_index()
The original intent was to fall back to "Disabled" only when the
formatted string is empty (no device name was found). The replacement
`if (s && *s)` is the logical opposite — true when the string is NOT
empty — so every successfully formatted device name was immediately
overwritten with "Disabled".
Result: the Device Index and Mouse Index dropdowns displayed "Disabled"
for every entry on all platforms, even with controllers actively
connected and working.
Fix: invert both conditions back to `if (!s || !*s)`, which is the
correct semantic equivalent of string_is_empty(s).
RetroArch Android collapsed both libretro rumble channels (RETRO_RUMBLE_STRONG
and RETRO_RUMBLE_WEAK) into a single vibration output by OR-merging their
amplitudes before calling InputDevice.getVibrator(). The effect type was
discarded at the JNI call site, making weak and strong rumble completely
indistinguishable on dual-motor controllers.
Introduces doVibrateJoypad, a new JNI method that accepts both channels
separately. On Android 12+ (API 31) it uses InputDevice.getVibratorManager()
to enumerate controller vibrator IDs and drives each motor independently via
CombinedVibration.startParallel(). Index 0 maps to the strong (large/low-freq)
motor, index 1 to the weak (small/high-freq) motor — consistent across tested
controllers but not guaranteed by the Android API; documented as a best-effort
heuristic. Falls back to the existing single-vibrator doVibrate() path on
Android < 12, single-vibrator controllers, or if doVibrateJoypad is not found
at JNI lookup time. The doVibrate() method and "Enable Device Vibration" path
are entirely unchanged.
compileSdkVersion bumped 30 -> 31 (minimum required for VibratorManager and
CombinedVibration at compile time; minSdkVersion unchanged at API 16). No
deprecated APIs introduced. C changes are C89 and ISO C++ compatible.
Fixeslibretro/swanstation#72.
Tested on Android 14:
- Sony DualShock 4 (Bluetooth and USB)
- Sony DualSense (Bluetooth and USB)
- 8BitDo Pro 2 in Xbox One mode (Bluetooth)
HAVE_NETWORK_CMD was already defined for Android, enabling the UI toggle
in Settings > Network > Network Commands. However, HAVE_COMMAND was missing,
which gates the actual command listener registration in configuration.c
(populate_settings_bool) and the socket initialization in command.c.
Without HAVE_COMMAND:
- network_cmd_enable is never registered in the settings table
- Config load never reads the key, config save never writes it
- The setting resets to false (C zero-init) on every launch
- The UI toggle has no persistent effect
With HAVE_COMMAND added:
- network_cmd_enable persists across launches
- READ_CORE_MEMORY and other UDP commands work correctly on Android
- External tools can read game memory without root
Tested: the READ_CORE_MEMORY command is already fully functional when
the listener starts (HAVE_NETWORK_CMD is set), the only missing piece
was HAVE_COMMAND to allow the setting to persist.
When launching a different game via FLAG_ACTIVITY_CLEAR_TOP (e.g., from
Daijishou), onNewIntent() was calling finish() + System.exit(0), which
killed the app without restarting. Users had to tap twice to launch a
new game.
Now starts a fresh activity with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK
before calling System.exit(0). This queues the new game to launch, then
kills the current process so Android starts fresh.
* Add a work-in-progress Storage Access Framework backend to libretro-common/vfs
* Fix compilation error in `retro_vfs_file_open_impl()` on Windows
* Don't gate the contents of vfs_implementation_saf.h behind `HAVE_SAF`
* Add a button to the file browser on Android for opening a directory picker
* Fix a bug in `retro_vfs_path_split_saf()`
* Don't include inaccessible directories in the file browser in Google Play Android builds
* Fix missing return statement in `action_ok_open_picker()`
* Improve the documentation for the SAF VFS backend in libretro-common
* Implement opening the path in the file browser when selected using the SAF tree picker
* Change indentation style in VfsImplementationSaf.java to 3 spaces
* Check if `vfs_saf_get_jni_env` is null in vfs_implementation_saf.c before performing VFS operations
* Fix stat function in SAF VFS backend
* Don't gate the definition of `safTreeAdded()` behind `HAVE_SAF`
* Fix memory leak when calling `retro_vfs_closedir_impl()` on a SAF directory
* Fix `NullPointerException` in `SafStat` constructor
* Create global references for all Java classes used by the SAF VFS backend
* Improve the readdir implementation for the SAF VFS backend
* Fix a bug in `retro_vfs_path_join_saf()`
* Show the list of SAF trees that the app can access in the file browser on Android
* Improve error handling in VfsImplementationSaf.java
* Check if the directory already exists in the mkdir implementation in VfsImplementationSaf.java
* Enforce a minimum Android API level of 21 in VfsImplementationSaf.java
* Remove the comma at the end of `enum vfs_scheme` in libretro-common/include/vfs/vfs.h
* Fix nonstandard C89 in libretro-common/vfs/vfs_implementation.c
* Fix mkdir implementation in VfsImplementationSaf.java
* Fix open implementation in VfsImplementationSaf.java not creating files that don't exist
* Simplified onNewIntent content detection logic and added activity state tracking
* Added activity state detection to prevent parameter conflicts when resuming from home screen
Enables launchers to use the FLAG_ACTIVITY_CLEAR_TOP launch flag to open games. It checks if the launched game is already running or if it's a new game to launch over the existing one. This provides an alternative to launchers using FLAG_ACTIVITY_CLEAR_TASK which results in a crash on many Android 13 devices if RetroArch is already running in the background.
Apply an aggressive set of ultimately deterministic optimizations to
raster image files where an actual file size reduction of not less than
50% was possible without the removal of intrinsic properties such as
alpha channels. Rendered image dimensions were always left unchanged.
The primary mechanisms employed during optimization are:
- PNG color mode conversion from TrueColor to indexed palette
+ Accomplished using NeuQuant neural-net color quantization algorithm
- Stripping of data blocks containing irrelevant information, such as:
+ XMP metadata fields
+ Embedded comment fields
+ ICC color profiles (simple graphics with basic color schemes only)
+ EXIF profiles
- Re-compression using "best" or "smallest" profile presets
All modifications are performed on lossless image formats and closely
visually inspected after processing to ensure no loss of apparent
details or introduction of new, unwanted artifacts.
Toolset used:
- RIOT (Radical Image Optimization Tool), v2024.1.0 64-bit
- AdvanceCOMP (part of the Advance Projects), v2.6 64-bit
- optiPNG, v7.9.1 64-bit
- PNGOUT, v2015.02.13 32-bit
Signed-off-by: Peter J. Mello <[email protected]>