Commit Graph
16351 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 c1f9f77a9e Fix Core.vcxproj.filters, broken by the POPS boot commit
0b1e1a9bb3 put the PopsMMIO entries inside the MeMMIO ones, leaving
unclosed tags, so Visual Studio couldn't load the filters. Also adds
the missing Util\KL4E.h.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 15:11:29 +01:00
Henrik Rydgård 17a61343d1 Merge pull request #22792 from hrydgard/more-ui-fixes
Settings: General tab, pause screen save state refresh, UI polish
2026-10-26 12:59:33 +01:00
Henrik Rydgård fe76beb04e Merge pull request #22789 from LunaMoo/xmb-home-exit
Xmb home exit
2026-10-26 12:59:17 +01:00
Henrik Rydgård e443e84d3c sceHprm: Write zeroes to the latch buffer in sceHprmPeekLatch too
We don't support the headphone remote buttons, so we just leave
the buffer all zeroes. Previously we didn't write, which could cause
games to register a ghost "track forward" or the other buttons.

Should fix the DJ Max 2 OST track-forwarding problem in #7468.
2026-10-26 12:46:31 +01:00
Henrik Rydgård 4cb7ddb50c More misc UI polish 2026-10-26 12:10:10 +01:00
Henrik RydgårdandClaude Opus 5.5 0ce79d4d7a Pause screen: Update the save state slots when a save finishes writing
Saves are written on a worker thread, so a state saved just before opening the pause
menu may not be on disk yet when the slots are listed, and the slot stayed empty.
Post SAVESTATE_SLOT_SAVED when a slot save is written, and recreate the pause
screen's views on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-26 11:49:44 +01:00
LunaMooandClaude Opus 5.5 90cea302b9 HOME button quit screen, with the firmware's art when available
As on a PSP: pressing HOME in a game shows "Do you want to quit the game?"
over it. The game keeps running but gets no input while it's up (sceCtrl
samples are blanked). Left/right picks Yes or No, the confirm button (our
Confirmation button setting, or what compat forces) chooses, the other face
button or HOME closes it, and Yes/No can be tapped. The volume keys change
the game volume a step at a time, shown on the volume bar.

Yes runs the game's registered exit callback first, like the PSP does,
showing "Please wait..." meanwhile. Then, if the XMB launched the game, the
XMB is booted again; otherwise it's back to the game browser, like Exit to
menu. If the callback hasn't ended the game in 10 s, it's quit anyway. Not
in the XMB itself, the PS1 emulator (its own HOME menu) or GE dumps.

It lives in UI/ImposeHome.cpp; EmuScreen only feeds it input and frames and
draws it over the PSP display.

With a firmware dump installed, it's drawn with the firmware's own art from
flash0:/kd/resource/impose.rsc, which is what impose.prx draws it from:
prerendered text for each system language (the question, Yes/No normal and
highlighted, Enter/Back, Please wait...), the button icons, the lines, the
volume bar, the battery and the clock glyphs. The format is documented in
ImposeHome.cpp. Without one, the same screen is drawn with our font and
simple shapes at the same positions.

