Commit Graph
16277 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 44b5dc1b95 Config: Default AutoFrameSkip to false
Its default called IsVREnabled() during static initialization, before VR is
set up, so it was always false in practice.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:00:17 +02:00
Henrik RydgårdandClaude Opus 5.5 729047d449 Config: Make the setting tables constexpr
SETTING now takes offsets with offsetof and passes them along with the
setting's type, instead of the block and member pointers, and the
ConfigSetting constructors are constexpr. So the tables are built at compile
time, and later per-setting metadata can be checked by the compiler too.

Defaults that weren't constants had to change:
- AutoFrameSkip called IsVREnabled() during static initialization, before VR
  is set up. It's now a default callback, evaluated when the config is
  loaded or reset, which can turn it on by default on VR devices.
- DisplayRefreshRate read g_Config's own initial value, which is 60.

Path defaults now share the string member of the default union.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 15:34:47 +02:00
Henrik RydgårdandClaude Opus 5.5 71b38e58e6 Config: Store the alternative speeds in percent
The settings screen showed them in percent but they were stored as frame
rates out of 60, so the screen kept converted copies and wrote them back. Now
they're stored in percent under new ini keys (AltSpeedPercent,
AltSpeedPercent2, AnalogAltSpeedPercent), migrated from the old frame rate
keys like the audio volumes were, and converted to a frame rate where the
timing needs one.

Low percentages no longer round to a 0 fps limit, which meant unlimited.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 15:28:18 +02:00
Henrik Rydgård 35180fb0bc Merge pull request #22726 from hrydgard/webserver-shutdown-no-hang
WebServer: Don't make shutdown wait for a stuck server registration
2026-10-22 12:22:45 +02:00
Henrik Rydgård bf3d9e7388 Merge pull request #22724 from hrydgard/vsh-kernel-fpl
HLE: Resolve the kernel sceKernelAllocateFpl
2026-10-22 12:09:10 +02:00
Henrik RydgårdandClaude Opus 5.5 a1b4a93cac HLE: Resolve the kernel sceKernelAllocateFpl
It was left unresolved with the kernel mutex calls, as they all let the
flash drivers reach the NAND controller during a VSH boot. It's the
mutexes that do that: with only the FPL resolved, sceIdStorage_Service
allocates from its pool and every firmware's VSH in 1.50-6.61 boots to
the same XMB picture as without it. Resolving the mutexes still hangs on
0xbd101300.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 11:24:14 +02:00
Henrik RydgårdandClaude Opus 5.5 a79999e715 WebServer: Don't make shutdown wait for a stuck server registration
Registering with report.ppsspp.org starts with a DNS lookup that can't be
cancelled, and it ran on the server thread, so ShutdownWebServer() from
onDestroy waited for it on the main thread (an ANR on Play). It now runs on its
own thread, which the server leaves behind if it's still in the lookup.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 11:22:04 +02:00
Henrik RydgårdandClaude Opus 5.5 37be6ce4e8 Config: Clamp the touch layout snap grid size on load
The layout editor divides by it, and drawing the grid never finishes when
it's 0. Only the slider limited it, so a hand-edited ini could hang.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 10:55:31 +02:00
Henrik Rydgård 1adf3c553d Merge pull request #22549 from Kethen/aemu_postoffice_enet
UDP(ENet) mode for adhocctl & aemu_postoffice relay
2026-10-21 23:51:28 +02:00
Henrik RydgårdandClaude Opus 5.5 1fe87ec263 Syphon Filter: Logan's Shadow: Fix the explosion smoke crashing at 60fps
The game shares Resistance: Retribution's effect engine, smoke trails and all: a trail gets a
new node on every update while the nodes expire by the effect's clock, so the faster the game
runs the more smoke there is. When the tank explodes, the smoke grows the display list past its
500KB buffer at a steady 60fps (an overclocked CPU gets there), the list runs into the heap and
the game crashes on a bone array it overwrote. Hook the same two functions as in Resistance:
nodes at most every 1/30 of the effect's clock, and the overflow guard as a backstop. At 428MHz
the list now peaks at 351KB, against 512KB and a crash before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 23:11:23 +02:00
Henrik Rydgård 03457d559b Merge pull request #22716 from hrydgard/vsh-mount-iso
Allow --mount in the app, and make VSH UMD autoboot a setting
2026-10-21 20:57:42 +02:00
Henrik RydgårdandClaude Opus 5.5 5a8e865532 Config: Default VshUmdAutoboot to off
Booting the VSH with a disc now stays in the XMB, with the disc in the
Game column, rather than starting the game right away.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:57:01 +02:00
Henrik RydgårdandClaude Opus 5.5 2d116a5b76 sceReg: Let a key read a bool setting, use it for VSH UMD autoboot
KeyValue gets an optional pointer to a g_Config bool, which the key
reads as 0/1. The setting wins over guest writes: since category_version
is kept low, the VSH upgrades the registry at every boot and rewrites
every key with its default.

