Commit Graph
49654 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 b3343af49c Developer tools: Use ChoiceGroup on all tabs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik Rydgård 44035e5256 MainScreen: Add some rounded corners 2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 ade9fc151e MainScreen: Split CreateMainButtons on portrait at the top level
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 8012d9dca7 Settings: Use ChoiceGroup on the remaining tabs
Each ItemHeader becomes a ChoiceGroup with that title, and the items
under it are added to the group, as on the audio tab.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik Rydgård c44a12363f Prototype the new ChoiceGroup component 2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 c13bffc79c TEMPORARY: Bring back the round rect test on the logo screen, with corner masks
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 2ed5aa264c UI: Let round rects keep some corners square
A RoundCorners mask picks the corners to round. A square corner is drawn
from the corner image's edge profiles, split along the diagonal, so its
antialiasing matches the straight edges and it needs no extra draw call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 1fb8c3090b UI: Fade out the scroll bob when the scroll view is left alone
It stays for a second after the last activity, then fades out over half
a second. Any scrolling, touch, bob drag, or the mouse over the bob's
strip brings it back within a tenth of a second. It also shows briefly
when the view appears, so it's clear that it scrolls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik Rydgård fe41253fb8 Remove the roundrect test 2026-10-24 01:52:34 +02:00
Henrik RydgårdandClaude Opus 5.5 110fb7fd6a UI: Rounded rectangles from precomputed atlas corners
UIAtlas generates an antialiased white corner for each radius in
ROUND_RECT_RADII (8, 16, 24, 32 UI units) and each style (filled, and
thin/medium/thick outlines from ROUND_RECT_THICKNESS) at the atlas DPI.
DrawBuffer::DrawRoundRect mirrors the corner into all four corners and
stretches the corner's last texel row/column along the edges, so a
round rect is 9 quads (8 for outlines).

Other radii use the nearest corner, scaled. The LogoScreen temporarily
draws some for testing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 01:52:34 +02:00
Henrik Rydgård 42bb9b134b Merge pull request #22750 from hrydgard/chd-psp-detection
Fix CHD identification bug (it routed some PSP CHDs to POPS)
2026-10-23 22:58:51 +02:00
Henrik RydgårdandClaude Opus 5.5 e91357c9a1 Identify a CHD as a PS1 disc by its volume descriptor, not by being a CD's
chdman createcd makes CD CHDs of PSP discs too, which were taken for PS1 games. A CHD is now a PS1
disc only when its volume descriptor's system ID is PLAYSTATION, and otherwise goes on to the PSP
disc checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 21:50:53 +02:00
Henrik Rydgård 31e94fcf68 Merge pull request #22747 from hrydgard/virtual-pops-eboot
POPS: Boot PS1 disc images (cue/bin, ccd/img, mds/mdf, CHD, iso) as converted EBOOTs made on the fly
2026-10-23 16:51:55 +02:00
Henrik Rydgård c57f8c993d Merge pull request #22745 from hrydgard/android-file-grants
Android: Manage our persisted URI grants before the system drops them
2026-10-23 16:51:23 +02:00
Henrik RydgårdandClaude Opus 5.5 ad75804c31 Translations: PS1 support is still experimental
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 16:06:10 +02:00
Henrik RydgårdandClaude Opus 5.5 4f1298982b POPS: Warn that PS1 support is still experimental when a PS1 game starts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 16:05:00 +02:00
Henrik RydgårdandClaude Opus 5.5 f4f68d37cc File dialogs: Offer PS1 disc images (.cue, .ccd, .mds, .mdf, .bin, .img)
Windows also gets a filter of its own for them. iOS's picker takes any
file and Android's doesn't filter by extension.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:50:28 +02:00
Henrik RydgårdandClaude Opus 5.5 4775d235ab UI: PS1 games get a region and the PS1 icon, and list views show the ID of games without an icon
DetectGameRegionFromID() only knew PSP IDs, so a PS1 one (SLUS, SCES,
SLPS and so on) came out as homebrew. A PS1 disc image or EBOOT with no
icon of its own showed the app icon in the game browser, and the list
views left out the ID of any game drawn without a picture.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:48:01 +02:00
Henrik RydgårdandClaude Opus 5.5 0829c442d0 Android: Don't prune grants from a fallback config, keep shortcut order
NativeInit falls back to the default memory stick when the one in memstick_dir.txt can't be read (on
Android 9 and older, /sdcard), and an old ppsspp.ini there would make the user's grants look unused.

