Commit Graph
50435 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 b3aee84a8e Path: Recognize content URIs on every platform
So code that handles them can be tested anywhere. Elsewhere, file operations
on them just fail, as they did when they were taken for native paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 17:53:02 +01:00
Henrik RydgårdandClaude Opus 5.5 a61893dcc1 Translations: Memory stick folder checks, move progress and failures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:16:29 +01:00
Henrik RydgårdandClaude Opus 5.5 edff9f4a95 MemStick: Delete the originals by top-level folder on content URIs
DeleteDir takes a folder's contents along there, so a copying move's cleanup
is one storage call per top-level item instead of one per file and folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:16:29 +01:00
Henrik RydgårdandClaude Opus 5.5 ec532bd420 MemStickScreen: Count save states as existing data, warn that a move replaces files
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:16:29 +01:00
Henrik RydgårdandClaude Opus 5.5 4079be9a05 StorageTest: Test the memory stick move and folder checks too
The rename, copy and undo paths of MoveDirectoryContentsSafe(), and
CheckNewMemstickFolder() on a renamed PSP folder, in the scratch folder,
so on a device they run through content URIs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:37 +01:00
Henrik RydgårdandClaude Opus 5.5 745602bd5c MemStickScreen: Count down five seconds to the restart after a move
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:37 +01:00
Henrik RydgårdandClaude Opus 5.5 dbca4f8069 MemStickScreen: Show the move's progress, and the free space after views are recreated
The spacer column was FILL_PARENT high, which in the new row of columns
took the whole screen and pushed the progress below off it. The free
space was set on a view that RecreateViews() then replaced with a new
one saying "...", so it's kept and used when creating the views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:37 +01:00
Henrik RydgårdandClaude Opus 5.5 1c0d8a2b96 MemStickScreen: Readable folder names, say what the move is doing
- Folders show as "Internal storage / PSP" or "SD card (1234-ABCD) / PSP",
  and the app's own folder as "App private data", instead of content URIs
  and /storage/emulated/0/Android/data/... paths. Here and in the
  settings.
- The move's progress names the pass it's in (moving, finding files,
  copying, checking, deleting the originals, putting things back) with
  the count, on a line of its own above the file, across the whole width.
- A successful move shows "Done!" for three seconds before restarting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:37 +01:00
Henrik RydgårdandClaude Opus 5.5 86fcd95a4f Add a memory stick start screen for Tools/android_run.py
--start-screen=memstick opens the memory stick screen as the settings do, for
moving an existing memory stick, where memsticksetup is the first-boot one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:37 +01:00
Henrik RydgårdandClaude Opus 5.5 481a15ed93 Path: Make ToVisualString's relative root work off Windows
The non-Windows handling sat inside the content URI branch, after its
return, so the root was ignored. Found by PathMore on Android.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 3088cf5d3d Path, AndroidContentURI: Stop ComputePathTo at whole folder names, more tests
ComputePathTo() trusted StartsWith(), a plain string prefix, so "/a/b" was
a parent of "/a/bcd" (giving "d"), and "primary:Games" of
"primary:Games2/x". AndroidContentURI's also always skipped a separator,
so from a volume root like "1234-ABCD:" the first letter of the next
folder went missing. CreateFullPath() computes paths from the tree root
this way.

New tests cover parsing and building content URIs, escaping, extensions
and navigation, and Path's components, joining, prefixes, HTTP paths and
visual strings. EXPECT_EQ_STR now parenthesizes its arguments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 261d33f5a1 MemStick: Recognize renamed PSP folders, safer leaving after a move
- A PSP folder under another name is recognized: picking one is refused,
  since the data would go into a new PSP folder inside it, and so is
  picking anything in it. On disk that's SAVEDATA with SYSTEM or GAME, or
  PPSSPP_STATE. For a picked tree, whose parents we can't see, a SAVEDATA
  or PPSSPP_STATE anywhere in its path.
- The new folder is saved as the one to use after a rename-based move
  too, not only a copy. So leaving the screen after the data moved but
  the switch failed restarts onto the new folder, instead of staying on
  the emptied old one.
