Commit Graph
48673 Commits
Author SHA1 Message Date
Henrik Rydgård e46bd61e2e When taking a screenshot, clear all notifications except achievements. 2026-10-11 17:40:01 +02:00
Henrik RydgårdandClaude Opus 5.5 9741bcacd9 iOS: Apply size changes between frames, fixing a glitch when rotating
UIKit lays out at the new size from the main thread, often while a frame
is being drawn, and the swapchain was only resized after presenting. So
the first frame of a rotation was laid out for one size and drawn at the
other. The view controller now hands the new size to the render thread,
and NativeFrame resizes before drawing instead of after presenting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:40:01 +02:00
Henrik Rydgård 7f8939173c Merge pull request #22502 from hrydgard/ios-remove-opengl
iOS: Remove last remains of OpenGL support, unify sideload and App Store builds
2026-10-11 16:49:39 +02:00
Henrik RydgårdandClaude Opus 5.5 59c7da7d67 iOS: Take the sideload app from the archive in b-ios.sh
Now that sideload builds share the App Store install settings
(SKIP_INSTALL NO), archiving moves the app into the xcarchive and
leaves nothing in Release-iphoneos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:25:22 +02:00
Henrik RydgårdandClaude Opus 5.5 81efd601f0 iOS: Merge the Metal view controller into the base one
With the GL view controller gone, PPSSPPViewControllerMetal was the only
subclass of PPSSPPBaseViewController. They're now one class,
PPSSPPViewController, in ios/ViewController.mm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:29 +02:00
Henrik RydgårdandClaude Opus 5.5 ebab68ac0a iOS: Remove availability checks for versions below the 15.0 minimum
Also drops what they guarded: the pre-14 UIImagePickerController and
document picker fallbacks, the psychic paper entitlement workaround
(fixed in 13.5), and the CS_EXECSEG_ALLOW_UNSIGNED JIT probe, which
returned false on everything from 14.4 on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:29 +02:00
Henrik RydgårdandClaude Opus 5.5 2a1269f572 iOS: Build the sideload and App Store bundles the same way
The sideload build ran ios/macbundle.sh after building, which looked for
git-version.cpp in the source root instead of the build dir, so sideload
builds shipped with an empty version in Info.plist. Both now use
iosbundle.sh, and share the Xcode target settings and scheme generation
except for bundle ID and signing.

RESOURCE was listed three times in one set_target_properties, and only
the last one counted, so the launch storyboard never got compiled into
either bundle. Settings.bundle only made it into sideload builds, and is
deleted: its one toggle, for external displays, hasn't been read since
the external display code was removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:29 +02:00
Henrik RydgårdandClaude Opus 5.5 9f1278b5d6 iOS: Link MoltenVK statically in sideload builds too, raise target to 15.0
Sideload builds used to dlopen a bundled libMoltenVK.dylib, which was
only worth having while they could still fall back to OpenGL. Now both
variants link the static MoltenVK from ios/MoltenVK and call Vulkan
directly. PPSSPP_VK_STATIC replaces the IOS_APP_STORE checks in the
Vulkan loader (the iOS libretro core still loads at runtime).

