This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
#include "spoofing.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <minwindef.h>
|
||||
|
||||
#include <nerutils/log.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
std::string getExeDir()
|
||||
{
|
||||
char buffer[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, buffer, MAX_PATH);
|
||||
std::string path(buffer);
|
||||
size_t pos = path.find_last_of("\\/");
|
||||
if (pos != std::string::npos) return path.substr(0, pos + 1);
|
||||
return "";
|
||||
}
|
||||
|
||||
void Spoofer::init(Proxy* proxy)
|
||||
{
|
||||
loadData();
|
||||
registerListeners(proxy);
|
||||
}
|
||||
|
||||
void Spoofer::registerListeners(Proxy* proxy)
|
||||
{
|
||||
proxy->OnServerResponse.addListener([this](const std::string& url, std::string& body, std::string& respHeaders) {
|
||||
this->serverResponseHandler(url, body, respHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
void Spoofer::loadData()
|
||||
{
|
||||
Log::info("Loading dump data");
|
||||
|
||||
std::string catalogDumpPath = getExeDir() + "catalog.json";
|
||||
std::ifstream catalogFile(catalogDumpPath);
|
||||
if (catalogFile.is_open())
|
||||
{
|
||||
std::stringstream buffer;
|
||||
buffer << catalogFile.rdbuf();
|
||||
parseCatalog(buffer.str());
|
||||
}
|
||||
else
|
||||
Log::warning("Missing catalog.json");
|
||||
|
||||
std::string itemDumpPath = getExeDir() + "items.json";
|
||||
std::ifstream itemFile(itemDumpPath);
|
||||
if (itemFile.is_open())
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string content((std::istreambuf_iterator<char>(itemFile)), std::istreambuf_iterator<char>());
|
||||
json doc = json::parse(content);
|
||||
|
||||
if (doc.contains("Camper") && doc["Camper"].is_object())
|
||||
if (doc["Camper"].contains("Items") && doc["Camper"]["Items"].is_array())
|
||||
for (const auto& item : doc["Camper"]["Items"])
|
||||
if (item.is_string()) _camperItemIds.insert(item.get<std::string>());
|
||||
|
||||
if (doc.contains("Slasher") && doc["Slasher"].is_object())
|
||||
if (doc["Slasher"].contains("Powers") && doc["Slasher"]["Powers"].is_array())
|
||||
for (const auto& item : doc["Slasher"]["Powers"])
|
||||
if (item.is_string()) _slasherPowerIds.insert(item.get<std::string>());
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", "items.json", e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
Log::warning("Missing items.json");
|
||||
|
||||
std::string offeringDumpPath = getExeDir() + "offerings.json";
|
||||
std::ifstream offeringsFile(offeringDumpPath);
|
||||
if (offeringsFile.is_open())
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string content((std::istreambuf_iterator<char>(offeringsFile)), std::istreambuf_iterator<char>());
|
||||
json doc = json::parse(content);
|
||||
|
||||
if (doc.contains("Slashers") && doc["Slashers"].is_array())
|
||||
for (const auto& offering : doc["Slashers"])
|
||||
if (offering.is_string()) _slasherOfferingIds.insert(offering.get<std::string>());
|
||||
|
||||
if (doc.contains("Campers") && doc["Campers"].is_array())
|
||||
for (const auto& offering : doc["Campers"])
|
||||
if (offering.is_string()) _camperOfferingIds.insert(offering.get<std::string>());
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", "offerings.json", e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
Log::warning("Missing offerings.json");
|
||||
|
||||
std::string addonDumpPath = getExeDir() + "addons.json";
|
||||
std::ifstream addonFile(addonDumpPath);
|
||||
if (addonFile.is_open())
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string content((std::istreambuf_iterator<char>(addonFile)), std::istreambuf_iterator<char>());
|
||||
json doc = json::parse(content);
|
||||
|
||||
if (doc.contains("Slashers") && doc["Slashers"].is_array())
|
||||
for (const auto& item : doc["Slashers"])
|
||||
if (item.is_string()) _slasherAddonIds.insert(item.get<std::string>());
|
||||
|
||||
if (doc.contains("Campers") && doc["Campers"].is_array())
|
||||
for (const auto& power : doc["Campers"])
|
||||
if (power.is_string()) _camperAddonIds.insert(power.get<std::string>());
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", "addons.json", e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
Log::warning("Missing addons.json");
|
||||
|
||||
try
|
||||
{
|
||||
std::string perkDumpPaths = getExeDir() + "perks.json";
|
||||
std::ifstream perkFile(perkDumpPaths);
|
||||
if (perkFile.is_open())
|
||||
{
|
||||
std::string content((std::istreambuf_iterator<char>(perkFile)), std::istreambuf_iterator<char>());
|
||||
json doc = json::parse(content);
|
||||
|
||||
if (doc.contains("Slashers") && doc["Slashers"].is_array())
|
||||
for (const auto& item : doc["Slashers"])
|
||||
if (item.is_string()) _slasherPerkIds.insert(item.get<std::string>());
|
||||
|
||||
if (doc.contains("Campers") && doc["Campers"].is_array())
|
||||
for (const auto& power : doc["Campers"])
|
||||
if (power.is_string()) _camperPerkIds.insert(power.get<std::string>());
|
||||
}
|
||||
else
|
||||
Log::warning("Missing perks.json");
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", "perks.json", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info("Loaded: Camper items={}, Slasher powers={}, "
|
||||
"Slasher addons={}, Camper addons={}, Camper perks={}, Slasher perks={}"
|
||||
"Slasher offerings={}, Camper offerings={}",
|
||||
_camperItemIds.size(), _slasherPowerIds.size(), _slasherAddonIds.size(), _camperAddonIds.size(),
|
||||
_camperPerkIds.size(), _slasherPerkIds.size(), _slasherOfferingIds.size(), _camperOfferingIds.size());
|
||||
}
|
||||
|
||||
void Spoofer::parseCatalog(std::string data)
|
||||
{
|
||||
try
|
||||
{
|
||||
json doc = json::parse(data);
|
||||
|
||||
if (doc.contains("data")) doc = doc["data"];
|
||||
|
||||
if (doc.contains("item") && doc["item"].contains("items") && doc["item"]["items"].is_array())
|
||||
for (const auto& item : doc["item"]["items"])
|
||||
if (item.is_string()) _catalogItemIds.insert(item.get<std::string>());
|
||||
|
||||
if (doc.contains("outfit") && doc["outfit"].contains("items") && doc["outfit"]["items"].is_array())
|
||||
for (const auto& item : doc["outfit"]["items"])
|
||||
if (item.is_string()) _catalogOutfitIds.insert(item.get<std::string>());
|
||||
|
||||
Log::info("Parsed {} items and {} outfits from catalog", _catalogItemIds.size(), _catalogOutfitIds.size());
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", "parseCatalog", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void Spoofer::parseAndDumpCatalog(std::string& data)
|
||||
{
|
||||
std::string path = getExeDir() + "catalog.json";
|
||||
std::ofstream file(path);
|
||||
file << data;
|
||||
file.close();
|
||||
Log::info("Raw catalog saved to {}", path);
|
||||
|
||||
parseCatalog(data);
|
||||
}
|
||||
|
||||
void Spoofer::modifyCharacterData(json& js)
|
||||
{
|
||||
std::unordered_set<std::string> existingItemIds;
|
||||
bool isSlasher = false;
|
||||
|
||||
if (js.contains("characterName") && js["characterName"].is_string())
|
||||
{
|
||||
std::string name = js["characterName"];
|
||||
if (name == "Bear" || name == "Bob" || name == "Chuckles" || (name.length() >= 2 && name[0] == 'K'))
|
||||
isSlasher = true;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> stackableIds;
|
||||
stackableIds.insert(_camperItemIds.begin(), _camperItemIds.end());
|
||||
stackableIds.insert(_camperOfferingIds.begin(), _camperOfferingIds.end());
|
||||
stackableIds.insert(_camperAddonIds.begin(), _camperAddonIds.end());
|
||||
stackableIds.insert(_slasherAddonIds.begin(), _slasherAddonIds.end());
|
||||
|
||||
if (js.contains("characterItems") && js["characterItems"].is_array())
|
||||
{
|
||||
for (auto& item : js["characterItems"])
|
||||
{
|
||||
if (item.contains("itemId") && item["itemId"].is_string())
|
||||
{
|
||||
std::string itemId = item["itemId"];
|
||||
existingItemIds.insert(itemId);
|
||||
|
||||
if (_slasherPowerIds.find(itemId) != _slasherPowerIds.end())
|
||||
isSlasher = true;
|
||||
else if (stackableIds.contains(itemId))
|
||||
item["quantity"] = 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSlasher)
|
||||
{
|
||||
for (const std::string& itemId : _camperItemIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 100}});
|
||||
for (const std::string& itemId : _camperAddonIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 100}});
|
||||
for (const std::string& itemId : _camperOfferingIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 100}});
|
||||
for (const std::string& itemId : _camperPerkIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 3}});
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const std::string& itemId : _slasherAddonIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 100}});
|
||||
for (const std::string& itemId : _slasherOfferingIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 100}});
|
||||
for (const std::string& itemId : _slasherPerkIds)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back({{"itemId", itemId}, {"quantity", 3}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spoofer::serverResponseHandler(const std::string& url, std::string& body, std::string& /*respHeaders*/)
|
||||
{
|
||||
if (url.find("bhvrdbd.com") != std::string::npos) Log::verbose("BHVR api res @ {}", url);
|
||||
|
||||
if (url.find("api/v1/extensions/store/getCatalogItems") != std::string::npos) return parseAndDumpCatalog(body);
|
||||
|
||||
if (url.find("api/v1/dbd-inventories/all") != std::string::npos)
|
||||
{
|
||||
try
|
||||
{
|
||||
json doc = json::parse(body);
|
||||
auto& itemsArr = doc["inventoryItems"];
|
||||
|
||||
std::unordered_set<std::string> foundObjects;
|
||||
std::unordered_set<std::string> foundAddons;
|
||||
std::unordered_set<std::string> foundPerks;
|
||||
std::unordered_set<std::string> foundOfferings;
|
||||
std::unordered_set<std::string> foundCatalogItems;
|
||||
|
||||
//std::unordered_set<std::string> objectIds;
|
||||
//objectIds.insert(_camperItemIds.begin(), _camperItemIds.end());
|
||||
//objectIds.insert(_slasherPowerIds.begin(), _slasherPowerIds.end());
|
||||
|
||||
//std::unordered_set<std::string> addonIds;
|
||||
//addonIds.insert(_camperAddonIds.begin(), _camperAddonIds.end());
|
||||
//addonIds.insert(_slasherAddonIds.begin(), _slasherAddonIds.end());
|
||||
|
||||
std::unordered_set<std::string> offeringIds;
|
||||
offeringIds.insert(_camperOfferingIds.begin(), _camperOfferingIds.end());
|
||||
offeringIds.insert(_slasherOfferingIds.begin(), _slasherOfferingIds.end());
|
||||
|
||||
std::unordered_set<std::string> catalogIds;
|
||||
catalogIds.insert(_catalogOutfitIds.begin(), _catalogOutfitIds.end());
|
||||
catalogIds.insert(_catalogItemIds.begin(), _catalogItemIds.end());
|
||||
|
||||
std::unordered_set<std::string> perkIds;
|
||||
perkIds.insert(_slasherPerkIds.begin(), _slasherPerkIds.end());
|
||||
perkIds.insert(_camperPerkIds.begin(), _camperPerkIds.end());
|
||||
|
||||
for (auto& item : itemsArr)
|
||||
{
|
||||
std::string objectId = item["objectId"];
|
||||
|
||||
/*if (objectIds.find(objectId) != objectIds.end())
|
||||
{
|
||||
foundObjects.insert(objectId);
|
||||
item["quantity"] = 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (addonIds.find(objectId) != addonIds.end())
|
||||
{
|
||||
foundAddons.insert(objectId);
|
||||
item["quantity"] = 100;
|
||||
continue;
|
||||
}*/
|
||||
|
||||
if (perkIds.find(objectId) != perkIds.end())
|
||||
{
|
||||
foundPerks.insert(objectId);
|
||||
item["quantity"] = 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offeringIds.find(objectId) != offeringIds.end())
|
||||
{
|
||||
foundOfferings.insert(objectId);
|
||||
item["quantity"] = 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (catalogIds.find(objectId) != catalogIds.end())
|
||||
{
|
||||
foundCatalogItems.insert(objectId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*for (const std::string& id : objectIds)
|
||||
{
|
||||
if (foundObjects.find(id) == foundObjects.end())
|
||||
{
|
||||
itemsArr.push_back({
|
||||
{"objectId", id},
|
||||
{"quantity", 100},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& id : addonIds)
|
||||
{
|
||||
if (foundAddons.find(id) == foundAddons.end())
|
||||
{
|
||||
itemsArr.push_back({
|
||||
{"objectId", id},
|
||||
{"quantity", 100},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
for (const std::string& id : perkIds)
|
||||
{
|
||||
if (foundPerks.find(id) == foundPerks.end())
|
||||
{
|
||||
itemsArr.push_back({
|
||||
{"objectId", id},
|
||||
{"quantity", 3},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& id : offeringIds)
|
||||
{
|
||||
if (foundOfferings.find(id) == foundOfferings.end())
|
||||
{
|
||||
itemsArr.push_back({
|
||||
{"objectId", id},
|
||||
{"quantity", 100},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& id : catalogIds)
|
||||
{
|
||||
if (foundCatalogItems.find(id) == foundCatalogItems.end())
|
||||
{
|
||||
itemsArr.push_back({
|
||||
{"objectId", id},
|
||||
{"quantity", 1},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::string updatedJson = doc.dump();
|
||||
body = updatedJson;
|
||||
|
||||
Log::verbose("Inventory updated: Items={}, Addons={}, Perks={}, Offerings={}", foundObjects.size(),
|
||||
foundAddons.size(), foundPerks.size(), foundOfferings.size());
|
||||
|
||||
return;
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", url, e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.find("api/v1/dbd-character-data/get-all") != std::string::npos)
|
||||
{
|
||||
try
|
||||
{
|
||||
json doc = json::parse(body);
|
||||
auto& charList = doc["list"];
|
||||
|
||||
for (auto& charInfo : charList)
|
||||
{
|
||||
charInfo["isEntitled"] = true;
|
||||
if (charInfo.contains("bloodWebLevel") && charInfo["bloodWebLevel"] <= 1)
|
||||
{
|
||||
charInfo["bloodWebLevel"] = 15;
|
||||
if (charInfo["bloodWebData"].contains("level")) charInfo["bloodWebData"]["level"] = 1;
|
||||
}
|
||||
|
||||
modifyCharacterData(charInfo);
|
||||
}
|
||||
body = doc.dump();
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", url, e.what());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.find("api/v1/dbd-character-data/") != std::string::npos)
|
||||
{
|
||||
try
|
||||
{
|
||||
json doc = json::parse(body);
|
||||
modifyCharacterData(doc);
|
||||
body = doc.dump();
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
Log::error("JSON parse error in {}: {}", url, e.what());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user