Where impose.rsc doesn't say where things go, the layout follows
impose_02g.prx (6.61), decrypted and disassembled:
- The screen is filled with 0x9CE7 (#393939); the images are opaque.
- Lines at (80, 96) and (80, 175); impose.prx grows them from the middle
  when it opens, which isn't done here.
- Volume bar: the speaker at (28, 223), 30 steps from x 62, 12 apart. It
  shows our game volume in the PSP's 30 steps.
- Button guide: the cross icon always left, the circle right, "Enter" and
  "Back" next to whichever confirms and cancels.
- Status bar: the battery at (450, 5) (60% and up full, 30% two bars, 12%
  one, then empty), the clock ending next to it at y 5: "M/D H:MM", or
  "D/M" for the DD/MM/YYYY format, no leading zero on the month, day or
  hour, AM/PM for the 12-hour clock.

The clock is the emulated PSP's local time (__RtcGetCurrentLocalTime, new),
in the system's date and time formats. The battery is this device's, since
the emulated PSP's is always full; a device without one shows it full.
For that, Windows now reports -1 for SYSPROP_BATTERY_PERCENTAGE without a
battery (was 100), as SDL already did; SDL polls the battery even when the
status overlay doesn't show it, and the overlay leaves it out when there's
none.

Core side: __CtrlSetGameInputBlocked; __KernelCanExitToVSH and
__KernelRequestExitToVSH to quit to the XMB from outside a syscall; and
__KernelInvokeRegisteredExitCallback(toVSH), so a callback that returns
without exiting ends in the XMB rather than powering down.

New strings: "Do you want to quit the game?", "Please wait..." (Dialog).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB244iFpvm14JH2FbwHXeQ
2026-10-26 11:39:48 +01:00
Henrik Rydgård 3aec770b22 Set Auto rotation to default, move more settings around. 2026-10-26 11:36:59 +01:00
Henrik Rydgård 29b690eebf Merge pull request #22786 from LunaMoo/umd-inserted-reset
UMD: Put the disc back in the drive on every boot
2026-10-25 23:00:56 +01:00
Claude 66422a6123 UMD: Put the disc back in the drive on every boot
The XMB ejects the disc when it boots without one, and __UmdInit resets
the rest of the UMD state but not UMDInserted. So after visiting the XMB,
any game booted next saw an empty drive and waited forever for its disc
(Brandish: The Dark Revenant stops after registering its UMD callback),
until PPSSPP was restarted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 22:29:54 +01:00
Henrik RydgårdandClaude Opus 5.5 d0e3141f43 FFmpeg: Vendor what PPSSPP uses into ext/ffmpeg, drop the submodule
The ffmpeg submodule held prebuilt libraries for every platform next to the
sources (about 1.5 GB checked out), and nothing uses those any more. Only the
needed sources are copied in, from the submodule's commit, along with every
header and template they include. The path is new, so checking out commits on
either side of this works.

It's also cut down to what PPSSPP uses: the H.264, AAC, MP3, ATRAC3 and ATRAC3+
decoders, the H.264 parser, the demuxers as before, and the FFV1 encoder, AVI
muxer and file protocol for AVI dumping. config.h takes its CONFIG_ values from
FFmpeg 3.0.2's own configure run with just those enabled. The raw H.264 demuxer
stays even though nothing opens raw H.264: probing PSMF data uses it to identify
the video stream's codec, which otherwise comes out as none.

On top of that:
- The files nothing links (configure would build them, but they're never pulled
  out of the static library, going by the GCC link map) are left out: mostly
  libavutil's crypto and hashing and the alternative DCT/IDCT/FFT versions.
- libswresample isn't built, now that nothing uses it.
- The H.264 decoder only does 8-bit video, which is all the PSP has. The SPS
  parser rejects any other bit depth, and the DSP files no longer build their 9-
  to 16-bit variants, which the linker kept because the init functions pick one
  by bit depth at run time.

That's 160 files to compile instead of 277, and 391 of FFmpeg's vendored, about
5.8 MB. The README in ext/ffmpeg has what was done, the PPSSPP patches in the
copy, how to add a component, and the tradeoffs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:54 +01:00
Henrik RydgårdandClaude Opus 5.5 a8068d1742 AVI dump: Always use FFV1, remove the option to use MPEG-4
The lossy MPEG-4 encoder (with the MPEG-video encoding code it needs) is most
of what FFmpeg's encoders weigh, and lossy footage is better recorded from the
output with a screen recorder. Dumps are now always lossless FFV1, which also
makes for much larger files than the old default.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:54 +01:00
Henrik RydgårdandClaude Opus 5.5 e031502d27 Audio: Convert FFmpeg's output to s16 ourselves, drop libswresample
FFmpegAudioDecoder only used swresample to turn the decoders' output into
interleaved stereo s16, at the same sample rate. A small conversion does that
now, with swresample's rounding: floats go through lrintf and are clamped, and
mono goes to both channels at -3 dB, the coefficient swresample's mixing matrix
uses. Its output is sample-identical to swresample's for the MP3, AAC and
ATRAC3+ decoders, mono and stereo.

With "Use FFMPEG for all compressed audio", ATRAC3 got closed on its first
frame: its decoder sets no channel layout, and swresample refused to set up
without one. The conversion goes by the channel count, so that no longer fails.

The system-FFmpeg build no longer looks for libswresample.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:53 +01:00
Henrik RydgårdandClaude Opus 5.5 3015d1f085 Windows: Build FFmpeg from source, in a project of its own
ext/ffmpeg-build/ffmpeg.vcxproj (and UWP/ffmpeg_UWP for UWP) builds the same
sources as CMake, copied from sources.mk by update_vcxproj.py. It's optimized in
Debug too, and without /Zc:inline, since MSVC otherwise keeps references to
disabled components that FFmpeg expects the compiler to drop.

The projects that linked the prebuilt libraries reference it instead, and take
the headers from the source tree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:53 +01:00
Henrik RydgårdandClaude Opus 5.5 99727d8fbc FFmpeg: Build it from source with CMake by default, without a configure step
ext/ffmpeg-build now has a committed config.h in place of the configure checks.
Assembly and arch-specific code are off, as in the --disable-asm builds of the
old prebuilt libraries, so only the platform has to be told apart, with a few
preprocessor checks. The sources are listed once in sources.mk, for every build
system to use, and build one static library. ATRAC3 and ATRAC3+ are enabled to
match the prebuilt libraries, and HAVE_AV_CONFIG_H is now defined, as FFmpeg's
own build does.

CMake builds this unless USE_SYSTEM_FFMPEG or FFMPEG_DIR is set, and no longer
looks for the prebuilt libraries in ffmpeg/. MinGW now builds it too, instead
of falling back to the system's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:53 +01:00
Henrik Rydgård 3a41fca43d Merge pull request #22782 from hrydgard/play-crash-fixes-2
Fixes for crashes and ANRs from the 1.21-beta1 Play reports
2026-10-25 20:22:21 +01:00
Henrik RydgårdandClaude Opus 5.5 42f9af1744 MemBlockInfo: Clamp marked ranges to the address space
A size that makes addr + size wrap around 32 bits had Split() build a slab
that ends before it starts. Lookups then skipped it, and a later
Merge() ran FillHeads() over it with a huge count, writing far outside
heads_. Sizes come from games through HLE calls, and the large ones are
recorded even without detailed mem info. Seen in a Play crash report, on
the FlushMemInfo thread.

FillHeads() also took the address one past the end of heads_ for a slab
in the last slice, which Debug builds assert on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:37:23 +01:00
Henrik RydgårdandClaude Opus 5.5 6a9b84a714 VSH: Add the 3.6x key for the XMB index
3.60's index.dat carries tag 0x0B2B10F0, which sceResmgr_9DC14891 had no key
for, so the shell couldn't read its item index and stopped on "A serious
error has occurred". The key follows the tag in 3.60's mesg_led_02g.prx, laid
out like the other versions' (3.52's mesg_led.prx has the 3.5x key the same
way). 3.60 now boots to the XMB.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:00:55 +01:00
Henrik RydgårdandClaude Opus 5.5 fb371af6bb PSAR: Unpack the 1.00 updater, and the pre-decrypted 3.60 archive
The 1.00 updater's DATA.PSAR is a version 1 archive, the only one there is,
and its blocks carry tag 0x06000000, which the PRX decrypter had no key for.
1.50's mesg_led.prx keeps its keys raw in a table after their tags, the
0x0E000000 one we already use among them, and the 0x06000000 one there
decrypts all of 1.00 (with KIRK code 0x49, following the other tags). Its
first record has no version text either, so the version now comes from the
firmware's own version.txt when that's missing.