/CONFIG/SYSTEM/umd_autoboot reads the new SystemParam VshUmdAutoboot
(default true, as before). Turned off, --vsh --mount stays in the XMB
with the disc in the Game column instead of starting it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:52:38 +02:00
Henrik RydgårdandClaude Opus 5.5 3a5c5a51c5 Allow --mount in the app, so --vsh can boot with a disc inserted
--mount ISO used to be headless-only, and EmuScreen always cleared
CoreParameter::mountIso. Pass it from the command line into the first
EmuScreen, which keeps it across resets and drops it when another game
is booted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:47:51 +02:00
Henrik RydgårdandClaude Opus 5.5 c4034700fc VSH: Return to the XMB with its own args, and when a launch fails
The XMB passes its state as vshmain args (0x400 bytes) when it launches
something. We now keep them and start vshmain with them when the game
exits, so it comes back where it left off instead of booting fresh: no
opening, the launched item selected, and no UMD auto-start. vshmain's
module_start only copies them in when they begin with their size and
0x20, which the kernel has to fill in.

A launch that fails to load (like the Video Demo Disc's game part, whose
EBOOT.BIN is 2384 zero bytes) now goes back to the XMB too, instead of
stopping emulation. Before the args were handed back, this looped: the
fresh XMB auto-started the disc again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:00:01 +02:00
Henrik RydgårdandClaude Opus 5.5 83df04686c sceNet: Register sceNetApctl_lib, for the VSH's network connection screen
pspnet_apctl.prx exports the internal handler, scan and BSS functions to
the VSH under sceNetApctl_lib (as listed by its 6.61 export table). We had
them, but only under sceNetApctl, so the XMB's netconf plugin got an
unresolved import and the browser and online manuals stopped at 8002013A
before their Network Connection screen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:00:01 +02:00
Henrik RydgårdandClaude Opus 5.5 9fec7d7fb0 VSH: A game launched from the XMB exits back to it
On a PSP, sceKernelExitGame in a game the XMB launched goes back to the
XMB. Ours ended the session. In a session booted as vshmain.prx, an exit
from anything but the VSH itself now LoadExecs vshmain.prx again; the
shell then plays its opening and comes back on the Game column.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:00:01 +02:00
Henrik RydgårdandClaude Opus 5.5 cf29cbdfd7 sceChnnlsv: Allow no key in sceSdGetLastIndex and sceSdCreateList
Both took their key through GetPointerWriteOrException, which raises a
memory exception for 0, though the code behind them treats a null key
as no key. The VSH's Saved Data Utility checks PARAM.SFO hashes without
one, and emulation stopped there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:00:01 +02:00
Henrik RydgårdandClaude Opus 5.5 8d6a0134c6 sceIo: The memory stick's raw device (msstor), for the Saved Data Utility
The VSH's Saved Data Utility opens msstor0p1:/ to ask whether a stick is
in and writable (ioctls 0x02125008/9) and what type it is (devctl
0x02125802), and gave up quietly when the open failed. msstor paths now
open as a node with nothing behind it, answering those as msstor.prx's
handlers and a PSP do (pspautotests io/msstor/msstor): in, writable, type
0x40, without rescheduling, and 0x80220183 for a short output buffer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:00:01 +02:00
Henrik RydgårdandClaude Opus 5.5 0877c3d19a SaveState: Refuse savestates and rewind in a session booted into the XMB
Inserting and ejecting a disc there adds and removes the umd0:/umd1:/
umd:/disc0: mounts, and MetaFileSystem's state pairs sections with mounts
by position without recording which disc was in. A state could then fail
to load, or load one disc's open files onto another. The pause screen
already hid its save buttons for vshmain.prx; the save hotkeys, rewind,
the ImDebugger and the WebSocket debugger all went around that. The check
is now in SaveState itself, and rewind snapshots aren't taken. Verify
still runs, since it reads back with the mounts as they are.