Also release before re-taking folders, since a take can make the system prune, keep going when one
release fails, save the shortcut list when only its order changed, and re-take the Remote ISO shared
folder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:29:26 +02:00
Henrik RydgårdandClaude Opus 5.5 9312bf06d1 POPS: Boot PS1 discs from CHD and Alcohol .mds/.mdf images
A CD CHD is read through its track metadata: each track's frames are
padded to a multiple of four, a pregap is stored only when its type
starts with V, and audio is big-endian. A .chd that's a CD is a PS1 disc,
PSP discs being DVDs. The FileLoader-backed core_file the PSP CHD block
device used is now OpenCHDThroughLoader(), for both.

An .mds gives each track's start, pregap and length, and where in the
.mdf it's stored, with 96 bytes of subchannel data after each sector.
Anything between tracks isn't stored. A bare .mdf goes through its .mds,
which the game browser lists, and archives install with both.

MODE2/2336 cue sheets, which only had their sector headers made before,
are now tested too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00
Henrik RydgårdandClaude Opus 5.5 34a0090a18 Install PS1 discs from zip and 7z archives, and show them as playable only with a firmware that runs them
A zip or 7z with a PS1 disc in it (a cue sheet and the files it names, a
.ccd with its .img and .sub, or a lone raw .bin or .img) is now installed
like a zipped ISO. A disc of several files goes into a folder of its own,
named after the cue sheet or .ccd. An ISO in the archive still takes
priority, and a lone .bin or .img only counts when nothing else does.

The game screen offers Play for a PS1 disc image only when the installed
firmware's POPS can run it: 3.95 to 6.61, with popsman.prx.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00
Henrik RydgårdandClaude Opus 5.5 23dffa9db3 POPS: Boot PS1 disc images as converted EBOOTs made on the fly
A cue sheet, a CloneCD .ccd, or a bare .bin, .img or .iso of a PS1 disc
now boots on the firmware's POPS, as the converted EBOOT.PBP popstation
would make from it. PS1EbootFileLoader builds the PBP header, PARAM.SFO
(the disc ID from SYSTEM.CNF) and the PSISOIMG header, TOC and block
index, and serves the disc image from the image's own files. Each block
of 16 sectors is a stored DEFLATE block: the manager won't read an image
of uncompressed blocks in its own format, but the converted PBP's inflate
takes stored ones, and with every block the same size, reads map straight
onto the files. Nothing is compressed or written anywhere.

Cue sheets can have any number of files, AUDIO tracks, INDEX 00 and
PREGAP, and a missing FILE falls back to the cue sheet's own name. A
2048-byte .iso gets Mode 2 Form 1 sectors made around its user data.

The EBOOT is mounted as the only file in host0:, through BlobFileSystem,
which now saves its open handles in savestates. The game browser lists
.cue, .ccd, .bin and .img, leaving out the .bin and .img files a cue
sheet or .ccd next to them describes, and a bare image opened directly
goes through the cue sheet that names it. GameInfoCache reads the title
and ID of these, and of real PS1 EBOOTs, from their PARAM.SFO.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00
Henrik Rydgård da92d72d81 Merge pull request #22746 from hrydgard/cpu-jump-low-bits
CPU: jr and jalr ignore the target's low two bits on the main CPU too
2026-10-23 14:59:31 +02:00
Henrik RydgårdandClaude Opus 5.5 88d6befc02 CPU: jr and jalr ignore the target's low two bits on the main CPU too
On a PSP, jr and jalr to an address with its low two bits set raise no
address error: they run from the aligned address, the PC keeps no low
bits, and jalr's link is unaffected (cpu/jump/unaligned, recorded on
firmware 6.61). This was done only for the Media Engine.