- Picked folders get the same write test as native ones, so a
  write-protected SD card is refused before anything is moved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 cff780f339 MemStick: Undo a failed move, save the new folder before deleting
A move that failed partway left the data split: what was renamed before
renaming gave up stayed in the new folder, and the copy's leftovers too.
The screen said "Failed to move some files!", which then wasn't even
shown. The move now records what it renames and creates, and a failure
puts it all back. The screen says nothing was changed, or in the rare
case that something couldn't be moved back, that the data is now partly
in each folder.

The copying move also deleted the originals before the new folder was
saved as the one to use, so being killed while deleting left the config
on a half-emptied folder. memstick_dir.txt is now written once the
copies are verified, before anything is deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 6f44800232 MemStickScreen: Finish directly, the screen manager handles it now
24d8955531 fixed a caller finishing itself from dialogFinished(), which
this screen worked around with a flag picked up in update().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 ed5ffe3d1d MemStickScreen: Don't blame the folder after the data has moved
If switching failed after the data was moved, the screen said the folder
doesn't work as a memory stick, though the data was already in it.

The switch's checks (the new folder and the one holding memstick_dir.txt
are writable) now run before anything is moved, where that message is
accurate. If the switch still fails after a move, a new message says the
data is in the new folder, and OK only retries the switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 86a12a7df2 MemStick: Fix data loss and leftovers in the copying move
When moving data can't rename (another device, or merging into a folder
with data), it copies, verifies, and deletes the old folders. On content
URIs that delete is recursive, but a subfolder that failed to list, or a
file whose relative path couldn't be computed, was silently left out of
the copy, and so got deleted with its folder. Any listing failure now
aborts the move before anything is copied.

On native paths, the same fallback never deleted the old files, only the
then non-empty folders, so "Move Data" left a full copy behind. The
verified originals are now deleted first, on both.

SwitchMemstickFolderTo() fails if it can't write memstick_dir.txt, rather
than restarting onto the old, by then emptied, folder. "Skip for now" in
setup only leaves when the switch worked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik RydgårdandClaude Opus 5.5 1832c9aced MemStickScreen: Refuse folders inside a memory stick's PSP folder
Picking the new memory stick folder is checked by CheckNewMemstickFolder(),
which compares locations across content URIs and Android storage paths,
case-insensitively and on whole folder names:

- Inside the current memory stick's PSP folder: refused. This used to be a
  warning only, compared by string prefix (so "Games2" was "inside"
  "Games"), and missed URIs from a different volume or a native path.
- Inside some other memory stick's PSP folder (like after a reinstall):
  refused. Found on disk among the parents of native paths, and from the
  names for picked trees, whose parents we can't look at.
- Same PSP folder as the current one (a PSP folder and its parent), or the
  current one inside the new one: allowed, without moving data.

GetPSPFolderOf() is the PSP folder rule GetSysDirectory() uses, now shared
with the move, which compared "PSP" case-sensitively and so could move data
into psp/PSP. FolderSeemsToBeUsed() looks in that folder too, and "Move
Data" starts unchecked when the new folder already has data, since merging
overwrites it.

Switching to a folder without a config no longer resets settings to the
defaults: the current ones are written there instead. Typing a manual path
without the leading slash works again (the fixup was unreachable).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:53:36 +01:00
Henrik Rydgård b69424e8f2 Merge pull request #22932 from hrydgard/http-ids-npauth-ticket
sceHttp: Stable ids; NpAuth: Return the ticket param asked for
2026-11-06 14:11:19 +01:00
Henrik Rydgård 3e82948c28 Merge pull request #22956 from hrydgard/gamebrowser-ctrl-wheel-zoom
Game browser: Ctrl/Cmd+wheel and Ctrl/Cmd+Plus/Minus icon zoom; unify wheel units; drop SDL2 leftovers
2026-11-06 14:02:16 +01:00
Henrik Rydgård 727805e193 Merge pull request #22959 from hrydgard/storage-fixes
Android storage: No numbered duplicates, real errors, faster startup
2026-11-06 13:46:51 +01:00
Henrik Rydgård a4296e57b8 Merge pull request #22958 from hrydgard/xmb-mac-address
VSH: Show the configured MAC address in System Information
2026-11-06 13:43:45 +01:00
Henrik RydgårdandClaude Opus 5.5 cdc4f522f4 Fewer storage calls at startup
CreateSysDirectories() lists the PSP folder once instead of checking each
folder, and only writes .nomedia into folders it creates, instead of
rewriting it into all of them every time. PreferUpperCaseDir() doesn't
check for both spellings on content URIs, where storage is
case-insensitive. Theme and post shader loading don't list an empty
folder of their own a second time through File::GetFilesInDir.