The static MoltenVK needs iOS 15.0, so that's now the deployment target
for all iOS builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik RydgårdandClaude Opus 5.5 5704b972e7 iOS: Remove OpenGL support from the app
The app now always uses the Metal view controller with Vulkan (MoltenVK),
for both the App Store and sideload builds. The GL view controller is gone,
and Common/GPU/OpenGL (except GLFeatures, used by the shader generators) and
GPU/GLES are no longer compiled for it. PPSSPP_API_ANY_GL is now off for
all of iOS except the libretro core, which still uses GLES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik Rydgård 7cab022eaf Merge pull request #22501 from hrydgard/golf2-portrait-readback
Everybody's Golf/Tennis: Fix player portraits
2026-10-11 15:47:24 +02:00
Henrik RydgårdandClaude Opus 5.5 24ea7c4193 Everybody's Golf: Read back the screen under the loading spinner
The spinner copies the displayed framebuffer under it into the transparent
texels of its frames, which came out as stale VRAM around the disc. Hook it
after it gets the framebuffer and read back the rows it covers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 15:10:34 +02:00
Henrik RydgårdandClaude Opus 5.5 2aaf13d8f8 Upload only the copied part of a row in a memcpy to a framebuffer
A memcpy of part of one row into a framebuffer was drawn at the buffer's full
width, pulling in whatever followed the source. Everybody's Golf copies its
loading screen spinner back to the screen 64 pixels at a time, so its sprite's
next rows were drawn alongside, as a row of discs across the bottom.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 15:10:34 +02:00
Henrik RydgårdandClaude Opus 5.5 94d943664e Everybody's Golf: Hook the player portrait copy too
The first game builds its portrait like Golf 2 and Tennis, but its copy
function keeps y and h in different registers and loads the framebuffer pointer
at a different offset. Move the readbacks into a helper and add a hook for this
layout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 15:06:24 +02:00
Henrik RydgårdandClaude Opus 5.5 6f56798c44 Everybody's Tennis: Hook the player portrait copy too
Tennis builds its portraits like Everybody's Golf 2: it memcpys a 64x64 rect of
the framebuffer and sets each texel's alpha bit from depth read straight from
VRAM, so the circles in the corners came out empty. Its copy function is
scheduled differently and hashes differently, so add it as a second entry for
the same hook, which is renamed to fit both.

Fixes #17826.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 15:00:40 +02:00
Henrik RydgårdandClaude Opus 5.5 fbcec91074 Debugger: Send whole pixels in gpu.buffer.* base64 responses
StreamBufferToBase64 sent width * height bytes, so any buffer with more than one
byte per pixel came back truncated: half a D16 depth buffer, a quarter of a
D32F or 8888 one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 14:50:19 +02:00
Henrik RydgårdandClaude Opus 5.5 50696c5b1f Remove the "game uses sequential copies" readback heuristic
After more than three partial readbacks of one buffer in a frame,
ReadFramebufferToMemory read the whole buffer instead for the rest of the
session, writing rows the game never asked for.

Everybody's Golf 2 reads back four portraits' color and depth per frame, which
turned it on. The whole 480x273 depth buffer then got written to VRAM, and its
last row is where the game keeps its texture heap. A texture header was
overwritten: the portrait showed the scene behind it, and the next time the
portraits were built the game crashed writing through a null pointer.

It was added for speed in Grand Knights History (c42d04829f), which copies many
slivers of a framebuffer, so that game may now do more readbacks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 14:50:19 +02:00
Henrik RydgårdandClaude Opus 5.5 9720041ecf Debugger: Add game.state.load
Queues loading a savestate file, for scripting repros that load a state partway
through a run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 14:50:19 +02:00
Henrik RydgårdandClaude Opus 5.5 648766dad4 Everybody's Golf 2: Read back color and depth for the player portraits
The game builds each 64x64 portrait by memcpying a rect of the framebuffer one
partial row at a time, then setting each texel's alpha bit from the depth
buffer, which it reads directly from VRAM. Neither got read back: the row
copies don't line up with a framebuffer, and depth is never downloaded.

Hook the copy function to read back both, whole rows at a time. That needed two
fixes to requested depth copies: they were dropped when the depth buffer lies
above all color buffers, and only an exact full-buffer size matched.

The portrait texture now matches the software renderer, though it still
doesn't show up on screen with Vulkan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 13:25:10 +02:00
Henrik Rydgård f70df399ab Merge pull request #22494 from hrydgard/jit-shadow-block-map
JIT shadow block map: Keep JIT block entries and replacement hooks out of PSP memory
2026-10-11 12:46:42 +02:00
Henrik Rydgård 9e4a309af3 Merge pull request #22500 from hrydgard/screenshot-fix
Screenshot code cleanup, fix issue where the notification would be in a second fast screenshot
2026-10-11 12:04:41 +02:00
Henrik Rydgård 7572ac05ca Screenshot code cleanup, fix issue where the notification would be in a second fast screenshot
Fixes #22497
2026-10-11 10:56:43 +02:00
Henrik RydgårdandClaude Opus 5.5 5fa4a475f9 Rename the replacement pseudo-op, drop the leftover emuhack defines
With block entries out of memory, the only fake opcode left is the one the CPU cores fetch in
place of a hooked instruction. Call it MIPS_CALL_REPLACEMENT_OP, and its handlers
Int_CallReplacement and Dis_CallReplacement, and remove the sub-op defines and the unused
MIPS_IS_REPLACEMENT.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 48973c83c6 Debugger: Show replacement hooks in the disassembly again
Memory no longer holds the hooks, so the disassembly fetches the op the CPU runs, which is the
CallRepl pseudo-op at a hooked address. That lookup can come from the Win32 debugger's UI thread,
so the CPU thread now changes the hook map under a lock (and reads it without, being the only
writer).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 6d122f416e Keep JIT block entries and replacement hooks out of PSP memory
The JITs used to find a block by overwriting its first instruction with an "emuhack" holding
the offset into the code space, and replacement functions installed their own emuhack the same
way. Anything that read code (savestates, the debugger, memory dumps, cheats, the function
hasher, games reading their own code) had to translate them back.

