2016-01-12 19:29:22 -05:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
2016-06-08 11:28:58 -04:00
|
|
|
Utils::Memory::Allocator StructuredData::MemAllocator;
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
const char* StructuredData::EnumTranslation[ENUM_MAX] =
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
"features",
|
|
|
|
"weapons",
|
|
|
|
"attachements",
|
|
|
|
"challenges",
|
|
|
|
"camos",
|
|
|
|
"perks",
|
|
|
|
"killstreaks",
|
|
|
|
"accolades",
|
|
|
|
"cardicons",
|
|
|
|
"cardtitles",
|
|
|
|
"cardnameplates",
|
|
|
|
"teams",
|
|
|
|
"gametypes"
|
|
|
|
};
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-05-25 19:39:33 -04:00
|
|
|
void StructuredData::PatchPlayerDataEnum(Game::StructuredDataDef* data, StructuredData::PlayerDataType type, std::vector<std::string>& entries)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
if (!data || type >= StructuredData::PlayerDataType::ENUM_MAX) return;
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-05-25 19:39:33 -04:00
|
|
|
Game::StructuredDataEnum* dataEnum = &data->enums[type];
|
2016-02-06 07:37:23 -05:00
|
|
|
|
|
|
|
// Find last index so we can add our offset to it.
|
|
|
|
// This whole procedure is potentially unsafe.
|
|
|
|
// If any index changes, everything gets shifted and the stats are fucked.
|
|
|
|
int lastIndex = 0;
|
2016-02-09 19:22:00 -05:00
|
|
|
for (int i = 0; i < dataEnum->numIndices; ++i)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
|
|
|
if (dataEnum->indices[i].index > lastIndex)
|
|
|
|
{
|
|
|
|
lastIndex = dataEnum->indices[i].index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate new count
|
2016-05-26 07:13:02 -04:00
|
|
|
unsigned int indexCount = dataEnum->numIndices + entries.size();
|
2016-02-06 07:37:23 -05:00
|
|
|
|
|
|
|
// Allocate new entries
|
2016-06-08 11:28:58 -04:00
|
|
|
Game::StructuredDataEnumEntry* indices = StructuredData::MemAllocator.AllocateArray<Game::StructuredDataEnumEntry>(indexCount);
|
2016-05-26 07:13:02 -04:00
|
|
|
memcpy(indices, dataEnum->indices, sizeof(Game::StructuredDataEnumEntry) * dataEnum->numIndices);
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-02-09 19:22:00 -05:00
|
|
|
for (unsigned int i = 0; i < entries.size(); ++i)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
|
|
|
unsigned int pos = 0;
|
|
|
|
|
|
|
|
for (; pos < (dataEnum->numIndices + i); pos++)
|
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
if (indices[pos].key == entries[i])
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-25 19:39:33 -04:00
|
|
|
Logger::Error("Duplicate playerdatadef entry found: %s", entries[i].data());
|
2016-02-06 07:37:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// We found our position
|
2016-05-26 07:13:02 -04:00
|
|
|
if (entries[i] < indices[pos].key)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// TODO directly shift the data using memmove
|
|
|
|
for (unsigned int j = dataEnum->numIndices + i; j > pos && j < indexCount; j--)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
memcpy(&indices[j], &indices[j - 1], sizeof(Game::StructuredDataEnumEntry));
|
2016-02-06 07:37:23 -05:00
|
|
|
}
|
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
indices[pos].index = i + lastIndex;
|
2016-06-08 11:28:58 -04:00
|
|
|
indices[pos].key = StructuredData::MemAllocator.DuplicateString(entries[i]);
|
2016-02-06 07:37:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply our patches
|
2016-05-26 07:13:02 -04:00
|
|
|
dataEnum->numIndices = indexCount;
|
|
|
|
dataEnum->indices = indices;
|
2016-02-06 07:37:23 -05:00
|
|
|
}
|
|
|
|
|
2016-01-12 19:29:22 -05:00
|
|
|
StructuredData::StructuredData()
|
|
|
|
{
|
2016-03-04 11:02:00 -05:00
|
|
|
// Only execute this when building zones
|
|
|
|
if (!ZoneBuilder::IsEnabled()) return;
|
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
AssetHandler::OnLoad([] (Game::XAssetType type, Game::XAssetHeader asset, std::string filename, bool* restrict)
|
|
|
|
{
|
|
|
|
// Only intercept playerdatadef loading
|
|
|
|
if (filename != "mp/playerdata.def" || type != Game::XAssetType::ASSET_TYPE_STRUCTUREDDATADEF) return;
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// Store asset
|
|
|
|
Game::StructuredDataDefSet* data = asset.structuredData;
|
|
|
|
if (!data) return;
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
if (data->count != 1)
|
|
|
|
{
|
|
|
|
Logger::Error("PlayerDataDefSet contains more than 1 definition!");
|
|
|
|
return;
|
|
|
|
}
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
if (data->data[0].version != 155)
|
|
|
|
{
|
|
|
|
Logger::Error("Initial PlayerDataDef is not version 155, patching not possible!");
|
|
|
|
return;
|
|
|
|
}
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
std::map<int, std::vector<std::vector<std::string>>> patchDefinitions;
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// First check if all versions are present
|
|
|
|
for (int i = 156;; ++i)
|
|
|
|
{
|
|
|
|
FileSystem::File definition(Utils::VA("%s/%d.json", filename.data(), i));
|
|
|
|
if (!definition.Exists()) break;
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
std::vector<std::vector<std::string>> enumContainer;
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
std::string errors;
|
|
|
|
json11::Json defData = json11::Json::parse(definition.GetBuffer(), errors);
|
2016-02-06 07:37:23 -05:00
|
|
|
|
2016-05-26 07:33:39 -04:00
|
|
|
if (!errors.empty())
|
|
|
|
{
|
|
|
|
Logger::Error("Parsing patch file '%s' for PlayerDataDef version %d failed: %s", definition.GetName().data(), i, errors.data());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!defData.is_object())
|
|
|
|
{
|
|
|
|
Logger::Error("PlayerDataDef patch for version %d is invalid!", i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
for (unsigned int pType = 0; pType < StructuredData::PlayerDataType::ENUM_MAX; ++pType)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
auto enumData = defData[StructuredData::EnumTranslation[pType]];
|
|
|
|
|
|
|
|
std::vector<std::string> entryData;
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
if (enumData.is_array())
|
|
|
|
{
|
|
|
|
for (auto rawEntry : enumData.array_items())
|
2016-05-25 19:39:33 -04:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
if (rawEntry.is_string())
|
2016-05-25 19:39:33 -04:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
entryData.push_back(rawEntry.string_value());
|
2016-05-25 19:39:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
enumContainer.push_back(entryData);
|
|
|
|
}
|
|
|
|
|
|
|
|
patchDefinitions[i] = enumContainer;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing to patch
|
|
|
|
if (patchDefinitions.empty()) return;
|
|
|
|
|
|
|
|
// Reallocate the definition
|
2016-06-08 11:28:58 -04:00
|
|
|
Game::StructuredDataDef* newData = StructuredData::MemAllocator.AllocateArray<Game::StructuredDataDef>(data->count + patchDefinitions.size());
|
2016-05-26 07:13:02 -04:00
|
|
|
memcpy(&newData[patchDefinitions.size()], data->data, sizeof Game::StructuredDataDef * data->count);
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// Prepare the buffers
|
|
|
|
for (unsigned int i = 0; i < patchDefinitions.size(); ++i)
|
|
|
|
{
|
|
|
|
memcpy(&newData[i], data->data, sizeof Game::StructuredDataDef);
|
|
|
|
newData[i].version = (patchDefinitions.size() - i) + 155;
|
|
|
|
|
|
|
|
// Reallocate the enum array
|
2016-06-08 11:28:58 -04:00
|
|
|
Game::StructuredDataEnum* newEnums = StructuredData::MemAllocator.AllocateArray<Game::StructuredDataEnum>(data->data->numEnums);
|
2016-05-26 07:13:02 -04:00
|
|
|
memcpy(newEnums, data->data->enums, sizeof Game::StructuredDataEnum * data->data->numEnums);
|
|
|
|
newData[i].enums = newEnums;
|
|
|
|
}
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// Apply new data
|
|
|
|
data->data = newData;
|
|
|
|
data->count += patchDefinitions.size();
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// Patch the definition
|
|
|
|
for (int i = 0; i < data->count; ++i)
|
|
|
|
{
|
|
|
|
// No need to patch version 155
|
|
|
|
if (newData[i].version == 155) continue;
|
|
|
|
|
|
|
|
if(patchDefinitions.find(newData[i].version) != patchDefinitions.end())
|
|
|
|
{
|
|
|
|
auto patchData = patchDefinitions[newData[i].version];
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// Invalid patch data
|
2016-05-26 07:33:39 -04:00
|
|
|
if (patchData.size() != StructuredData::PlayerDataType::ENUM_MAX)
|
|
|
|
{
|
|
|
|
Logger::Error("PlayerDataDef patch for version %d wasn't parsed correctly!", newData[i].version);
|
|
|
|
continue;
|
|
|
|
}
|
2016-05-25 19:39:33 -04:00
|
|
|
|
2016-05-26 07:13:02 -04:00
|
|
|
// Apply the patch data
|
|
|
|
for (unsigned int pType = 0; pType < StructuredData::PlayerDataType::ENUM_MAX; ++pType)
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
if (!patchData[pType].empty())
|
2016-02-06 07:37:23 -05:00
|
|
|
{
|
2016-05-26 07:13:02 -04:00
|
|
|
StructuredData::PatchPlayerDataEnum(&newData[i], static_cast<StructuredData::PlayerDataType>(pType), patchData[pType]);
|
2016-02-06 07:37:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2016-01-12 19:29:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
StructuredData::~StructuredData()
|
|
|
|
{
|
2016-06-08 11:28:58 -04:00
|
|
|
StructuredData::MemAllocator.Free();
|
2016-01-12 19:29:22 -05:00
|
|
|
}
|
|
|
|
}
|