Instead of "volume:path" strings, the volume and the path in it are kept
apart, and IsInside() says which side is the folder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
It's possible to try to recv more than fits in memory using
sceNetAdhocPtpRecv, but it's ok as long as the received data fits,
as shown by the new pspautotests net/adhoc/ptp. We refused it with
INVALID_ARG on every call. Now the recv is limited to what fits in
memory, and the game's length is put back if the call fails without
waiting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
From pspautotests net/inet/apscan2: sceNetApctlScanSSID2 takes an SSID
to look for (or NULL), doesn't change the state, and ends with
SCAN_STOP about 2.2s later. Its results, read with GetBSSDescIDList2
and GetBSSDescEntry2, are separate from the User ones and cleared when
it starts, and a ScanUser called meanwhile runs after it. A scan for
one SSID looks among the access points recent scans made up.
A User scan's SCAN_REQUEST now comes at once and SCAN_COMPLETE 2.2s
later; the handler used to see both at the end. Results are filled in
when a scan ends, so both lists are empty until then, and a second
ScanUser before the first ends fails with 0x80410a80 (net/inet/offline).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fixes#20703
MGS: Portable Ops makes a recruit from the strongest access point a scan
finds (after mashing O to fill the meter), and won't take the same one
again for a long time. The made-up access points never changed, and the
configured one was always the strongest at 99%, so after a recruit or
two every scan came up empty. Now each scan makes up new BSSIDs, SSIDs
and strengths, and sorts them strongest first.
The User functions now also match pspautotests net/inet/apscan and
net/inet/offline: IDs from 1 (slot 0 is valid but empty), 0x80410a09
past the last one found and 0x80410a82 past the 20 slots, a 33-byte
SSID, and no SCAN_STOP event after SCAN_COMPLETE. The ...2 functions
Peace Walker uses keep their old numbering and SCAN_STOP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
- 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>
--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>
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>
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>
- 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>
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>
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>
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>
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>
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>
The last entry's contents can run past the size the header declares (1.x
updaters, and the 4.21 and 5.70 PSARs), so the wraparound check from
c5673294ab refused them. The file size is enough to stop the wrap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zlib 1.4's uncompress2 returns Z_DATA_ERROR, not Z_BUF_ERROR, when the input
runs out with the output full, so every file in the pre-decrypted 3.60 archive
failed to inflate after the zlib update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 6.61 updater records its second block as 408 bytes, but the block is padded to 416 and
only decrypts at that size, so the install failed with "Couldn't read the PSAR's second block".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>