Blocks now live in a block shadow: one u32 per guest instruction, indexed by the PC masked with
BLOCK_SHADOW_MASK (1GB on 64-bit, folding the kernel and uncached mirrors; 256MB on 32-bit, iOS
and Switch, where address space is short), with the low bits cleared so a misaligned PC can't
read across two entries. Only the ranges under memory views are committed, so a bad PC still
faults in the dispatcher, and the shadow is reserved before the memory map so it can't land in
a gap of the 4GB window. Entries hold full 32-bit offsets, lifting the 16MB limit. The
dispatchers on every backend, and the IR interpreter's loop, look blocks up there.

A hooked instruction without a compiled block gets the JIT's hook value in its entry: the offset
of the dispatcher's compile path (1 for the interpreters, the IR arena keeping that offset
empty), so dispatching there compiles a block, and the frontends substitute the hook. Compiling
replaces the entry with the block's, and destroying the block puts the hook value back. The hook
value is rewritten when the JIT changes or regenerates its dispatcher.

Hooks live in a map keyed by the masked address, and are dropped when the game overwrites the
hooked instruction, or when an icache invalidation finds the function's hash (immediates masked,
as when matching it) changed. Those checks run when queued invalidations are processed, since
cheats and the debugger queue them before writing. Installing and removing hooks invalidates
deferred, as it happens in HLE calls that can run inside a block. The cache instruction goes
through MIPSState, so it drops changed hooks too.

With clean memory, the save/clear/restore around savestates, memory reads and dumps goes away,
as do the source-range invalidations in the memcpy replacements, sceDmac and the cheats.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 0a8ec4d8f1 Invalidate code that HLE and the debugger write over
Helper thread stubs, fake syscall stubs and the module start trampoline go into memory that may
have held code (freed kernel memory, a reused thread stack), and the debugger memory views can
edit code. Invalidate what they write, which so far only happened by accident when the write hit
the first instruction of a JIT block.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 59fed92dac IR native: Invalidate linked blocks again
78c75e1a0b dropped IRNativeJit::InvalidateCacheAt as a duplicate, and with it the only call to
the backend's InvalidateBlock. Since then a block linked to an invalidated one kept jumping into
its old code, past the dispatcher. Call it from IRJit::InvalidateCacheAt through a virtual, like
the other native block hooks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 e0f2060cd5 Tools: Find ARM64 builds in update-dispatcher.py, fix headless_bench.py options
update-dispatcher.py only knew x64 paths, so on Windows-on-ARM it regenerated from a stale x64
build. headless_bench.py handed the options after -- to argparse's REMAINDER, which newer
Pythons treat as more binaries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik Rydgård c51daff839 Merge pull request #22498 from hrydgard/sce-audio-reschedule-fix
sceAudio rescheduling fix
2026-10-11 09:22:18 +02:00
Henrik RydgårdandClaude Opus 5.5 a4678112c4 Headless: Don't call __StartLogAudio on mobile builds
The audio dumping code is compiled out with MOBILE_DEVICE, which broke the
Android headless link.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 01:25:33 +02:00
Henrik Rydgård 72b338001f Throw in a speculative workaround for #17474 . Not gonna hurt. 2026-10-11 00:57:14 +02:00
Henrik RydgårdandClaude Opus 5.5 76a8e9d45f sceAudio: Don't reschedule before the mixed block is pushed
Waking a thread from the mixer reschedules, which runs any due CoreTiming
events. When an audio tick runs late, the next tick would already be due, and
it would mix over mixBuffer before this one had been output. The next block
would then be played twice and this one dropped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 00:56:20 +02:00
Henrik Rydgård 921a0d7083 Merge pull request #22496 from hrydgard/vsh-icons
VSH/XMB: Fix icons, drop hacks
2026-10-10 23:09:20 +02:00
Henrik RydgårdandClaude Opus 5.5 2cf8b69bab VSH: Drop the 6.6x alarm-category patch
6.60 and 6.61 crashed in vsh_module's alarm-task scan on a count nothing
initialized, and a data patch zeroed it. With the shell's modules started
in order, both reach the XMB, show icons and launch homebrew without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:32:46 +02:00
Henrik RydgårdandClaude Opus 5.5 09974f9e82 VSH: Start the shell's modules one at a time, fixing game icons
The kernel starts modules one by one, each module_start returning before
the next starts. We started them all as threads side by side, and
vsh_module's start ran before paf's. Its 12MB request for game_plugin's
surface pools then went to the 8.3MB heap we pre-filled scePaf's pool
slot with, failed, and game_plugin threw away every decoded ICON0 and
PIC1 (white tiles on 3.80-5.05, grey placeholders from 5.50).