The firmware's PS1 emulator returns through such an address. The
interpreters stopped there with an invalid jump, and the JITs found the
block at the aligned address only if it was already compiled, and
otherwise compiled one at the unaligned address.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 14:23:39 +02:00
Henrik Rydgård c86d7a268d Merge pull request #22744 from hrydgard/vsh-disc-boot
VSH: Boot games from XMB like any other game launch (save states, compat etc)
2026-10-23 13:56:11 +02:00
Henrik Rydgård 01508a9f38 Merge pull request #22743 from hrydgard/text-drop-shadow
TextDrawer: Soft drop shadow flag, shared with the UI atlas
2026-10-23 13:54:42 +02:00
Henrik RydgårdandClaude Opus 5.5 8e146f4c24 Android: Keep folder and shortcut grants, re-take the folders we use
Pruning now only runs when the config was really read: when the memory
stick isn't reachable at startup (an unmounted SD card, or right after
boot), NativeInit falls back to defaults and the recent list is empty, so
every recent game's grant looked unused and was released.

Grants for the memory stick, the Games tab folder and pinned folders are
re-taken at every startup. That moves their persisted time up, so they're
the last the system drops if we ever reach its limit. The new time is
written out with the next grant change, which is also the only time the
system prunes.

Home screen shortcuts can't be enumerated, so the 64 most recently
created or launched are remembered in a new [Shortcuts] section of
ppsspp.ini, and their grants are kept. ShortcutActivity runs without the
config loaded, so it queues them in SharedPreferences until the next
startup saves them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:54:31 +02:00
Henrik RydgårdandClaude Opus 5.5 020e39baa9 RecentFiles: Make the loaded list visible right away
Load() only queued the list for the worker thread, so reading it right
after loading the config could still see an empty list. Android's grant
pruning does exactly that, and would then treat recent games as
unreferenced, release their grants, and the queued clean would drop them
from the list as missing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:54:31 +02:00
Henrik RydgårdandClaude Fable 5.1 d7bac55348 Android: Release old unused file grants before the system starts dropping ours
An app can hold 512 persisted URI grants (128 before Android 11). Over
that, the system drops the oldest, and ours is usually the memory stick
folder. We take a grant for every file picked one at a time - games loaded
through the file picker, shortcut targets, sound effects - and never
released any.

At startup, log what we hold, and past half the limit release the oldest
single-file grants that neither the recent list nor a setting points at.
Folder grants are always kept. Home screen shortcuts need their file's
grant and can't be enumerated, which is why this only acts when it has to
and goes oldest first: the files it takes are ones the system would have
taken too, after the folder.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-23 13:54:31 +02:00
Henrik RydgårdandClaude Opus 5.5 b547e01c9d VSH: Boot games the XMB starts like any other game launch
A disc game or memory stick EBOOT started from the XMB used to be swapped in under the XMB's session,
so it ran without its PARAM.SFO, compat flags, game config, window title, savestates and so on.
Much of that is decided once, at boot (the memory map, the GPU backend, the shader cache), so
sceKernelLoadExec now asks the frontend for a reboot into the disc image or EBOOT (PSP_RequestBoot)
and stops. The app and headless boot it like a game from the game list.