Fix a zlib usage bug.

Headless --unpack-updater also exited 0 when every file failed, so now it
fails if any did.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:00:48 +01:00
Henrik Rydgård 16103889f4 Merge pull request #22777 from hrydgard/remove-outdated-reporting
Cut down on outdated/useless REPORTing and logging
2026-10-25 18:07:02 +01:00
Henrik Rydgård 1c329a1e44 Merge pull request #22776 from hrydgard/umd-video-interlace
UMD Video: Give a field pair one SEI record per field
2026-10-25 17:36:27 +01:00
Henrik Rydgård 518795e35f Cut down on outdated/useless REPORTing and logging 2026-10-25 17:33:44 +01:00
Henrik RydgårdandClaude Opus 5.5 22348ddbff UMD Video: Give a field pair one SEI record per field
Family Guy and The Dark Knight are coded as separate field pictures,
bottom field first. ffmpeg hands each pair back as one frame, and we
described it with the first field's SEI record only.

The UMD Video player (video_main_plugin.prx) shows such a picture a field
per vsync, and takes the field order from comparing the output times of
the picture's two records. With the second left empty it always chose
top first, so every pair ran backwards in time and the picture jittered
between frames. Now the pair carries both fields' timing, the top field's
record first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 16:51:24 +01:00
Henrik RydgårdandClaude Opus 5.5 a64d590c94 XMB: Show it in Recents, with an icon and its firmware version as the title
The XMB (vshmain.prx) was kept out of Recents because it had no icon. Now
the game list shows it as "XMB 6.61" (the installed firmware's version, from
its version.txt), with an abstract XMB icon in white on blue, and the window
title says the same instead of "Unidentified PSP title".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 16:15:24 +01:00
Henrik RydgårdandClaude Opus 5.5 b91bd6af86 Settings: Build the settings screen from pages described as data
Each tab is now a SettingsPage: a list of items (headers, settings, buttons,
hints, links to other pages) that a function builds and views are created
from. Settings are added with page->Setting(&g_Config.x), which takes
the label, the kind of view, the range or choices, units, labels for special
values and the default from the config. The pages keep what depends on the
UI (which settings to show and when, enable rules, and behavior that needs the
screen), and reach the screen through the page's SettingsPageHost.

