diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index c0cdcf9825..63166da135 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -209,8 +209,10 @@ void MemSlabMap::DoState(PointerWrap &p) { Slab *old = first_; Slab *oldBulk = bulkStorage_; Do(p, count); - // Every slab takes up at least one byte. - if (count < 1 || !p.CheckRead(count)) { + // Every slab takes up at least 30 bytes in the file (even the oldest version: its section + // version, fields and tag), so a count from a broken file can't size a huge allocation: each + // takes over 150 in memory. + if (count < 1 || !p.CheckRead((size_t)count * 30)) { p.SetError(p.ERROR_FAILURE); count = 1; } diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index 89df3e8e44..c22263be6c 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -105,6 +105,16 @@ SOCKET SocketManager::GetHostSocketFromInetSocket(int sock) { return inetSockets_[sock].sock; } +void SocketManager::CloseAll(SocketState state) { + for (auto &sock : inetSockets_) { + if (sock.state == state) { + closesocket(sock.sock); + sock.state = SocketState::Unused; + sock.sock = 0; + } + } +} + void SocketManager::CloseAll() { for (auto &sock : inetSockets_) { if (sock.state != SocketState::Unused) { diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index 0a09efc4dc..a9e75f057f 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -42,6 +42,8 @@ public: SOCKET GetHostSocketFromInetSocket(int sock); bool Close(InetSocket *inetSocket); void CloseAll(); + // Only the sockets in this state (sceNetInet's, or proAdhoc's). + void CloseAll(SocketState state); // For debugger const InetSocket *Sockets() { diff --git a/Core/HLE/sceHttp.cpp b/Core/HLE/sceHttp.cpp index cf6dda49db..d385b01413 100644 --- a/Core/HLE/sceHttp.cpp +++ b/Core/HLE/sceHttp.cpp @@ -29,6 +29,8 @@ #include "Core/HLE/sceHttp.h" #include "Core/HLE/sceNet.h" #include "Core/Debugger/MemBlockInfo.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" #include "Common/StringUtils.h" #include "Common/LogReporting.h" #include "Common/Net/URL.h" @@ -308,6 +310,29 @@ void __HttpShutdown() { httpObjects.clear(); } +void __HttpDoState(PointerWrap &p) { + // Older states don't have it. + auto s = p.Section("sceHttp", 0, 1); + if (!s) + return; + + // The game remembers whether it initialized the library. + Do(p, httpInited); + Do(p, httpsInited); + Do(p, httpCacheInited); + if (p.mode == PointerWrap::MODE_READ) { + // Templates, connections and requests hold host connections, which can't be restored. The + // game's ids for them now fail as invalid, rather than reach objects from before the load. + std::lock_guard guard(httpLock); + for (const auto &it : httpObjects) { + if (it->className() == name_HTTPRequest) + (static_cast(it.get()))->abortRequest(); + } + httpObjects.clear(); + httpObjects.reserve(1); + } +} + // id: ID of the template or connection int sceHttpSetResolveRetry(int id, int retryCount) { if (id <= 0 || id > (int)httpObjects.size()) diff --git a/Core/HLE/sceHttp.h b/Core/HLE/sceHttp.h index 8c532bc2cc..22c4158e99 100644 --- a/Core/HLE/sceHttp.h +++ b/Core/HLE/sceHttp.h @@ -287,6 +287,7 @@ public: void __HttpInit(); +void __HttpDoState(PointerWrap &p); void __HttpShutdown(); void Register_sceHttp(); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index aaf5214cf2..b8d3b9b6c9 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -322,6 +322,7 @@ void __KernelDoState(PointerWrap &p) __DmacDoState(p); __UsbCamDoState(p); __NpDrmDoState(p); + __HttpDoState(p); // IMPORTANT! Add new sections last! } diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 2e6a22fee1..52196216fd 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -56,6 +56,7 @@ #include "Core/HLE/sceNp.h" #include "Core/HLE/sceNp2.h" #include "Core/HLE/sceNetInet.h" +#include "Core/HLE/SocketManager.h" #include "Core/HLE/sceNetResolver.h" #include "Core/HLE/NetAdhocCommon.h" @@ -712,10 +713,6 @@ void __NetDoState(PointerWrap &p) { if (!s) return; - auto cur_netInited = g_netInited; - auto cur_netInetInited = netInetInited; - auto cur_netApctlInited = g_netApctlInited; - Do(p, g_netInited); Do(p, netInetInited); Do(p, g_netApctlInited); @@ -778,10 +775,12 @@ void __NetDoState(PointerWrap &p) { } if (p.mode == p.MODE_READ) { - // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks - g_netApctlInited = cur_netApctlInited; - netInetInited = cur_netInetInited; - g_netInited = cur_netInited; + // The game remembers which libraries it initialized, so the "Inited" flags come from the state + // (sceNetAdhoc does the same). Keeping the current ones left a state saved after + // sceNetApctlInit, loaded into a fresh boot, without apctl, and an earlier state loaded + // mid-session with it already inited but no apctl thread. Host sockets can't be restored: + // close them, rather than leak ports the loaded game may bind again. + g_socketManager.CloseAll(SocketState::UsedNetInet); // Discard created resolvers for now (since i'm not sure whether the information in the struct is sufficient or not, and we don't support multi-threading yet anyway) __NetResolverShutdown(); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 0e21b8e807..8abc8670f1 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -333,6 +333,7 @@ void discardMatchingEvents(); void DoNetAdhocMatchingInited(PointerWrap &p); void DoNetAdhocMatchingThreads(PointerWrap &p); void ZeroNetAdhocMatchingThreads(); +void NetAdhocMatching_DeleteAllBeforeLoad(); bool __NetAdhocConnected() { return netAdhocInited && netAdhocctlInited && (adhocctlState == ADHOCCTL_STATE_CONNECTED || adhocctlState == ADHOCCTL_STATE_GAMEMODE); @@ -1868,6 +1869,12 @@ void __NetAdhocDoState(PointerWrap &p) { auto cur_netAdhocInited = netAdhocInited; auto cur_netAdhocctlInited = netAdhocctlInited; + if (p.mode == p.MODE_READ) { + // Matching contexts aren't saved, and their host threads use sockets deleted below. Before + // the matching thread ids are loaded over. + NetAdhocMatching_DeleteAllBeforeLoad(); + } + Do(p, netAdhocInited); Do(p, netAdhocctlInited); DoNetAdhocMatchingInited(p); diff --git a/Core/HLE/sceNetAdhocMatching.cpp b/Core/HLE/sceNetAdhocMatching.cpp index b28bad96d3..d03375cf82 100644 --- a/Core/HLE/sceNetAdhocMatching.cpp +++ b/Core/HLE/sceNetAdhocMatching.cpp @@ -1708,24 +1708,33 @@ int sceNetAdhocMatchingInit(u32 memsize) { return 0; } +static void DeleteAllMatchingContexts() { + SceNetAdhocMatchingContext *next = NULL; + SceNetAdhocMatchingContext *context = contexts; + while (context != NULL) { + next = context->next; + NetAdhocMatching_Delete(context->id); + context = next; + } + contexts = NULL; +} + int NetAdhocMatching_Term() { if (netAdhocMatchingInited) { - // Delete all Matching contexts - SceNetAdhocMatchingContext* next = NULL; - SceNetAdhocMatchingContext* context = contexts; - while (context != NULL) { - next = context->next; - //if (context->running) NetAdhocMatching_Stop(context->id); - NetAdhocMatching_Delete(context->id); - context = next; - } - contexts = NULL; + DeleteAllMatchingContexts(); matchingThreads.clear(); } return 0; } +void NetAdhocMatching_DeleteAllBeforeLoad() { + // Their fake PSP threads' ids are from the kernel before the load, and the kernel is the loaded + // one now: zeroed, stopping a context leaves the threads alone, and only the host side goes. + ZeroNetAdhocMatchingThreads(); + DeleteAllMatchingContexts(); +} + int sceNetAdhocMatchingTerm() { WARN_LOG(Log::sceNet, "UNTESTED sceNetAdhocMatchingTerm() at %08x", currentMIPS->pc); // Should we cleanup all created matching contexts first? just in case there are games that doesn't delete them before calling this diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 14e6b3b324..be2552fb3d 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -998,6 +998,10 @@ void DoState(PointerWrap &p) { Do(p, data_size); if (p.mode == PointerWrap::MODE_READ) { WARN_LOG(Log::Achievements, "Save state contained achievement data, but achievements are not active. Ignore."); + // The size is straight from the file: skipping it unchecked can wrap the pointer on 32-bit. + if (!p.CheckRead(data_size)) { + return; + } } p.SkipBytes(data_size); return;