The game keeps the XMB's launch args (CoreParameter::launchedFromVSH), so exiting boots the XMB
again, with the disc that's in the drive and those args, and the XMB comes back where it was without
auto-starting the disc. Homebrew keeps the disc that was in the drive. A disc whose executables are
all zeroes (the EUR Video Demo Disc's "game") still loads in place and fails back to the XMB.

In headless, the run carries on through the reboot, starting stopped under the debugger like the
first boot, since the reboot loses a pending cpu.runUntilTime.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:36:28 +02:00
Henrik Rydgård 4c51104940 Merge pull request #22740 from hrydgard/vsh-umd-video
Play UMD Video discs in the XMB from the game list, and fix video disc detection
2026-10-23 13:15:32 +02:00
Henrik RydgårdandClaude Opus 5.5 e86113afa3 Fix the Windows build of draw_text.cpp
CommonWindows.h defines WIN32_LEAN_AND_MEAN, which leaves out what the
Direct2D/DirectWrite headers in draw_text_uwp.h need. Include
<windows.h> with just NOMINMAX instead, and keep std::max out of
DropShadow.h so it doesn't depend on which order windows.h comes in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:04:18 +02:00
Henrik RydgårdandClaude Opus 5.5 dea182f1d8 Use one UI drop shadow for text and atlas images, fix Windows build
Text and the SVG button images are mixed on screen, so they now share
UIDropShadowParams (3 UI units of blur, 0.66 intensity, no offset, the
atlas's existing look) instead of the text having its own.

draw_text.cpp included <windows.h> directly, whose min/max macros broke
the std::max calls added here. Include CommonWindows.h instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:32:25 +02:00
Henrik RydgårdandClaude Opus 5.5 a1ade54aac UMD Video: Boot the XMB for a video disc however it's started
The game list already did, but booting a video disc any other way (the command line, a file
association, dropping it on a running game, headless) tried it as a game and failed. The loader now
swaps in vshmain.prx with the disc mounted and auto-start on whenever it identifies a UMD Video
disc and the installed firmware can play it. GetUmdVideoVSHPath() moves from the UI to
Core/Util/PSARUnpack.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:08:10 +02:00
Henrik Rydgård aeb6f80a01 Merge pull request #22741 from hrydgard/ui-atlas-speedup
UI atlas: Rasterize each SVG image separately, in parallel
2026-10-23 12:06:21 +02:00
Henrik RydgårdandClaude Opus 5.5 fcfac13643 Share the drop shadow code between the UI atlas and the text drawer
CompositeDropShadow in Common/Render/DropShadow.cpp blurs a premultiplied
RGBA image's alpha (with an optional offset) and composites the image
over it. The atlas's AddDropShadow and TextDrawer::AddDropShadow are now
thin wrappers that pick the parameters.

The blur scatters only the non-transparent texels, which makes atlas
shadows 3-7x faster than the old gather-with-clamping version. Atlas
output is unchanged apart from rounding (alpha differs by at most 1).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:56:24 +02:00
Henrik RydgårdandClaude Opus 5.5 352c0da492 TextDrawer: Add FLAG_DROP_SHADOW for a soft drop shadow under text
The backend rasterizes coverage (or color, for emoji) as usual, then a
shared CPU pass pads the bitmap, blurs an offset copy of the coverage
and composites the text over it as RGBA8888. Blur and offset scale with
the font size and with DPI. The flag is part of the string cache key,
and the text is still aligned by its own size, with the quad grown to
fit the shadow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:51:18 +02:00
Henrik RydgårdandClaude Opus 5.5 5bd94f9fa3 Recents: Add UMD Video discs played in the XMB
Picking one boots vshmain.prx with the disc mounted, and only the file booted went into Recents
(minus vshmain.prx itself), so the disc never did.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:42:58 +02:00
Henrik RydgårdandClaude Opus 5.5 450b6a7ffc Translations: Firmware needed for UMD Video, replacing "doesn't support UMD Video"
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:37:37 +02:00
Henrik RydgårdandClaude Opus 5.5 69ef4ce9fb UMD Video: Say which firmware is needed, instead of that it isn't supported
Without firmware that can play UMD Video (2.71 or later), the info screen and the boot error said
"PPSSPP doesn't support UMD Video", which is no longer true. They now say the firmware version
needed, from UMD_VIDEO_MIN_FIRMWARE, the same constant the check uses.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:36:50 +02:00
Henrik RydgårdandClaude Opus 5.5 6597483755 UI: No game-or-video prompt for a disc whose game is a placeholder
The EUR Video Demo Disc's UMD_DATA.BIN says GVA, but its EBOOT.BIN and BOOT.BIN are all zeroes, so
"Play game" could only fail. It now goes straight to the XMB, like "Watch video". What the disc
reports to the emulated PSP doesn't change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:24:55 +02:00
Henrik RydgårdandClaude Opus 5.5 9be0dffc41 Translations: The game-or-video choice for mixed UMDs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:58:15 +02:00
Henrik RydgårdandClaude Opus 5.5 6a1b802e09 UI: Shorter label for the video choice on a mixed disc
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:56:43 +02:00
Henrik RydgårdandClaude Opus 5.5 dc41268bfc Firmware: Ignore empty updaters on discs
UMD Video discs ship an updater PARAM.SFO next to a 0-byte DATA.BIN, and PSN releases zero-fill it.
MountedDiscHasUpdater() only checked that DATA.BIN existed, so booting the EUR Video Demo Disc's
game with no firmware installed started an auto-install of "2.00" that failed with an OSD error.
It now wants a PSAR archive, like ReadBundledUpdateInfo() already did. The game info screen no
longer looks for an updater on UMD Video discs at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:55:27 +02:00
Henrik RydgårdandClaude Opus 5.5 c1ec2044db Game list: Don't draw the file type icon on top of a game's picture
UMD Video discs, PS1 EBOOTs and frame dumps have both a picture and a type icon, and the icon and
title were drawn over the picture, showing up as a square in the middle of it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:46:59 +02:00
Henrik RydgårdandClaude Opus 5.5 d331bd6c8c UI: Play UMD Video discs in the XMB, straight from the game list
With firmware 2.71 or later installed, picking a UMD Video disc boots the XMB with the disc in the
drive and UMD auto-start forced on for that boot, so the disc's menu comes up. Without one, the
info screen saying UMD Video isn't supported shows as before. The info screen's Play does the same.

A disc with both a game and video asks which to start. For the video, the XMB comes up with
auto-start forced off, since auto-start runs the game.

Older firmware can't play these: 1.50, 2.00 and 2.60 crash in video_plugin or paf with a video disc
in the drive, and 2.50 refuses it. 2.71 through 6.61 reach Bourne Supremacy's menu.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:46:59 +02:00
Henrik RydgårdandClaude Opus 5.5 bf1f88d6e6 UMD: Tell video discs from game discs by UMD_DATA.BIN, and fix eject+insert while paused
- Fixes #22737

Some video discs (Spider-Man) have a PSP_GAME holding only a firmware update. We reported them as
game discs too, and the XMB said "The disc could not be read". The type now comes from the fourth
field of UMD_DATA.BIN (G, V, A; the EUR Video Demo Disc says GVA), with the directories as a
fallback, and PSP_GAME without SYSDIR/EBOOT.BIN doesn't make a mixed disc a game. Identify_File uses
the same check, so these discs list as UMD Video.

Ejecting and inserting a disc from the pause menu without resuming in between left the old disc in
the XMB: callback notifications don't queue, so the insert's argument replaced the eject's before
the drive callback ran. The insert now waits until the callback has seen the drive empty.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:46:59 +02:00
Henrik RydgårdandClaude Opus 5.5 05273d9da3 UI atlas: Rasterize each SVG image separately, in parallel
nanosvg walks every scanline of the target for each visible shape, so
rasterizing the whole SVG and cropping the images out of it cost
shapes * canvas area. Each image is now rasterized into a buffer the size
of its bounding box, from a list of copies of its own shapes, so the
images can be rasterized on the thread pool.

Atlas generation on a Redmi 9A (32-bit) goes from about 165 ms to about
95 ms, and on a desktop from about 10 ms to 6 ms. Parsing the SVG is now
the largest part. The atlas layout is unchanged; a few edge pixels differ
by at most 2/255 from the different subpixel offsets.

Also copy whole rows in Image::copyfrom, and fix a leak of the SVG file
data when parsing fails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:16:31 +02:00
Henrik Rydgård 902e5ccd8c Merge pull request #22738 from hrydgard/android-tv-output
Android TV mode, and an "External display" setting (iOS and Android)
2026-10-23 00:00:12 +02:00
Henrik RydgårdandClaude Opus 5.5 f795f7e07f UI: A press and release in one event is a press only for the top screen
Key ups go to every screen, so keys don't get stuck, and that included
events that are a press and a release at once, like iOS's back swipe. So
with settings open from the pause menu, a back swipe made the pause
screen try to close too. It isn't on top, so finishDialog refused, but
the screen had already stopped taking input, and once settings closed it
was left on top, ignoring everything.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:16:47 +02:00