A page can link to other pages, which open on top of it in the same tab,
under a bar showing where you are, like Tools > Developer tools > Graphics,
where the earlier pages can be clicked to go back to them. The bar has back,
search and help buttons, and the back key also goes back a page (or out of a
search) before leaving the screen. Each page remembers its scroll position.
In portrait there's no tab strip: a page links to the tabs, and the bar shows
just the current page's title. Rotating keeps showing the same page. Moving
between pages only focuses an item when navigating with a keyboard or
controller.

The developer tools are now pages linked from the Tools tab, instead of a
screen of their own. The in-game developer menu and
--start-screen=developertools open the settings on that page. The toggles of
live state (the remote debugger and the MIPS tracer) show and change it
through functions, and pretending to be in-game for the notification tests is
a flag on the OSD. The memory map and memstick tests, only reachable from
commented-out code, are removed.

Search lists the matching settings from all tabs and the pages they link to,
grouped by where they are. Each result goes to the setting on its page,
scrolled to and highlighted (outlined, when picked with a mouse or touch), or
for a page, opens it. This replaces the search tab and the old filter that
hid the views that didn't match, and with them ShowSearchControls. Changing
tabs leaves the search.

The pages, the bar and search are in SettingsPagesDialogScreen, between the
tabbed dialog screen and the settings screen, so the other tabbed screens
keep plain tabs.

Other visible changes:
- Reset buttons that went to the wrong value now go to the real default: the
  touch control auto-hide delay (was 20 seconds instead of 8), the mouse
  wheel delay (1 ms, below its own range, instead of 80) and the UI size (0
  instead of -1 on desktop). The alternative speeds get a reset button.
