11 std::string_view name;
12 std::span<const uint32_t> colours;
20constexpr std::array<uint32_t, 64> nes = {
21 0x808080, 0x003DA6, 0x0012B0, 0x440096, 0xA1005E, 0xC70028, 0xBA0600, 0x8C1700,
22 0x5C2F00, 0x104500, 0x054A00, 0x00472E, 0x004166, 0x000000, 0x050505, 0x050505,
23 0xC7C7C7, 0x0077FF, 0x2155FF, 0x8237FA, 0xEB2FB5, 0xFF2950, 0xFF2200, 0xD63200,
24 0xC46200, 0x358000, 0x058F00, 0x008A55, 0x0099CC, 0x212121, 0x090909, 0x090909,
25 0xFFFFFF, 0x0FD7FF, 0x69A2FF, 0xD480FF, 0xFF45F3, 0xFF618B, 0xFF8833, 0xFF9C12,
26 0xFABC20, 0x9FE30E, 0x2BF035, 0x0CF0A4, 0x05FBFF, 0x5E5E5E, 0x0D0D0D, 0x0D0D0D,
27 0xFFFFFF, 0xA6FCFF, 0xB3ECFF, 0xDAABEB, 0xFFA8F9, 0xFFABB3, 0xFFD2B0, 0xFFEFA6,
28 0xFFF79C, 0xD7E895, 0xA6EDAF, 0xA2F2DA, 0x99FFFC, 0xDDDDDD, 0x111111, 0x111111,
32constexpr std::array<uint32_t, 16> cga = {
33 0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA,
34 0x555555, 0x5555FF, 0x55FF55, 0x55FFFF, 0xFF5555, 0xFF55FF, 0xFFFF55, 0xFFFFFF,
37constexpr std::array<uint32_t, 16> pico8 = {
38 0x000000, 0x1D2B53, 0x7E2553, 0x008751, 0xAB5236, 0x5F574F, 0xC2C3C7, 0xFFF1E8,
39 0xFF004D, 0xFFA300, 0xFFEC27, 0x00E436, 0x29ADFF, 0x83769C, 0xFF77A8, 0xFFCCAA,
42constexpr std::array<uint32_t, 16> tic80 = {
43 0x1A1C2C, 0x5D275D, 0xB13E53, 0xEF7D57, 0xFFCD75, 0xA7F070, 0x38B764, 0x257179,
44 0x29366F, 0x3B5DC9, 0x41A6F6, 0x73EFF7, 0xF4F4F4, 0x94B0C2, 0x566C86, 0x333C57,
54constexpr std::array<uint32_t, 4> cgaGraphics = {0x050505, 0xFFFFFF, 0xFF45F3, 0x05FBFF};
55constexpr std::array<uint32_t, 4> kokobatoru = {0x050505, 0xFFFFFF, 0xFF9C12, 0xFF2200};
56constexpr std::array<uint32_t, 4> mario = {0x69A2FF, 0xFF2200, 0xFF9C12, 0xC46200};
57constexpr std::array<uint32_t, 4> world = {0x69A2FF, 0x9FE30E, 0x058F00, 0x050505};
59constexpr std::array<Known, 8> known = {{
64 {
"cgagraphics", cgaGraphics},
65 {
"kokobatoru", kokobatoru},
70std::optional<size_t> readNumber(std::string_view text) {
72 const auto* end = text.data() + text.size();
73 const auto parsed = std::from_chars(text.data(), end, value);
74 if (parsed.ec != std::errc{} || parsed.ptr != end)
return std::nullopt;
83 static const auto names = [] {
84 std::array<std::string_view, known.size()> out{};
85 std::ranges::transform(known, out.begin(), [](
const Known& one) { return one.name; });
93 std::string_view slice;
99 if (
const auto colon = spec.find(
':'); colon != std::string_view::npos) {
100 name = spec.substr(0, colon);
101 slice = spec.substr(colon + 1);
104 "\"" + std::string(spec) +
"\" names nothing after its colon"});
108 const auto found = std::ranges::find_if(known, [name](
const Known& one) {
return one.name == name; });
109 if (found == known.end()) {
110 std::string message =
"no palette called \"" + std::string(name) +
"\"; haio knows ";
111 for (
const auto& one : known) {
112 message += std::string(one.name);
113 if (&one != &known.back()) message +=
", ";
118 const auto colours = found->colours;
119 if (slice.empty())
return std::vector<uint32_t>(colours.begin(), colours.end());
121 std::vector<uint32_t> out;
132 const bool isBank = slice.find(
',') == std::string_view::npos
133 && slice.find(
"..") == std::string_view::npos;
136 const auto bank = readNumber(slice);
137 if (!bank || *bank == 0) {
139 "a palette bank is counted from one, as in " + std::string(name)
140 +
":1, and a span is written " + std::string(name) +
":1..3"});
144 "a bank is as wide as the picture needs, and this picture needs nothing"});
147 const auto from = (*bank - 1) * wanted;
148 if (from + wanted > colours.size()) {
150 std::string(name) +
" has " + std::to_string(colours.size())
151 +
" colours, which is not enough for bank " + std::to_string(*bank)
152 +
" of " + std::to_string(wanted)});
154 out.assign(colours.begin() + from, colours.begin() + from + wanted);
158 const auto colourAt = [&](std::string_view piece) ->
Result<size_t> {
159 const auto at = readNumber(piece);
160 if (!at || *at == 0 || *at > colours.size()) {
162 "\"" + std::string(piece) +
"\" is not a colour of " + std::string(name)
163 +
", which has " + std::to_string(colours.size())
164 +
" counted from one"});
169 while (!slice.empty()) {
170 const auto comma = slice.find(
',');
171 const auto piece = slice.substr(0, comma);
173 if (
const auto dots = piece.find(
".."); dots != std::string_view::npos) {
174 const auto first = colourAt(piece.substr(0, dots));
175 if (!first)
return std::unexpected(first.error());
176 const auto last = colourAt(piece.substr(dots + 2));
177 if (!last)
return std::unexpected(last.error());
179 if (*first > *last) {
181 "\"" + std::string(piece) +
"\" runs backwards"});
183 for (
auto at = *first; at <= *last; at++) out.push_back(colours[at - 1]);
185 const auto at = colourAt(piece);
186 if (!at)
return std::unexpected(at.error());
187 out.push_back(colours[*at - 1]);
190 if (comma == std::string_view::npos)
break;
191 slice.remove_prefix(comma + 1);
Result< std::vector< uint32_t > > paletteNamed(std::string_view spec, size_t wanted)
std::expected< T, Error > Result
std::span< const std::string_view > paletteNames()