From d9ee8cb538e3b7c96f30645448048bc1b29b5332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 5 Apr 2026 12:57:41 -0600 Subject: [PATCH 1/2] Fix json parsing bugs, add data.json for Relay Brasiliero --- Core/HLE/sceNetAdhoc.cpp | 5 +++++ UI/AdhocServerScreen.cpp | 39 +++++++++++++++++++++++++++++++-------- assets/adhoc-servers.json | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 6ccff286a5..30542c2b0e 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -154,6 +154,11 @@ static bool ParseServerListEntriesJSON(std::string_view json) { entry.description = server.getStringOr("description", ""); entry.mode = equals(server.getStringOr("data_mode", ""), "AemuPostoffice") ? AdhocDataMode::AemuPostoffice : AdhocDataMode::P2P; entry.dataJsonUrl = server.getStringOr("status_data_json", ""); + if (entry.dataJsonUrl.empty()) { + // This second field has a different name because it's more tolerant of int vs string in the json. + // Allowing these to get into old clients causes a crash. This will be removed after a while. + entry.dataJsonUrl = server.getStringOr("status_data_json_2", ""); + } entry.statusXmlUrl = server.getStringOr("status_xml", ""); entry.statusWebUrl = server.getStringOr("status_web", ""); diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index 7a09536941..066a8075d6 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -37,28 +37,51 @@ std::vector ParseDataJson(std::string_view json) { const auto& gamesArray = d["games"]; for (auto& g : gamesArray.GetArray()) { AdhocGame game; + if (!g.HasMember("name")) + continue; game.name = g["name"].GetString(); UpgradeGameName(&game.name); - // Handle string-to-int conversion for usercount - game.usercount = std::stoi(g["usercount"].GetString()); - + // Handle string-to-int conversion for usercount if needed. + if (g.HasMember("usercount")) { + if (g["usercount"].IsString()) { + game.usercount = std::stoi(g["usercount"].GetString()); + } else if (g["usercount"].IsInt()) { + game.usercount = g["usercount"].GetInt(); + } + } else { + game.usercount = 0; + } if (g.HasMember("groups")) { for (auto& grp : g["groups"].GetArray()) { AdhocGroup group; + if (!grp.HasMember("name")) + continue; group.name = grp["name"].GetString(); - group.usercount = std::stoi(grp["usercount"].GetString()); + if (grp.HasMember("usercount")) { + if (grp["usercount"].IsString()) { + group.usercount = std::stoi(grp["usercount"].GetString()); + } else if (grp["usercount"].IsInt()) { + group.usercount = grp["usercount"].GetInt(); + } + } else { + group.usercount = 0; + } if (grp.HasMember("users")) { for (auto& u : grp["users"].GetArray()) { AdhocUser user; user.name = u["name"].GetString(); - for (auto& p : u["pdp_ports"].GetArray()) - user.pdp_ports.push_back(p.GetInt()); + if (u.HasMember("pdp_ports")) { + for (auto& p : u["pdp_ports"].GetArray()) + user.pdp_ports.push_back(p.GetInt()); + } - for (auto& p : u["ptp_ports"].GetArray()) - user.ptp_ports.push_back(p.GetInt()); + if (u.HasMember("ptp_ports")) { + for (auto& p : u["ptp_ports"].GetArray()) + user.ptp_ports.push_back(p.GetInt()); + } group.users.push_back(user); } diff --git a/assets/adhoc-servers.json b/assets/adhoc-servers.json index a7e94b0dde..7f447fda4b 100644 --- a/assets/adhoc-servers.json +++ b/assets/adhoc-servers.json @@ -48,7 +48,7 @@ "location": "São Paulo Brazil", "description": "For players looking to play any games", "data_mode": "AemuPostoffice", - "status_xml": "https://jpa36a7.glddns.com/", + "status_data_json_2": "http://jpa36a7.glddns.com/data.json", "icon": "/favicon.ico" }, { From a20b26f02f494f82c3c417d65f8e112c315c9433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 5 Apr 2026 13:10:19 -0600 Subject: [PATCH 2/2] Even more robust json parsing --- UI/AdhocServerScreen.cpp | 79 +++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index 066a8075d6..ceb18a9214 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -1,4 +1,5 @@ #include +#include #include "ppsspp_config.h" #undef new @@ -11,6 +12,7 @@ #include "Common/Net/Resolve.h" #include "Common/UI/Root.h" #include "Common/UI/PopupScreens.h" +#include "Common/Data/Text/Parsers.h" #include "Common/StringUtils.h" #include "Common/Net/HTTPClient.h" #include "Core/HLE/sceNetAdhoc.h" @@ -26,61 +28,78 @@ static void UpgradeGameName(std::string *str) { } } +static int ParseUserCountValue(const rapidjson::Value &v) { + if (v.IsInt()) + return v.GetInt(); + else if (v.IsString()) { + int value = 0; + if (TryParse(v.GetString(), &value)) + return value; + } + return 0; +} + +static int ParsePortValue(const rapidjson::Value &v) { + if (v.IsInt()) + return v.GetInt(); + return -1; +} + std::vector ParseDataJson(std::string_view json) { rapidjson::Document d; d.Parse(json.data(), json.size()); std::vector gameList; - if (d.HasParseError() || !d.HasMember("games")) return gameList; + if (d.HasParseError() || !d.IsObject() || !d.HasMember("games") || !d["games"].IsArray()) + return gameList; const auto& gamesArray = d["games"]; for (auto& g : gamesArray.GetArray()) { + if (!g.IsObject()) + continue; + AdhocGame game; - if (!g.HasMember("name")) + if (!g.HasMember("name") || !g["name"].IsString()) continue; game.name = g["name"].GetString(); UpgradeGameName(&game.name); - // Handle string-to-int conversion for usercount if needed. - if (g.HasMember("usercount")) { - if (g["usercount"].IsString()) { - game.usercount = std::stoi(g["usercount"].GetString()); - } else if (g["usercount"].IsInt()) { - game.usercount = g["usercount"].GetInt(); - } - } else { - game.usercount = 0; - } - if (g.HasMember("groups")) { + game.usercount = g.HasMember("usercount") ? ParseUserCountValue(g["usercount"]) : 0; + + if (g.HasMember("groups") && g["groups"].IsArray()) { for (auto& grp : g["groups"].GetArray()) { + if (!grp.IsObject()) + continue; + AdhocGroup group; - if (!grp.HasMember("name")) + if (!grp.HasMember("name") || !grp["name"].IsString()) continue; group.name = grp["name"].GetString(); - if (grp.HasMember("usercount")) { - if (grp["usercount"].IsString()) { - group.usercount = std::stoi(grp["usercount"].GetString()); - } else if (grp["usercount"].IsInt()) { - group.usercount = grp["usercount"].GetInt(); - } - } else { - group.usercount = 0; - } + group.usercount = grp.HasMember("usercount") ? ParseUserCountValue(grp["usercount"]) : 0; - if (grp.HasMember("users")) { + if (grp.HasMember("users") && grp["users"].IsArray()) { for (auto& u : grp["users"].GetArray()) { + if (!u.IsObject() || !u.HasMember("name") || !u["name"].IsString()) + continue; + AdhocUser user; user.name = u["name"].GetString(); - if (u.HasMember("pdp_ports")) { - for (auto& p : u["pdp_ports"].GetArray()) - user.pdp_ports.push_back(p.GetInt()); + if (u.HasMember("pdp_ports") && u["pdp_ports"].IsArray()) { + for (auto& p : u["pdp_ports"].GetArray()) { + int port = ParsePortValue(p); + if (port >= 0) + user.pdp_ports.push_back(port); + } } - if (u.HasMember("ptp_ports")) { - for (auto& p : u["ptp_ports"].GetArray()) - user.ptp_ports.push_back(p.GetInt()); + if (u.HasMember("ptp_ports") && u["ptp_ports"].IsArray()) { + for (auto& p : u["ptp_ports"].GetArray()) { + int port = ParsePortValue(p); + if (port >= 0) + user.ptp_ports.push_back(port); + } } group.users.push_back(user);