From 2bd79838bfb278ebd8ef0dc82f8d088ebadf8523 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 10:36:11 +0700 Subject: [PATCH 1/7] Fix invalid argument issue of sceNetAdhocPtpAccept on Bomberman Panic Bomber. --- Core/HLE/sceNetAdhoc.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index b6528489ba..d60faa3b14 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -3465,9 +3465,11 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Set Connection State internal->data.ptp.state = ADHOC_PTP_STATE_ESTABLISHED; - // Return Peer Address Information - *addr = internal->data.ptp.paddr; - if (port != NULL) *port = internal->data.ptp.pport; + // Return Peer Address & Port Information + if (addr != NULL) + *addr = internal->data.ptp.paddr; + if (port != NULL) + *port = internal->data.ptp.pport; // Link PTP Socket adhocSockets[i] = internal; @@ -3528,8 +3530,8 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Library is initialized if (netAdhocInited) { - // Valid Arguments - if (addr != NULL) { //GTA:VCS seems to use 0 for the portPtr + // TODO: Validate Arguments. GTA:VCS seems to use 0/null for the peerPortPtr, and Bomberman Panic Bomber is using null/0 on both peerMacAddrPtr & peerPortPtr, so i guess it's optional. + if (true) { // FIXME: Not sure what kind of arguments considered as invalid (need to be tested on a homebrew), might be the flag? // Valid Socket if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) { // Cast Socket From 9efdaf9ba7946e54c4c9bae21528773fbed35bcf Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 10:51:43 +0700 Subject: [PATCH 2/7] Updated sceNetAdhocGameModeUpdateReplica (Fix Bomberman Panic Bomber) --- Core/HLE/sceNetAdhoc.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index d60faa3b14..0962c6f79a 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4444,18 +4444,25 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { if (it == replicaGameModeAreas.end()) return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created"); + // Bomberman Panic Bomber is using 0/null on infoAddr, so i guess it's optional. + GameModeUpdateInfo* gmuinfo = NULL; + if (Memory::IsValidAddress(infoAddr)) { + gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr); + } + for (auto gma : replicaGameModeAreas) { if (gma.id == id) { - if (Memory::IsValidAddress(infoAddr)) { - GameModeUpdateInfo* gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr); - gmuinfo->length = sizeof(GameModeUpdateInfo); - if (gma.data && gma.dataUpdated) { - Memory::Memcpy(gma.addr, gma.data, gma.size); - gma.dataUpdated = 0; + if (gma.data && gma.dataUpdated) { + Memory::Memcpy(gma.addr, gma.data, gma.size); + gma.dataUpdated = 0; + if (gmuinfo != NULL) { + gmuinfo->length = sizeof(GameModeUpdateInfo); gmuinfo->updated = 1; - gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); + gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); } - else { + } + else { + if (gmuinfo != NULL) { gmuinfo->updated = 0; } } From 4d0c7385ece225cd1afabfd80bbd33fab32b5c7d Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 13:03:58 +0700 Subject: [PATCH 3/7] Fix an issue where sceNetAdhocGameModeCreateReplica blocked current thread for initial sync while Master data hasn't been created yet. --- Core/HLE/sceNetAdhoc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 0962c6f79a..b9054e7898 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4365,7 +4365,7 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s ret = gma.id; // Valid id for replica is higher than 0? // Block current thread to sync initial master data after Master and all Replicas have been created - if (replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) { + if (masterGameModeArea.data != NULL && replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) { if (CoreTiming::IsScheduled(gameModeNotifyEvent)) { __KernelWaitCurThread(WAITTYPE_NET, GAMEMODE_WAITID, ret, 0, false, "syncing master data"); DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread()); From 73dff0566edafc2cd93f3000cb8675ddcbe75859 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 14:02:33 +0700 Subject: [PATCH 4/7] Fix an issue where more than one GameMode Scheduler can be scheduled. --- Core/HLE/sceNetAdhoc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index b9054e7898..63b57a21d7 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -440,6 +440,10 @@ int ScheduleAdhocctlState(int event, int newState, int usec, const char* reason) int StartGameModeScheduler() { INFO_LOG(SCENET, "Initiating GameMode Scheduler"); + if (CoreTiming::IsScheduled(gameModeNotifyEvent)) { + WARN_LOG(SCENET, "GameMode Scheduler is already running!"); + return -1; + } u64 param = ((u64)__KernelGetCurThread()) << 32; CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_INIT_DELAY), gameModeNotifyEvent, param); return 0; From 2d5699d62b7acfcd1186d2f7731e97d2cbd39b0a Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 14:06:51 +0700 Subject: [PATCH 5/7] Prevent creating another GameMode Master, since we don't support more than one Master data, and there is no ID related to Master. --- Core/HLE/sceNetAdhoc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 63b57a21d7..8e350a7b90 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4297,6 +4297,9 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) { if (size < 0 || !Memory::IsValidAddress(dataAddr)) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); + if (masterGameModeArea.data) + return hleLogError(SCENET, ERROR_NET_ADHOC_ALREADY_CREATED, "already created"); // FIXME: Should we return a success instead? (need to test this on a homebrew) + hleEatMicro(1000); SceNetEtherAddr localMac; getLocalMac(&localMac); From 4b6431f7fe5dbad5d8b80aec5ae529b63edb264f Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 15:35:16 +0700 Subject: [PATCH 6/7] GameMode Scheduler should only runs when there are active Master. --- Core/HLE/sceNetAdhoc.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 8e350a7b90..cb6125b663 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -151,11 +151,12 @@ void __NetAdhocShutdown() { } bool IsGameModeActive() { - return netAdhocGameModeEntered; + return netAdhocGameModeEntered && masterGameModeArea.data; } static void __GameModeNotify(u64 userdata, int cyclesLate) { SceUID threadID = userdata >> 32; + u32 error; if (IsGameModeActive()) { // Need to make sure all replicas have been created before we start syncing data @@ -182,6 +183,10 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { auto sock = adhocSockets[gameModeSocket - 1]; if (!sock) { WARN_LOG(SCENET, "GameMode: Socket (%d) got deleted", gameModeSocket); + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + __KernelResumeThreadFromWait(threadID, waitVal); + } return; } @@ -209,7 +214,6 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { } // Need to sync (send + recv) all players initial data (data from CreateMaster) after Master + All Replicas are created, and before the first UpdateMaster / UpdateReplica is called for Star Wars The Force Unleashed to show the correct players color on minimap (also prevent Starting issue on other GameMode games) else { - u32 error; SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); if (error == 0 && waitID == GAMEMODE_WAITID) { // Resume thread after all replicas data have been received @@ -293,6 +297,11 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { return; } INFO_LOG(SCENET, "GameMode Scheduler (%d, %d) has ended", gameModeSocket, gameModeBuffSize); + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + DEBUG_LOG(SCENET, "GameMode: Resuming Thread %d after Master Deleted (Result = %08x)", threadID, waitVal); + __KernelResumeThreadFromWait(threadID, waitVal); + } } static void __AdhocctlNotify(u64 userdata, int cyclesLate) { @@ -4409,8 +4418,14 @@ static int sceNetAdhocGameModeUpdateMaster() { } int NetAdhocGameMode_DeleteMaster() { + if (CoreTiming::IsScheduled(gameModeNotifyEvent)) { + __KernelWaitCurThread(WAITTYPE_NET, GAMEMODE_WAITID, 0, 0, false, "deleting master data"); + DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to End GameMode Scheduler", __KernelGetCurThread()); + } + if (masterGameModeArea.data) { free(masterGameModeArea.data); + masterGameModeArea.data = nullptr; } //NetAdhocPdp_Delete(masterGameModeArea.socket, 0); gameModePeerPorts.erase(masterGameModeArea.mac); From d5036ff2aee3702d2ec6098b5bf97bb4023bc345 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 13 Jan 2022 16:13:53 +0700 Subject: [PATCH 7/7] Fix buffer overflow issue on GameMode. --- Core/HLE/sceNetAdhoc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index cb6125b663..12bbe54752 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4313,6 +4313,9 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) { SceNetEtherAddr localMac; getLocalMac(&localMac); gameModeBuffSize = std::max(gameModeBuffSize, size); + u8* buf = (u8*)realloc(gameModeBuffer, gameModeBuffSize); + if (buf) + gameModeBuffer = buf; u8* data = (u8*)malloc(size); if (data) { @@ -4371,6 +4374,9 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s int ret = 0; gameModeBuffSize = std::max(gameModeBuffSize, size); + u8* buf = (u8*)realloc(gameModeBuffer, gameModeBuffSize); + if (buf) + gameModeBuffer = buf; u8* data = (u8*)malloc(size); if (data) {