heaparea1, vshbridge, paf, common_gui and common_util now start in order,
and vsh_module waits for the last. The kd/ drivers still start side by
side, since syscon's start blocks on its hardware interrupt. paf fills
its own pool slot now, so the pre-fill is gone. Icons show on every
version from 1.50 to 6.61, and homebrew launches on the same ones as
before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:32:46 +02:00
Henrik Rydgård 214a259fb1 Merge pull request #22495 from hrydgard/vsh-homebrew
VSH: Launch unsigned homebrew from the XMB, on every firmware version
2026-10-10 22:06:13 +02:00
Henrik RydgårdandClaude Opus 5.5 06326d69bc Update pspautotests for unloadfill's stop and unload timing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:41:53 +02:00
Henrik RydgårdandClaude Opus 5.5 eb7fdf1c2d Add the modules/unloadfill and video/mpeg/deletetiming tests
Both in tests_next. unloadfill's fill matches (text becomes break 1,
data and bss 0xFFFFFFFF, as we already do), but sceKernelQueryModuleInfo
reports a text_size 6 words larger than the PSP's. deletetiming's
sceMpegDelete timing matches (it reschedules, in the 20-60 ms range); the
rest of the mpeg setup has older differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 9fa53c8337 HLE: Add sceKernelSetParamSfo and sceMePowerControlAvcPower
The VSH's sceVshBridge_Driver calls both when launching a game, and some
firmware versions give up on the launch unless they succeed (5.xx for the
first, 2.00 for the second). Neither has anything to do here: our HLE
LoadExec reads the PBP itself, and there's no AVC decoder to power.

Added under the NIDs whose names are known: sceKernelSetParamSfo's old,
5.xx and 6.60 NIDs (from DC-M33's NID resolver, Adrenaline's NID table and
uOFW), and sceMePowerControlAvcPower's old and 6.60 NIDs (jpcsp's
psplibdoc, uOFW). Appended at the end of SysMemForKernel, and
sceMePower_driver registered last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 ba4a95d8ff HLE: Send the VSH's LoadExec calls to HLE on every firmware version
The VSH launches games through the VSH variants of LoadExecForKernel,
whose NIDs most firmware versions changed. Where HLE doesn't know the NID,
the firmware's own loadexec.prx ran instead, and the launch got nowhere,
so homebrew launched from the XMB only on firmware 3.73 and older, and
6.60/6.61.

Each variant is the same small wrapper, loadExecVSH(apiType, file, opt,
0x10000), so when loadexec.prx exports one we don't know, look at the API
type it loads into a0, and for Disc (0x120) and MS2 (0x141), overwrite it
with a call to our HLE implementation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 2072e0d568 VSH: Find the homebrew patches by pattern, for every firmware version
Instead of 6.61's offsets, look for the checks by their instructions,
each of which has to match exactly once. Checked against vshmain.prx from
every firmware version, 1.50 to 6.61:

