diff --git a/src/unlocker/spoofer.cpp b/src/unlocker/spoofer.cpp index 5943719..2f076c2 100644 --- a/src/unlocker/spoofer.cpp +++ b/src/unlocker/spoofer.cpp @@ -306,6 +306,95 @@ void Spoofer::onServerMessageList(std::string& body) _log->verbose("Spoofed message list"); #endif } +void Spoofer::onServerUpdateEntitlements(const std::string& url, std::string& body) +{ + if (!_spoofDLCs) return; + + glz::generic doc; + auto ec = glz::read_json(doc, body); + if (ec) + { + _log->error("JSON parse error for onClientUpdateEntitlements"); + return; + } + + if (doc.contains("entitlements") && doc["entitlements"].is_array()) + { + auto& entitlements = doc["entitlements"].get_array(); + + std::unordered_set* dlcList = nullptr; + + if (url.starts_with("https://grdk.live.bhvrdbd.com/")) + dlcList = &_dlcListGRDK; + else if (url.starts_with("https://egs.live.bhvrdbd.com/")) + dlcList = &_dlcListEGS; + else + return _log->error("Entitlement spoofing only works with Epic / Xbox"); + + for (const std::string& dlcId : *dlcList) + { + auto it = std::ranges::find_if( + entitlements, [&](const auto& item) { return item.is_string() && item.get_string() == dlcId; }); + if (it == entitlements.end()) entitlements.emplace_back(dlcId); + } + } + + ec = glz::write_json(doc, body); + if (ec) + { + _log->error("JSON write error for onClientUpdateEntitlements"); + return; + } + +#ifdef _DEBUG + _log->verbose("Spoofed entitlements (client)"); +#endif +} +void Spoofer::onClientUpdateEntitlements(const std::string& url, std::string& body) +{ + if (!_spoofDLCs) return; + + glz::generic doc; + auto ec = glz::read_json(doc, body); + if (ec) + { + _log->error("JSON parse error for onClientUpdateEntitlements"); + return; + } + + if (doc.contains("clientEntitlementIds") && doc["clientEntitlementIds"].is_array()) + { + auto& entitlements = doc["clientEntitlementIds"].get_array(); + + std::unordered_set* dlcList = nullptr; + + if (url.starts_with("https://grdk.live.bhvrdbd.com/")) + dlcList = &_dlcListGRDK; + else if (url.starts_with("https://egs.live.bhvrdbd.com/")) + dlcList = &_dlcListEGS; + else + return _log->error("Entitlement spoofing only works with Epic / Xbox"); + + for (const std::string& dlcId : *dlcList) + { + auto it = std::ranges::find_if( + entitlements, [&](const auto& item) { return item.is_string() && item.get_string() == dlcId; }); + if (it == entitlements.end()) entitlements.emplace_back(dlcId); + } + } + + ec = glz::write_json(doc, body); + if (ec) + { + _log->error("JSON write error for onClientUpdateEntitlements"); + return; + } + +#ifdef _DEBUG + _log->verbose("Spoofed entitlements (client)"); +#endif +} + /* proxy handlers */ @@ -317,6 +406,8 @@ void Spoofer::serverResponseHandler(const std::string& url, std::string& body, s std::lock_guard lock(_mutex); if (url.find("/api/v1/messages/listV2") != std::string::npos) return onServerMessageList(body); + if (url.find("/api/v1/owned-products/get-update-entitlements") != std::string::npos) + return onServerUpdateEntitlements(url, body); } void Spoofer::clientRequestHandler(const std::string& url, std::string& body, std::string& /*headers*/, bool /*wasBlocked*/) @@ -325,4 +416,6 @@ void Spoofer::clientRequestHandler(const std::string& url, std::string& body, st std::lock_guard lock(_mutex); + if (url.find("/api/v1/owned-products/get-update-entitlements") != std::string::npos) + return onClientUpdateEntitlements(url, body); }