- Units are shown on more sliders (frames, MHz, seconds, Hz), and popups no
  longer spell out what 0 means in the units, since the zero label does.
  Units of up to three characters (%, x, ms, MHz) aren't translated.
- The Android HW scaler hides the scales the display is too small for,
  instead of cutting the list short.
- Choices are translated in the setting's own section.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:26:58 +01:00
Henrik RydgårdandClaude Opus 5.5 3bb4664fd6 Config: Describe the settings for the UI, and find them by pointer
ConfigSetting gets a ConfigSettingInfo, set with constexpr chained setters:
a label, choices, a range with step and units, labels for 0 and negative
values (like Unlimited and Disabled), and a callback for what has to happen
when the UI changes the value. Using a setter on the wrong type of setting is
a compile error. The settings in the config tables now have these, so the
settings screen can take them from there instead of repeating them, including
what changing them needs (resize and GPU config messages, fullscreen, audio
and camera notifications and so on). The settings screen starts using them
in a later commit.

Config::FindSetting finds a setting from a pointer to its value. Defaults can
now also be looked up for blocks that reset as a whole (display layouts, touch
and gesture controls), by reading them from a freshly reset copy of the block.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:12:24 +01:00
Henrik RydgårdandClaude Opus 5.5 c88850a11a VSH: Alias sceMeBootStart's NIDs for firmware 2.80 to 6.39
The video player only gets the AAC flags it needs when the XMB has booted
the ME in mode 3, and an XMB older than 6.60 asks for that under another
NID, which stayed unresolved: on 6.00, SAMPLE.MP4 said "This video cannot
be played". The NIDs come from matching the function in each version's
me_wrapper.prx. hleKernelNids grows three columns for the generations
before 3.95.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 11:38:51 +01:00
Henrik RydgårdandClaude Opus 5.5 9daf26c01f sceMpegbase: sceMpegBaseCscVme converts two fields, like CscAvc
- Fixes #22764

pspautotests video/mpeg/cscvme shows the two luma buffers are fields, each
with its own chroma, and that the rows land as they do for
sceMpegBaseCscAvc: woven together without a second buffer, packed one per
buffer with one. The XMB's Motion JPEG player passes the second buffer
right after the first with the picture's halves as the fields, which is
why the halves model happened to work for it.

The hardware's colours match the scalar conversion exactly, so CscVme uses
that rather than swscale. Fields packed back to back now reach the GPU as
one write, so a texture over the whole picture isn't cut at the first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 11:29:16 +01:00
Henrik RydgårdandClaude Opus 5.5 ae7f312d34 sceMpegbase: Add the Vme copy and colour conversion, fix YCrCbCopy's size
The XMB's video player renders Motion JPEG AVIs with sceMpegBaseYCrCbCopyVme
and sceMpegBaseCscVme (JPCSP's names), which take planar YCbCr in two halves
rather than the ME's tiled layout. The copy sizes follow mpegbase_260.prx.

sceMpegBaseYCrCbCopy took the frame size from the destination descriptor,
which the same player leaves unset. mpegbase_260.prx reads it from the
source, in pixels.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 11:29:16 +01:00
Henrik RydgårdandClaude Opus 5.5 fcff5a981c VSH: Play MP4 video from the memory stick
- Fixes #22763

The XMB's video player boots the ME in mode 3 through sceMeCore_driver's
sceMeBootStart (5dff5c50), where games get mode 2. Mode 3 only changes the
argument to ME command 0x185 (me_wrapper.prx), and the player sets the AAC
context flag at 0x2d on anything newer than a PSP-1000 and plays those
files, so sceAudiocodecInit now accepts the 0x2c/0x2d flags in mode 3.
Games still get the failure pspautotests audio/audiocodec/context records.

Also implements the two vaudio calls the player uses: a ChReserveBuffering
that routes each buffer through an ME effect (E8E78DC8), and the setter for
its parameter (504E4745). The effect isn't known, so audio goes out as is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 11:29:16 +01:00
Henrik Rydgård b970cfece6 Skip the logo when launching the vsh, also add --skip-logo cmdline param 2026-10-25 10:42:12 +01:00
Henrik Rydgård daadd1a15c Change default background animation to wave. 2026-10-25 10:12:32 +01:00
Henrik RydgårdandClaude Opus 5.5 a5c90b61df Android: Use OpenSL ES for audio on TVs, add audio API and MMAP settings
An Android 11 TV played nothing over a Bluetooth headset since the switch
to Oboe, while its speakers worked. Testing on it showed AAudio with MMAP
is silent there and AAudio without MMAP works, as does OpenSL ES (also
when the headset is connected mid-game), whatever the stream usage. MMAP
should never be used for a Bluetooth route, so it's a firmware bug.

Audio settings get an Audio API choice and a Low-latency audio (MMAP)
checkbox, with hints, so other devices with the same kind of bug can be
worked around. The API defaults to OpenSL ES on TVs and before Android
8.1, where the choice isn't offered, and AAudio elsewhere. Changing them
reopens the streams right away. System Information shows the output
stream that got opened (API, MMAP or not, rate, buffers, device) and any
audio error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 23:58:42 +02:00
Henrik Rydgård c37498808c Home key improved look 2026-10-24 14:14:41 +02:00
Henrik RydgårdandClaude Opus 5.5 0da3129a69 Touch controls: Add a HOME button, and list HOME with the standard PSP controls
POPS (the PS1 emulator) uses HOME for its menu. The touch button is hidden by
default, and HOME still has no default key mapping. It's also appended to the
custom button and gesture key lists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:14:17 +02:00
Henrik Rydgård c844c0c049 Merge pull request #22754 from hrydgard/syphon-filter-handoff
Syphon Filter: Make cutscene timing frame rate independent (Dark Mirror, Logan's Shadow)
2026-10-24 13:46:28 +02:00
Henrik RydgårdandClaude Opus 5.5 46bf080bd9 Syphon Filter: Forget the hooks' per-object state when the game shuts down
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 11:34:09 +02:00
Henrik Rydgård 3dc01bd318 Turn off roundrect test on LogoScreen, reduce logging 2026-10-24 10:35:11 +02:00
Henrik RydgårdandClaude Opus 5.5 a5550cb853 Syphon Filter: Logan's Shadow: Hold the black screen before a cutscene for PSP time
Part of #22669.

An in-mission cutscene starts on a black screen held for a number of updates
set by the cutscene, while its setup moves the actors at a speed given in
time. In Episode 1 Part 4 the hold is 12 updates: at 60fps the fade in starts
0.25s in, and Gabe is seen sliding across the room for 0.3s on his way to his
start position. Count the hold at 20fps, which starts the fade 0.6s in, right
after he arrives, as in the PSP footage.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 10:21:13 +02:00
Henrik RydgårdandClaude Opus 5.5 5eae37aa5b Syphon Filter: Dark Mirror: Make the actor's turn after a scripted move frame rate independent
Part of #22669.

When a scripted move reaches its target, the AI only issues the next command
("face this way") two updates later, so the actor coasts for two frames before
turning. At 60fps that's 33ms: the turn starts further from the target, the slide
carries Gabe out of the 1.0 target radius, the steering seeks the target again and
he spins, ending up with his back to the camera in the Episode 1 Part 1 cutscene.
At 20fps (where the pose matches PSP footage) he coasts for 0.1s.

Hook the steering function to hold the turn rate at zero for the first 0.1s after
arriving, so every frame rate gets the 20fps outcome. Anchor heading measured in
that scene: 60fps -81 (was -150), 30fps -82 (was -116), 20fps -80, 15fps -75.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 02:00:09 +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 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 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