fd956435939e5d02e1f8ab9f50da5386c51254f6
sudo ./scripts/fsp-server.py --root /workspace --password alltheroms
Swiss streams games over FSP/UDP rather than TCP, so hosting an image needs an
FSP server. Serves a directory read-only: VERSION, STAT, GET_DIR, GET_FILE,
BYE, with CC_ERR for anything else. Reports the achieved read rate every two
seconds, which is the measurement this was really built for -- the 850 KB/s
figure came from the menu's lwIP/TCP path and says nothing about streaming.
The protocol was taken from Swiss's own two clients rather than from memory,
because they disagree about nothing but exercise different commands:
cube/swiss/source/devices/fsp/fsplib.c (menu: browse, stat, read)
cube/patches/bba/tcpip.c (in-game streaming reads)
Details that are easy to get wrong, all verified against that source:
* checksum: sum = total_size + every byte with the checksum byte zeroed,
then sum += sum >> 8, low 8 bits.
* password: the data field is "path\npassword\0" -- the '\n' REPLACES the
path's terminating NUL (fsplib buildfilename), it is not appended after it.
* GET_DIR: entries are time(4) size(4) type(1) name(NUL) padded to 4 bytes;
the client reads blocks until one is shorter than the first, so the whole
listing then an EMPTY reply is what terminates it. dirpos % 4 != 0 is
rejected outright by fsplib, hence the alignment padding.
* STAT: the same 9-byte time/size/type prefix; type 0 means ENOENT.
* GET_FILE: sequence AND position must be echoed -- the in-game client
discards any reply where either differs.
* ⚠️ a GET_FILE reply must be a multiple of 32 bytes unless it is the tail,
because the in-game client does
data_size = MIN(requested, OSRoundDown32B(reply_len))
so a non-32-aligned reply is silently rounded DOWN and the stream stalls.
test-fsp-server.py runs a real server on 127.0.0.1 and drives it with a client
that packs requests exactly as Swiss does: checksums verify both ways, STAT
returns the true size, GET_DIR parses with fsplib's own walk and the second
page is empty, GET_FILE returns byte-exact data at three offsets with seq and
pos echoed, a 1024-byte request comes back 32-aligned, a wrong password gives
CC_ERR, and ../../etc/passwd is refused.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Description
No description provided
Languages
Python
84.9%
C
12.4%
Shell
1.1%
Dockerfile
0.7%
Makefile
0.4%
Other
0.5%