Following pspautotests net/inet/errors, states and reuse:
- bind decides itself whether a port is in use, as 4.4BSD's in_pcbbind()
does with SO_REUSEADDR and SO_REUSEPORT. The host can't, since its
sockets have both on for running several instances, so any number of
sockets could bind the same port. A socket that's bound already is
EINVAL.
- Shutting down the sending side of an unconnected TCP socket, or a
failed connect, throws away its TCP state as in 4.4BSD's tcp_usrreq(),
after which bind, listen, connect, accept and shutdown fail with
EINVAL, and its port is free again. shutdown otherwise only fails for
a bad how, and still reaches the host, so a thread waiting in accept
wakes up.
- listen on an unbound TCP socket binds one, which Winsock didn't, and on
a connected one succeeds.
- connect on a listening socket is EOPNOTSUPP. Winsock's WSAEINVAL for
it looked like a connect in progress, and a blocking socket waited
forever. A second connect on a loopback connection the host hasn't
noticed finishing is EISCONN once it has.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recvmsg was a stub that failed without setting errno. It now receives
like recvfrom, scattering into the buffers and waiting like the other
blocking calls. Sendmsg is rewritten around the same msghdr reading.
Following pspautotests net/inet/msg:
- Recvmsg truncates the address to namelen, gives TCP none and a namelen
of 0, sets MSG_TRUNC in msg_flags, and controllen to 0.
- Sendmsg with no buffers sends an empty datagram, which macOS failed.
- sendto without an address works on a connected socket. Linux failed
it, given an address of length 0.
On Windows, Sendmsg uses WSASendTo, since WSASendMsg only takes
datagram sockets, and Recvmsg WSARecvFrom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Splitting larger checks into groups of 64 for Winsock's select() was
untested, and no game is known to poll more sockets than that. Past 64,
it now logs an error and checks the first 64, as the old code did.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Following pspautotests net/inet/polledge:
- poll reports only what was asked for, and never POLLHUP or POLLERR,
even for a peer that closed or reset the connection.
- select with a closed descriptor in a set fails with EBADF and leaves
the sets alone. It used to skip it.
The host side now uses poll() outside Windows, where select() can't take
a descriptor numbered FD_SETSIZE or above and FD_SET wrote past the
fd_set. macOS reports a peer that closed as POLLHUP with POLLPRI, and
the POLLPRI is ignored, as its select() shows nothing for it. Winsock's
select() has no such limit, but takes only 64 sockets a set, so it's
called on groups of 64, and poll is no longer cut short at 64 entries
there. A failing poll sets the host's errno rather than EINTR.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PSP's getsockopt and setsockopt work like 4.4BSD's, which
pspautotests net/inet/options records in detail, and most of it didn't
match: flags read back as their bit (SO_REUSEADDR as 4, SO_NBIO as
0x80), buffer sizes have their own defaults, getsockopt copies as much
as fits and says how much, and unknown options and levels fail with
ENOPROTOOPT and EINVAL.
So the socket-level options are now kept in InetSocket, and only the
ones that change what the host socket does are passed on to it. That
also stops the timeouts reaching the host, where Winsock read the
timeval as a millisecond count. Accepted sockets copy their options from
the listener, like BSD's sonewconn(). IP_TTL is kept too, as Windows'
default is 128 and the PSP's 64.
Timeouts are set in microseconds, but kept in 10 ms clock ticks, and
one under a tick is no timeout at all (net/inet/limits). Buffers can be
up to 233016 bytes (net/inet/limits), 4.4BSD's limit with 256-byte
mbufs, where we allowed 8 MB. SO_ERROR reads as an SCE error code rather
than an errno (net/inet/tcp).
The host's TCP send buffer is capped at 64 KB, closer to the PSP's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- accept and recvfrom write only as much of the address as its length
allows, and set the length to that (pspautotests net/inet/udp and
tcp). They wrote all 16 bytes and set it to 16. They now also give the
host an address of their own, so one the game passes without a length
can't make the host fail the call with EFAULT.
- InetAton and InetAddr take the forms 4.4BSD's inet_aton() does
("1.2.3", "16909060", "0x7f.1", octal with a leading zero), which the
host's inet_pton() didn't. InetPton takes only a plain dotted quad,
and only AF_INET. All three stop at a space. InetNtop returns NULL with
ENOSPC when the string doesn't fit, leaving the buffer alone, and with
EAFNOSUPPORT for other families (net/inet/addr).
- getpeername after the peer closed is ENOTCONN rather than macOS's
EINVAL, as 4.4BSD checks for a connection first (net/inet/errors).
Coded Arms Contagion calls InetNtop with a 4-byte buffer, which now gets
NULL, as on hardware.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Some old homebrew, like Ozone and Trig Wars, ships an ELF named
EBOOT.PBP. Identify_File accepts it and it boots, but the browser
required a PARAM.SFO, so the Homebrew tab left it out entirely.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Leaving a game creates a new MainScreen, which picked the default tab
(Recent unless changed) whenever there were recent files. Remember the
tab for the rest of the session, so the default tab only applies to the
first main screen after startup.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Several comments called PSP behaviour a workaround for one game: PDP
send and receive returning 0 and writing the length back, Scan while
connected reporting ALREADY_CONNECTED, PtpOpen connecting by itself,
connecting a connected socket returning 0, PtpAccept's optional peer
address and port, the malloc stat's pool size, and matching dropping
hellos from a host mid-join. The pspautotests recorded on PSPs show the
PSP does all of these, so say that, and keep the games as examples.
Also bumps pspautotests for the new adhocctl Disconnect events and the
pair/matching3 test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CancelTarget on a MAC that isn't a peer, and Start on a context ID that
doesn't exist, faked success for every game. On the PSP they fail with
UNKNOWN_TARGET and INVALID_ID (pspautotests net/adhoc/matching). Only
the games they were added for keep them now:
MatchingCancelUnknownTargetSucceeds for Soulcalibur: Broken Destiny, and
MatchingStartUnknownIdSucceeds for GTA: Vice City Stories.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP, Create, lookups, Stop and Delete all work before Init and after
Term, and a second Init fails with 0x80000020 (pspautotests
net/inet/offline). Instead of starting out initialized, which Outrun 2006
needed, drop the init requirement, so every game gets the PSP's
behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From pspautotests net/inet/offline, recorded on a PSP:
- sceNetGetMallocStat(NULL) gives 0x80410103, and sceNetThreadAbort and
sceNetFreeThreadinfo on something that isn't a thread 0x80410104.
- sceNetApctlAddHandler takes two handlers (the internal handler calls
aren't limited), DelHandler tells an impossible ID (0x80410A82) from a
free one (INVALID_ID), and GetState(NULL) and
GetBSSDescIDListUser(NULL size) give 0x80410A03. A NULL buffer in
GetBSSDescIDListUser asks for the size instead of failing.
GetState before Init and GetInfo while disconnected still succeed: the
first check was disabled on purpose at some point, and the simulated
infrastructure connection relies on the second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceNetResolverStartNtoA with a NULL address pointer wrote to address 0
and stopped the game. From pspautotests net/inet/offline, recorded on a
PSP: a NULL name or address gives INVALID_PTR, an empty name 0x80410418,
a NULL buffer in Create INVALID_PTR, and Stop with nothing running
ALREADY_STOPPED (the check was inverted). PollAsync and WaitAsync check
the resolver ID, and PollAsync its status pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the net/adhoc/matching and limits pspautotests, recorded on PSPs:
- Create: a bad mode gives INVALID_MODE, rxbuflen has to be at least
6 * maxnum + 4, host and P2P contexts need nonzero hello and keep-alive
intervals, init_count can't be negative and rexmt_int can't be 0. At
most 15 contexts exist at once.
- Start on a running context gives IS_RUNNING. Start2 takes each
thread's partition after its stack size, not before.
- Stop and Delete on unknown IDs give INVALID_ID, and Delete on a running
context IS_RUNNING (internal cleanup still stops and deletes).
- GetHelloOpt fails before Start, copies at most the game's buffer and
leaves the length alone. It used to write the whole hello past the
buffer.
CancelTarget on an unknown peer still succeeds, as Soul Calibur needs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the net/adhoc pspautotests (pdp, ptp, poll, limits, adhocctl,
gamemode), recorded on PSPs:
- PDP and PTP: NULL pointers and nonzero flags give INVALID_ARG, before
the socket ID is checked. Zero and multicast addresses give
INVALID_ADDR, ports 0 and 65535 INVALID_PORT, and send lengths over
65523 or NULL data INVALID_DATALEN. A length of 0 sends nothing and
succeeds. Zero buffer sizes, retransmit settings and backlogs are
accepted. PdpRecv takes a NULL sender address or port. Running out of
socket IDs gives SOCKET_ID_NOT_AVAIL, and out-of-range IDs on
delete/close INVALID_SOCKET_ID. PollSocket rejects flags other than
NONBLOCK.
- adhocctl: Init checks its stack size, priority and ad hoc ID;
AddHandler needs Init, allows the same function twice and takes at most
4 handlers; DelHandler on an unknown ID gives ID_NOT_FOUND; bad group
names give adhocctl's INVALID_ARG; GetParameter and GetPeerList fail
outside a group.
- Game mode: a NULL group name and a bad minimum are rejected,
GetGameModeInfo fails outside game mode, and the entry timeout reports
adhocctl's TIMEOUT.
The DBZ Shin Budokai 2 workaround that replaces the game's local MAC with
ours stays, so another console's MAC is still accepted when creating a
socket.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
errno:
- A thread that hasn't made a call yet reads 0x80410001 from GetErrno,
as the PSP has no errno record for it until then. errno is saved in
savestates now, in a new sceNet section version.
- socket(), close() and setsockopt() clear it when they succeed.
- Running out of descriptors is EMFILE, at 255 like the PSP, and an
unconnected UDP socket sending without an address EDESTADDRREQ, which
Winsock calls ENOTCONN.
- On Windows, ENOBUFS, EPROTOTYPE, ENOPROTOOPT, EMSGSIZE, ENETRESET and
EDESTADDRREQ now map to the PSP's errnos. They only did elsewhere.
All from pspautotests net/inet/errors, options and errnothread.
The socket table:
- Descriptor numbers are handed out round-robin up to 255, as on the PSP
(net/inet/states), rather than the lowest free one.
- Closing frees the slot even if the host's close fails, and Close and
CloseAll take the lock the rest of SocketManager does.
- accept no longer leaks the host socket when it runs out of numbers.
- bind no longer asks UPnP to forward port 0, and closing the socket
removes its forward, unless another socket still uses the port.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bad arguments now fail the way the PSP fails them, with -1 and errno
set, rather than reaching the host or returning something else:
- A descriptor that isn't open is EBADF. The result used to be EBADF's
number, 9, which looks like success (pspautotests net/inet/errors).
- Buffers and addresses the host reads or writes directly are checked to
lie in game memory, failing with EFAULT, rather than letting a host
recv or send run past the end of it.
- socket() with anything but an AF_INET TCP, UDP or raw socket, with a
protocol that fits, is EPROTONOSUPPORT (net/inet/errors).
- bind with an address shorter than 16 bytes is EINVAL, and with another
family than AF_INET EADDRNOTAVAIL (net/inet/errors).
- shutdown with a how other than 0-2 is EINVAL (net/inet/states).
- sendto with an address shorter than 16 bytes is EINVAL, which Winsock
calls EFAULT, and Sendmsg with any control data, or a short address,
EINVAL (net/inet/msg).
- poll reports a negative fd as POLLNVAL like any other that isn't open,
rather than failing (net/inet/polledge).
The pspautotests update brings in the new net/inet tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core/Util/PSARUnpack.cpp uses uncompress2(), which zlib has since 1.2.9.
libretro's Linux buildbot (Ubuntu 16.04, zlib 1.2.8) has failed on it
since #22781.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On OpenGL the render thread's ThreadStart fills in the GL strings and
picks the buffer strategy, while the emu thread, started just before
it, could already be in NativeInitGraphics reading them. A Play report
crashed walking the strings map in BuildReportingInfo. ThreadStart also
resets the exit flags, which could undo an early exit by the emu
thread. Libretro already starts its emu thread in this order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The multi-name path counted names straight into saveDataListCount, so
returning on a path traversal name left a count behind with the list
already freed by Clear(). The next savestate then serialized that many
entries from a null list and crashed in Do(std::string). Count on the
side, and don't write entries for a list that isn't there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Half the radius of a Choice. Covers notices, progress bars, the
leaderboard tracker and RetroAchievements popups (not the listed
achievements on the achievement screens).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FillRoundRect falls back to a square FillRect for drawables it can't
round, and a ViewGroup with round corners set rounds its background.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceNetAdhocPdpSend with NULL data and a length of 0, sceNetAdhocPdpRecv
with a NULL sender MAC or port, and sceNetAdhocPtpSend with a NULL length
pointer all raised a memory exception before the call ran, which stopped
the game. On a PSP the first two succeed and the last returns INVALID_ARG
(net/adhoc/pdp and net/adhoc/ptp in pspautotests).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--instance=N sets PPSSPP_ID, so each instance gets its own 127.0.0.N address
and MAC; headless left it at 0, which made its address 127.0.0.0.
--realtime keeps emulated time from running ahead of the wall clock. Only
display flips paced headless, and a test that doesn't draw never flips, so
two instances waiting for each other over the network timed out at once.
--adhoc-server runs the built-in ad hoc server.
Tools/adhoc_multi.py runs a multi-console pspautotest (X_host, X_client,
X_client2, ...) on one instance per role, with the built-in server or, with
--relay, a local aemu_postoffice in relay mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>