- The DISC_ID check is there from 2.00 on.
- The DATA.PSP header check from 5.00 on, in two forms: 5.xx compares
  the header bytes against ~PSP and ~SCE itself, and 6.30 on test a type
  a helper worked out. 6.00 to 6.20 have both. For the byte check, the
  "first byte isn't ~" branch is pointed at where a good header goes.
- 1.50 and 1.52 have neither, and run unsigned code anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 80e346d802 Headless: Honor --nand
ApplyToConfig() set nandRootDirectory from it, but headless then reset it to
the memstick's PSP/NAND unconditionally, so --nand did nothing there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 24a2f6110d VSH: Patch vsh_module so the XMB can launch unsigned homebrew
Picking homebrew in the XMB gave "The game cannot be started. The data
is corrupted." without ever calling LoadExec: vshmain.prx rejects a
PARAM.SFO without a valid DISC_ID, and a DATA.PSP that's a plain ELF
rather than ~PSP. NOP the three branches that do that, the same patches
ProCFW's vshctrl makes and JPCSP applies. They're for 6.61's build, and
are only applied if all three instructions are what we expect.

With it, Kosmodrones launches from the XMB, through the PSP boot logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 3dddacbfe0 Headless: Add --dump-audio=FILE.wav to record the mixed audio output
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 20:04:37 +02:00
Henrik Rydgård dcb1df6930 Merge pull request #22493 from hrydgard/ios-share-file
iOS: Add support for the internal "Share file" API just like Android
2026-10-10 19:55:48 +02:00
Henrik Rydgård 8e407deb90 Merge pull request #22492 from hrydgard/android-oboe-mic
Android: Hand microphone input over on the CPU thread, like the other hosts
2026-10-10 19:23:59 +02:00
Henrik RydgårdandClaude Opus 5.5 cfcf8cc17c iOS: Remove the iCade support
It has done nothing since 2017. iCade input came through iCadeReaderView,
a hidden UIKeyInput view that decoded the controller's keystrokes, and
f0ba66fcd6 ("add support for smart keyboard") commented out the code
that created it. Nothing has made one since, so the view controller's
iCadeEventDelegate methods and the ICadeTracker behind them were never
called.

iCade controllers present themselves to iOS as Bluetooth keyboards, so
their keys still arrive as hardware keyboard presses, which can be
mapped like any other keys. Modern controllers go through
GameController.framework anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:10:37 +02:00
Henrik RydgårdandClaude Opus 5.5 c8c3ba8bb6 iOS: Give the soft keyboard a view of its own
The view controller was the keyboard's text input, and UIKit hands first
responder back to it whenever something it presented goes away: a share
sheet, a photo picker, the alert after an app icon change. Each time,
up came the keyboard, and hideKeyboard calls were sprinkled around to
put it away again, one on a four second timer.

Text input now goes to a small view that only becomes first responder
when a text field gets focus. The view controller is first responder
otherwise, for hardware keyboard presses, but takes no text, so it can't
bring up the keyboard. The workaround hideKeyboard calls are gone, and
so is the endEditing on rotation, which dropped the keyboard if you
turned the phone while typing. Showing and hiding are synchronous now,
since every caller is on the main thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:09:41 +02:00
Henrik RydgårdandClaude Opus 5.5 d80c0b9472 iOS: Support System_ShareFile
Offers the file through the share sheet, which works out the type from
the extension. Also gives NativeInit a cache directory, Library/Caches,
which ShareFilesAsZip needs for its zips. That moves the SPIR-V and
download caches there from Documents/PSP/SYSTEM/CACHE, out of backups
and the Files app, which is where Apple wants caches anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:57:18 +02:00
Henrik RydgårdandClaude Opus 5.5 6ed178075c sceUsbMic: Fix QueueBuf::resize losing what was buffered
It allocated the new buffer, pointed buf_ at it, then popped from buf_,
so it copied the uninitialized new buffer onto itself. That happened
whenever a game asked for a larger block than before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 18:55:13 +02:00
Henrik RydgårdandClaude Opus 5.5 8ef157ab21 iOS: Use the shared SampleRing for microphone input
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 18:53:08 +02:00