A proper fix records the mounted image in sceUmd's state and remounts it
on load.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 18:25:01 +02:00
Katharine Chui 07190a820d adhoc load state bring up adhoc before adhocctl and bring down adhoc after adhocctl 2026-10-21 18:13:40 +02:00
Henrik RydgårdandClaude Opus 5.5 3bba517d46 sceMpegbase: Field conversion in sceMpegBaseCscAvc
The descriptor's +0x08 and the second output pointer (pspautotests
video/mpeg/cscavc): with a second buffer the fields are split and packed,
even rows into the first and odd ones into the second, and +0x08 leaves
the odd field out; without one, rows stay in place and +0x08 skips the
odd ones. The VSH's UMD Video player converts the top field of its 720x480
pictures this way, and our 480 full rows ran over the UI library's VRAM
allocator. Each field is converted with its own chroma rows, as
interlaced 4:2:0 has them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:44 +02:00
Henrik RydgårdandClaude Opus 5.5 add84fb36f VSH: sceVaudio_driver, sceVaudioChReserveBuffering and the display panel ID
The VSH's video player outputs sound through vshbridge.prx, which imports
sceVaudio under its driver name, so every sceVaudioOutputBlocking went
nowhere. Register the same functions as sceVaudio_driver. The player
reserves its channel with sceVaudioChReserveBuffering, which in vaudio.prx
is sceVaudioChReserve with a flag set; treat it as that.

It also polls sceDisplay_driver_996881D2 through vshbridge.prx, and skips
setting up its picture output when it's 0. display_02g.prx's init sets
that from the LCD panel type in IdStorage (0x0103000B for type 1,
0x0103020F for type 2), so it's never 0 on a PSP.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:44 +02:00
Henrik RydgårdandClaude Opus 5.5 9f5d074a0e sceVideocodec: Picture records, SEI and sequence timing, for UMD Video
The VSH's UMD Video player reads what the ME writes beside each decoded
picture, which games' mpeg.prx only passes through (pspautotests
video/mpeg/avcdetail, recorded on a PSP):

- Given a 0x540-byte buffer at ctx+0x38, as mpeg_vsh.prx provides in the
  modes the player uses, the ME points each picture descriptor's +0x24
  and +0x28 at two 0xA4-byte records for it. The first is the picture's
  SEI record: cpb_removal_delay at +0x18, dpb_output_delay at +0x1C, the
  pic_struct at +0x20 (which the player picks a duration with), and flags
  of 7 for a keyframe and 2 otherwise. sceVideocodecGetSEI writes the
  record of the last access unit decoded to the buffer at ctx+0x50.
- A descriptor's +0x20 is 1 for a keyframe. The player drops every
  picture after starting or seeking until it sees one.
- The 0x64 bytes at ctx+0x30 carry the VUI timing (num_units_in_tick at
  240000 ticks a second), which the player checks against 5005 for film.

All of it comes from what ffmpeg's H.264 parser reports per access unit,
which AvcDecoder now hands back with each picture.