On a phone with the memory stick on a content URI, startup now makes 12
storage provider calls before the game list, down from 82.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:53:31 +01:00
Henrik RydgårdandClaude Opus 5.5 ad748f31e9 Android: Don't reload the config for the storage permission startup report
Android reports the storage permission at every startup, and the handler
took it for a new grant: it created the system directories, reloaded the
whole config, and created them again. With a memory stick on a content
URI that was about half a second of storage calls in every startup. Only
redo it when the config couldn't be loaded before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:53:31 +01:00
Henrik RydgårdandClaude Opus 5.5 f519f40f65 Android storage: Never make numbered duplicates, report real errors
Given a taken name, the storage provider makes "name (1)" rather than
fail. Renaming onto an existing file did exactly that and reported
success, so a game's sceIoRename onto a save file left a duplicate
instead of failing like on a PSP. Creating, renaming and copying now
check the name the document ended up with, undo a renamed one, and
return ALREADY_EXISTS. A new storage test checks that no operation onto
a taken name leaves a duplicate.

The Java side only ever returned UNKNOWN. It now maps a missing file to
NOT_FOUND and a full disk to DISK_FULL, and OpenFD/OpenCFile set errno
from those, so DirectoryFileSystem reports a full disk on content URIs
like on native paths, instead of "file not found".

That also lets writing open first, and only create when the file is
missing: overwriting a file is one call to the provider instead of two
(18.6 -> 13.2 ms in the storage test on an SD card).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:53:31 +01:00
Henrik Rydgård 37a0d427ac Merge pull request #22957 from hrydgard/storage-tests
Android: Storage tests for content URIs, and fixes from running them
2026-11-06 12:52:42 +01:00
Henrik Rydgård c7e7ee2821 Config: Default UI scale factor 0 on Android and iOS
PPSSPP_PLATFORM(LINUX) is also true on Android, so it got the desktop
default of -1, which was never intended.
2026-11-06 12:46:34 +01:00
Henrik RydgårdandClaude Opus 5.5 f1ef96e3dd SDL: Halve wheel deltas on macOS, and zoom by the fingers' direction
Trackpad scrolling went about twice as far as it should. SDL3's wheel
event doesn't tell a trackpad from a mouse, so this applies to both.

With natural scrolling the wheel direction is the opposite of the
fingers', which is right for scrolling but made the icon zoom feel
backwards. SDL's FLIPPED direction is now passed on as WHEEL_FLIPPED, and
the zoom undoes it, so fingers up make the icons bigger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:39:50 +01:00
Henrik RydgårdandClaude Opus 5.5 a3cbbd086b VSH: Show the configured MAC address in System Information
- See #22924

The page reads IdStorage key 0x44 (6 bytes at offset 0) through
vshbridge.prx, as does wlan.prx's sceWlanGetEtherAddr. The real
idstorage.prx never finishes its init (it stops at the kernel mutexes we
leave unresolved), so every lookup failed and the page showed "-".

