Set the iOS release metadata and app-visible branding to 2.4.0.
The marketing version is 2.4.0 (major 2, minor 4, patch 0), the build
number is 240, the HTTP user agent reports ARMSX2-iOS/v2.4.0, and the
on-screen overlay brand reads ARMSX2 iOS 2.4.0. The new Swift sources
added by this release (pause menu, overlay design system, per-game
settings panel, animated library background, audio and app-icon
settings) are registered in the iOS CMake source list.
Remove dead code surfaced by the 2.4.0 work.
Cheats & Patches: drop an unused hasNamedGroups result, a
never-assigned PatchSource.legacy case, and collapse the duplicate
notify() into applyFeedback() (the bodies were identical). Virtual
pad: remove unused skin-typed asset overloads and helpers superseded
by the descriptor-based overloads, an always-true background flag, and
stale versioned history markers. Localization: drop six stale help
strings no longer referenced after the JIT help UI was generalized.
Widen the disclosure-group hit target in the cheats manager and help
view for accessibility.
On iOS, suppress two startup warnings that do not apply: "controller 1
has no input bindings" (expected with the virtual pad and self-binding
MFi controllers) and the renderer-not-Automatic warning (iOS always
renders with Metal).
Also wire the Audio settings entry into the settings root and show the
active layout name in the pad layout editor, matching the pause menu.
Port a batch of graphics, DEV9, and GameDB correctness and stability
fixes from the shared core.
Texture replacement packs no longer crash when a pack declares an
off-by-one mip count (the DDS loader now clamps the level count to the
actual texture size). Vulkan pipeline-cache writes are atomic
(temp file plus rename), so a mid-write crash cannot leave a
half-written cache that some drivers then render garbage from. The
OpenGL program selector now compares shader keys instead of a
padding-inclusive memcmp, so identical selectors no longer recompile
every frame. TextureCache::Move refreshes the destination target's
buffer width when the incoming data exceeds it, fixing texture
corruption in titles that do move-based framebuffer expansion.
Add the software anisotropic filter to the Vulkan shader (PCSX2 PR
#14465). The Max Anisotropy setting previously had no effect on Vulkan
because the shader had no handler for it; anisotropic filtering now
actually applies. The shader cache version is bumped (103 -> 104), so
the Vulkan disk cache is rebuilt on first launch after updating.
Add a DEV9 DNS fallback for iOS. The app sandbox has no
/etc/resolv.conf, so hostname lookup returned an empty list and every
DEV9 hostname failed; it now falls back to public resolvers and is
still overrideable via Network settings, so online and HDD games that
resolve hostnames can connect.
Cap the on-screen overlay scale at 2x so high-resolution screens do
not balloon the OSD, and show (ROV) / (Debug & ROV) in the GPU status
line when rasterizer-ordered rendering is active. Re-initialize the
focused window's navigation rect on resize so ImGui controller and
keyboard navigation keep working after a surface resize or orientation
change.
Sync GameIndex.yaml with the upstream refresh-experimental database
and add targeted title fixes: Jackie Chan Adventures EE/VU clamp
modes (stops Jackie falling through the floor after door transitions),
Ace Combat 5 / Zero partial texture preloading (avoids FMV hash-cache
stalls), Gran Turismo lens-flare / autoFlush / VU-clamp tuning, and
Burnout 3 memory-card cross-save filters. Add recommended AAT and AA1
GameDB hint fields. This is an upstream sync plus targeted title fixes,
not a large set of newly supported games.
Expose the global graphics and CPU round/clamp mode pickers in settings
to match the per-game options.
Compile more Emotion Engine instructions natively in the arm64
recompiler instead of falling back to slow single-step interpretation.
Native paths are added for the MADD family and pipeline-1 HI/LO moves,
BC0/CPCOND0 conditional branches (which also lets DMA busy-waits
qualify for the existing wait-loop idle-skip), EI/ERET as block
terminators, live-cycle handling for the COP0 Count and PERF registers,
native DI, trap instructions, and SYNC/CACHE. Correctness is inherited
from the interpreter and matches the x86 paths.
Port SkipMPEGHack to arm64. The game-fix previously only had a codegen
path in the x86 recompiler, so the toggle did nothing on the ARM64
backend.
Reduce the MTGS spin-wait window from 50us to 2us on Apple silicon.
The EE->MTGS producer arrival is bimodal on mobile, so the desktop
50us default burned a large share of host CPU in ShortSpin during frame
handoffs. The shorter window reclaims wasted CPU for better thermals
and battery, with no measurable effect on throughput. This matches the
existing Android arm64 tuning.
The MADD/HI-LO-1, BC0/CPCOND0, and COP0 live-cycle codegen is ported
from the upstream PCSX2 mac arm64 backend.
Add a Left/Right Channel Swap toggle that swaps the stereo channels
live (no restart), useful for flipped-speaker or reverse-landscape
setups. The channel swap is applied at the SPU2 output stage.
Raise the emulator volume slider cap from 100% to 150%. The default is
unchanged; values above 100% amplify SPU2 output and may distort.
Add an On-screen Notifications toggle that surfaces the existing core
OSD-messages setting, so transient in-game messages (shader
compilation, save states, settings-applied notices) can be hidden
without affecting critical errors and alerts.
Carry the prior output stream's paused state when recreating the SPU2
output stream, instead of reading live VM state which can read Paused
during a transient ScopedVMPause (save-state load or settings apply)
and leave the new stream muted after the menu closes.
Add an optional NEON reverb FIR backend for SPU2. When the
SPU2/NeonReverbSIMD INI setting is on, the reverb down/up-sample FIRs
use a SIMD fast path that frees CPU on CPU-limited devices. This is an
advanced, INI-only option on iOS: there is no Swift toggle for it, and
the bit-inexact NEON rounding differs slightly from the scalar
reference. The SIMD backend is ported from the ARMSX2 Android SPU2
work; iOS wires only the reverb FIR override and does not include the
Android prime-core pinning, SCHED_FIFO, or SVE2 runtime paths.
Allow deleting a memory card directly from settings. Deleting a card
also clears any slot still assigned to it, so a removed card no longer
leaves a stale slot that can mislabel the other card at boot.
Slot state self-heals when a card file is removed outside the app (for
example via the Files app): the slot list drops references to cards
that no longer exist on disk. Cards currently loaded into slot 1 or 2
are highlighted with a check in the list.
Delete is guarded by a confirmation dialog.
Play sound effects for achievement unlock, message, and leaderboard
submit events. The previous Common::PlaySoundAsync stub was a no-op on
iOS; it now plays the bundled WAVs through AVAudioPlayer on a dedicated
serial background queue, so the main thread and the SPU2/cubeb stream
are never touched. The delegate is retained strongly for the duration
of playback so the chime is not deallocated mid-playback. The core
gates calls behind the existing SoundEffects setting.
Sound assets and their licenses are documented in
sounds/achievements/README.txt: unlock.wav and lbsubmit.wav are CC0
(public domain) from freesound.org contributors rhodesmas and Eponn,
and message.wav is MIT from RetroAchievements/RAInterface.
Show a confirmation before disabling Hardcore Mode so a stray tap
cannot silently drop the user to Casual for the session, and rename
the shared-core "softcore" terminology to "Casual" to match upstream.
Add an alternate app icon picker under Appearance with Default plus ten
alternates: Synthwave, Christmas, Dark, Frosted, Gray, Light, Mystic
Purple, Purple, Pride Dark, and Pride Light. Each alternate ships in
the required device sizes and is declared in CFBundleIcons and
CFBundleIcons~ipad.
When the app is running inside a sideloading container such as
LiveContainer, the Home Screen icon belongs to the host and iOS's
setAlternateIconName does not apply. The picker detects that case and
offers an Export Icon fallback that writes a 1024 px icon to a share
sheet, so it can be set as a container shortcut icon.
Persist per-game metadata (serial, CRC, region, title) to a codable
JSON cache keyed by entry id, with the ISO modification date (or file
size for external folders) as the invalidation token. Cold launches and
routine reloads reuse cached metadata and only open new or changed
ISOs, instead of scanning every ISO on the main thread each time.
Entries for removed games are purged automatically.
Polish the library cards: reserve stable title heights so cards line up
whether a title is one or two lines (and let them grow with Dynamic
Type), and add region-flag emoji to grid, list, and cover-flow cards
via a shared RegionFlag helper. Widen disclosure-group hit targets in
the help view for accessibility.
Allow animated GIF, APNG, and animated WebP files to be used as the
wallpaper behind the game grid.
Frames are decoded lazily one at a time via CGImageSource, instead of
the full-frame memory cache UIImage.animatedImage builds. Playback is
driven by a CADisplayLink on a UIImageView and honors per-frame
durations. Safety caps (max 120 frames, max 1280 px on the long edge)
fall back to a static first frame for oversized or unsupported
animations, and decoding runs off the main actor so a large image
cannot stall the library UI.
Animation pauses on background entry and resumes in the foreground, and
Reduce Motion renders the static first frame only.
Add a generic per-game INI accessor in the bridge and expose a broad
set of new per-game overrides on top of it. Per-game overrides are
stored in the core's standard per-game settings file and cleared with a
-1 "Use Global" sentinel, so a cleared key falls back to the global
value.
New per-game options include FXAA, Shade Boost (master plus brightness,
contrast, saturation, gamma), TV/CRT shader, CAS sharpening and
sharpness, max anisotropy, screen offsets, show overscan, anti-blur,
disable interlace offset, integer scaling, skip duplicate frames,
dithering, hardware download mode, CPU/GPU CLUT, vsync queue size,
sync to host refresh, texture replacement (load/async/precache),
fast-forward volume, buffer size, output latency, the IOP/VU0/VU1
recompilers, EE cycle skip, widescreen and no-interlacing patches, the
per-game renderer (Metal vs software), accurate alpha test, texture
inside RT, and the 17 manual game fixes (VU Add-Sub through Full VU0
Sync).
The panel preloads per-game fixes in a single read on open instead of
one bridge call per key.
Fix per-game Fast Boot so the boot path reads the per-game override
instead of only the global value, and stop writing the resolved value
back into global settings. Standalone ELF boots now load their per-game
settings too: UpdateGameSettingsLayer falls back to the ELF CRC when
there is no disc CRC, so homebrew, mods, and HostFS titles get their
per-game settings instead of only the global profile.
The bridge accessors, debounced INI-save coalescing, live-apply helper,
and memory-card delete bridge op live in ARMSX2Bridge and SettingsStore
and are used by several later commits.
Show an OPH Flag Hack hint under the Internal Resolution picker when it
is above 1x and OPH is not in effect, since it can reduce slowdowns at
higher resolutions.
Rebuild the Per-Game Settings panel around category navigation instead
of a segmented tab strip. The seven categories are General, Graphics,
Audio, CPU & Speedhacks, Virtual Pad, Fixes & Compatibility, and
Cheats & Patches.
In-game Per-Game Settings are now presented as an overlay that matches
the pause deck: opaque graphite surfaces, a forced dark color scheme so
native Form rows match, and a dark Save/Cancel footer. The Cancel
button reads Close when there are no pending changes.
Pending changes are tracked by fingerprinting every editable field.
Swipe-to-dismiss is blocked and a confirmation is shown before changes
are discarded or before Reset All Overrides runs, so a stray swipe or
tap cannot silently lose edits.
Landscape uses an adaptive category rail plus a detail pane, so iPhone
landscape and iPad share one workbench layout.
Replace the sheet-style in-game menu with a native pause overlay that
opens instantly over the paused game. Gameplay is paused while the menu
is open, and Resume is pinned in the panel footer.
The menu is presented as an opaque graphite "command deck" with grouped
sections (Quick Actions, This Game, Game Tools, Reset & Exit). A new
shared overlay design system supplies the tokens and components so the
pause menu and Per-Game Settings share one consistent look.
The backdrop is a deterministic dim instead of a SwiftUI Material. A
Material has nothing game-derived to sample over a paused Metal frame
and collapses to a flat grey wash (worst on iPad and under Reduce
Transparency), so a plain opacity scrim is used instead. Heavier
scrims and opaque fallbacks are applied under Reduce Transparency and
Increase Contrast.
Returning to the app from the background while a game is running now
opens the pause menu, so resuming gameplay is always deliberate. The
active controller skin and pad-layout name are shown inline.
The host routes children (Per-Game Settings, save states, cheats,
layout editor) through an overlay state machine that always returns to
the pause card instead of dropping back into gameplay.