Each picture also goes into the next of the four frame stores, as the ME
rotates through its own: the player converts a picture in strips spread
over its next few frames, while decoding goes on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:44 +02:00
Henrik RydgårdandClaude Opus 5.5 7d2663bf73 VSH: Insert and eject UMDs from the pause screen
While the XMB is running, the pause screen offers "Insert UMD" with the
drive empty, opening the Switch UMD game picker, and "Eject UMD" with a disc
in. The XMB notices the change through its drive callback, as it does over
the debugger's game.umd.insert/eject. Games keep "Switch UMD".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik RydgårdandClaude Opus 5.5 71974b2eab sceMpegbase: Add AC9E717E, CscInit with a caller's colour matrix
In mpegbase_260.prx, sceMpegBaseCscInit calls this with its built-in
22-byte table. The VSH's video player imports it directly. We keep
converting with the standard matrix, and log the one passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik RydgårdandClaude Opus 5.5 e5f1e335f6 sceVideocodec: Give the ME 4MB, room for UMD Video's frame buffers
Four 720x480 pictures take 2MB, all of the 2MB ME memory we modelled, so
UMD Video's decoder never got its frame buffers. mpeg.prx accepts ME
addresses up to 4MB. What we allocate still starts at 1MB, so games' videos
get the same addresses as before, and a savestate from before grows its
memory in place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik RydgårdandClaude Opus 5.5 61db5f9459 VSH: Insert UMD Video discs, and report what kind of disc is in the drive
A UMD Video image can now be inserted, and the drive's disc type (devctl
0x01F20001) comes from what's on the disc: PSP_GAME, UMD_VIDEO and UMD_AUDIO
each set their bit. With a video disc the XMB moves to the Video column,
shows the disc with its icon and background, and hands it to the firmware's
video player when selected. sceUmdGetDiscInfo still tells games they're on
a game disc.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik RydgårdandClaude Opus 5.5 8560a2df75 IR: Log compiled jumps to invalid addresses at debug level
A block that jumps to an invalid address is usually code that never runs,
such as a call through an import that isn't linked yet, and the error-level
"Jump to invalid address" sent people chasing it. A jump that actually runs
still fails as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik RydgårdandClaude Opus 5.5 eee153b220 VSH: An empty UMD drive, and inserting a disc over the WebSocket debugger
The XMB, booted without a game, now starts with an empty drive: when
vsh_module loads with nothing at disc0:, the drive is marked empty, and
activating it tells the shell's drive callback (SceVshMediaDetectUMD)
there's no disc. Activation used to report a readable disc regardless,
so the shell asked for its type and showed "The disc could not be read."
The disc type devctl (0x01F20001) answers 0 without a disc, as JPCSP
does.

game.umd.insert (path) mounts a disc image into the empty drive and
calls the drive callback as inserting one does; game.umd.eject takes it
out. UmdMount() is UmdReplace() without its need for a disc already in.

Inserted into the 6.61 XMB, Daxter shows in the Game column with its
icon, background and animated preview, and launching it boots the game
to its title menu.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik Rydgård b225c33639 Merge pull request #22713 from hrydgard/drop-legacy-cpu-flags
CmdLine: Drop the legacy cpu core flags, use --cpu=
2026-10-21 17:11:07 +02:00
Katharine Chui c086eab2ed spin down aemu_postoffice during netAdhocInited -> !netAdhocInited load state transition 2026-10-21 16:22:57 +02:00
Henrik Rydgård 4387e60f6f Merge pull request #22709 from hrydgard/ir-interpreter-faults
IR interpreter: Halt with a memory exception on a bad guest access
2026-10-21 16:14:14 +02:00
Henrik Rydgård 267546c608 Merge pull request #22711 from hrydgard/activision-hits-frame-limit
Activision Hits Remixed: Limit the front end to one frame per vblank
2026-10-21 16:13:48 +02:00
Katharine Chui 3db71ea1ed fix missing htons in getRelativeLocalIpFromRoutingTable 2026-10-21 16:08:49 +02:00
Katharine Chui ee06afa3bc do not hold adhocctlMutex while adhocctl client is being created 2026-10-21 16:06:29 +02:00
Henrik RydgårdandClaude Opus 5.5 6e3994aac0 CmdLine: Drop the legacy cpu core flags, use --cpu=
-i, -j, -r, -J, --ir and --jit-ir are gone. -r was also --root's short form,
and --root won, so -r took the next argument as the root path and nothing
booted (test.py -r failed every test).

-i, -j and -J now fail with an error pointing at --cpu= instead of quietly
running on the default core. Docs and test.py updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 16:05:55 +02:00
Katharine Chui 23dadb11bf fix savestate enet thread handling again 2026-10-21 15:39:32 +02:00
Henrik RydgårdandClaude Opus 5.5 ed6e5f0e6f Activision Hits Remixed: Limit the front end to one frame per vblank
- Fixes #20287