sceIdStorageLookup is now HLE, answering key 0x44 from the same MAC
sceWlanGetEtherAddr uses. Other keys fail with 0x80000108, what the real
driver returns uninitialized, and the rest of the library still resolves
to idstorage.prx. Taken from the 6.61 modules' disassembly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:34:02 +01:00
Henrik RydgårdandClaude Opus 5.5 360b48706b Remove the SDL2 leftovers
FindSDL2.cmake and the cmake/sdl module path, SDL/macbundle.sh (which
only copied SDL2.framework into the bundle, and nothing calls any more),
b.sh's --no-sdl2 and its USE_SYSTEM_LIBSDL2 flags (no such option exists),
and SDL2 mentions in the README, a log message and a commented-out CI line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:14:13 +01:00
Henrik RydgårdandClaude Opus 5.5 666dfd2187 Android: Hold a client for the storage provider between calls
Without one, every query and DocumentsContract call acquires the provider
and releases it again, and when our reference count drops to zero, each
of those is a binder call to the system as well. Holding an unstable
ContentProviderClient keeps the count up, without tying our process to
the provider's. Queries go through it, and a dead provider is dropped
and acquired again.

Storage test timings on an SD card, median of five runs before and after:
exists 9.3 -> 6.7 ms, file info 10.4 -> 7.4, overwrite 21.8 -> 18.6,
read 28.5 -> 26.6. Creating, deleting and listing are dominated by the
provider's own work, and gain a few percent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:09:55 +01:00
Henrik RydgårdandClaude Opus 5.5 97099b4d96 android_run.py: Let our log tag through on devices that silence all tags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:07:32 +01:00
Henrik RydgårdandClaude Opus 5.5 9e1044cf66 Android: Report the free space of the volume a content URI is on
The fstatvfs of a descriptor opened through the storage provider gave the
internal storage's free space for a folder on an SD card. On Android 11
and later, find the volume from the document ID and ask it instead.
Found by the storage tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:07:32 +01:00
Henrik RydgårdandClaude Opus 5.5 aa4053c2b0 Add a storage test screen, to test content URIs on a device
The unit tests have no Java VM on Android, so the content URI storage
code could only be tested by hand. RunStorageTests() exercises file
creation, overwrite, listing, rename, moves, copies, deletes, odd names
and free space in a scratch folder, and times the calls the emulator
makes most.

The new StorageTestScreen (--start-screen=storagetest) runs them in a
picked folder and remembers it. Tools/android_storage_test.py starts it
over adb and collects the results from logcat, and Tools/android_run.py
builds, installs and starts the app with any options. The screen raises
the System log channel to INFO during a run, so the results get through.
The Storage unit test runs the same tests on a native folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:01:28 +01:00
Henrik Rydgård 22bc3f9db4 Merge pull request #22955 from hrydgard/android-unittest-runner
Android: Run the unit tests on a device, and make them pass there
2026-11-06 11:14:37 +01:00
Henrik Rydgård e237f77358 Merge pull request #22954 from hrydgard/langtool-preserve-line-endings
langtool: Keep each ini file's line endings
2026-11-06 11:05:39 +01:00
Henrik RydgårdandClaude Opus 5.5 f64f806626 Android: Run the unit tests on a device, and make them pass there
CI builds ppsspp_unittest for Android but never runs it, and on a device
ten tests failed. Tools/android_unittest.py builds it, pushes it and the
files tests read, and runs each test as its own process, so a crash
doesn't swallow the buffered output of the rest.

- MemArena picked the pre-Android 8 /dev/ashmem path whenever
  SYSPROP_SYSTEMVERSION was unknown, which it is outside the app, and
  newer Android doesn't allow that. So Memory::Init failed and the JIT
  tests crashed. ASharedMemory_create is looked up at runtime anyway, so
  try it first. Older versions don't have it and fall back as before.
- JNILocalFrame tolerates a null JNIEnv, so content URI calls fail like
  missing files in executables without a Java VM, instead of crashing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 10:39:32 +01:00
Henrik RydgårdandClaude Opus 5.5 edc085eb05 Input: Report mouse wheel deltas as 120 per notch on every backend
Windows sent 120 per notch, SDL and Android 30, and UWP nothing, and
ScrollView scaled the delta by a fudge factor. It now scrolls 150 pixels
per notch everywhere, which is what Windows did; SDL and Android scrolled
a quarter of that.

