16#include "absl/status/status.h"
17#include "absl/status/statusor.h"
18#include "absl/strings/str_format.h"
20#include "imgui/imgui.h"
28 assert((
sizeof(data) % 4 == 0) && (
sizeof(data) <= 32));
29 for (
unsigned i = 0; i <
sizeof(data); i += 2) {
31 col.
set_snes(
static_cast<uint8_t
>(data[i + 1]) << 8);
32 col.
set_snes(col.
snes() |
static_cast<uint8_t
>(data[i]));
38 assert((
sizeof(snes_pal) % 4 == 0) && (
sizeof(snes_pal) <= 32));
39 for (
unsigned i = 0; i <
sizeof(snes_pal); i += 2) {
41 col.
set_snes(snes_pal[i + 1] << (uint16_t)8);
49 uint16_t color = (
static_cast<uint8_t
>(data[i + 1]) << 8) |
50 static_cast<uint8_t
>(data[i]);
56 for (
const auto& color : colors) {
64 for (
const auto& color : colors) {
72 for (
const auto& color : colors) {
83namespace palette_group_internal {
85 std::optional<uint32_t> snes_address, uint64_t color_count,
86 size_t normalized_rom_size) {
87 if (!snes_address.has_value()) {
88 return absl::FailedPreconditionError(
"Palette record storage is unbound");
90 if (color_count > std::numeric_limits<uint32_t>::max() / 2u) {
91 return absl::OutOfRangeError(
"Palette record byte size overflows");
93 if (color_count != 35 && color_count != 21) {
94 return absl::InvalidArgumentError(
95 "Native palette records require 35 or 21 colors");
97 const uint32_t address = *snes_address;
98 if (address > 0xFFFFFFu || (address & 0x8000u) == 0) {
99 return absl::InvalidArgumentError(
"Palette address is not mapped LoROM");
101 if (((address >> 16) & 0x7Fu) != 0x1Bu) {
102 return absl::FailedPreconditionError(
103 "Palette bank ABI is bound only to bank 1B");
105 const size_t bytes =
static_cast<size_t>(color_count) * 2;
106 if (bytes > 0x10000u - (address & 0xFFFFu)) {
107 return absl::OutOfRangeError(
"Palette record crosses its native bank");
111 return absl::InvalidArgumentError(
"Palette address does not round trip");
113 if (pc > normalized_rom_size || bytes > normalized_rom_size - pc) {
114 return absl::OutOfRangeError(
"Palette record exceeds normalized ROM body");
117 static_cast<int>(color_count));
121 int color_index)
const {
123 return absl::OutOfRangeError(
124 "Palette color selector is outside its record");
126 return pc_address_ +
static_cast<uint32_t
>(color_index) * 2u;
129absl::StatusOr<std::pair<uint32_t, uint32_t>>
133 return address.status();
135 return std::make_pair(*address, *address + 2u);
160 const auto* metadata = OverworldMetadata(kind);
164 return metadata->palette_count +
170 const auto* metadata = OverworldMetadata(kind);
172 return absl::InvalidArgumentError(
"Unknown overworld palette kind");
174 if (selector < 0 || selector >=
RecordCount(kind)) {
175 return absl::OutOfRangeError(
176 "Palette record selector is outside its layout");
178 if (selector >= metadata->palette_count) {
180 normalized_rom_size);
183 metadata->base_address +
184 static_cast<uint64_t
>(selector) * metadata->colors_per_palette * 2u;
185 if (pc > std::numeric_limits<uint32_t>::max()) {
186 return absl::OutOfRangeError(
"Palette record address overflows");
189 metadata->colors_per_palette,
190 normalized_rom_size);
195 const std::vector<uint8_t>& normalized_rom_data,
PaletteGroup& group) {
198 return absl::InvalidArgumentError(
"Unknown overworld palette kind");
200 std::vector<ResolvedPaletteRecord> records;
201 records.reserve(count);
202 for (
int selector = 0; selector < count; ++selector) {
203 auto record = layout.
Resolve(kind, selector, normalized_rom_data.size());
205 return record.status();
207 records.push_back(*record);
210 for (
const auto& record : records) {
212 record.pc_address(), record.color_count(), normalized_rom_data.data()));
214 return absl::OkStatus();
225 const std::vector<uint8_t>& rom_data,
233 const std::vector<uint8_t>& rom_data,
235 auto data = rom_data.data();
236 size_t rom_size = rom_data.size();
237 for (
int i = 0; i < 14; i++) {
240 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
241 return absl::OutOfRangeError(absl::StrFormat(
242 "Overworld animated palette %d out of bounds: offset %d, size %zu", i,
248 return absl::OkStatus();
253 auto data = rom_data.data();
254 size_t rom_size = rom_data.size();
255 for (
int i = 0; i < 2; i++) {
258 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
259 return absl::OutOfRangeError(
260 absl::StrFormat(
"HUD palette %d out of bounds: offset %d, size %zu",
261 i, offset, rom_size));
266 return absl::OkStatus();
271 auto data = rom_data.data();
272 size_t rom_size = rom_data.size();
276 int num_colors1 = 60;
277 if (offset1 < 0 || offset1 + (num_colors1 * 2) >
static_cast<int>(rom_size)) {
278 return absl::OutOfRangeError(absl::StrFormat(
279 "Global sprite LW palette out of bounds: offset %d, size %zu", offset1,
287 int num_colors2 = 60;
288 if (offset2 < 0 || offset2 + (num_colors2 * 2) >
static_cast<int>(rom_size)) {
289 return absl::OutOfRangeError(absl::StrFormat(
290 "Global sprite DW palette out of bounds: offset %d, size %zu", offset2,
295 return absl::OkStatus();
300 auto data = rom_data.data();
301 size_t rom_size = rom_data.size();
302 for (
int i = 0; i < 5; i++) {
305 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
306 return absl::OutOfRangeError(
307 absl::StrFormat(
"Armor palette %d out of bounds: offset %d, size %zu",
308 i, offset, rom_size));
313 return absl::OkStatus();
318 auto data = rom_data.data();
319 size_t rom_size = rom_data.size();
320 for (
int i = 0; i < 4; i++) {
323 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
324 return absl::OutOfRangeError(
325 absl::StrFormat(
"Sword palette %d out of bounds: offset %d, size %zu",
326 i, offset, rom_size));
331 return absl::OkStatus();
336 auto data = rom_data.data();
337 size_t rom_size = rom_data.size();
338 for (
int i = 0; i < 3; i++) {
341 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
342 return absl::OutOfRangeError(absl::StrFormat(
343 "Shield palette %d out of bounds: offset %d, size %zu", i, offset,
349 return absl::OkStatus();
354 auto data = rom_data.data();
355 size_t rom_size = rom_data.size();
356 for (
int i = 0; i < 12; i++) {
359 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
360 return absl::OutOfRangeError(absl::StrFormat(
361 "Sprite aux1 palette %d out of bounds: offset %d, size %zu", i,
367 return absl::OkStatus();
372 auto data = rom_data.data();
373 size_t rom_size = rom_data.size();
374 for (
int i = 0; i < 11; i++) {
377 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
378 return absl::OutOfRangeError(absl::StrFormat(
379 "Sprite aux2 palette %d out of bounds: offset %d, size %zu", i,
385 return absl::OkStatus();
390 auto data = rom_data.data();
391 size_t rom_size = rom_data.size();
392 for (
int i = 0; i < 24; i++) {
395 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
396 return absl::OutOfRangeError(absl::StrFormat(
397 "Sprite aux3 palette %d out of bounds: offset %d, size %zu", i,
403 return absl::OkStatus();
408 auto data = rom_data.data();
409 size_t rom_size = rom_data.size();
410 for (
int i = 0; i < 20; i++) {
413 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
414 return absl::OutOfRangeError(absl::StrFormat(
415 "Dungeon main palette %d out of bounds: offset %d, size %zu", i,
421 return absl::OkStatus();
426 size_t rom_size = rom_data.size();
431 return absl::OutOfRangeError(
432 absl::StrFormat(
"Grass LW color out of bounds: offset %d, size %zu",
440 return absl::OutOfRangeError(
441 absl::StrFormat(
"Grass DW color out of bounds: offset %d, size %zu",
449 return absl::OutOfRangeError(absl::StrFormat(
450 "Grass special color out of bounds: offset %d, size %zu",
455 return absl::OkStatus();
460 auto data = rom_data.data();
461 size_t rom_size = rom_data.size();
465 if (offset1 < 0 || offset1 + (num_colors1 * 2) >
static_cast<int>(rom_size)) {
466 return absl::OutOfRangeError(
467 absl::StrFormat(
"Triforce palette out of bounds: offset %d, size %zu",
475 if (offset2 < 0 || offset2 + (num_colors2 * 2) >
static_cast<int>(rom_size)) {
476 return absl::OutOfRangeError(
477 absl::StrFormat(
"Crystal palette out of bounds: offset %d, size %zu",
482 return absl::OkStatus();
486 const std::vector<uint8_t>& rom_data,
488 auto data = rom_data.data();
489 size_t rom_size = rom_data.size();
490 for (
int i = 0; i < 2; i++) {
492 int num_colors = 128;
493 if (offset < 0 || offset + (num_colors * 2) >
static_cast<int>(rom_size)) {
494 return absl::OutOfRangeError(absl::StrFormat(
495 "Overworld minimap palette %d out of bounds: offset %d, size %zu", i,
501 return absl::OkStatus();
505constexpr std::array<std::pair<absl::string_view, uint32_t>, 15>
524constexpr std::array<std::pair<absl::string_view, uint32_t>, 15>
530 {
"global_sprites", 60},
537 {
"dungeon_main", 90},
540 {
"ow_mini_map", 128},
545 absl::string_view group_name,
546 const std::array<std::pair<absl::string_view, uint32_t>, N>& table) {
547 for (
const auto& [name, value] : table) {
548 if (name == group_name) {
552 throw std::out_of_range(
553 absl::StrFormat(
"Unknown palette group: %s", group_name));
557 size_t color_index) {
559 const uint32_t base_address =
561 const uint32_t colors_per_palette =
565 uint32_t address = base_address + (palette_index * colors_per_palette * 2) +
598 int color_offset = 0;
599 std::vector<gfx::SnesColor> colors(num_colors);
601 while (color_offset < num_colors) {
608 uint16_t snes_color_word = (uint16_t)((rom[offset + 1]) << 8) | rom[offset];
612 new_color.
red = (snes_color_word & 0x1F) * 8;
613 new_color.
green = ((snes_color_word >> 5) & 0x1F) * 8;
614 new_color.
blue = ((snes_color_word >> 10) & 0x1F) * 8;
631 std::array<float, 4> colorArray;
632 colorArray[0] = color.
rgb().x / 255.0f;
633 colorArray[1] = color.
rgb().y / 255.0f;
634 colorArray[2] = color.
rgb().z / 255.0f;
635 colorArray[3] = color.
rgb().w;
640 std::vector<SnesColor>& palette_rows) {
642 for (
int i = 0; i < palette_rows.size(); i += 8) {
644 for (
int j = 0; j < 8; j++) {
645 palette.
AddColor(palette_rows[i + j]);
649 return palette_group;
670 for (
int i = 0; i < palette.
size(); i += num_colors) {
672 if (i + num_colors <= palette.
size()) {
673 for (
int j = 0; j < num_colors; j++) {
674 auto color = palette[i + j];
682 return palette_group;
685using namespace palette_group_internal;
705 return absl::OkStatus();
static std::unordered_map< uint8_t, gfx::Paletteset > palettesets_
constexpr ImVec4 rgb() const
Get RGB values (WARNING: stored as 0-255 in ImVec4)
void set_snes(uint16_t val)
Set color from SNES 15-bit format.
constexpr uint16_t snes() const
Get SNES 15-bit color.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void AddColor(const SnesColor &color)
static constexpr size_t kMaxColors
OverworldPaletteLayout(bool proposed)
static OverworldPaletteLayout ProposedUnbound()
absl::StatusOr< ResolvedPaletteRecord > Resolve(OverworldPaletteKind kind, int selector, size_t normalized_rom_size) const
int RecordCount(OverworldPaletteKind kind) const
static OverworldPaletteLayout Legacy()
absl::StatusOr< std::pair< uint32_t, uint32_t > > ColorWriteRange(int color_index) const
absl::StatusOr< uint32_t > ColorAddress(int color_index) const
const zelda3::PaletteGroupMetadata * OverworldMetadata(OverworldPaletteKind kind)
absl::Status LoadOverworldPaletteRecords(OverworldPaletteKind kind, const OverworldPaletteLayout &layout, const std::vector< uint8_t > &normalized_rom_data, PaletteGroup &group)
absl::StatusOr< ResolvedPaletteRecord > ResolvePaletteRecord(std::optional< uint32_t > snes_address, uint64_t color_count, size_t normalized_rom_size)
absl::Status LoadDungeonMainPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadOverworldMiniMapPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadSpriteAux2Palettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadOverworldMainPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadSpriteAux1Palettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadGlobalSpritePalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadOverworldAnimatedPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadArmorPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadGrassColors(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadSpriteAux3Palettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadHUDPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadShieldPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadOverworldAuxiliaryPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status Load3DObjectPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
absl::Status LoadSwordPalettes(const std::vector< uint8_t > &rom_data, gfx::PaletteGroupMap &palette_groups)
Contains classes for handling graphical data.
constexpr int kHardcodedGrassSpecial
constexpr int kHudPalettes
constexpr int kOverworldPaletteAux
SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t *rom)
Read a palette from ROM data.
uint32_t LookupPaletteGroupValue(absl::string_view group_name, const std::array< std::pair< absl::string_view, uint32_t >, N > &table)
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromLargePalette(SnesPalette &palette, int num_colors)
Create a PaletteGroup by dividing a large palette into sub-palettes.
constexpr int kHardcodedGrassLW
constexpr int kArmorPalettes
constexpr std::array< std::pair< absl::string_view, uint32_t >, 15 > kPaletteGroupAddressMap
constexpr int kCrystalPalette
uint16_t ConvertRgbToSnes(const snes_color &color)
Convert RGB (0-255) to SNES 15-bit color.
constexpr int kShieldPalettes
constexpr int kSpritesPalettesAux2
constexpr int kOverworldPaletteAnimated
SnesColor ReadColorFromRom(int offset, const uint8_t *rom)
constexpr int kOverworldMiniMapPalettes
constexpr int kOverworldPaletteMain
constexpr int kSwordPalettes
constexpr int kHardcodedGrassDW
constexpr int kGlobalSpritesLW
constexpr int kSpritesPalettesAux1
constexpr std::array< std::pair< absl::string_view, uint32_t >, 15 > kPaletteGroupColorCounts
std::array< float, 4 > ToFloatArray(const SnesColor &color)
constexpr int kGlobalSpritePalettesDW
constexpr int kSpritesPalettesAux3
constexpr int kDungeonMainPalettes
uint32_t GetPaletteAddress(const std::string &group_name, size_t palette_index, size_t color_index)
constexpr int kTriforcePalette
absl::Status LoadAllPalettes(const std::vector< uint8_t > &rom_data, PaletteGroupMap &groups)
Loads all the palettes for the game.
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromColFile(std::vector< SnesColor > &palette_rows)
uint32_t PcToSnes(uint32_t addr)
uint32_t SnesToPc(uint32_t addr) noexcept
SDL2/SDL3 compatibility layer.
#define RETURN_IF_ERROR(expr)
SNES color in 15-bit RGB format (BGR555)
Represents a mapping of palette groups.
PaletteGroup overworld_main
PaletteGroup overworld_aux
PaletteGroup overworld_mini_map
PaletteGroup sprites_aux1
PaletteGroup sprites_aux2
PaletteGroup overworld_animated
PaletteGroup sprites_aux3
PaletteGroup dungeon_main
PaletteGroup global_sprites
Represents a group of palettes.
void AddColor(SnesColor color)
void AddPalette(SnesPalette pal)