4717 Commits
Author SHA1 Message Date
scribam 9f4ad6d6db android: update oboe and project 2026-11-08 18:11:36 +01:00
Flyinghead 8aec4a9220 elan: raise YUV_DMA interrupt when DMA is done
Fixes initdo texture corruption on start screen.
Issue #2101
[ci skip]
2026-11-08 18:10:56 +01:00
Flyinghead b6f03ecca2 msvc build fix
[ci skip]
2026-11-08 18:07:32 +01:00
Flyinghead 4600d08dbb wccf: add naomi:WCCFNode config. Handle socket to port 59200 locally
Replace naomi:WCCFSlave by naomi:WCCFNode to set node number for
projectors and satellites.
Handle socket to port 59200 locally to allow connecting from the same IP
and bypass wccfd completely.
2026-11-08 17:14:23 +01:00
Flyinghead 03e5704908 libretro: disable capcom direct on android 2026-11-08 16:35:05 +01:00
davey a14c02e2bd libretro: enable the Vulkan device extensions the frontend requires
The context negotiation interface passes the device extensions the
frontend needs, but they were never enabled. Frontends such as RetroArch
pass the extensions an OpenXR runtime needs to share the device, and
without them SteamVR crashes in xrCreateSession.
2026-11-04 18:34:34 +01:00
Flyinghead 802874e79d arm64 linux: get page size at runtime. Upload symbols to sentry 2026-11-02 14:59:56 +01:00
raph 1fac3d3fef libretro build fix 2026-11-01 18:38:49 +01:00
Alex Stapleton 6a6038f910 picotcp: make room for more than ~1,000 timers (#2595)
The timer heap is capped at MAX_BLOCK_COUNT blocks of 1600 bytes, about
1,050 timers. Each TCP socket keeps several (keepalive, stats,
retransmission, linger) and closed sockets linger for 30 seconds, so a few
dozen connections a second fill it. When pico_timer_add() fails,
socket_del() frees the socket at once instead of deferring it, while
pico_tcp_input() is still using it: AddressSanitizer reports a
heap-use-after-free in pico_tcp_input(), and release builds crash.

Raise the cap to 1024 blocks (~67,000 timers). There is a single heap, and
blocks are only allocated as it grows, so this costs 8 KB of pointers.
[ci skip]
2026-11-01 18:04:12 +01:00
Alex Stapleton f86e4c6ea4 picotcp: don't connect a socket whose quad is already in use (#2594)
pico_socket_connect() ignored the result of pico_socket_alter_state(),
which fails when a socket with the same local/remote address and port is
already in the socket tree. The new socket then carried on untracked, and
deleting it later found and unlinked the other socket instead, which was
then freed while still in use: a double free or heap corruption.

picoppp hits this when it forwards an inbound connection whose client
reused a source port while picotcp still holds the previous connection
(Linux reuses loopback source ports at once). Fail the connect with
EADDRINUSE, and have picoppp retry with a port picotcp picks.

Found by forwarding a few hundred connections a second to the Broadband
Adapter; the crash went away with this.
[ci skip]
2026-11-01 18:03:52 +01:00
Alex Stapleton 7b1df94c8e picoppp: don't terminate when shutting down a disconnected host socket (#2593)
TcpSocket called the throwing overloads of asio's socket.shutdown() and
socket.close() when the guest closed its side of a connection. If the
host peer had already disconnected (or reset the connection),
shutdown() throws std::system_error ("Transport endpoint is not
connected"), nothing catches it, and Flycast terminates.

Use the error_code overloads: a peer that is already gone just means the
socket is already closed.

Found forwarding a few hundred connections a second to a KallistiOS
program, where clients and guest close connections in either order:
Flycast terminated within about a thousand connections.
[ci skip]
2026-11-01 18:03:33 +01:00
Alex Stapleton 973e569a7d picotcp: fix double free racing pico_enqueue() with the pico thread (#2592)
Since c52e6cc, the emulation thread calls pico_stack_recv() directly,
which enqueues on the device's protected q_in while the pico thread
dequeues it. pico_enqueue() unlocked the queue and then read q->size for
its return value. If the pico thread dequeued and processed the frame in
between, the size could read 0, and pico_stack_recv() took that as a
failed enqueue and freed a frame the pico thread had already freed.

AddressSanitizer: attempting double-free in pico_stack_recv(), called
from qemu_send_packet() on the emulation thread; freed by
pico_ethernet_receive() on the pico thread. It took a few dozen
forwarded TCP connections to crash a release build.

Read the size under the lock. Also create the queue mutex in
pico_queue_protect(): creating it lazily on first lock can give the two
threads different mutexes.
[ci skip]
2026-11-01 18:03:11 +01:00
Flyinghead e778e529be save to temp file then rename over previous one when possible
[ci skip]
2026-11-01 18:00:35 +01:00
Flyinghead 98ceceed1e aica: decrease the aica DMA xfer rate from 25 MB/s to 16 MiB/s
Fixes NCAA College Football 2K2, NBA 2K and NFL 2K right music channel
getting louder.
Issue #2525
2026-10-28 14:37:54 +01:00
Flyinghead 697d15a133 naomi: drop support for .lst files 2026-10-26 13:08:42 +01:00
Flyinghead 34e1211871 mem: return non-null value when reading ext. device (area 5)
Fixes NHL 2K2 freeze when pressing DPad right on Credits menu.
Issue #2535
2026-10-26 12:38:16 +01:00
Flyinghead cd89ae96ab aica: decrease the aica DMA xfer rate from 50 to 25 MiB/s
Fixes NCAA College Football 2K2 right music channel getting louder.
Issue #2525
Small ADPCM decoding optimization
2026-10-26 12:29:50 +01:00
Joe Mattiello 6d49796a15 flashrom: write the flash and nvmem saves atomically (#2579)
WritableChip::Save() wrote the image in place. A crash or a kill while
it is being written leaves a truncated file: the Dreamcast then boots
into the date and time setup with its settings gone, and an arcade
board loses its nvmem (service settings, high scores, unlocked content).

Write to a temp file next to the real one, flush it, then rename it
over the real path, so the file on disk is always either the previous
image or the complete new one. On Windows, rename() refuses to replace
an existing file, so flycast::rename() (which had no callers) now uses
MoveFileEx with MOVEFILE_REPLACE_EXISTING, matching POSIX.

Testing: compiles on macOS arm64; the Windows path is not built here.

Co-authored-by: Claude Fable 5.1 <[email protected]>
[ci skip]
2026-10-26 10:26:57 +01:00
Joe Mattiello 907d15155e rend: NEON for the 0888 and 8888 framebuffer write paths on arm64 (#2557)
0888 KRGB and 8888 ARGB, still went through the scalar per-pixel writer.
One packed pixel is exactly one VRAM word, so they fit the same 8-word
interleaved store as the 16-bit path, 8 pixels per iteration. The store
is factored out of storeFramebuffer16Pixels() so both use it.

888 (24-bit packed) stays scalar: its pixels straddle VRAM words, so the
store pattern only repeats every 4 pixels / 3 words.

The address walk is the same as writeFramebufferLW(), so the output is
identical to the scalar path, including when the padding isn't a
multiple of 4. A harness writing 7,056 frames through both paths (both
channel orders, modes 4 to 6, 6 frame sizes, 7 destination addresses
including bank and VRAM edges, 7 line strides, 4 clip windows) gives
byte-identical VRAM. The same algorithm measured about 2x faster on a
640x480 8888 frame (0.213 ms to 0.105 ms) on Apple Silicon.

tests/src/rend/FramebufferWriteTest.cpp checks both 32-bit modes against
an independent reference (one pvr_write32p per pixel, the scalar walk)
over 3,600 cases: widths around the 8-pixel vector width, padding
including values that aren't a multiple of 4, destinations at the 32-bit
bank boundary and the end of VRAM, and clip windows, for both channel
orders. All of VRAM is compared, so a store past the frame fails too. On
arm64 it exercises the NEON path; elsewhere the scalar one. It fails when
the padding computation is deliberately broken.

Other architectures are unchanged.

Co-authored-by: Claude Opus 5.5 <[email protected]>
[ci skip]
2026-10-26 10:26:27 +01:00
Givo e2f1484571 changed achievement draw shortcut to return false rather than true 2026-10-24 17:24:35 +02:00
Will fe58ee8c20 add the ability to hide achievement popups 2026-10-24 17:24:35 +02:00
Flyinghead 3a498b1cb5 initialize all scheduler ids to -1
[ci skip]
2026-10-24 17:18:03 +02:00
Flyinghead c52e6ccf56 bba: move frame queueing to net services. Call service to rx frame
New Service::sendEthFrame function to send frame to bba
Let net services implement queuing and synchronization
capcom: don't interfere when not in use
2026-10-22 16:32:34 +02:00
Flyinghead de7597f0bc picotcp: fix race condition in enqueue/dequeue 2026-10-22 15:01:18 +02:00
Flyinghead 5f35ec7d72 Merge remote-tracking branch 'origin/master' into dev 2026-10-22 14:13:58 +02:00
Flyinghead fa9d39a355 bba: skip deserialization if device doesn't exist
regression from 5543a95c4f
2026-10-22 12:36:23 +02:00
Flyinghead db065c5700 audio: put the RingBuffer cursors on separate cache lines 2026-10-21 18:24:07 +02:00
Flyinghead 620699865b Merge remote-tracking branch 'origin/master' into dev 2026-10-21 17:47:06 +02:00
Flyinghead 5543a95c4f bba: start polling only when needed. Increase poll interval to 1 ms 2026-10-21 16:54:14 +02:00
Joseph MattielloandClaude Fable 5.1 31697241b7 sdl: initialize the axis direction in SDLControllerMappingParser
SDL_GameControllerButtonBind2 is a struct over a union whose first member
is `int button`. `bind{}` zero-initializes that member only, so
value.axis.direction holds whatever was on the stack. getBindForAxis()
assigns it only when the mapping has a '+' or '-' prefix or a '~'
suffix; for a plain "lefttrigger:a4" it is returned as read, and the
full-axis branch at the end tests it too. At -O2 on macOS/arm64 a
lefttrigger lookup came back with direction 1 instead of 0 and
"-lefty:-a1" with 1 instead of -1; SDLControllerMappingTest.split_axis
and .reverse_axis fail there. The value just happened to be 0 on Linux.

Set axis and direction to 0 before parsing the axis.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-21 12:09:24 +02:00
Joe Mattiello e9f4020c56 maplelink: keep the link registry alive through static destruction (#2577)
MapleDevices[][] in maple_if.cpp holds shared_ptr<maple_device>s with
static storage, so the VMUs are destroyed when the process exits.
~maple_sega_vmu() clears the LCD on any attached maple link, which goes
through MapleLink::GetMapleLink() and locks MapleLink::Mutex, another
object with static storage, in another translation unit, that may
already be gone. With libc++ locking a destroyed mutex throws
("mutex lock failed: Invalid argument") and the process aborts; the
unit test binary exits with SIGABRT on macOS after every test passed.
On glibc the same lock happens to succeed, which is why Linux never
showed it. The Links table is read in the same state.

Give both to function-local statics that are allocated once and never
destroyed, so they outlive everything that can still reach them.

Co-authored-by: Claude Fable 5.1 <[email protected]>
[ci skip]
2026-10-21 12:08:48 +02:00
Joe MattielloandClaude Fable 5.1 5eb52090ad arm7: keep the V flag across logical ops in the arm64 recompiler (#2576)
On ARMv7, AND, EOR, ORR, BIC, MOV, MVN, TST and TEQ with the S bit set
leave V unchanged. The AArch64 instructions the recompiler uses for them
(ANDS, BICS, TST) clear V, and storeFlags() wrote the host NZCV back as
is, so every flag-setting logical op cleared the guest's V. A signed
conditional branch (BVS, BGE, BLT, BGT, BLE) after such an op then went
the wrong way. The x64 recompiler already preserves V for these ops
(saveFlags(save_v_flag)); the interpreter does too.
[ci skip]

TST and TEQ had a save/restore under STRICT_MODE with a note that nobody
probably relies on it. That path was never built, and it did not cover
ANDS, EORS, ORRS, BICS, MOVS or MVNS.

Restore V after the op from the PSR that loadFlags() loaded before it,
in the same place the shifter carry is merged, for every logical op that
sets flags. Three instructions, only on those ops.

AicaArmTest.LogicOpsTest asserted exactly this (teq with C,V set must
keep V) and failed on arm64; it passes now, with the rest of the suite.

Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-10-21 12:07:57 +02:00
jutler 128e6ce29c addrspace: mask the shift count taken from the tagged pointer (#2574)
readConst, writeConst, readt and writet shift the 32-bit guest address by
the whole tagged memInfo_ptr value (pointer | shift). Shifting a u32 by 32
or more is undefined, and GCC 16 targeting 32-bit ARM uses that: once the
pointer is known to be non-null (iirf > 31) it drops the offset, so
read32/write16 compile to a plain load/store at the region base. Every
memory access then hits offset 0, the BIOS trampoline copied to 8C0000E0
is lost and the console never boots (black screen, no sound).

Mask the shift with HANDLER_MAX, which is what x86 does in hardware and
what the code intends. No change in behaviour where the old code worked.

Co-authored-by: hitenmandalia <[email protected]>
[ci skip]
2026-10-21 12:07:14 +02:00
Rich Stokes 38a3d83c77 bba: hand Ethernet frames over on the threads that own them (#2569)
Received frames were passed to the RTL8139 from the network thread,
raising the ASIC interrupt there and racing SIIDRebuild() on the SH-4
thread. Queue them and deliver them from an SH-4 scheduler callback.

Sent frames were pushed into picoTCP from the SH-4 thread, but picoTCP
is built without PICO_SUPPORT_MUTEX so its device queue is unlocked.
Queue them and feed them to picoTCP from the pico thread before each
stack tick.
[ci skip]
2026-10-21 12:05:47 +02:00
Joe Mattiello 19ed307f74 rend: rotate sorted triangle indices for flat shading instead of copying colors (#2553)
Vulkan without VK_EXT_provoking_vertex, DX9 and DX11 take flat-shaded
colors from the first vertex, while on Dreamcast it's the last. For the
depth-sorted triangle list, setFirstProvokingVertex() copied each
triangle's last vertex colors onto its first vertex. But sortTriangles()
builds these triangles from strips, so neighbors share vertices:
tri0=(P0,P1,P2), tri1=(P2,P1,P3). P2 is tri0's source and tri1's
destination, and the list is processed in depth order, so one
triangle's flat color can overwrite another's before it's read. In
strips of three or more triangles, consecutive triangles even share
their first vertex. Flat-shaded translucent polygons then pick up a
neighbor's color, e.g. the player shadow decals in Tony Hawk's Pro
Skater 2 and Mat Hoffman's Pro BMX turn uniformly dark.

Each sorted triangle has its own three slots in idx, so rotate them
(a,b,c) -> (c,a,b) instead. The winding is unchanged and the provoking
vertex comes first, without touching shared vertices. Strips keep the
color copy, which is safe there.

Adds SorterTest, which fails on the old code and passes with this change.

Co-authored-by: Claude Opus 5.5 <[email protected]>
[ci skip]
2026-10-21 12:04:35 +02:00
Joe Mattiello d207cd9198 addrspace: clear the address map when its backing memory is freed (#2546)
When nvmem is disabled, termMappings() frees main RAM, VRAM, ARAM and
the Elan RAM but leaves memInfo_ptr unchanged. Each entry still holds
the old host base, so any guest access before the next remap returns a
pointer into freed memory.

Zero the table, as init() does. A null entry routes to handler 0,
readMemNotMapped/writeMemNotMapped, which only log.

Co-authored-by: Claude Opus 5.5 <[email protected]>
[ci skip]
2026-10-21 11:19:28 +02:00
Chris Dixon abfc411392 libretro: report a fixed savestate size for each system (#2561)
A frontend may size its savestate buffer once, as libretro.h allows,
but a state grows and shrinks with the devices plugged in and the
frames being drawn, and states then outgrow the buffer. Report the
system's memory regions plus the allowance GGPO makes for everything
else, which is constant for a game.

System SP gets NAOMI's allowance, plus the game's media if it has one:
the media is loaded into memory-backed RAM, so a state holds all of it.
[ci skip]
2026-10-20 16:32:32 +02:00
Flyinghead 9981e84912 breakpad: dwarf5 reader fix 2026-10-20 15:51:14 +02:00
Flyinghead b5acd1b533 wccf: card reader fix. Player names and images for 331j and 400j
Fix card reader error on reinsertion with 116, 212e and 322e.
Add support for player names and images for 331j and 400j
2026-10-20 15:50:07 +02:00
Joseph MattielloandClaude Opus 5.5 08afcbe6b9 rend: clamp xBRZ texture upscale factor to 2..6
xbrz::scale() only implements factors 2 to 6. Any other value hits an
assert that's compiled out in release builds, and scale() returns
without writing the destination buffer. The texture is then uploaded
from uninitialized memory at the requested size.

rend.TextureUpscale2 can be set to 8 or 16 through emu.cfg or Lua.
Clamp the factor once and use it for the buffer size, the scale call
and the upscaled dimensions so they always agree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 20:14:45 +02:00
Joe Mattiello e9dd6187cb cdipsr: make temp_value a local (#2549)
temp_value was a file-scope global shared by every CDI parse. The boxart
scraper opens discs on its own thread, so it can parse a CDI while a
game is loading and corrupt the other parse's reads.

Use a local in each function. It's a uint32_t initialized to 0 because
every read into it is 4 bytes; a local unsigned long would leave the top
half uninitialized on LP64.

Co-authored-by: Claude Opus 5.5 <[email protected]>
[ci skip]
2026-10-16 20:13:29 +02:00
Joe Mattiello a5dc811814 savestate: reject invalid broadcast/cable/region instead of aborting (#2555)
dc_deserialize() read the broadcast, cable and region settings straight
into the config and checked them with verify(), so a corrupt or
incompatible state killed the process, after already writing the bad
values into the settings.

Read them into locals and throw Deserializer::Exception when one is out
of range. dc_loadstate() and the libretro core already catch it and
report the state as unusable. Valid states load exactly as before.

Co-authored-by: Claude Opus 5.5 <[email protected]>
[ci skip]
2026-10-16 20:12:36 +02:00
Joe Mattiello 5c86428c04 input: stop the emulator while saving an in-RAM savestate (#2554)
The in-RAM save hotkey called gui_saveState(false, true), which runs
dc_serialize() without stopping the emulator. With threaded rendering
on (the default), the SH4 and AICA keep running on the emulator thread
while the input thread serializes, so the state can be torn: memory and
registers from different moments.

Stop and restart around the save like the disk save hotkey and the
in-RAM load hotkey already do.

Co-authored-by: Claude Opus 5.5 <[email protected]>
[ci skip]
2026-10-16 20:11:22 +02:00
Flyinghead 91c3084d52 imgread: add 2336 to 2340 bytes sector conversion as used by Bleemcast 2026-10-14 13:26:57 +02:00
Flyinghead 506b4efd04 capcom: use peer-to-peer raw mode for in-game communication
Let peers exchange V.42 frames instead of implementing the protocol
locally so that error correction works end to end.
2026-10-14 13:20:12 +02:00
Flyinghead 518c496251 sh4: set Sh4Cntx address on Reset
RAM base and Sh4Cntx address might change on libretro without Init being
called. So set the correct Sh4Cntx address on Reset and propagate to
dynarec, cycle counter and caches.
[ci skip]
2026-10-13 11:25:52 +02:00
Flyinghead 804edb00d7 dx11,gl: fix clear framebuffer and scissor regressions
dx11: don't use scissor rect for final A-buffer render
gl4: clear geom_fbo[1] (color pass) not geom_fbo[0] (depth pass) when
needed
Issue #1197
2026-10-13 11:14:45 +02:00
Flyinghead fa23eef593 multiboard: avoid crash when raw input is enabled
Fixes MINIDUMP-A9F
2026-10-12 09:44:10 +02:00
Flyinghead 658abb386a maple: don't crash on invalid maple device. Just ignore it
Fixes MINIDUMP-A9C
2026-10-12 09:42:20 +02:00
Jordi Serra fa3c526e4f ggpo: resolve the peer host name (#2514)
GGPO parses the peer address with inet_pton() only, so a host name in
the Peer field silently becomes 0.0.0.0 and the session never syncs,
without any error. Resolve non-numeric addresses with getaddrinfo()
before handing them to GGPO, and fail with a clear message when the
name cannot be resolved.

Co-authored-by: unjordi <[email protected]>
[ci skip]
2026-10-12 09:34:50 +02:00