sceNetAdhocGameMode: Create master and replicas as a PSP does

pspautotests net/adhoc/pair/gamemode:
- CreateMaster with size 0 fails with INVALID_DATALEN.
- CreateReplica for a MAC that isn't in the game mode group fails with
  0x80410D13. PPSSPP made one, and then blocked the thread forever
  waiting to sync with it.
- CreateReplica for the same MAC again gives ALREADY_CREATED. The check
  compared the pointer instead of the MAC, so it never matched.
- A replica's ID is the member's place in the member list, host first:
  the client's replica of the host is 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-30 10:15:14 +01:00
1 parent 60a49ccfd2
commit 08c76b8b25
2 files changed
+22 -11

No files matched your search

+2
View File
@@ -609,6 +609,8 @@ enum PSPErrorCode : u32 {
SCE_NET_ADHOC_ERROR_ALREADY_CREATED = 0x8041071A,
SCE_NET_ADHOC_ERROR_NOT_IN_GAMEMODE = 0x8041071B,
SCE_NET_ADHOC_ERROR_NOT_CREATED = 0x8041071C,
// GameModeCreateReplica for a MAC that isn't in the game mode group. The name is ours.
SCE_NET_ADHOC_ERROR_NOT_GAMEMODE_MEMBER = 0x80410D13,
// pspnet_adhoc_matching
SCE_NET_ADHOC_MATCHING_ERROR_INVALID_MODE = 0x80410801,
+20 -11
View File
@@ -6194,6 +6194,10 @@ static int sceNetAdhocGameModeCreateMaster(MIPSState *mips, u32 dataAddr, int si
if (size < 0 || !Memory::IsValidAddress(dataAddr))
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg");
// pspautotests net/adhoc/pair/gamemode
if (size == 0)
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_INVALID_DATALEN, "invalid data length");
if (masterGameModeArea.data)
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_ALREADY_CREATED, "already created"); // FIXME: Should we return a success instead? (need to test this on a homebrew)
@@ -6247,18 +6251,23 @@ static int sceNetAdhocGameModeCreateReplica(MIPSState *mips, const char *mac, u3
if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr))
return hleLogError(Log::sceNet, SCE_NET_ADHOCCTL_ERROR_INVALID_ARG, "invalid arg");
const SceNetEtherAddr &replicaMac = *(const SceNetEtherAddr *)mac;
// The ID is the member's place in the member list (the host first), and a MAC that isn't a member is
// refused. With two consoles, the host's replica of the client is 1 and the client's of the host 0
// (pspautotests net/adhoc/pair/gamemode).
auto member = std::find_if(gameModeMacs.begin(), gameModeMacs.end(), [&](const SceNetEtherAddr &m) { return IsMatch(m, replicaMac); });
if (member == gameModeMacs.end())
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_NOT_GAMEMODE_MEMBER, "not a member");
int replicaId = (int)(member - gameModeMacs.begin());
hleEatMicro(1000);
int maxid = 0;
auto it = std::find_if(replicaGameModeAreas.begin(), replicaGameModeAreas.end(),
[mac, &maxid](GameModeArea const& e) {
if (e.id > maxid) maxid = e.id;
return IsMatch(e.mac, mac);
[&](GameModeArea const& e) {
return IsMatch(e.mac, replicaMac);
});
// MAC address already existed!
if (it != replicaGameModeAreas.end()) {
WARN_LOG(Log::sceNet, "sceNetAdhocGameModeCreateReplica - [%s] is already existed (id: %d)", mac2str((SceNetEtherAddr*)mac).c_str(), it->id);
return it->id; // SCE_NET_ADHOC_ERROR_ALREADY_CREATED
}
// pspautotests net/adhoc/pair/gamemode
if (it != replicaGameModeAreas.end())
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_ALREADY_CREATED, "already created");
int ret = 0;
gameModeBuffSize = std::max(gameModeBuffSize, size);
@@ -6270,9 +6279,9 @@ static int sceNetAdhocGameModeCreateReplica(MIPSState *mips, const char *mac, u3
if (data) {
Memory::Memcpy(data, dataAddr, size);
//int sock = sceNetAdhocPdpCreate(mac, ADHOC_GAMEMODE_PORT, size, 0);
GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, 0, *(SceNetEtherAddr*)mac, data };
GameModeArea gma = { replicaId, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, 0, replicaMac, data };
replicaGameModeAreas.push_back(gma);
ret = gma.id; // Valid id for replica is higher than 0?
ret = gma.id;
// Block current thread to sync initial master data after Master and all Replicas have been created
if (masterGameModeArea.data != NULL && replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {