feat: add endsWith and splitDataPath

This commit is contained in:
dualfroz committed 2026-09-07 10:23:01 +02:00
1 parent e8c4fc27f4
commit 570172c86d
1 file changed
+159
+159
View File
@@ -0,0 +1,159 @@
#include "vf/DataPack.h"
#include <algorithm>
#include <cctype>
#include "vf/Command.h"
namespace vf {
namespace {
const char* const knownFolders[] = {"recipes", "lootTables", "tags/blocks", "tags/items",
"advancements"};
std::string loweredWithoutSeparators(const std::string& text) {
std::string out;
out.reserve(text.size());
for (char c : text) {
if (c == '_' || c == '-') continue;
out.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
}
return out;
}
bool endsWith(const std::string& text, const std::string& suffix) {
return text.size() >= suffix.size() &&
text.compare(text.size() - suffix.size(), suffix.size(), suffix) == 0;
}
bool splitDataPath(const std::string& file, std::string& space, std::string& folder,
std::string& name) {
const std::string root = "data/";
if (file.compare(0, root.size(), root) != 0) return false;
std::size_t spaceEnd = file.find('/', root.size());
if (spaceEnd == std::string::npos) return false;
space = file.substr(root.size(), spaceEnd - root.size());
std::string rest = file.substr(spaceEnd + 1u);
if (!endsWith(rest, ".json")) return false;
rest = rest.substr(0, rest.size() - 5u);
std::string bestFolder;
for (const char* candidate : knownFolders) {
std::string prefix = std::string(candidate) + "/";
if (rest.compare(0, prefix.size(), prefix) != 0 || rest.size() <= prefix.size()) continue;
if (bestFolder.size() < prefix.size() - 1u) bestFolder = candidate;
}
if (bestFolder.empty()) return false;
folder = bestFolder;
name = rest.substr(bestFolder.size() + 1u);
return !name.empty();
}
bool readResourceField(const JsonValue& owner, const std::string& key,
const std::string& defaultSpace, ResourceId& out, std::string& problem) {
const JsonValue* field = owner.find(key);
if (field == nullptr || !field->isString()) {
problem = "\"" + key + "\" must be a resource id string";
return false;
}
if (!ResourceId::parse(field->stringValue(), defaultSpace, out)) {
problem = "\"" + key + "\" is not a valid resource id: " + field->stringValue();
return false;
}
return true;
}
bool readItemField(const JsonValue& owner, const std::string& key, const std::string& defaultSpace,
ItemId& out, std::string& problem) {
ResourceId id;
if (!readResourceField(owner, key, defaultSpace, id, problem)) return false;
if (!resolveItemId(id, out)) {
problem = "no such item: " + id.text();
return false;
}
return true;
}
struct NamedFlag {
const char* name;
uint32_t bit;
};
const NamedFlag lootConditionFlags[] = {
{"playerKill", LootEntry::PlayerKill},
{"fiery", LootEntry::Fiery},
{"notFiery", LootEntry::NotFiery},
{"silkTouched", LootEntry::SilkTouched},
{"notSilkTouched", LootEntry::NotSilkTouched},
{"needsLooting", LootEntry::NeedsLooting},
{"needsFortune", LootEntry::NeedsFortune},
{"explosive", LootEntry::Explosive},
{"ranged", LootEntry::Ranged},
};
const NamedFlag lootFunctionFlags[] = {
{"countRange", LootEntry::CountRange},
{"oreFortune", LootEntry::OreFortune},
{"lootingBonus", LootEntry::LootingBonus},
{"chanceLooting", LootEntry::ChanceLooting},
{"chanceFortune", LootEntry::ChanceFortune},
{"smeltIfFiery", LootEntry::SmeltIfFiery},
};
bool readFlagList(const JsonValue& owner, const std::string& key, const NamedFlag* table,
std::size_t tableSize, uint32_t& out, std::string& problem) {
const JsonValue* list = owner.find(key);
if (list == nullptr) return true;
if (!list->isArray()) {
problem = "\"" + key + "\" must be an array of names";
return false;
}
for (const JsonValue& element : list->elements()) {
if (!element.isString()) {
problem = "\"" + key + "\" holds a non string entry";
return false;
}
bool matched = false;
for (std::size_t i = 0u; i < tableSize; ++i) {
if (element.stringValue() == table[i].name) {
out |= table[i].bit;
matched = true;
break;
}
}
if (!matched) {
problem = "unknown " + key + " name: " + element.stringValue();
return false;
}
}
return true;
}
bool readCountRange(const JsonValue& owner, const std::string& key, int& minOut, int& maxOut,
std::string& problem) {
const JsonValue* field = owner.find(key);
if (field == nullptr) return true;
if (field->isNumber()) {
minOut = static_cast<int>(field->intValue());
maxOut = minOut;
} else if (field->isObject()) {
minOut = static_cast<int>(field->intOr("min", minOut));
maxOut = static_cast<int>(field->intOr("max", minOut));
} else {
problem = "\"" + key + "\" must be a number or a {min,max} object";
return false;
}
if (minOut > maxOut) {
problem = "\"" + key + "\" has min above max";
return false;
}
if (minOut < 0) {
problem = "\"" + key + "\" cannot be negative";
return false;
}
return true;
}
} // namespace