The game browser's icon zoom uses the shortcut modifier now (Cmd on Apple
platforms, where Ctrl+scroll is the system's accessibility zoom), and
also takes Ctrl/Cmd with Plus and Minus. It keeps the focused game
focused when the icons are resized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 10:38:25 +01:00
Henrik RydgårdandClaude Opus 5.5 1df7557dda Remove the SDL2 macOS framework submodule
We build with SDL3 now, and nothing references it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 10:36:37 +01:00
Henrik RydgårdandClaude Opus 5.5 a6d869e38f GameBrowser: Step the Ctrl+wheel zoom once per notch, for touchpads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 10:12:07 +01:00
Henrik RydgårdandClaude Opus 5.5 aa6be1fe58 SDL: Don't undo the OS's natural scrolling setting on the mouse wheel
SDL3 reports wheel.y with the setting already applied; direction ==
SDL_MOUSEWHEEL_FLIPPED only says that it was. Negating it, added in the
SDL3 port, made touchpad scrolling go backwards on macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 10:12:06 +01:00
Henrik Rydgård 5327f828ec Merge pull request #22940 from hrydgard/hle-hardening
HLE: Range-check buffers and structs from game arguments
2026-11-06 10:01:54 +01:00
Henrik RydgårdandClaude Opus 5.5 f6c5d5014c langtool: Keep each ini file's line endings
lines() strips both LF and CRLF, and write() always wrote LF, so every
run on a Windows checkout rewrote all the language files from CRLF to
LF. Remember which one a file used and write that back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 09:59:58 +01:00
Henrik Rydgård cca8b518ca Merge pull request #22953 from hrydgard/libretro-cache-iso-in-ram
libretro: Make "Cache Full ISO in RAM" work again, only offer it on desktops
2026-11-06 09:49:14 +01:00
Henrik Rydgård df65149a08 Merge pull request #22952 from hrydgard/zlib-1.4
Update our vendored copy of zlib to 1.4
2026-11-06 09:38:16 +01:00
Henrik RydgårdandClaude Opus 5.5 674bf9d29d libretro: Only offer "Cache Full ISO in RAM" where it works
The option is left out of the core options on mobile, Switch, UWP and
32-bit builds, where the cache isn't used. One macro decides both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 09:27:08 +01:00
Henrik RydgårdandClaude Opus 5.5 5f6db5a178 libretro: Make "Cache ISO in RAM" work again on desktops
Since b05d6171d4, the RAM cache is only used when the platform reports
SYSPROP_ENOUGH_RAM_FOR_FULL_ISO, which the libretro core never did, so
the ppsspp_cache_iso option silently did nothing. Report it on 64-bit
desktops, like the standalone app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 08:38:28 +01:00
Henrik RydgårdandClaude Opus 5.5 009faf8f61 Update our vendored copy of zlib to 1.4
Mostly security fixes: a gzwrite() buffer overflow (CVE-2026-85091),
and several out-of-bounds reads and writes in inflate, inflateBack and
deflate on unusual call sequences or invalid data.

Taken from the signed release tarball. Our only local change, the
<unistd.h> include in gzguts.h, is carried over. The library source
file list is unchanged, so no build files needed updating.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:17:04 +01:00
Henrik Rydgård ee714567ee Merge pull request #22947 from hrydgard/gpu-hardening
GE, texture packs, shaders: Bound memory access from GE commands and shared content
2026-11-06 01:07:57 +01:00
Henrik Rydgård 8cbde31a5a Merge pull request #22949 from Nemoumbra/nocash-map-fix
Wrong file type for the Symbol Map in ImDebugger
2026-11-06 01:04:35 +01:00
Henrik Rydgård 75c35f99e2 Merge pull request #22950 from hrydgard/imdebugger-func-scan
ImDebugger: Add scan/remove functions in range, reapply replacements
2026-11-06 01:04:22 +01:00