diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2024-12-06 17:33:40 -0500 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2024-12-06 17:35:35 -0500 |
commit | d1024b559c44a143eca214fb8732001080e8b037 (patch) | |
tree | 1c6ad799195b9f917047aa11b72e1f7dc0ecb013 /generator/generator.cpp | |
parent | ad8243c74c1d718b94a2a4bf4f0fa56d4c9dbb45 (diff) | |
download | lingo-randomizer-d1024b559c44a143eca214fb8732001080e8b037.tar.gz lingo-randomizer-d1024b559c44a143eca214fb8732001080e8b037.tar.bz2 lingo-randomizer-d1024b559c44a143eca214fb8732001080e8b037.zip |
Change output format to zstd compressed Godot serialized variant
Diffstat (limited to 'generator/generator.cpp')
-rw-r--r-- | generator/generator.cpp | 159 |
1 files changed, 113 insertions, 46 deletions
diff --git a/generator/generator.cpp b/generator/generator.cpp index 7263591..7016885 100644 --- a/generator/generator.cpp +++ b/generator/generator.cpp | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <fmt/core.h> | 3 | #include <fmt/core.h> |
4 | #include <hkutil/progress.h> | 4 | #include <hkutil/progress.h> |
5 | #include <hkutil/string.h> | 5 | #include <hkutil/string.h> |
6 | #include <zstd.h> | ||
6 | 7 | ||
7 | #include <algorithm> | 8 | #include <algorithm> |
8 | #include <filesystem> | 9 | #include <filesystem> |
@@ -10,13 +11,17 @@ | |||
10 | #include <list> | 11 | #include <list> |
11 | #include <regex> | 12 | #include <regex> |
12 | #include <set> | 13 | #include <set> |
14 | #include <sstream> | ||
13 | #include <stdexcept> | 15 | #include <stdexcept> |
14 | #include <string> | 16 | #include <string> |
15 | #include <unordered_map> | 17 | #include <unordered_map> |
16 | #include <unordered_set> | 18 | #include <unordered_set> |
17 | #include <vector> | 19 | #include <vector> |
18 | 20 | ||
21 | #include "godot_variant.h" | ||
22 | |||
19 | constexpr int MIN_FREQUENCY = 2000000; | 23 | constexpr int MIN_FREQUENCY = 2000000; |
24 | constexpr int kCompressionLevel = 3; | ||
20 | 25 | ||
21 | namespace { | 26 | namespace { |
22 | 27 | ||
@@ -877,7 +882,7 @@ void generator::run() { | |||
877 | } | 882 | } |
878 | 883 | ||
879 | // Orange addition | 884 | // Orange addition |
880 | std::vector<std::string> orange_addition; | 885 | std::vector<GodotVariant> orange_addition; |
881 | { | 886 | { |
882 | hatkirby::progress ppgs("Generating orange addition puzzles...", | 887 | hatkirby::progress ppgs("Generating orange addition puzzles...", |
883 | wanderlust_.size()); | 888 | wanderlust_.size()); |
@@ -894,8 +899,12 @@ void generator::run() { | |||
894 | continue; | 899 | continue; |
895 | } | 900 | } |
896 | if (wanderlust_.count(cipher - cipher2)) { | 901 | if (wanderlust_.count(cipher - cipher2)) { |
897 | orange_addition.push_back(fmt::format( | 902 | std::vector<GodotVariant> addition_entry; |
898 | "[{},{},{}]", form_id2, wanderlust_[cipher - cipher2], form_id)); | 903 | addition_entry.emplace_back(static_cast<int32_t>(form_id2)); |
904 | addition_entry.emplace_back( | ||
905 | static_cast<int32_t>(wanderlust_[cipher - cipher2])); | ||
906 | addition_entry.emplace_back(static_cast<int32_t>(form_id)); | ||
907 | orange_addition.emplace_back(addition_entry); | ||
899 | } | 908 | } |
900 | } | 909 | } |
901 | } | 910 | } |
@@ -995,83 +1004,141 @@ void generator::run() { | |||
995 | std::cout << "Yellow top yellow middle combos: " | 1004 | std::cout << "Yellow top yellow middle combos: " |
996 | << combos_[kYellowTop][kYellowMiddle].size() << std::endl; | 1005 | << combos_[kYellowTop][kYellowMiddle].size() << std::endl; |
997 | 1006 | ||
998 | std::vector<std::string> form_entry; | 1007 | // 0: forms |
1008 | // 1: paintings | ||
1009 | // 2: wanderlust | ||
1010 | // 3: addition | ||
1011 | // 4: walls | ||
1012 | // 5: combos | ||
1013 | std::vector<GodotVariant> full_variant; | ||
1014 | |||
1015 | std::vector<GodotVariant> form_entry; | ||
999 | form_entry.reserve(forms_.size()); | 1016 | form_entry.reserve(forms_.size()); |
1000 | for (const Form& form : forms_) { | 1017 | for (const Form& form : forms_) { |
1001 | if (form.puzzles.empty()) { | 1018 | if (form.puzzles.empty()) { |
1002 | form_entry.push_back(fmt::format("\"{}\"", form.text)); | 1019 | form_entry.emplace_back(form.text); |
1003 | } else { | 1020 | } else { |
1004 | std::vector<std::string> entry_per_type; | 1021 | std::map<GodotVariant, GodotVariant> entry_per_type; |
1005 | for (const auto& [puzzle_type, puzzles] : form.puzzles) { | 1022 | for (const auto& [puzzle_type, puzzles] : form.puzzles) { |
1006 | std::vector<std::string> entry_per_puzzle; | 1023 | std::vector<GodotVariant> entry_per_puzzle; |
1007 | for (size_t puzzle : puzzles) { | 1024 | for (size_t puzzle : puzzles) { |
1008 | entry_per_puzzle.push_back(std::to_string(puzzle)); | 1025 | entry_per_puzzle.emplace_back(puzzle); |
1009 | } | 1026 | } |
1010 | entry_per_type.push_back( | 1027 | entry_per_type.emplace( |
1011 | fmt::format("{}:[{}]", static_cast<int>(puzzle_type), | 1028 | std::piecewise_construct, |
1012 | hatkirby::implode(entry_per_puzzle, ","))); | 1029 | std::forward_as_tuple(static_cast<int32_t>(puzzle_type)), |
1030 | std::forward_as_tuple(entry_per_puzzle)); | ||
1031 | } | ||
1032 | std::vector<GodotVariant> pair_entry; | ||
1033 | pair_entry.emplace_back(form.text); | ||
1034 | if (!entry_per_type.empty()) { | ||
1035 | pair_entry.emplace_back(entry_per_type); | ||
1013 | } | 1036 | } |
1014 | form_entry.push_back(fmt::format("[\"{}\",{{{}}}]", form.text, | 1037 | form_entry.emplace_back(pair_entry); |
1015 | hatkirby::implode(entry_per_type, ","))); | ||
1016 | } | 1038 | } |
1017 | } | 1039 | } |
1040 | full_variant.emplace_back(form_entry); | ||
1018 | 1041 | ||
1019 | std::ofstream output_file(outputPath_); | 1042 | std::vector<GodotVariant> painting_entries; |
1020 | output_file << "extends Node\n\nvar forms = [" | ||
1021 | << hatkirby::implode(form_entry, ",") << "]" << std::endl; | ||
1022 | |||
1023 | std::vector<std::string> painting_entries; | ||
1024 | { | 1043 | { |
1025 | std::list<std::string> paintings(readFile(datadirPath_ / "paintings.txt")); | 1044 | std::list<std::string> paintings(readFile(datadirPath_ / "paintings.txt")); |
1026 | for (const std::string& line : paintings) { | 1045 | for (const std::string& line : paintings) { |
1027 | auto parts = hatkirby::split<std::vector<std::string>>(line, ","); | 1046 | auto parts = hatkirby::split<std::vector<std::string>>(line, ","); |
1028 | painting_entries.push_back( | 1047 | std::vector<GodotVariant> painting_entry; |
1029 | fmt::format("[\"{}\",\"{}\"]", parts[0], parts[1])); | 1048 | painting_entry.emplace_back(parts[0]); |
1049 | painting_entry.emplace_back(parts[1]); | ||
1050 | painting_entries.emplace_back(painting_entry); | ||
1030 | } | 1051 | } |
1031 | } | 1052 | } |
1032 | output_file << "var paintings = [" << hatkirby::implode(painting_entries, ",") | 1053 | full_variant.emplace_back(painting_entries); |
1033 | << "]" << std::endl; | ||
1034 | 1054 | ||
1035 | std::vector<std::string> cipher_lines; | 1055 | std::vector<GodotVariant> wanderlust_entries; |
1036 | for (const auto& [cipher, form_id] : wanderlust_) { | 1056 | for (const auto& [cipher, form_id] : wanderlust_) { |
1037 | cipher_lines.push_back(std::to_string(form_id)); | 1057 | wanderlust_entries.emplace_back(static_cast<int32_t>(form_id)); |
1038 | } | 1058 | } |
1039 | output_file << "var wanderlust = [" << hatkirby::implode(cipher_lines, ",") | 1059 | full_variant.emplace_back(wanderlust_entries); |
1040 | << "]" << std::endl; | 1060 | |
1041 | output_file << "var addition = [" << hatkirby::implode(orange_addition, ",") | 1061 | full_variant.emplace_back(orange_addition); |
1042 | << "]" << std::endl; | ||
1043 | 1062 | ||
1044 | std::vector<std::string> walls_entries; | 1063 | std::vector<GodotVariant> walls_entries; |
1045 | { | 1064 | { |
1046 | std::list<std::string> walls(readFile(datadirPath_ / "walls.txt")); | 1065 | std::list<std::string> walls(readFile(datadirPath_ / "walls.txt")); |
1047 | for (const std::string& line : walls) { | 1066 | for (const std::string& line : walls) { |
1048 | auto parts = hatkirby::split<std::vector<std::string>>(line, ","); | 1067 | auto parts = hatkirby::split<std::vector<std::string>>(line, ","); |
1049 | walls_entries.push_back( | 1068 | std::vector<GodotVariant> walls_entry; |
1050 | fmt::format("[\"{}\",\"{}\"]", parts[0], parts[1])); | 1069 | walls_entry.emplace_back(parts[0]); |
1070 | walls_entry.emplace_back(parts[1]); | ||
1071 | walls_entries.emplace_back(walls_entry); | ||
1051 | } | 1072 | } |
1052 | } | 1073 | } |
1053 | output_file << "var walls_puzzles = [" | 1074 | full_variant.emplace_back(walls_entries); |
1054 | << hatkirby::implode(walls_entries, ",") << "]" << std::endl; | ||
1055 | 1075 | ||
1056 | std::vector<std::string> combo_entries; | 1076 | std::map<GodotVariant, GodotVariant> combo_entries; |
1057 | for (const auto& [left_type, left_join] : combos_) { | 1077 | for (const auto& [left_type, left_join] : combos_) { |
1058 | std::vector<std::string> left_entries; | 1078 | std::map<GodotVariant, GodotVariant> left_entries; |
1059 | for (const auto& [right_type, choices] : left_join) { | 1079 | for (const auto& [right_type, choices] : left_join) { |
1060 | std::vector<std::string> choice_entries; | 1080 | std::vector<GodotVariant> choice_entries; |
1061 | for (const auto& [hint1, hint2, answer] : choices) { | 1081 | for (const auto& [hint1, hint2, answer] : choices) { |
1062 | choice_entries.push_back( | 1082 | std::vector<GodotVariant> choice_entry; |
1063 | fmt::format("[{},{},{}]", hint1, hint2, answer)); | 1083 | choice_entry.emplace_back(hint1); |
1084 | choice_entry.emplace_back(hint2); | ||
1085 | choice_entry.emplace_back(answer); | ||
1086 | choice_entries.emplace_back(choice_entry); | ||
1064 | } | 1087 | } |
1065 | left_entries.push_back( | 1088 | left_entries.emplace( |
1066 | fmt::format("{}:[{}]", static_cast<int>(right_type), | 1089 | std::piecewise_construct, |
1067 | hatkirby::implode(choice_entries, ","))); | 1090 | std::forward_as_tuple(static_cast<int32_t>(right_type)), |
1091 | std::forward_as_tuple(choice_entries)); | ||
1068 | } | 1092 | } |
1069 | combo_entries.push_back(fmt::format("{}:{{{}}}", | 1093 | combo_entries.emplace( |
1070 | static_cast<int>(left_type), | 1094 | std::piecewise_construct, |
1071 | hatkirby::implode(left_entries, ","))); | 1095 | std::forward_as_tuple(static_cast<int32_t>(left_type)), |
1096 | std::forward_as_tuple(left_entries)); | ||
1097 | } | ||
1098 | full_variant.emplace_back(combo_entries); | ||
1099 | |||
1100 | std::ostringstream serialized_variant_buffer; | ||
1101 | GodotVariant(full_variant).Serialize(serialized_variant_buffer); | ||
1102 | |||
1103 | std::string serialized_variant = serialized_variant_buffer.str(); | ||
1104 | std::ostringstream variant_file_buffer; | ||
1105 | int32_t variant_len = serialized_variant.size(); | ||
1106 | variant_file_buffer.write(reinterpret_cast<char*>(&variant_len), 4); | ||
1107 | variant_file_buffer.write(serialized_variant.data(), | ||
1108 | serialized_variant.size()); | ||
1109 | |||
1110 | std::string variant_file = variant_file_buffer.str(); | ||
1111 | |||
1112 | size_t max_output_size = ZSTD_compressBound(variant_file.size()); | ||
1113 | std::string compressed_variant(max_output_size, 0); | ||
1114 | size_t compressed_size = ZSTD_compress( | ||
1115 | compressed_variant.data(), max_output_size, variant_file.data(), | ||
1116 | variant_file.size(), kCompressionLevel); | ||
1117 | |||
1118 | // Write Godot's weird gzip container | ||
1119 | { | ||
1120 | std::ofstream output_file(outputPath_, std::ios::binary); | ||
1121 | |||
1122 | int32_t bytes = 0x46504347; | ||
1123 | output_file.write(reinterpret_cast<char*>(&bytes), 4); | ||
1124 | |||
1125 | bytes = 0x00000002; // ZSTD | ||
1126 | output_file.write(reinterpret_cast<char*>(&bytes), 4); | ||
1127 | |||
1128 | bytes = variant_file.size() + 1; | ||
1129 | output_file.write(reinterpret_cast<char*>(&bytes), 4); | ||
1130 | |||
1131 | bytes = variant_file.size(); | ||
1132 | output_file.write(reinterpret_cast<char*>(&bytes), 4); | ||
1133 | |||
1134 | bytes = compressed_size; | ||
1135 | output_file.write(reinterpret_cast<char*>(&bytes), 4); | ||
1136 | |||
1137 | output_file.write(compressed_variant.data(), compressed_size); | ||
1138 | |||
1139 | bytes = 0x46504347; | ||
1140 | output_file.write(reinterpret_cast<char*>(&bytes), 4); | ||
1072 | } | 1141 | } |
1073 | output_file << "var combos = {" << hatkirby::implode(combo_entries, ",") | ||
1074 | << "}" << std::endl; | ||
1075 | } | 1142 | } |
1076 | 1143 | ||
1077 | size_t generator::LookupOrCreatePronunciation(const std::string& phonemes) { | 1144 | size_t generator::LookupOrCreatePronunciation(const std::string& phonemes) { |