feat: add endsWith and splitDataPath
This commit is contained in:
1 parent
e8c4fc27f4
commit
570172c86d
1 file changed
+159
@@ -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
|
||||||
|
|
||||||
Reference in new issue
Block a user