mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
5fbd11921a
The lobby's BIOS identity was a two-value token, openbios or scph1001, and scph1001 meant "some retail dump". An SCPH-5552 peer and an SCPH-1001 peer both offered it, the host settled it, each peer booted its own image, and the rollback boot digest never matched: about 20 s of black window, then both players back in the lobby with no reason (reported in #243). Each offer now carries the CRC-32 of the retail image that peer would boot (online bios_offer.retail_crc; LAN JOIN line 6). The host settles retail only when every seat offers the same CRC, else OpenBIOS when every seat links it, else refuses to start and puts the reason on the launcher's status line ("Players use different BIOS images (SCPH-5552 and SCPH-1001) ..."). The settled CRC ships in match_caps (session_bios_crc) and on MOTK1 START line 7, and each peer boots only a dump with that CRC. Rooms where every peer has the same image keep working, including SCPH-5552-only builds with no OpenBIOS. Old peers send no CRC, so a mixed old/new room falls back to OpenBIOS or refuses; a session from an old host still accepts any retail dump. The lobby server relays bios_offer and match_caps as opaque objects, so it needs no change. The rule lives in netplay_bios_settle.c, shared by the online and LAN paths and covered by netplay_bios_settle_test. last_error grows to fit one sentence, with psx_lobby_set_last_error to set it.
157 lines
5.7 KiB
C
157 lines
5.7 KiB
C
/* Netplay BIOS settle rule and its wire helpers.
|
|
* Plain checks, not assert(): Release test builds define NDEBUG. */
|
|
#include "netplay_bios_settle.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
static int failures;
|
|
#define CHECK(cond) do { if (!(cond)) { \
|
|
fprintf(stderr, "%s:%d: CHECK failed: %s\n", __FILE__, __LINE__, #cond); \
|
|
++failures; } } while (0)
|
|
|
|
enum { SCPH1001 = 0x37157331u, SCPH5552 = 0xD786F0B9u };
|
|
|
|
/* A seat with a retail dump; OpenBIOS as given. */
|
|
static NetplayBiosSeat retail(uint32_t crc, int can_open, int host)
|
|
{
|
|
NetplayBiosSeat s;
|
|
memset(&s, 0, sizeof(s));
|
|
s.offered = 1;
|
|
s.can_openbios = can_open;
|
|
s.can_retail = 1;
|
|
s.retail_crc = crc;
|
|
s.is_host = host;
|
|
return s;
|
|
}
|
|
|
|
static NetplayBiosSeat openbios_only(int prefer, int host)
|
|
{
|
|
NetplayBiosSeat s;
|
|
memset(&s, 0, sizeof(s));
|
|
s.offered = 1;
|
|
s.can_openbios = 1;
|
|
s.prefer_openbios = prefer;
|
|
s.is_host = host;
|
|
return s;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
NetplayBiosSeat seats[3];
|
|
NetplayBiosSettle r;
|
|
char text[256];
|
|
|
|
/* The 09-16 defect: SCPH-5552 host, SCPH-1001 guest, both link OpenBIOS.
|
|
* Before the fix both booted their own dump. Now: OpenBIOS. */
|
|
seats[0] = retail(SCPH5552, 1, 1);
|
|
seats[1] = retail(SCPH1001, 1, 0);
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_OPENBIOS);
|
|
CHECK(strcmp(netplay_bios_token(r.kind), "openbios") == 0);
|
|
|
|
/* Same pair, but builds that link one retail backend and no OpenBIOS:
|
|
* no BIOS both can boot, so the host must refuse, naming both images. */
|
|
seats[0] = retail(SCPH5552, 0, 1);
|
|
seats[1] = retail(SCPH1001, 0, 0);
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_NONE);
|
|
CHECK(r.seat_a == 0 && r.seat_b == 1);
|
|
netplay_bios_describe_refusal(&r, seats, 2, text, sizeof(text));
|
|
CHECK(strstr(text, "SCPH-5552") != NULL);
|
|
CHECK(strstr(text, "SCPH-1001") != NULL);
|
|
|
|
/* EU vs EU without OpenBIOS works today and must keep working. */
|
|
seats[0] = retail(SCPH5552, 0, 1);
|
|
seats[1] = retail(SCPH5552, 0, 0);
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_RETAIL);
|
|
CHECK(r.retail_crc == SCPH5552);
|
|
CHECK(strcmp(netplay_bios_token(r.kind), "scph1001") == 0);
|
|
|
|
/* US vs US with OpenBIOS linked: retail, as before. */
|
|
seats[0] = retail(SCPH1001, 1, 1);
|
|
seats[1] = retail(SCPH1001, 1, 0);
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_RETAIL && r.retail_crc == SCPH1001);
|
|
|
|
/* A guest prefers OpenBIOS; the host's retail pick still wins. */
|
|
seats[1].prefer_openbios = 1;
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_RETAIL && r.retail_crc == SCPH1001);
|
|
|
|
/* The host prefers OpenBIOS: OpenBIOS. */
|
|
seats[0].prefer_openbios = 1;
|
|
seats[1].prefer_openbios = 0;
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_OPENBIOS);
|
|
|
|
/* Three seats, nobody prefers OpenBIOS: retail when every image matches. */
|
|
seats[0] = retail(SCPH1001, 1, 1);
|
|
seats[1] = retail(SCPH1001, 1, 0);
|
|
seats[2] = retail(SCPH1001, 1, 0);
|
|
r = netplay_bios_settle(seats, 3);
|
|
CHECK(r.kind == NETPLAY_BIOS_RETAIL);
|
|
|
|
/* One seat has only OpenBIOS: OpenBIOS for everyone who links it. */
|
|
seats[2] = openbios_only(1, 0);
|
|
r = netplay_bios_settle(seats, 3);
|
|
CHECK(r.kind == NETPLAY_BIOS_OPENBIOS);
|
|
|
|
/* An older peer sends no CRC: retail is unsafe, OpenBIOS if all link it. */
|
|
seats[0] = retail(SCPH1001, 1, 1);
|
|
seats[1] = retail(0, 1, 0);
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_OPENBIOS);
|
|
/* ...and a refusal when the host cannot run OpenBIOS. */
|
|
seats[0].can_openbios = 0;
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_NONE);
|
|
CHECK(r.seat_a == 1 && r.seat_b == -1);
|
|
netplay_bios_describe_refusal(&r, seats, 2, text, sizeof(text));
|
|
CHECK(strstr(text, "no usable BIOS") != NULL);
|
|
|
|
/* A seat that sent no offer at all is an older client: OpenBIOS. */
|
|
memset(&seats[1], 0, sizeof(seats[1]));
|
|
seats[0] = retail(SCPH1001, 1, 1);
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_OPENBIOS);
|
|
|
|
/* Every seat prefers OpenBIOS but one cannot run it: retail if possible. */
|
|
seats[0] = retail(SCPH5552, 0, 1);
|
|
seats[1] = retail(SCPH5552, 1, 0);
|
|
seats[1].prefer_openbios = 1;
|
|
r = netplay_bios_settle(seats, 2);
|
|
CHECK(r.kind == NETPLAY_BIOS_RETAIL && r.retail_crc == SCPH5552);
|
|
|
|
/* No seats: OpenBIOS, the old default. */
|
|
r = netplay_bios_settle(seats, 0);
|
|
CHECK(r.kind == NETPLAY_BIOS_OPENBIOS);
|
|
CHECK(strcmp(netplay_bios_token(NETPLAY_BIOS_NONE), "") == 0);
|
|
|
|
/* Wire helpers. */
|
|
netplay_bios_format_crc(SCPH5552, text, sizeof(text));
|
|
CHECK(strcmp(text, "d786f0b9") == 0);
|
|
CHECK(netplay_bios_parse_crc(text) == SCPH5552);
|
|
CHECK(netplay_bios_parse_crc("D786F0B9") == SCPH5552);
|
|
CHECK(netplay_bios_parse_crc(" 37157331\n") == SCPH1001);
|
|
netplay_bios_format_crc(0, text, sizeof(text));
|
|
CHECK(text[0] == '\0');
|
|
CHECK(netplay_bios_parse_crc("") == 0);
|
|
CHECK(netplay_bios_parse_crc(NULL) == 0);
|
|
CHECK(netplay_bios_parse_crc("1234567") == 0); /* too short */
|
|
CHECK(netplay_bios_parse_crc("123456789") == 0); /* too long */
|
|
CHECK(netplay_bios_parse_crc("scph1001") == 0); /* a token, not a CRC */
|
|
netplay_bios_describe_crc(SCPH1001, text, sizeof(text));
|
|
CHECK(strcmp(text, "SCPH-1001") == 0);
|
|
netplay_bios_describe_crc(0x12345678u, text, sizeof(text));
|
|
CHECK(strcmp(text, "BIOS 12345678") == 0);
|
|
|
|
if (failures) {
|
|
fprintf(stderr, "netplay_bios_settle: %d check(s) FAILED\n", failures);
|
|
return 1;
|
|
}
|
|
puts("netplay_bios_settle: PASS");
|
|
return 0;
|
|
}
|