The menus and manuals flip from a GE finish callback and only sleep 100us
per frame, so the GE's drawing time sets the frame rate. Ours is instant,
so they ran at 400-800fps and the host couldn't keep up. Hook the end of
the per-frame update and wait for a vblank unless one has already passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 15:23:59 +02:00
Henrik RydgårdandClaude Opus 5.5 ef7ca8b85f GPU: Move sprites on pixel centers back half a pixel above 1x (compat SnapHalfPixelSprites)
The PS1 emulator draws sprites with their corners on pixel centers (x.5) and
their texture coordinates on texel centers. At 1x that covers whole pixels,
but above 1x it covers half a pixel more on the right and bottom and reads
the neighbouring texels, which shows as seams between tiles. With compat.ini's
SnapHalfPixelSprites, such sprites and their texture coordinates are moved back
half a pixel, which draws the same at 1x. Texture coordinates are also nudged
a 64th of a texel in, as a scaled sprite can sample right on a texel edge,
where interpolation can fall short of the texel the GE reads (the emulator's
video unpacking does).

Set for PS1 games (POPS00000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:51:06 +02:00
Henrik RydgårdandClaude Opus 5.5 cf788d72ca GPU: Optionally draw logic ops done on a framebuffer copy one at a time (compat SeparateLogicOpDraws)
Where the backend has no logic op and no framebuffer fetch, the logic op is
done in the shader against a copy of the framebuffer taken when the batch is
drawn, so draws batched together don't see each other. The PS1 emulator puts
each 24-bit video frame together by ORing passes into the same pixels, so it
lost all but the last. With compat.ini's SeparateLogicOpDraws, such draws are
flushed one at a time, and the rectangles of one draw are split, as they can
overlap too.

It costs a framebuffer copy per draw. Set for PS1 games (POPS00000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:51:06 +02:00
Henrik RydgårdandClaude Opus 5.5 da0ecd838e GPU: Framebuffers kept at native resolution (compat NativeResolutionFramebuffer)
compat.ini's NativeResolutionFramebuffer (an address) keeps that framebuffer at
1x whatever the render resolution, for one that holds data read back texel by
texel: the PS1 emulator unpacks 24-bit video into one three bytes per pixel.

Set for PS1 games (POPS00000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:51:06 +02:00
Henrik RydgårdandClaude Opus 5.5 c9641b1332 GPU: Fixed framebuffers, whole from the start (compat FixedFramebuffer)
A framebuffer the game always keeps at one size, and also reads and writes
outside the area it draws: the PS1 emulator's VRAM, a 1024x512 16-bit surface
it copies between and reads back from the CPU. compat.ini's FixedFramebuffer
("address,width,height") makes it that size instead of one estimated from the
drawing, initializes it from memory even when the first draw doesn't look like
one, and uploads into it without filtering, since what goes in is data that's
read back (#22686 saw FFVI crash on bilinear uploads at 4x).

Set for PS1 games (POPS00000).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:51:06 +02:00
Henrik RydgårdandClaude Opus 5.5 4197d0b8fc Compat: Look PS1 games up as POPS
They're all run by the firmware's PS1 emulator, whose workarounds don't
depend on the game, so compat.ini settings for it go under POPS00000
(letters and digits like other IDs). Only the compat.ini lookup uses it:
savestates, save data and everything else keep the game's own ID.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:51:06 +02:00
Henrik Rydgård 2c235b585e Merge pull request #22707 from hrydgard/me-pops
Boot PS1 EBOOTs through the firmware's POPS emulator
2026-10-21 14:42:06 +02:00
Henrik RydgårdandClaude Opus 5.5 c366da70d1 IR interpreter: Make the fault flag a plain volatile global
Only the CPU thread runs IRInterpret, so it doesn't need to be thread-local.
Volatile keeps the compiler from dropping or sinking the stores, which
nothing but the fault handler reads.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:40:45 +02:00
Henrik RydgårdandClaude Opus 5.5 4b6aea51e7 IR interpreter: Halt with a memory exception on a bad guest access
HandleFault only handled faults in JIT code, so a game's bad load or store
under the IR interpreter crashed the app. Now IRInterpret marks itself
(a thread-local, cleared around HLE calls, replacements and the MIPS
interpreter fallback) and the handler steps over the faulting instruction,
reports the exception and stops the core, or just ignores the access when
IgnoreBadMemAccess is on. If the instruction can't be decoded it still
crashes, as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:35:00 +02:00
Katharine Chui 8474ca35b6 guard built-in server warnings with isLocalServer 2026-10-21 14:29:16 +02:00
Katharine Chui 9c8ebfd59f make use of isLocalServer during adhocctl client binding 2026-10-21 14:19:55 +02:00
Katharine Chui 3662d69fb0 restore port offset hint on udp bind failure 2026-10-21 14:19:55 +02:00