Commit Graph
2133 Commits
Author SHA1 Message Date
LunaMooandClaude Opus 5.5 3ce3973781 Add overlay (bezel) support
Draws a user image around or behind the PSP screen, to fill the black bars
on displays whose aspect ratio doesn't match the PSP's (#20325).

- Overlays are PNGs in PSP/OVERLAYS. A plain PNG there just works: it's
  drawn in front of the game, and its transparent hole is found by walking
  from the image center to each edge until a nearly opaque pixel. Existing
  RetroArch overlay images can be dropped in as-is.
- A subfolder with an overlay.ini can define overlays with a Name, Layer
  (Foreground/Background) and an explicit Hole=x,y,w,h.
- "Load overlay image..." in the display layout screen imports any PNG
  (including Android content URIs) into the overlays folder and selects it.
- The overlay is fit to the screen (aspect preserved, centered). The hole is
  mapped with the same scale, and the game is fit into it with the normal
  stretch / integer scale / aspect ratio options. Offset and scale don't
  apply while an overlay with a hole is active. This goes through
  CalculateDisplayOutputRect, so touch mapping and post shaders agree.
- The selection is a per-orientation, per-game DisplayLayout setting.
- Disabled in VR, Cardboard, and with skip buffer effects (except software
  rendering), since the presentation path doesn't run there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBg32EFPsTXjAFu7dnTcz
2026-10-22 14:09:51 +02:00
Henrik Rydgård 1adf3c553d Merge pull request #22549 from Kethen/aemu_postoffice_enet
UDP(ENet) mode for adhocctl & aemu_postoffice relay
2026-10-21 23:51:28 +02:00
Henrik Rydgård 2c235b585e Merge pull request #22707 from hrydgard/me-pops
Boot PS1 EBOOTs through the firmware's POPS emulator
2026-10-21 14:42:06 +02:00
Katharine Chui 880158cfdb initial enet mode for adhocctl & aemu_postoffice relay 2026-10-21 14:19:54 +02:00
Henrik Rydgård 3647b9e8a4 Merge pull request #22706 from hrydgard/gyro-tilt
Tilt: Gyro-stabilized tilt on Android, right analog stick output
2026-10-21 13:55:37 +02:00
Henrik RydgårdandClaude Opus 5.5 0b1e1a9bb3 POPS: Boot PS1 EBOOTs through the firmware's POPS manager
A PS1 EBOOT now boots the installed firmware's POPS manager
(flash0:/kd/popsman.prx), which loads its PS1 emulator (pops_0Xg.prx) for
the game. Load_PSP_POPS stands in for the kernel's reboot into its POPS
configuration: it loads the manager and that configuration's user modules,
and a helper thread makes the kernel init's scePopsManLoadModule(path, 0)
call. InitForKernel reports a POPS boot from the memory stick.

The emulator plays its sound on the Media Engine, so a PS1 boot always
gets the emulated ME, whatever the homebrew setting says, and is refused
on platforms that can't emulate it. The manager boots the ME with its own
code (POPS's reboot has no me_wrapper, so Sony's ME firmware isn't booted
for it), and its ME loop runs there unchanged, playing through the audio
block.

Everything in POPS that reaches hardware registers is kernel code, so a
POPS boot leaves the ME's kernel mode stopgap (g_kernelModeAnywhere) off:
7% fewer host instructions in Crash Bandicoot.

The manager also drives the GE directly: its stall register and FINISH
status are an MMIO device that only answers during a POPS boot, and stall
writes extend the HLE GPU's current list.

The 6.60 and 6.61 popsman.prx and pops_01g/02g.prx have the same code. The
manager's is checked when it's loaded, and any other one fails the boot
with a message. Savestates include the boot's threads.

Reading a game's disc image needs the next two changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 13:15:30 +02:00
Francisco José García García 539490b0c8 HLE: sceAmctrl_driver, on libkirk's amctrl
amctrl.prx's PGD (BB) MAC and cipher functions, for kernel modules that
import them: the firmware's POPS manager checks and decrypts a store PS1
game's disc image with them. libkirk's amctrl.c reimplements the module, and
its MAC_KEY and CIPHER_KEY have the layout of the contexts the callers keep in
their own memory, so those are used in place. 6.60 NIDs.
2026-10-21 12:50:45 +02:00
Henrik RydgårdandClaude Opus 5.5 fbf0cc2df4 Android: Detect the accelerometer and gyro instead of assuming them
SYSPROP_HAS_ACCELEROMETER was true for every phone and tablet, whether or
not it had the sensor. The activity now tells the native side what the
sensor manager found, which also answers the new SYSPROP_HAS_GYRO. VR
headsets still report neither. Every other platform returns false for the
gyro for now.

The "Use gyroscope" checkbox uses the new property instead of waiting for
gyro data to show up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 12:20:20 +02:00
Henrik RydgårdandClaude Opus 5.5 8af7643efa ME: sceSysreg_driver's ME reset and clock calls, MMIO copies in sysclib
The calls homebrew starts the ME with (1.50's NIDs, and 3.71's for the
ME's reset and bus clock) set or clear a bit of the reset or bus clock
register the way the firmware's driver does, returning whether it was
set. Only those NIDs, so a later firmware's lowio.prx keeps its own
exports under --vsh.

Kernel modules copy their ME code to the boot page with SysclibForKernel's
memcpy, which now goes through MMIO for hardware addresses like the
replaced memcpy already did (MMIO::Copy, shared).

pspsdk's mebasic, MElib's test, PSP Bunny, snes9xTYL and ProTrekkr's test
now start their ME.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 12:06:11 +02:00
Henrik RydgårdandClaude Opus 5.5 f84e63f753 Tilt: Steady the tilt input with the gyroscope where there is one
Tilt used the raw accelerometer as the gravity vector, which jitters with
sensor noise and jumps when the device is shaken. Now, when gyro data is
coming in, a complementary filter integrates the gyro rates and only pulls
the result toward the accelerometer with a 0.5 s time constant, trusting
the accelerometer less the further it reads from 1g. The fused vector goes
through the same ProcessTilt path, so calibration and sensitivity work as
before. Without a gyro, nothing changes.

The fusion lives in TiltEventProcessor behind NativeGyroscope(), so other
sources (DualSense) can feed it too. Android registers TYPE_GYROSCOPE next
to the accelerometer. A "Use gyroscope" checkbox (on by default, per game)
appears in tilt setup once gyro data has been seen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 10:45:37 +02:00
Henrik RydgårdandClaude Opus 5.5 e52cc38866 ME: Reset, interrupts and the other registers homebrew runs the ME with
Sysreg (0xBC100000) gets the ME's reset bit (0x4C bit 2), the soft
interrupt (0x44, which interrupts the other CPU: the ME's controller from
the main CPU, PSP_MECODEC_INTR from the ME), the hardware mutex (0x48,
each CPU sets its own bit only while the other's is clear) and the clock
and config registers it writes. The ME's interrupt controller (0xBC300000,
write-1-to-clear, mask at 8) answers the ME only. All of it is the ME's
state and stays unhandled without an ME.

On the ME: interrupts to EBase with EPC and EXL, halt sleeping until an
unmasked interrupt, and Count, which doesn't run: it stays 0 across 10ms
with ERL set after reset and with Status cleared, and reads back what was
written. Reset keeps the GPRs and sets Status ERL/BEV/SR, which homebrew
relies on: mcidclan's samples run C on the stack Sony's ME code left. The
values are from a kernel probe on a PSP-1000 (sp 0x801FFFE0, COP0 22 = 1,
Status 0x20500404, mfic 0, which doesn't gate interrupts).

The main CPU runs base interrupt handlers the way interruptman calls them
(a0 intno, a1 arg, a2 interrupted pc; -2 disables), and interrupt 31 has
a stand-in for me_wrapper's handler, which homebrew releases first.

While the ME is emulated, any main CPU code may access hardware registers:
homebrew calls its own code in kernel mode through a kcall module. This
is a stopgap (g_kernelModeAnywhere, marked HACK): kernel mode should be
part of the CPU's state, switched on while a kernel module's export runs.
The memcpy/memmove replacements copy through MMIO when an end is a
register address, for code copied to the boot page.

mcidclan's me-soft-interrupt sample now runs, counting each interrupt in
both directions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 09:33:26 +02:00
Henrik RydgårdandClaude Opus 5.5 fef84cf0ca CPU: Add the Media Engine core, off by default
MeCore owns a second MIPSState in the ME's address space, held in reset
until released (SetReset). It runs on the interpreter, a slice behind the
main CPU: CoreTiming runs it for the cycles the main CPU has just used, in
Advance and ForceCheck. Its own PRId (COP0 22) and mfic/mtic flag; MMIO
counts as kernel mode on it, and a syscall stops it with an error rather
than reaching HLE. The interpreter's block stepping reads delay slots
through the stepping CPU's memory, and cache instructions log at verbose
level, as ME code flushes its caches in a loop.

Created at boot only for homebrew (PBP with MG category or no SFO, ELF;
the VSH counts, as vshmain.prx loads as an ELF), when "Enable ME
emulation for homebrew" is on: a global checkbox in developer settings,
off by default and not translated yet, or --me-core. Saved in its own
section after MMIO (SaveStart v5), with its own memory; loading a state
creates or removes the ME to match.

The boot page now starts with a reset stub that sends the ME on to
0xBFC00040, where homebrew puts its code (from the firmware's reset code,
read on a PSP-1000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 22:22:40 +02:00
Henrik Rydgård aba3a99888 Merge pull request #22679 from hrydgard/exit-on-game-exit
Android frontends: --pause-menu-exit exits when the game ends, Args extra with URIs, Copy deep link
2026-10-20 13:25:53 +02:00
Henrik RydgårdandClaude Opus 5.5 55f02684f5 Android: Enable "Copy deep link" on the game screen, copying the content URI
PPSSPP has access to the URI, so a frontend or adb can launch the game with it,
which isn't true of a plain path on modern Android.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:42:07 +02:00
Henrik RydgårdandClaude Opus 5.5 b7b4a265b7 MMIO: Register hardware register blocks as devices
The interpreter's MMIO dispatch was a hardcoded chain for GPIO and the syscon
serial port. Devices now register an address range in Core/HW/MMIO and get
each access with its width and the MIPSState making it, for registers that
answer each CPU differently. An access no device handles is logged and reads
as poison, as before.

GPIO and syscon serial become the first two devices. They're reset at boot,
which they never were, and saved in savestates (SaveStart section version 4).

ll/sc, lwl/lwr and lwc1/swc1 reach MMIO in the interpreter too, and kernel
blocks leave lwc1/swc1 to it like the other loads and stores. swl/swr and
lv/sv still treat it as a bad address. TestMMIO runs a kernel-mode access on
each core and the registers through a savestate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:36:52 +02:00
Henrik RydgårdandClaude Opus 5.5 df76bae5dc Android: Read the Args extra also when launched with a URI
- Fixes #18528

Launchers typically open the game with ACTION_VIEW and a content:// URI, so
they could never pass options before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:30:19 +02:00
Henrik Rydgård 6015ff06a7 Merge pull request #22616 from LunaMoo/native-cso-pack
Add built-in CSO packing functionality
2026-10-19 18:53:00 +02:00
Claude 363d816789 Game screen: Compress game as CSO
"Compress game" in the game screen's ... menu, for ISO, CSO, CHD and PSN
EBOOT.PBP games. It opens a list of the CSO presets (and Extract to ISO
for anything that's not an ISO already):

- Best: libdeflate level 12, 64K blocks
- PSP Compatible: zlib level 9, 2K blocks, readable by custom firmware

These match maxcso's presets, and the CSOs come out byte-identical to its.
The writer, Core/FileSystems/CSOCompress, reads the input through PPSSPP's
own block devices, so recompressing is a single pass with no temporary
file.

The original is deleted when done, unless "Keep the original file" is
checked (off by default). When the new name is taken, by a kept original
or another file, a number is added to it.

The work runs on a thread using every core, with a progress bar and time
left at the bottom of the game screen. It keeps going when the screen is
left, and the ... menu offers Cancel meanwhile. On the game list the
game's icon is covered by "Compressing...", and clicking it opens the
game screen instead of playing. When done, recents follow the new file
and the game screen and lists refresh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCxekYqPnzNr5d8Tp1q7aZ
2026-10-19 16:01:53 +02:00
Claude 4ab1066ade ext: Add libdeflate's deflate encoder
Only the compressor (deflate_compress.c, utils.c and their headers) from
libdeflate 1.7, the version LunaMoo/maxcso vendors, for writing CSO files.
Built as part of Core on every platform. MIT license.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCxekYqPnzNr5d8Tp1q7aZ
2026-10-19 15:58:54 +02:00
Henrik RydgårdandClaude Opus 5.5 515b497f91 Android: Apply size changes on the render thread, between frames
backbufferResize comes from the UI thread, and changed g_display while the
render thread was drawing with it. Like iOS, it now leaves the new size for
the render thread to apply before its next frame, or applies it directly if
no render loop is running. A rotation in a resize that's superseded before
it's applied still forces the swapchain recreate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 01:44:03 +02:00
Henrik RydgårdandClaude Opus 5.5 d6985d81eb Android: Simplify joystickAxis and queryGameInfo, drop dead JNI calls
joystickAxis copies the axes in batches on the stack instead of allocating
per event, and bounds the count by the arrays it got. queryGameInfo's cache
is a local, in a helper so it's gone before the thread manager is torn down.

Removes Android_GetFreeSpaceByFilePath (never called) with its Java side, and
isLandscape, which only existed to call NativeGetAppInfo early.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 01:36:35 +02:00
Henrik RydgårdandClaude Opus 5.5 a09f59910b Android: Fix JNI edge cases, read Java strings as real UTF-8
- Scoped storage no longer flips on while no activity is registered: whether
  legacy storage was preserved is fixed per install, so it's asked once.
- Free space reads as unknown (-1), not 0 bytes, without an activity.
- Strings from Java (file listings and info, crash history, the camera list,
  the input debug string) go through GetJavaString, so names with characters
  outside the BMP aren't mangled, and a null string can't crash the crash
  history.
- Requests (inputbox, browse_*) whose command can't be delivered are failed
  instead of left waiting forever.
- A font that fails to load uses the default typeface (-1) instead of font 0,
  and is retried later if there was no activity yet. Missing exception checks
  added in the text renderer.
- Method IDs and the storage callbacks are looked up once in JNI_OnLoad.
- Unknown permissions are logged correctly.

Also removes what was left of Moga support, the unused backbuffer format and
a stray MessageBox macro, and tidies System_GetCameraDeviceList.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 01:29:18 +02:00
Henrik RydgårdandClaude Opus 5.5 363fe679c2 Android: Make location an optional feature
The location permissions imply android.hardware.location as required,
on top of the .gps and .network subfeatures we already marked optional.
Also skip the permission prompt on devices without location hardware.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 19:49:12 +02:00
Henrik RydgårdandClaude Opus 5.5 1d7458c4f9 Android: Don't ask for microphone permission on devices without one
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 19:28:12 +02:00
Henrik RydgårdandClaude Opus 5.5 b3ed6971d2 Android: Make the microphone an optional feature
RECORD_AUDIO implies android.hardware.microphone with required=true
unless declared otherwise, which hides the app from devices without one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 19:26:29 +02:00
Henrik RydgårdandClaude Opus 5.5 beaae7016f Android: Keep the orientation setting on large Android 17 screens
Android 17 ignores fixed orientations on sw600dp+ displays, but exempts
games, and we declare appCategory="game". Tested on an Android 17 tablet
emulator: with the category, Portrait is honored (letterboxed); without it,
the system logs "Ignoring requested fixed orientation". So stop hiding the
rotation setting there, and drop the smallestScreenWidthDp plumbing that
only existed for that check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:07 +02:00
Henrik RydgårdandClaude Opus 5.5 d2e9e87242 Android: Ask for the local network permission before starting file upload
Part of #21585

Android 17 apps need ACCESS_LOCAL_NETWORK to serve other devices on the LAN.
The upload screen now asks for it when it opens, starts the web server once
it's granted, and shows a warning and a retry button if it's denied.

The permission only exists from API 37, so check for that rather than 35.
On Android 15 and 16 it reads as denied, which left Remote ISO's connect tab
stuck behind a permission request that could never succeed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:04 +02:00
Henrik Rydgård a0638c49cd Merge pull request #22590 from LunaMoo/PSPAR_support
Cheats: Support PSPAR (_M) codes
2026-10-16 17:50:11 +02:00
Henrik RydgårdandClaude Opus 5.5 1d7487e5a7 GPU: Add a null GPU, and headless --graphics=null to use it
GPU_Null derives straight from GPUCommon and has no thin3d context, framebuffer
manager, texture cache or shaders. It runs display lists with the flow control,
bounding boxes, matrix loads (the hardware backends'), immediate vertices and
block transfers of the others, moves the vertex and index pointers past each
draw and curve, and charges the per-vertex and fill cost the software renderer
does. A DrawEngineCommon that queues nothing provides the vertex decoders.

It's for measuring the CPU emulation. From savestates, God of War, Tekken 6,
Wipeout Pure and LocoRoco flip the same frames in 10 emulated seconds as with
Vulkan and software, in 44-77% fewer instructions than Vulkan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:12:38 +02:00
LunaMooandClaude Opus 5.5 3e175060dd Cheats: Support PSPAR (_M) codes
PSPAR code types run the way TempAR's cheat_apply_pspar() runs them:
writes, 32-bit and masked 16-bit conditions with the D0 terminator stack,
load/set/add/write offset, C0 loops with D1/D2, the Dx data register
codes, patch (0x0E), memory copy (0x0F) and the safe data store (0xC4),
which lets a cheat write over its own lines.

PSPAR's button codes (type 9/A at 0x09F00000 and up) read the real pad,
like TempAR's hijack option, as does TempAR's fake pad address
0x0A000000. TempAR's _N extensions aren't supported, and folder codes
(0xCF) do nothing.

Lines can now end in a # or // comment, a cheat can't mix _L and _M
lines, and importing from cheat.db keeps _M lines. The unit test runs
the PSPAR tests from TempAR's tests.db.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:27:33 +02:00
Henrik RydgårdandClaude Opus 5.5 1d5e2689b5 JIT: Add --jit-profile, a sampling profiler for the CPU cores
The native IR JITs get the emulator thread's host PC sampled every 50 us (a
signal on macOS and Linux, SuspendThread on Windows), mapped to blocks, the
routines they call and host functions. The report has each hot block's MIPS,
IR and host code with the samples per host instruction. The IR interpreter's
block timing, previously behind IR_PROFILING, now runs on the same switch.
Both cost nothing when off.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:01:52 +02:00
Henrik RydgårdandClaude Opus 5.5 0edfd93a3a Depth raster: Stop a row once it has left the triangle
The triangle is convex, so once a row has been entered and left, the
rest of it is outside. This cut cycles by 5% in Wipeout Pure, where depth
raster is on for the lens flares.

The test checks the covered pixels against a per-pixel edge test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 10:14:54 +02:00
Henrik RydgårdandClaude Opus 5.5 83639a73e8 GPU: Cull-test draws four vertices at a time
TestBoundingBoxFast transformed and tested one vertex per iteration,
with a scalar divide and a min/max chain each vertex had to wait on.
It now transposes four vertices into lanes and does it all as vectors,
with the same operations in the same order, so the results don't change.
It was 16.6% of the emulator thread in Wipeout Pure (with depth raster
off) and 5% in GTA: Vice City Stories.

Per vertex, it's 1.8x faster on an M1, 2x on x86-64, 1.7x on a Galaxy S8+
and 10% faster in 32-bit mode there.

The test compares it with the old version on random draws in every
position and index format.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 10:14:51 +02:00
Henrik RydgårdandClaude Opus 5.5 f3c69fc5d1 Remove FakeJit and the pre-IR JitBlockCache
FakeJit was the native JIT for architectures without one, but the config
already forces those onto the IR interpreter, so CreateNativeJit() now
returns that instead. The vertex decoder fallback no longer needs
FakeEmitter.

JitBlockCache was the block cache of the pre-IR JITs. Its header keeps
the debug interface types the IR block caches use. Also removes the
JitInterface link hooks only it called, and the JitState fields tied to
it. This fixes a null dereference in the ImDebugger memory section,
which read GetBlockCache(), always null with the IR JITs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 02:24:25 +02:00
Henrik RydgårdandClaude Opus 5.5 74703b031e Remove the legacy ARM32 JIT
The IR-based one replaces it. With that, every native JIT is IR-based, which the JIT viewer and
the settings' comments now assume. The ARM emitter test loses its part that drove the old FPU
register cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 17:32:53 +02:00
Henrik RydgårdandClaude Opus 5.5 6d105ae35d ARM32: Start an IR-based JIT
The backend follows the ARM64 one: a dispatcher, a register cache on top of the IR one, and
native exits, syscalls, breakpoints and transfers. Everything else goes to the IR interpreter
for now. Floats are mapped one lane per S register (no SIMD mapping yet), and addresses are
masked, as on every 32-bit build.

The JIT core now uses it on ARM32 in place of the old ArmJit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 17:32:53 +02:00
Henrik RydgårdandClaude Opus 5.5 66d7975e7f Remove the legacy (non-IR) x86-64 and ARM64 JITs
Their IR-based replacements stay. The JIT core now creates the IR JIT on both, so "JIT using
IR" is the same as "JIT" on every architecture (ARM32 still uses its legacy JIT for both). The
settings no longer offer it, a saved JIT_IR loads as JIT, and the JIT harness and CPUEquivalence
fuzzer drop the duplicate run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 14:39:45 +02:00
Henrik RydgårdandClaude Opus 5.5 f7db56628c ARM disassembler: Rewrite, cover everything the emitter can emit, add a test
The old disarm handled the integer instructions but only a fraction of VFP
and NEON, and got several of those wrong. The new one decodes ARMv7-A ARM
state with VFPv4 and NEON, printing UAL with upper case mnemonics.

The new ArmDisasm test runs every ARM emitter function through it, two
examples each. Tools/arm_disasm_check.py compares the test's output with
llvm-mc; they agree on all of it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:23:14 +02:00
Henrik RydgårdandClaude Opus 5.5 4163737bbc ARM64 disassembler: Cover everything the emitter can emit, add a test
Rewrites most of DisArm64.cpp. Previously missing: test-and-branch,
exceptions other than brk, hints and barriers, adc/sbc, conditional
compare, extr, literal loads, exclusives, unscaled and FP register
offset loads, ldpsw, SIMD structure loads/stores, scalar pairwise,
fixed-point conversions, fccmp, most two-reg misc ops, modified
immediates, smov/umov/dup from GPR, and the scalar shift forms.

Fixed: sp printed as x31, unsigned load offsets sign-extended, ldrsb and
friends shown as stores, adrp ignoring the page, double precision
1-source ops shown as single, fcmpe shown as fcmp, smulh/umulh, the
bitmask decode for 64-bit elements, and fpcr shown as fpsr. Common
aliases are used now (cset, mul, tst, cmn, lsl/asr/ubfx, sxtw...).

TestArm64Disasm runs two examples of every emitter function through it.
The expected strings were verified with Tools/arm64_disasm_check.py,
which compares all 833 instruction words against llvm-mc.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 20:59:51 +02:00
Henrik RydgårdandClaude Opus 5.5 c7d60a5cfb Headless: Vulkan and OpenGL ES on Android, and frametests.py --adb
The Android headless build only did software rendering. Vulkan takes the
offscreen path desktop headless already uses, and OpenGL ES renders into an
EGL pbuffer, so both run from adb shell with no window. Assets are found next
to the executable, like on desktop, instead of in an APK path that no longer
exists.

frametests.py --adb pushes the binary, assets and dumps to a device, runs each
dump there with Vulkan and GLES, and compares the screenshots with the host's
Vulkan render using Tools/image_compare.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:44:34 +02:00
Henrik RydgårdandClaude Opus 5.5 6a15aff789 Android.mk: Don't link the adrenotools hook libraries into ppsspp_common
They are meant to be separate shared libraries that driver.cpp loads into the
driver's namespace by name (ext/libadrenotools builds them that way, and so
does the gradle build). Linked into an executable, file_redirect_hook.c's
global fopen() takes over every fopen in the process, including the one in
libc's own startup, and hook_fopen() calls fopen() again forever. That hung
ppsspp_headless (and the unit test executable) before main().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:44:34 +02:00
Henrik RydgårdandClaude Opus 5.5 3afb17f7d4 Android: Don't leave room for system bars we hid
Before Android 11, decorFitsSystemWindows(false) sets LAYOUT_STABLE, and
the system bar insets then keep the space of hidden bars. So in
landscape, where the status bar is always hidden, the UI had an empty
band at the top (seen on a Galaxy S8 and a Pocophone F1). Count only
the bars we show, and reapply the insets after changing that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:59:06 +02:00
Henrik RydgårdandClaude Opus 5.5 6d9a54d60d Android: Don't restart the microphone on resume after it was stopped while paused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:41:19 +02:00
Henrik RydgårdandClaude Opus 5.5 56580581e0 Android: Check for Java exceptions after content URI calls
ContentUri catches Exception, but an Error got through, and none of the
native wrappers checked. A thrown int call returned 0, which reads as
SUCCESS, and the exception stayed pending, so the next JNI call aborted.
copyDocument/moveDocument are API 24/25 while the folder browser is
offered from 21, so those threw NoSuchMethodError there - now they return
a failure so the callers fall back.

Also convert non-URI names to Java's modified UTF-8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:41:19 +02:00
Henrik RydgårdandClaude Opus 5.5 bfa0ef9d1f Android: Small crash fixes in the activity and shortcut creation
- Catch takePersistableUriPermission failing in ShortcutActivity, as
  DocumentResultProxyActivity already does. Some pickers don't grant it.
- Treat a cancelled permission request (empty results) as denied.
- Handle getCacheDir() returning null, like getFilesDir().
- Don't let the input device debug string, read from the render thread,
  throw ConcurrentModificationException into native code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:41:19 +02:00
Henrik RydgårdandClaude Opus 5.5 51a799623c Android: Fix GPS and infrared crashes on devices without them
requestLocationUpdates() throws IllegalArgumentException for a provider
that doesn't exist (WiFi-only tablets, TVs, Chromebooks), which crashed
any game opening the GPS. Register each provider separately.

Without an IR blaster, the IR service is null, and InfraredHelper was
created anyway, so a sceSircs call NPE'd. Also catch transmit() failing,
like the SecurityException on API 21/22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:41:19 +02:00
Henrik Rydgård 8d8009840e Merge pull request #22511 from hrydgard/android-inputbox-fixes
Claude code review: Android inputbox fixes
2026-10-12 13:20:25 +02:00
Henrik RydgårdandClaude Opus 5.5 7678336e28 Android/VR input: Lock the VR key state, fix device tracking and release
pspKeys and pspAxis were written from the input thread and the render
thread without a lock. Entering VR camera adjust "released" held keys by
sending PSP button codes as keycodes, which released nothing; use
ControlMapper::ReleaseAll instead.

Input devices are now matched by ID. InputDevice objects are replaced after
device changes, so reference comparison registered the same device again.
Removing a device now releases what it was holding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:56:32 +02:00
Henrik RydgårdandClaude Opus 5.5 468b83a738 Android: Fix JNI string encoding, thread safety, password in debug log
GetJavaString() returned Java's modified UTF-8, so input like emoji
reached C++ as invalid UTF-8. Commands to Java now get converted the other
way. Request IDs are made on several threads, and the permissions map was
written on the Java UI thread and read on the render thread. The frame
command debug log no longer prints params, which could hold a password.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:37:19 +02:00
Henrik RydgårdandClaude Opus 5.5 2aa3e8d6d3 Android: Always answer native requests
An input box whose show() threw, a second input box open at onDestroy, and
browse commands dropped for a destroyed activity or failing in
processCommand were never answered, leaving the native side waiting (a
game's OSK forever). The image proxy also crashed the app on a picker
result without a URI, a SecurityException or an OOM.

Input boxes now answer exactly once, instead of a success followed by an
ignored failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:37:19 +02:00