10#include "yaml-cpp/yaml.h"
40std::map<std::filesystem::path, YAML::Node> yaml_cache;
41std::map<std::filesystem::path, std::vector<std::string>> file_lines_cache;
54std::string get_line_content(
const std::filesystem::path &path, std::size_t line_num)
56 const auto it = file_lines_cache.find(path);
57 if (it != file_lines_cache.end() && line_num < it->second.size()) {
58 return it->second[line_num];
76std::string make_source_string(
const TextFormatter *format,
const std::string &file_path,
const YAML::Mark &mark)
105std::vector<std::string>
106make_source_details(
const TextFormatter *format,
const std::string &file_path,
const YAML::Mark &mark)
108 const std::filesystem::path path{file_path};
109 const auto it = file_lines_cache.find(path);
110 if (it == file_lines_cache.end()) {
114 const auto &lines = it->second;
115 const std::size_t line_num = mark.line;
117 if (lines.empty() || line_num >= lines.size()) {
123 return printer.print(lines, std::vector{line_num});
137void collect_yaml_paths(
138 const YAML::Node &node,
const std::string &prefix, std::vector<std::pair<std::string, YAML::Mark>> &paths)
144 for (
const auto &kv : node) {
145 const auto key = kv.first.as<std::string>();
146 const auto full_path = prefix.empty() ? key : prefix +
"." + key;
147 paths.emplace_back(full_path, kv.first.Mark());
150 if (kv.second.IsMap()) {
151 collect_yaml_paths(kv.second, full_path, paths);
170[[nodiscard]]
bool validate_yaml_paths(
173 const std::filesystem::path &file_path,
174 const YAML::Node &node)
176 if (diagnostics ==
nullptr) {
180 bool found_unknown =
false;
181 std::vector<std::pair<std::string, YAML::Mark>> paths;
182 collect_yaml_paths(node,
"", paths);
184 for (
const auto &[path, mark] : paths) {
187 bool is_map_child =
false;
189 if (path.starts_with(prefix +
".")) {
198 const auto source = make_source_string(format, file_path.string(), mark);
199 auto details = make_source_details(format, file_path.string(), mark);
201 std::vector<std::string> error_lines;
202 error_lines.push_back(format->
format(
"Unknown configuration key '{}'.",
FormatParam{path, Style::bold}));
203 error_lines.emplace_back();
204 error_lines.push_back(format->
format(
"Source: {}",
FormatParam{source, Style::italic}));
205 error_lines.emplace_back();
206 for (
auto &detail : details) {
207 error_lines.push_back(std::move(detail));
210 diagnostics->
error(
"unknown-config-key", error_lines);
211 found_unknown =
true;
215 return found_unknown;
228parse_size_t(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
230 if (!node.IsDefined()) {
235 const auto value = node.as<std::size_t>();
236 const auto mark = node.Mark();
237 const auto source = make_source_string(format, file_path, mark);
238 const auto details = make_source_details(format, file_path, mark);
241 catch (
const YAML::Exception &e) {
242 const auto mark = node.Mark();
245 const auto source = make_source_string(format, file_path, mark);
246 const auto details = make_source_details(format, file_path, mark);
261parse_bool(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
263 if (!node.IsDefined()) {
268 const auto value = node.as<
bool>();
269 const auto mark = node.Mark();
270 const auto source = make_source_string(format, file_path, mark);
271 const auto details = make_source_details(format, file_path, mark);
274 catch (
const YAML::Exception &e) {
275 const auto mark = node.Mark();
278 const auto source = make_source_string(format, file_path, mark);
279 const auto details = make_source_details(format, file_path, mark);
294parse_string(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
296 if (!node.IsDefined()) {
301 const auto value = node.as<std::string>();
302 const auto mark = node.Mark();
303 const auto source = make_source_string(format, file_path, mark);
304 const auto details = make_source_details(format, file_path, mark);
307 catch (
const YAML::Exception &e) {
308 const auto mark = node.Mark();
311 const auto source = make_source_string(format, file_path, mark);
312 const auto details = make_source_details(format, file_path, mark);
331parse_rgba32(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
333 if (!node.IsDefined()) {
338 const auto mark = node.Mark();
339 const auto source = make_source_string(format, file_path, mark);
340 const auto details = make_source_details(format, file_path, mark);
342 if (!node.IsSequence()) {
348 if (node.size() < 3 || node.size() > 4) {
350 "'{}' must have 3 or 4 elements [r, g, b] or [r, g, b, a], got {}",
356 const auto r = node[0].as<std::uint8_t>();
357 const auto g = node[1].as<std::uint8_t>();
358 const auto b = node[2].as<std::uint8_t>();
361 const Rgba32 color{r, g, b, a};
364 catch (
const YAML::Exception &e) {
365 const auto mark = node.Mark();
367 const auto source = make_source_string(format, file_path, mark);
368 const auto details = make_source_details(format, file_path, mark);
374 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
376 if (!node.IsDefined()) {
381 const auto mark = node.Mark();
382 const auto details = make_source_details(format, file_path, mark);
384 if (!node.IsSequence()) {
387 const auto source = make_source_string(format, file_path, mark);
391 std::vector<PaletteHint> hints;
392 for (std::size_t i = 0; i < node.size(); ++i) {
393 const auto &hint_node = node[i];
395 if (!hint_node.IsMap()) {
396 const auto hint_mark = hint_node.Mark();
398 "'{}[{}]' must be a map with 'name' and 'colors' keys",
401 const auto source = make_source_string(format, file_path, hint_mark);
402 const auto hint_details = make_source_details(format, file_path, hint_mark);
407 const auto name_node = hint_node[
"name"];
408 if (!name_node.IsDefined()) {
409 const auto hint_mark = hint_node.Mark();
412 const auto source = make_source_string(format, file_path, hint_mark);
413 const auto hint_details = make_source_details(format, file_path, hint_mark);
416 const auto name = name_node.as<std::string>();
419 const auto colors_node = hint_node[
"colors"];
420 if (!colors_node.IsDefined()) {
421 const auto hint_mark = hint_node.Mark();
424 const auto source = make_source_string(format, file_path, hint_mark);
425 const auto hint_details = make_source_details(format, file_path, hint_mark);
429 if (!colors_node.IsSequence()) {
430 const auto colors_mark = colors_node.Mark();
433 const auto source = make_source_string(format, file_path, colors_mark);
434 const auto colors_details = make_source_details(format, file_path, colors_mark);
439 std::vector<Rgba32> colors;
440 for (std::size_t j = 0; j < colors_node.size(); ++j) {
441 const auto &color_node = colors_node[j];
443 if (!color_node.IsSequence() || color_node.size() != 3) {
444 const auto color_mark = color_node.Mark();
446 "'{}[{}].colors[{}]' must be [r, g, b]",
450 const auto source = make_source_string(format, file_path, color_mark);
451 const auto color_details = make_source_details(format, file_path, color_mark);
455 const auto r = color_node[0].as<std::uint8_t>();
456 const auto g = color_node[1].as<std::uint8_t>();
457 const auto b = color_node[2].as<std::uint8_t>();
458 const auto a = (color_node.size() == 4) ? color_node[3].as<std::uint8_t>() :
Rgba32::alpha_opaque;
460 colors.emplace_back(r, g, b, a);
463 hints.emplace_back(name,
Palette{std::move(colors)});
466 const auto source = make_source_string(format, file_path, mark);
469 catch (
const YAML::Exception &e) {
470 const auto mark = node.Mark();
473 const auto source = make_source_string(format, file_path, mark);
474 const auto details = make_source_details(format, file_path, mark);
493 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
495 if (!node.IsDefined()) {
500 const auto mark = node.Mark();
501 const auto source = make_source_string(format, file_path, mark);
502 const auto details = make_source_details(format, file_path, mark);
504 if (!node.IsSequence()) {
509 std::vector<std::string> result;
510 for (std::size_t i = 0; i < node.size(); ++i) {
511 result.push_back(node[i].as<std::string>());
515 catch (
const YAML::Exception &e) {
516 const auto mark = node.Mark();
519 const auto source = make_source_string(format, file_path, mark);
520 const auto details = make_source_details(format, file_path, mark);
538 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
540 if (!node.IsDefined()) {
545 const auto mark = node.Mark();
546 const auto source = make_source_string(format, file_path, mark);
547 const auto details = make_source_details(format, file_path, mark);
548 const auto node_value = node.as<std::string>();
551 if (!mode_opt.has_value()) {
559 catch (
const YAML::Exception &e) {
560 const auto mark = node.Mark();
563 const auto source = make_source_string(format, file_path, mark);
564 const auto details = make_source_details(format, file_path, mark);
570 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
572 if (!node.IsDefined()) {
577 const auto mark = node.Mark();
578 const auto source = make_source_string(format, file_path, mark);
579 const auto details = make_source_details(format, file_path, mark);
580 const auto node_value = node.as<std::string>();
583 if (!mode_opt.has_value()) {
591 catch (
const YAML::Exception &e) {
592 const auto mark = node.Mark();
595 const auto source = make_source_string(format, file_path, mark);
596 const auto details = make_source_details(format, file_path, mark);
602 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
604 if (!node.IsDefined()) {
609 const auto mark = node.Mark();
610 const auto source = make_source_string(format, file_path, mark);
611 const auto details = make_source_details(format, file_path, mark);
612 const auto node_value = node.as<std::string>();
615 if (!mode_opt.has_value()) {
623 catch (
const YAML::Exception &e) {
624 const auto mark = node.Mark();
627 const auto source = make_source_string(format, file_path, mark);
628 const auto details = make_source_details(format, file_path, mark);
634 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
636 if (!node.IsDefined()) {
641 const auto mark = node.Mark();
642 const auto source = make_source_string(format, file_path, mark);
643 const auto details = make_source_details(format, file_path, mark);
652 for (
const auto &kv : node) {
653 const auto anim_name = kv.first.as<std::string>();
654 const auto &anim_node = kv.second;
659 if (!anim_node.IsMap()) {
660 const auto anim_mark = kv.first.Mark();
661 const auto anim_source = make_source_string(format, file_path, anim_mark);
662 const auto anim_details = make_source_details(format, file_path, anim_mark);
664 "'{}' animation '{}' must be a map.",
671 if (anim_node[
"frame_linking"].IsDefined()) {
672 const auto linking_str = anim_node[
"frame_linking"].as<std::string>();
674 if (!linking_opt.has_value()) {
675 const auto linking_mark = anim_node[
"frame_linking"].Mark();
676 const auto linking_source = make_source_string(format, file_path, linking_mark);
677 const auto linking_details = make_source_details(format, file_path, linking_mark);
679 "'{}' animation '{}' has invalid frame_linking value '{}'.",
685 const auto fl_mark = anim_node[
"frame_linking"].Mark();
688 key +
"." + anim_name +
".frame_linking",
689 "Animation Config (" + anim_name +
") frame_linking",
690 make_source_string(format, file_path, fl_mark),
691 make_source_details(format, file_path, fl_mark)};
695 if (anim_node[
"palette_resolution_strategy"].IsDefined()) {
696 const auto &strategy_node = anim_node[
"palette_resolution_strategy"];
697 const auto strategy_str = strategy_node.as<std::string>();
699 if (!strategy_opt.has_value()) {
700 const auto strategy_mark = strategy_node.Mark();
701 const auto strategy_source = make_source_string(format, file_path, strategy_mark);
702 const auto strategy_details = make_source_details(format, file_path, strategy_mark);
704 "'{}' animation '{}' palette_resolution_strategy has invalid value '{}'.",
710 const auto pal_mark = strategy_node.Mark();
712 strategy_opt.
value(),
713 key +
"." + anim_name +
".palette_resolution_strategy",
714 "Animation Config (" + anim_name +
") per-anim strategy",
715 make_source_string(format, file_path, pal_mark),
716 make_source_details(format, file_path, pal_mark)};
720 if (anim_node[
"key_frame_resolution_strategy"].IsDefined()) {
721 const auto &strategy_node = anim_node[
"key_frame_resolution_strategy"];
722 const auto strategy_str = strategy_node.as<std::string>();
724 if (!strategy_opt.has_value()) {
725 const auto strategy_mark = strategy_node.Mark();
726 const auto strategy_source = make_source_string(format, file_path, strategy_mark);
727 const auto strategy_details = make_source_details(format, file_path, strategy_mark);
729 "'{}' animation '{}' key_frame_resolution_strategy has invalid value '{}'.",
735 const auto kf_mark = strategy_node.Mark();
737 strategy_opt.
value(),
738 key +
"." + anim_name +
".key_frame_resolution_strategy",
739 "Animation Config (" + anim_name +
") key_frame_resolution_strategy",
740 make_source_string(format, file_path, kf_mark),
741 make_source_details(format, file_path, kf_mark)};
745 if (anim_node[
"multi_palette_subtile_resolution_strategy"].IsDefined()) {
746 const auto &strategy_node = anim_node[
"multi_palette_subtile_resolution_strategy"];
747 const auto strategy_str = strategy_node.as<std::string>();
749 if (!strategy_opt.has_value()) {
750 const auto strategy_mark = strategy_node.Mark();
751 const auto strategy_source = make_source_string(format, file_path, strategy_mark);
752 const auto strategy_details = make_source_details(format, file_path, strategy_mark);
754 "'{}' animation '{}' multi_palette_subtile_resolution_strategy has invalid value '{}'.",
760 const auto mps_mark = strategy_node.Mark();
762 strategy_opt.
value(),
763 key +
"." + anim_name +
".multi_palette_subtile_resolution_strategy",
764 "Animation Config (" + anim_name +
") multi_palette_subtile_resolution_strategy",
765 make_source_string(format, file_path, mps_mark),
766 make_source_details(format, file_path, mps_mark)};
770 if (anim_node[
"per_tile_palette_resolution_strategies"].IsDefined()) {
771 const auto &strategies_node = anim_node[
"per_tile_palette_resolution_strategies"];
772 if (!strategies_node.IsSequence()) {
773 const auto strategies_mark = strategies_node.Mark();
774 const auto strategies_source = make_source_string(format, file_path, strategies_mark);
775 const auto strategies_details = make_source_details(format, file_path, strategies_mark);
777 "'{}' animation '{}' per_tile_palette_resolution_strategies must be a sequence.",
783 for (std::size_t i = 0; i < strategies_node.size(); ++i) {
784 const auto strategy_str = strategies_node[i].as<std::string>();
785 if (strategy_str ==
"_") {
790 if (!strategy_opt.has_value()) {
791 const auto strategy_mark = strategies_node[i].Mark();
792 const auto strategy_source = make_source_string(format, file_path, strategy_mark);
793 const auto strategy_details = make_source_details(format, file_path, strategy_mark);
795 "'{}' animation '{}' per_tile_palette_resolution_strategies[{}] has invalid value "
803 const auto tile_mark = strategies_node[i].Mark();
806 strategy_opt.
value(),
807 key +
"." + anim_name +
".per_tile_palette_resolution_strategies[" + std::to_string(i) +
809 "Animation Config (" + anim_name +
") subtile " + std::to_string(i),
810 make_source_string(format, file_path, tile_mark),
811 make_source_details(format, file_path, tile_mark)});
816 configs[anim_name] = std::move(anim_config);
821 catch (
const YAML::Exception &e) {
822 const auto mark = node.Mark();
825 const auto source = make_source_string(format, file_path, mark);
826 const auto details = make_source_details(format, file_path, mark);
832 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
834 if (!node.IsDefined()) {
839 const auto mark = node.Mark();
840 const auto source = make_source_string(format, file_path, mark);
841 const auto details = make_source_details(format, file_path, mark);
842 const auto node_value = node.as<std::string>();
845 if (!mode_opt.has_value()) {
853 catch (
const YAML::Exception &e) {
854 const auto mark = node.Mark();
857 const auto source = make_source_string(format, file_path, mark);
858 const auto details = make_source_details(format, file_path, mark);
864 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
866 if (!node.IsDefined()) {
871 const auto mark = node.Mark();
872 const auto source = make_source_string(format, file_path, mark);
873 const auto details = make_source_details(format, file_path, mark);
874 const auto node_value = node.as<std::string>();
877 if (!mode_opt.has_value()) {
885 catch (
const YAML::Exception &e) {
886 const auto mark = node.Mark();
888 "Failed to parse '{}' as AnimMultiPalSubtileResolutionStrategy: {}.",
891 const auto source = make_source_string(format, file_path, mark);
892 const auto details = make_source_details(format, file_path, mark);
898 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
900 if (!node.IsDefined()) {
905 const auto mark = node.Mark();
906 const auto source = make_source_string(format, file_path, mark);
907 const auto details = make_source_details(format, file_path, mark);
908 const auto node_value = node.as<std::string>();
911 if (!mode_opt.has_value()) {
919 catch (
const YAML::Exception &e) {
920 const auto mark = node.Mark();
923 const auto source = make_source_string(format, file_path, mark);
924 const auto details = make_source_details(format, file_path, mark);
930 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
932 if (!node.IsDefined()) {
937 const auto mark = node.Mark();
938 const auto source = make_source_string(format, file_path, mark);
939 const auto details = make_source_details(format, file_path, mark);
940 const auto node_value = node.as<std::string>();
943 if (!mode_opt.has_value()) {
951 catch (
const YAML::Exception &e) {
952 const auto mark = node.Mark();
955 const auto source = make_source_string(format, file_path, mark);
956 const auto details = make_source_details(format, file_path, mark);
962 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
964 if (!node.IsDefined()) {
969 const auto mark = node.Mark();
970 const auto source = make_source_string(format, file_path, mark);
971 const auto details = make_source_details(format, file_path, mark);
972 const auto node_value = node.as<std::string>();
975 if (!mode_opt.has_value()) {
983 catch (
const YAML::Exception &e) {
984 const auto mark = node.Mark();
987 const auto source = make_source_string(format, file_path, mark);
988 const auto details = make_source_details(format, file_path, mark);
994 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
996 if (!node.IsDefined()) {
1001 const auto mark = node.Mark();
1002 const auto source = make_source_string(format, file_path, mark);
1003 const auto details = make_source_details(format, file_path, mark);
1004 const auto node_value = node.as<std::string>();
1007 if (!mode_opt.has_value()) {
1015 catch (
const YAML::Exception &e) {
1016 const auto mark = node.Mark();
1019 const auto source = make_source_string(format, file_path, mark);
1020 const auto details = make_source_details(format, file_path, mark);
1026 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
1028 if (!node.IsDefined()) {
1033 const auto mark = node.Mark();
1034 const auto source = make_source_string(format, file_path, mark);
1035 const auto details = make_source_details(format, file_path, mark);
1036 const auto node_value = node.as<std::string>();
1039 if (!mode_opt.has_value()) {
1047 catch (
const YAML::Exception &e) {
1048 const auto mark = node.Mark();
1051 const auto source = make_source_string(format, file_path, mark);
1052 const auto details = make_source_details(format, file_path, mark);
1058 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
1060 if (!node.IsDefined()) {
1065 const auto mark = node.Mark();
1066 const auto source = make_source_string(format, file_path, mark);
1067 const auto details = make_source_details(format, file_path, mark);
1069 if (!node.IsMap()) {
1078 if (node[
"backtracking"].IsDefined()) {
1079 const auto &bt_node = node[
"backtracking"];
1080 if (!bt_node.IsMap()) {
1081 const auto bt_mark = bt_node.Mark();
1082 const auto bt_source = make_source_string(format, file_path, bt_mark);
1083 const auto bt_details = make_source_details(format, file_path, bt_mark);
1088 if (bt_node[
"search_algorithm"].IsDefined()) {
1089 const auto &sa_node = bt_node[
"search_algorithm"];
1090 const auto sa_str = sa_node.as<std::string>();
1092 if (!sa_opt.has_value()) {
1093 const auto sa_mark = sa_node.Mark();
1094 const auto sa_source = make_source_string(format, file_path, sa_mark);
1095 const auto sa_details = make_source_details(format, file_path, sa_mark);
1097 "'{}' backtracking search_algorithm has invalid value '{}'.",
1102 const auto sa_mark = sa_node.Mark();
1105 key +
".backtracking.search_algorithm",
1106 "Packing Strategy Params (backtracking) search_algorithm",
1107 make_source_string(format, file_path, sa_mark),
1108 make_source_details(format, file_path, sa_mark)};
1111 if (bt_node[
"node_cutoff"].IsDefined()) {
1112 const auto &nc_node = bt_node[
"node_cutoff"];
1113 const auto nc_val = nc_node.as<std::size_t>();
1114 const auto nc_mark = nc_node.Mark();
1117 key +
".backtracking.node_cutoff",
1118 "Packing Strategy Params (backtracking) node_cutoff",
1119 make_source_string(format, file_path, nc_mark),
1120 make_source_details(format, file_path, nc_mark)};
1123 if (bt_node[
"best_branches"].IsDefined()) {
1124 const auto &bb_node = bt_node[
"best_branches"];
1125 const auto bb_val = bb_node.as<std::size_t>();
1126 const auto bb_mark = bb_node.Mark();
1129 key +
".backtracking.best_branches",
1130 "Packing Strategy Params (backtracking) best_branches",
1131 make_source_string(format, file_path, bb_mark),
1132 make_source_details(format, file_path, bb_mark)};
1135 if (bt_node[
"smart_prune"].IsDefined()) {
1136 const auto &sp_node = bt_node[
"smart_prune"];
1137 const auto sp_val = sp_node.as<
bool>();
1138 const auto sp_mark = sp_node.Mark();
1141 key +
".backtracking.smart_prune",
1142 "Packing Strategy Params (backtracking) smart_prune",
1143 make_source_string(format, file_path, sp_mark),
1144 make_source_details(format, file_path, sp_mark)};
1149 if (node[
"overload_and_remove"].IsDefined()) {
1150 const auto &oar_node = node[
"overload_and_remove"];
1151 if (!oar_node.IsMap()) {
1152 const auto oar_mark = oar_node.Mark();
1153 const auto oar_source = make_source_string(format, file_path, oar_mark);
1154 const auto oar_details = make_source_details(format, file_path, oar_mark);
1160 if (oar_node[
"max_attempts"].IsDefined()) {
1161 const auto &ma_node = oar_node[
"max_attempts"];
1162 const auto ma_val = ma_node.as<std::size_t>();
1163 const auto ma_mark = ma_node.Mark();
1166 key +
".overload_and_remove.max_attempts",
1167 "Packing Strategy Params (overload_and_remove) max_attempts",
1168 make_source_string(format, file_path, ma_mark),
1169 make_source_details(format, file_path, ma_mark)};
1172 if (oar_node[
"seed"].IsDefined()) {
1173 const auto &seed_node = oar_node[
"seed"];
1174 const auto seed_val = seed_node.as<std::uint64_t>();
1175 const auto seed_mark = seed_node.Mark();
1178 key +
".overload_and_remove.seed",
1179 "Packing Strategy Params (overload_and_remove) seed",
1180 make_source_string(format, file_path, seed_mark),
1181 make_source_details(format, file_path, seed_mark)};
1184 if (oar_node[
"shuffle_strategy"].IsDefined()) {
1185 const auto &ss_node = oar_node[
"shuffle_strategy"];
1186 const auto ss_str = ss_node.as<std::string>();
1188 if (!ss_opt.has_value()) {
1189 const auto ss_mark = ss_node.Mark();
1190 const auto ss_source = make_source_string(format, file_path, ss_mark);
1191 const auto ss_details = make_source_details(format, file_path, ss_mark);
1193 "'{}' overload_and_remove shuffle_strategy has invalid value '{}'.",
1198 const auto ss_mark = ss_node.Mark();
1201 key +
".overload_and_remove.shuffle_strategy",
1202 "Packing Strategy Params (overload_and_remove) shuffle_strategy",
1203 make_source_string(format, file_path, ss_mark),
1204 make_source_details(format, file_path, ss_mark)};
1210 catch (
const YAML::Exception &e) {
1211 const auto mark = node.Mark();
1214 const auto source = make_source_string(format, file_path, mark);
1215 const auto details = make_source_details(format, file_path, mark);
1234std::optional<YAML::Node> load_yaml_file(
1235 const std::filesystem::path &path,
1238 bool *out_had_unknown_keys =
nullptr)
1241 const auto cache_it = yaml_cache.find(path);
1242 if (cache_it != yaml_cache.end()) {
1243 return cache_it->second;
1247 if (!std::filesystem::exists(path)) {
1248 return std::nullopt;
1253 auto node = YAML::LoadFile(path.string());
1254 yaml_cache[path] = node;
1257 std::ifstream file{path};
1258 std::vector<std::string> lines;
1260 while (std::getline(file, line)) {
1261 lines.push_back(line);
1263 file_lines_cache[path] = std::move(lines);
1266 if (format !=
nullptr && diagnostics !=
nullptr) {
1267 if (validate_yaml_paths(format, diagnostics, path, node) && out_had_unknown_keys !=
nullptr) {
1268 *out_had_unknown_keys =
true;
1274 catch (
const YAML::Exception &) {
1276 return std::nullopt;
1297std::vector<std::filesystem::path>
1298get_tileset_config_path_chain(
const std::filesystem::path &project_root,
const std::string &
tileset)
1300 std::vector<std::filesystem::path> paths;
1303 const auto porytiles_dir = project_root /
"porytiles";
1307 paths.push_back(porytiles_dir /
"tilesets" /
tileset /
"config.local.yaml");
1310 paths.push_back(porytiles_dir /
"tilesets" /
tileset /
"config.yaml");
1313 paths.push_back(porytiles_dir /
"config.local.yaml");
1316 paths.push_back(porytiles_dir /
"config.yaml");
1334get_config_path_chain(
const std::filesystem::path &project_root,
ConfigScopeType type,
const std::string &scope)
1337 case ConfigScopeType::tileset:
1338 return get_tileset_config_path_chain(project_root, scope);
1339 case ConfigScopeType::layout:
1340 panic(
"Layout config path chain resolution is not yet implemented.");
1343 panic(
"Invalid ConfigScopeType");
1361[[nodiscard]]
bool preload_and_validate_yaml_files(
1364 const std::filesystem::path &project_root,
1366 const std::string &scope)
1368 auto paths_result = get_config_path_chain(project_root, type, scope);
1369 if (!paths_result.has_value()) {
1373 bool had_unknown_keys =
false;
1374 for (
const auto &path : paths_result.value()) {
1375 load_yaml_file(path, format, diagnostics, &had_unknown_keys);
1378 return had_unknown_keys;
1406template <
typename T,
typename LoadFunc,
typename NodeExtractFunc,
typename ParseFunc>
1409 const std::vector<std::filesystem::path> &paths,
1411 NodeExtractFunc extract_node_func,
1412 ParseFunc parse_func,
1413 const std::string &key,
1414 const std::string &provider_name)
1416 for (
const auto &path : paths) {
1417 const auto yaml_doc = load_func(path);
1418 if (!yaml_doc.has_value()) {
1424 const auto node = extract_node_func(yaml_doc.value());
1425 auto result = parse_func(format, node, key, path.string());
1428 if (result.state == ValidationState::valid || result.state == ValidationState::invalid) {
1429 result.source_key = provider_name;
1435 catch (
const YAML::Exception &) {
A result type that maintains a chainable sequence of errors for debugging and error reporting.
A service for printing file lines with highlighted lines and line numbers.
A generic palette container for colors that support transparency checking.
Represents a 32-bit RGBA color.
static constexpr std::uint8_t alpha_opaque
static const Style bold
Bold text formatting.
Abstract base class for applying text styling with context-aware formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > ¶ms) const
Formats a string with styled parameters using fmtlib syntax.
Abstract class for structured error reporting and diagnostic output.
virtual void error(const std::string &tag, const std::vector< std::string > &lines) const =0
Display a tagged error message.
const std::unordered_set< std::string > valid_yaml_paths
Set of valid YAML configuration paths.
std::optional< TilesPalMode > tiles_pal_mode_from_str(const std::string &str)
Parses a string into a TilesPalMode with fuzzy matching.
std::optional< ArtifactEditMode > artifact_edit_mode_from_str(const std::string &str)
Parses a string into a ArtifactEditMode with fuzzy matching.
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
std::optional< PackingStrategyType > packing_strategy_type_from_str(const std::string &str)
Parses a string into a PackingStrategyType with fuzzy matching.
std::optional< FrameLinking > frame_linking_from_str(const std::string &str)
Parses a string into a FrameLinking with fuzzy matching.
std::optional< PrimaryPairingMode > primary_pairing_mode_from_str(const std::string &str)
Parses a string into a PrimaryPairingMode with fuzzy matching.
std::optional< AnimPalResolutionStrategy > anim_pal_resolution_strategy_from_str(const std::string &str)
Parses a string into a AnimPalResolutionStrategy with fuzzy matching.
const std::unordered_set< std::string > valid_yaml_map_prefixes
Set of YAML path prefixes that represent map-type configuration values.
std::optional< ShuffleStrategy > shuffle_strategy_from_str(const std::string &str)
Parses a string into a ShuffleStrategy with fuzzy matching.
std::unordered_map< std::string, PerAnimOverride > PerAnimOverrides
Per-animation configuration map.
@ error
Emit a formatted error and fail decompilation.
std::optional< TileSharingPacking > tile_sharing_packing_from_str(const std::string &str)
Parses a string into a TileSharingPacking with fuzzy matching.
std::optional< TileSharingAlignment > tile_sharing_alignment_from_str(const std::string &str)
Parses a string into a TileSharingAlignment with fuzzy matching.
ConfigScopeType
Specifies the scope type for configuration value lookups.
@ tileset
Configuration scoped to a specific tileset.
std::optional< AnimMultiPalSubtileResolutionStrategy > anim_multi_pal_subtile_resolution_strategy_from_str(const std::string &str)
Parses a string into a AnimMultiPalSubtileResolutionStrategy with fuzzy matching.
std::optional< SearchAlgorithm > search_algorithm_from_str(const std::string &str)
Parses a string into a SearchAlgorithm with fuzzy matching.
std::optional< AnimKeyFrameResolutionStrategy > anim_key_frame_resolution_strategy_from_str(const std::string &str)
Parses a string into a AnimKeyFrameResolutionStrategy with fuzzy matching.
ConfigPODField< std::size_t > best_branches
ConfigPODField< std::size_t > node_cutoff
ConfigPODField< bool > smart_prune
ConfigPODField< SearchAlgorithm > search_algorithm
A lightweight wrapper for per-field configuration values with source metadata.
A small container that holds an optional-wrapped value, validation state, and metadata about the valu...
static LayerValue valid(T val, std::string source_key, std::string source_info)
Creates a LayerValue representing a valid configuration value.
static LayerValue not_provided()
Creates a LayerValue representing that the provider does not supply this configuration.
static LayerValue invalid(std::string error, std::string source_info)
Creates a LayerValue representing an invalid configuration value.
ConfigPODField< ShuffleStrategy > shuffle_strategy
ConfigPODField< std::uint64_t > seed
ConfigPODField< std::size_t > max_attempts
Container for per-strategy packing parameters.
BacktrackingParams backtracking
OverloadAndRemoveParams overload_and_remove
Per-animation configuration override for animation decompilation.
std::vector< ConfigPODField< AnimPalResolutionStrategy > > per_tile_pal_resolution_strategies
ConfigPODField< AnimMultiPalSubtileResolutionStrategy > multi_pal_subtile_resolution_strategy
ConfigPODField< AnimKeyFrameResolutionStrategy > key_frame_resolution_strategy
ConfigPODField< FrameLinking > linking
ConfigPODField< AnimPalResolutionStrategy > pal_resolution_strategy