6#include <initializer_list>
11#include <yaml-cpp/yaml.h>
14#define YAML_HEX(num) YAML::Hex << (num) << YAML::Dec
15#define FORMAT_HEX(x, w) \
16 std::hex << std::uppercase << std::setfill('0') << std::setw(w) << x << std::nouppercase << std::dec
21inline void EmitText(YAML::Emitter& out,
const std::string& str) {
22 const char* s = str.c_str();
24 for (
const char* p = s; *p !=
'\0'; p++) {
25 if (
static_cast<uint8_t
>(*p) < 0x20 ||
static_cast<uint8_t
>(*p) > 0x7E || *p ==
'\\') {
31 out << YAML::DoubleQuoted << s;
35 for (
const char* p = s; *p !=
'\0'; p++) {
36 const auto c =
static_cast<uint8_t
>(*p);
39 }
else if (c < 0x20 || c > 0x7E) {
41 std::snprintf(buf,
sizeof(buf),
"\\x%02X", c);
44 escaped +=
static_cast<char>(c);
47 out << YAML::SingleQuoted << escaped;
51inline std::string DecodeText(
const std::string& str) {
53 out.reserve(str.size());
54 for (
size_t i = 0; i < str.size(); i++) {
55 if (str[i] ==
'\\' && i + 1 < str.size()) {
56 if (str[i + 1] ==
'\\') {
61 if ((str[i + 1] ==
'x' || str[i + 1] ==
'X') && i + 3 < str.size() &&
62 std::isxdigit(
static_cast<unsigned char>(str[i + 2])) &&
63 std::isxdigit(
static_cast<unsigned char>(str[i + 3]))) {
64 out +=
static_cast<char>(std::stoi(str.substr(i + 2, 2),
nullptr, 16));
75[[noreturn]]
inline void YamlFail(
const std::string& where,
const std::string& problem) {
76 throw std::runtime_error(where +
": " + problem);
79inline uint32_t YamlNumber(
const YAML::Node& node,
const std::string& where, uint32_t max) {
80 const std::string text = node.as<std::string>();
82 unsigned long value = 0;
84 value = std::stoul(text, &used, 0);
85 }
catch (
const std::exception&) { used = 0; }
86 if (used == 0 || used != text.size()) {
87 YamlFail(where,
"\"" + text +
"\" isn't a number");
90 YamlFail(where, text +
" is over " + std::to_string(max));
92 return static_cast<uint32_t
>(value);
95inline int16_t YamlShort(
const YAML::Node& node,
const std::string& where) {
96 const std::string text = node.as<std::string>();
100 value = std::stol(text, &used, 0);
101 }
catch (
const std::exception&) { used = 0; }
102 if (used == 0 || used != text.size()) {
103 YamlFail(where,
"\"" + text +
"\" isn't a number");
105 if (value < INT16_MIN || value > INT16_MAX) {
106 YamlFail(where, text +
" is out of range");
108 return static_cast<int16_t
>(value);
112inline uint32_t YamlNumber(
const YAML::Node& entry,
const char* key,
const std::string& where, uint32_t max) {
114 YamlFail(where, std::string(
"needs ") + key);
116 return YamlNumber(entry[key], where +
" " + key, max);
119inline int16_t YamlShort(
const YAML::Node& entry,
const char* key,
const std::string& where) {
121 YamlFail(where, std::string(
"needs ") + key);
123 return YamlShort(entry[key], where +
" " + key);
126inline float YamlFloat(
const YAML::Node& entry,
const char* key,
const std::string& where) {
128 YamlFail(where, std::string(
"needs ") + key);
131 return entry[key].as<
float>();
132 }
catch (
const YAML::Exception&) {
133 YamlFail(where +
" " + key,
"\"" + entry[key].as<std::string>() +
"\" isn't a number");
138inline YAML::Node YamlEntry(
const std::vector<uint8_t>& buffer,
const std::string& where) {
141 root = YAML::Load(std::string(buffer.begin(), buffer.end()));
142 }
catch (
const YAML::Exception& e) { YamlFail(where, e.what()); }
143 if (!root.IsMap() || root.size() != 1) {
144 YamlFail(where,
"needs one top-level key, the asset's name");
146 return root.begin()->second;
150inline void YamlKeys(
const YAML::Node& node, std::initializer_list<const char*> keys,
const std::string& where) {
152 YamlFail(where,
"needs to be a map");
154 for (
const auto& item : node) {
155 const std::string key = item.first.as<std::string>();
157 for (
const char* name : keys) {
158 known |= key == name;
161 YamlFail(where,
"doesn't take " + key);
166template <
typename T>
void ReadList(
const YAML::Node& list, T* values,
int count,
const std::string& at) {
167 if (!list.IsSequence() || (
int)list.size() != count) {
168 YamlFail(at,
"needs " + std::to_string(count) +
" numbers");
170 for (
int i = 0; i < count; i++) {
171 if constexpr (std::is_same_v<T, int16_t>) {
172 values[i] = YamlShort(list[i], at);
175 values[i] = list[i].as<T>();
176 }
catch (
const YAML::Exception&) { YamlFail(at,
"\"" + list[i].as<std::string>() +
"\" isn't a number"); }
182void ReadList(
const YAML::Node& entry,
const char* key, T* values,
int count,
const std::string& where) {
183 const YAML::Node list = entry[key];
185 YamlFail(where, std::string(
"needs a ") + key);
187 ReadList(list, values, count, where +
" " + key);
190template <
typename T>
void EmitList(YAML::Emitter& out,
const char* key,
const T* values,
int count) {
191 out << YAML::Key << key << YAML::Value << YAML::Flow << YAML::BeginSeq;
192 for (
int i = 0; i < count; i++) {
198inline void EmitList(YAML::Emitter& out,
const char* key, std::initializer_list<int> values) {
199 EmitList(out, key, values.begin(), (
int)values.size());