yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_draw_import.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_SPRITE_SPRITE_DRAW_IMPORT_H_
2#define YAZE_APP_EDITOR_SPRITE_SPRITE_DRAW_IMPORT_H_
3
4#include <array>
5#include <cctype>
6#include <charconv>
7
8#include <string_view>
10
12
14// Reads only literal tables in one already-resolved global label scope. This is
15// a frame-data copy, not an assembler or a claim about the surrounding driver.
16inline absl::StatusOr<std::vector<zsprite::Frame>> ImportDrawTables(
17 const std::vector<std::string>& lines, size_t first_line) {
18 constexpr std::array<std::string_view, 7> names = {
19 ".start_index", ".nbr_of_tiles", ".x_offsets", ".y_offsets",
20 ".chr", ".properties", ".sizes"};
21 std::array<std::vector<int>, 7> values;
22 std::array<bool, 7> seen{};
23 int active = -1;
24 auto trim = [](std::string_view value) {
25 const auto begin = value.find_first_not_of(" \t\r");
26 return begin == std::string_view::npos
27 ? std::string_view{}
28 : value.substr(begin,
29 value.find_last_not_of(" \t\r") - begin + 1);
30 };
31 auto invalid = [] {
32 return absl::InvalidArgumentError(
33 "Unsupported draw tables: need seven unique literal db/dw tables in "
34 "one label scope");
35 };
36 for (size_t i = first_line; i < lines.size(); ++i) {
37 auto line = trim(std::string_view(lines[i]).substr(0, lines[i].find(';')));
38 if (line.empty() || line == "{")
39 continue;
40 if (line == "}")
41 break;
42 if (line.back() == ':' && line.front() != '.')
43 break;
44 int table = -1;
45 for (size_t j = 0; j < names.size(); ++j) {
46 if (line == names[j] ||
47 (line.back() == ':' && line.substr(0, line.size() - 1) == names[j]))
48 table = j;
49 }
50 if (table >= 0) {
51 if (seen[table])
52 return invalid();
53 seen[table] = true;
54 active = table;
55 continue;
56 }
57 if (active < 0)
58 continue; // Skip instructions before the first table.
59 if (line.size() < 4 ||
60 line.substr(0, 2) != (active == 2 || active == 3 ? "dw" : "db") ||
61 (line[2] != ' ' && line[2] != '\t'))
62 return invalid();
63 line = trim(line.substr(3));
64 while (!line.empty()) {
65 auto comma = line.find(',');
66 auto token = trim(line.substr(0, comma));
67 if (token.empty())
68 return invalid();
69 int base = 10;
70 if (token.front() == '$') {
71 base = 16;
72 token.remove_prefix(1);
73 } else if (token.front() == '%') {
74 base = 2;
75 token.remove_prefix(1);
76 }
77 int value = 0;
78 auto result = std::from_chars(token.data(), token.data() + token.size(),
79 value, base);
80 if (result.ec != std::errc{} || result.ptr != token.data() + token.size())
81 return invalid();
82 if (active == 2 || active == 3) {
83 if (value < -32768 || value > 65535)
84 return invalid();
85 if (value > 32767)
86 value -= 65536;
87 } else if (value < 0 || value > 255)
88 return invalid();
89 values[active].push_back(value);
90 if (values[active].size() > 256)
91 return invalid();
92 if (comma == std::string_view::npos)
93 break;
94 line = trim(line.substr(comma + 1));
95 if (line.empty())
96 return invalid();
97 }
98 }
99 for (size_t i = 0; i < names.size(); ++i)
100 if (!seen[i] || values[i].empty())
101 return invalid();
102 if (values[0].size() != values[1].size())
103 return invalid();
104 const size_t tiles = values[2].size();
105 if (tiles > 128)
106 return invalid();
107 for (size_t i = 3; i < names.size(); ++i)
108 if (values[i].size() != tiles)
109 return invalid();
110 std::vector<zsprite::Frame> frames;
111 for (size_t i = 0; i < values[0].size(); ++i) {
112 const size_t start = values[0][i], count = values[1][i] + 1;
113 if (start + count > tiles)
114 return invalid();
115 zsprite::Frame frame;
116 for (size_t j = start; j < start + count; ++j) {
117 if (values[2][j] < -128 || values[2][j] > 127 || values[3][j] < -112 ||
118 values[3][j] > 143 || (values[6][j] != 0 && values[6][j] != 2))
119 return invalid();
120 const int property = values[5][j];
121 frame.Tiles.emplace_back(
122 values[2][j] + kOriginX, values[3][j] + kOriginY, property & 64,
123 property & 128, values[4][j] | ((property & 1) << 8),
124 (property >> 1) & 7, values[6][j] == 2, (property >> 4) & 3);
125 }
126 frames.push_back(std::move(frame));
127 }
128 return frames;
129}
130
131// Explicit adapter for the reviewed Oracle Maple driver. Unlike the generic
132// importer, this verifies the complete instruction body before supplying the
133// two constants absent from its tables. Code changes require adapter review.
134inline absl::StatusOr<std::vector<zsprite::Frame>> ImportMapleDrawTables(
135 const std::vector<std::string>& lines, size_t first_line) {
136 std::string driver;
137 size_t table_start = lines.size();
138 for (size_t i = first_line; i < lines.size(); ++i) {
139 auto text = lines[i].substr(0, lines[i].find(';'));
140 text.erase(std::remove_if(text.begin(), text.end(),
141 [](unsigned char c) { return std::isspace(c); }),
142 text.end());
143 if (text == ".start_index" || text == ".start_index:") {
144 table_start = i;
145 break;
146 }
147 if (text.empty() || text == "{")
148 continue;
149 if (text == "}")
150 break;
151 driver += text;
152 }
153 constexpr const char* kReviewedDriverSha256 =
154 "45fc82df667e131b3979473c4c6e9d12c890a7fb1e7589b024405a98b3987e6f";
155 if (core::ComputeSourceArtifactSha256(driver) != kReviewedDriverSha256)
156 return absl::FailedPreconditionError(
157 "Maple draw driver changed; adapter review required");
158 std::vector<std::string> tables;
159 size_t tile_count = 0;
160 bool reading_chr = false;
161 for (size_t i = table_start; i < lines.size(); ++i) {
162 auto text = lines[i].substr(0, lines[i].find(';'));
163 auto begin = text.find_first_not_of(" \t\r");
164 if (begin == std::string::npos)
165 continue;
166 text = text.substr(begin, text.find_last_not_of(" \t\r") - begin + 1);
167 if (text == "}" || (text.back() == ':' && text.front() != '.'))
168 break;
169 if (text.front() == '.')
170 reading_chr = (text == ".chr" || text == ".chr:");
171 else if (reading_chr && text.rfind("db", 0) == 0)
172 tile_count += std::count(text.begin(), text.end(), ',') + 1;
173 tables.push_back(text);
174 }
175 if (tile_count == 0 || tile_count > 128)
176 return absl::InvalidArgumentError("Invalid Maple tile count");
177 std::string xs = "dw 0", sizes = "db 2";
178 for (size_t i = 1; i < tile_count; ++i) {
179 xs += ", 0";
180 sizes += ", 2";
181 }
182 tables.insert(tables.end(), {".x_offsets", xs, ".sizes", sizes});
183 return ImportDrawTables(tables, 0);
184}
185
186inline absl::StatusOr<std::vector<zsprite::Frame>> ImportDrawAsset(
187 const std::vector<std::string>& lines, size_t first_line,
188 const std::string& adapter) {
189 if (adapter == "literal_v1")
190 return ImportDrawTables(lines, first_line);
191 if (adapter == "oracle_maple_v1")
192 return ImportMapleDrawTables(lines, first_line);
193 return absl::InvalidArgumentError("Unsupported draw adapter");
194}
195} // namespace yaze::editor::sprite_authoring
196#endif
std::string ComputeSourceArtifactSha256(std::string_view content)
absl::StatusOr< std::vector< zsprite::Frame > > ImportDrawAsset(const std::vector< std::string > &lines, size_t first_line, const std::string &adapter)
absl::StatusOr< std::vector< zsprite::Frame > > ImportDrawTables(const std::vector< std::string > &lines, size_t first_line)
absl::StatusOr< std::vector< zsprite::Frame > > ImportMapleDrawTables(const std::vector< std::string > &lines, size_t first_line)
std::vector< OamTile > Tiles
Definition zsprite.h:112