yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
snes_palette.cc
Go to the documentation of this file.
2
3#include <SDL.h>
4
5#include <cstdint>
6#include <cstdlib>
7#include <cstring>
8#include <vector>
9
10#include "absl/container/flat_hash_map.h"
11#include "absl/status/status.h"
12#include "absl/status/statusor.h"
14#include "imgui/imgui.h"
15#include "util/macro.h"
16
17namespace yaze::gfx {
18
20 assert((sizeof(data) % 4 == 0) && (sizeof(data) <= 32));
21 for (unsigned i = 0; i < sizeof(data); i += 2) {
22 SnesColor col;
23 col.set_snes(static_cast<uint8_t>(data[i + 1]) << 8);
24 col.set_snes(col.snes() | static_cast<uint8_t>(data[i]));
25 snes_color mColor = ConvertSnesToRgb(col.snes());
26 col.set_rgb(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f));
27 colors_[size_++] = col;
28 }
29}
30
31SnesPalette::SnesPalette(const unsigned char *snes_pal) {
32 assert((sizeof(snes_pal) % 4 == 0) && (sizeof(snes_pal) <= 32));
33 for (unsigned i = 0; i < sizeof(snes_pal); i += 2) {
34 SnesColor col;
35 col.set_snes(snes_pal[i + 1] << (uint16_t)8);
36 col.set_snes(col.snes() | snes_pal[i]);
37 snes_color mColor = ConvertSnesToRgb(col.snes());
38 col.set_rgb(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f));
39 colors_[size_++] = col;
40 }
41}
42
43SnesPalette::SnesPalette(const char *data, size_t length) : size_(0) {
44 for (size_t i = 0; i < length && size_ < kMaxColors; i += 2) {
45 uint16_t color = (static_cast<uint8_t>(data[i + 1]) << 8) |
46 static_cast<uint8_t>(data[i]);
47 colors_[size_++] = SnesColor(color);
48 }
49}
50
51SnesPalette::SnesPalette(const std::vector<uint16_t> &colors) : size_(0) {
52 for (const auto &color : colors) {
53 if (size_ < kMaxColors) {
54 colors_[size_++] = SnesColor(color);
55 }
56 }
57}
58
59SnesPalette::SnesPalette(const std::vector<SnesColor> &colors) : size_(0) {
60 for (const auto &color : colors) {
61 if (size_ < kMaxColors) {
62 colors_[size_++] = color;
63 }
64 }
65}
66
67SnesPalette::SnesPalette(const std::vector<ImVec4> &colors) : size_(0) {
68 for (const auto &color : colors) {
69 if (size_ < kMaxColors) {
70 colors_[size_++] = SnesColor(color);
71 }
72 }
73}
74
79namespace palette_group_internal {
80absl::Status LoadOverworldMainPalettes(const std::vector<uint8_t> &rom_data,
81 gfx::PaletteGroupMap &palette_groups) {
82 auto data = rom_data.data();
83 for (int i = 0; i < 6; i++) {
84 palette_groups.overworld_main.AddPalette(
86 /*num_colors=*/35, data));
87 }
88 return absl::OkStatus();
89}
90
92 const std::vector<uint8_t> &rom_data,
93 gfx::PaletteGroupMap &palette_groups) {
94 auto data = rom_data.data();
95 for (int i = 0; i < 20; i++) {
96 palette_groups.overworld_aux.AddPalette(
98 /*num_colors=*/21, data));
99 }
100 return absl::OkStatus();
101}
102
104 const std::vector<uint8_t> &rom_data,
105 gfx::PaletteGroupMap &palette_groups) {
106 auto data = rom_data.data();
107 for (int i = 0; i < 14; i++) {
109 kOverworldPaletteAnimated + (i * (7 * 2)), /*num_colors=*/7, data));
110 }
111 return absl::OkStatus();
112}
113
114absl::Status LoadHUDPalettes(const std::vector<uint8_t> &rom_data,
115 gfx::PaletteGroupMap &palette_groups) {
116 auto data = rom_data.data();
117 for (int i = 0; i < 2; i++) {
119 kHudPalettes + (i * 64), /*num_colors=*/32, data));
120 }
121 return absl::OkStatus();
122}
123
124absl::Status LoadGlobalSpritePalettes(const std::vector<uint8_t> &rom_data,
125 gfx::PaletteGroupMap &palette_groups) {
126 auto data = rom_data.data();
127 palette_groups.global_sprites.AddPalette(
128 gfx::ReadPaletteFromRom(kGlobalSpritesLW, /*num_colors=*/60, data));
130 kGlobalSpritePalettesDW, /*num_colors=*/60, data));
131 return absl::OkStatus();
132}
133
134absl::Status LoadArmorPalettes(const std::vector<uint8_t> &rom_data,
135 gfx::PaletteGroupMap &palette_groups) {
136 auto data = rom_data.data();
137 for (int i = 0; i < 5; i++) {
139 kArmorPalettes + (i * 30), /*num_colors=*/15, data));
140 }
141 return absl::OkStatus();
142}
143
144absl::Status LoadSwordPalettes(const std::vector<uint8_t> &rom_data,
145 gfx::PaletteGroupMap &palette_groups) {
146 auto data = rom_data.data();
147 for (int i = 0; i < 4; i++) {
149 kSwordPalettes + (i * 6), /*num_colors=*/3, data));
150 }
151 return absl::OkStatus();
152}
153
154absl::Status LoadShieldPalettes(const std::vector<uint8_t> &rom_data,
155 gfx::PaletteGroupMap &palette_groups) {
156 auto data = rom_data.data();
157 for (int i = 0; i < 3; i++) {
159 kShieldPalettes + (i * 8), /*num_colors=*/4, data));
160 }
161 return absl::OkStatus();
162}
163
164absl::Status LoadSpriteAux1Palettes(const std::vector<uint8_t> &rom_data,
165 gfx::PaletteGroupMap &palette_groups) {
166 auto data = rom_data.data();
167 for (int i = 0; i < 12; i++) {
169 kSpritesPalettesAux1 + (i * 14), /*num_colors=*/7, data));
170 }
171 return absl::OkStatus();
172}
173
174absl::Status LoadSpriteAux2Palettes(const std::vector<uint8_t> &rom_data,
175 gfx::PaletteGroupMap &palette_groups) {
176 auto data = rom_data.data();
177 for (int i = 0; i < 11; i++) {
179 kSpritesPalettesAux2 + (i * 14), /*num_colors=*/7, data));
180 }
181 return absl::OkStatus();
182}
183
184absl::Status LoadSpriteAux3Palettes(const std::vector<uint8_t> &rom_data,
185 gfx::PaletteGroupMap &palette_groups) {
186 auto data = rom_data.data();
187 for (int i = 0; i < 24; i++) {
189 kSpritesPalettesAux3 + (i * 14), /*num_colors=*/7, data));
190 }
191 return absl::OkStatus();
192}
193
194absl::Status LoadDungeonMainPalettes(const std::vector<uint8_t> &rom_data,
195 gfx::PaletteGroupMap &palette_groups) {
196 auto data = rom_data.data();
197 for (int i = 0; i < 20; i++) {
199 kDungeonMainPalettes + (i * 180), /*num_colors=*/90, data));
200 }
201 return absl::OkStatus();
202}
203
204absl::Status LoadGrassColors(const std::vector<uint8_t> &rom_data,
205 gfx::PaletteGroupMap &palette_groups) {
206 palette_groups.grass.AddColor(
207 gfx::ReadColorFromRom(kHardcodedGrassLW, rom_data.data()));
208 palette_groups.grass.AddColor(
209 gfx::ReadColorFromRom(kHardcodedGrassDW, rom_data.data()));
210 palette_groups.grass.AddColor(
212 return absl::OkStatus();
213}
214
215absl::Status Load3DObjectPalettes(const std::vector<uint8_t> &rom_data,
216 gfx::PaletteGroupMap &palette_groups) {
217 auto data = rom_data.data();
218 palette_groups.object_3d.AddPalette(
220 palette_groups.object_3d.AddPalette(
222 return absl::OkStatus();
223}
224
226 const std::vector<uint8_t> &rom_data,
227 gfx::PaletteGroupMap &palette_groups) {
228 auto data = rom_data.data();
229 for (int i = 0; i < 2; i++) {
231 kOverworldMiniMapPalettes + (i * 256), /*num_colors=*/128, data));
232 }
233 return absl::OkStatus();
234}
235} // namespace palette_group_internal
236
237const absl::flat_hash_map<std::string, uint32_t> kPaletteGroupAddressMap = {
238 {"ow_main", kOverworldPaletteMain},
239 {"ow_aux", kOverworldPaletteAux},
240 {"ow_animated", kOverworldPaletteAnimated},
241 {"hud", kHudPalettes},
242 {"global_sprites", kGlobalSpritesLW},
243 {"armors", kArmorPalettes},
244 {"swords", kSwordPalettes},
245 {"shields", kShieldPalettes},
246 {"sprites_aux1", kSpritesPalettesAux1},
247 {"sprites_aux2", kSpritesPalettesAux2},
248 {"sprites_aux3", kSpritesPalettesAux3},
249 {"dungeon_main", kDungeonMainPalettes},
250 {"grass", kHardcodedGrassLW},
251 {"3d_object", kTriforcePalette},
252 {"ow_mini_map", kOverworldMiniMapPalettes},
253};
254
255const absl::flat_hash_map<std::string, uint32_t> kPaletteGroupColorCounts = {
256 {"ow_main", 35}, {"ow_aux", 21}, {"ow_animated", 7},
257 {"hud", 32}, {"global_sprites", 60}, {"armors", 15},
258 {"swords", 3}, {"shields", 4}, {"sprites_aux1", 7},
259 {"sprites_aux2", 7}, {"sprites_aux3", 7}, {"dungeon_main", 90},
260 {"grass", 1}, {"3d_object", 8}, {"ow_mini_map", 128},
261};
262
263uint32_t GetPaletteAddress(const std::string &group_name, size_t palette_index,
264 size_t color_index) {
265 // Retrieve the base address for the palette group
266 uint32_t base_address = kPaletteGroupAddressMap.at(group_name);
267
268 // Retrieve the number of colors for each palette in the group
269 uint32_t colors_per_palette = kPaletteGroupColorCounts.at(group_name);
270
271 // Calculate the address for thes specified color in the ROM
272 uint32_t address = base_address + (palette_index * colors_per_palette * 2) +
273 (color_index * 2);
274
275 return address;
276}
277
302SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t *rom) {
303 int color_offset = 0;
304 std::vector<gfx::SnesColor> colors(num_colors);
305
306 while (color_offset < num_colors) {
307 // Read SNES 15-bit color (little endian)
308 uint16_t snes_color_word = (uint16_t)((rom[offset + 1]) << 8) | rom[offset];
309
310 // Extract RGB components (5-bit each) and expand to 8-bit (0-255)
311 snes_color new_color;
312 new_color.red = (snes_color_word & 0x1F) * 8; // Bits 0-4
313 new_color.green = ((snes_color_word >> 5) & 0x1F) * 8; // Bits 5-9
314 new_color.blue = ((snes_color_word >> 10) & 0x1F) * 8; // Bits 10-14
315
316 // Create SnesColor by converting RGB back to SNES format
317 // (This ensures all internal representations are consistent)
318 colors[color_offset].set_snes(ConvertRgbToSnes(new_color));
319
320 // DO NOT mark as transparent - preserve actual ROM color data!
321 // Transparency is handled at render time, not in the data
322
323 color_offset++;
324 offset += 2; // SNES colors are 2 bytes each
325 }
326
327 return gfx::SnesPalette(colors);
328}
329
330std::array<float, 4> ToFloatArray(const SnesColor &color) {
331 std::array<float, 4> colorArray;
332 colorArray[0] = color.rgb().x / 255.0f;
333 colorArray[1] = color.rgb().y / 255.0f;
334 colorArray[2] = color.rgb().z / 255.0f;
335 colorArray[3] = color.rgb().w;
336 return colorArray;
337}
338
339absl::StatusOr<PaletteGroup> CreatePaletteGroupFromColFile(
340 std::vector<SnesColor> &palette_rows) {
341 PaletteGroup palette_group;
342 for (int i = 0; i < palette_rows.size(); i += 8) {
343 SnesPalette palette;
344 for (int j = 0; j < 8; j++) {
345 palette.AddColor(palette_rows[i + j]);
346 }
347 palette_group.AddPalette(palette);
348 }
349 return palette_group;
350}
351
367absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
368 SnesPalette &palette, int num_colors) {
369 PaletteGroup palette_group;
370 for (int i = 0; i < palette.size(); i += num_colors) {
371 SnesPalette new_palette;
372 if (i + num_colors <= palette.size()) {
373 for (int j = 0; j < num_colors; j++) {
374 auto color = palette[i + j];
375 // DO NOT mark as transparent - preserve actual color data!
376 // Transparency is handled at render time, not in the data
377 new_palette.AddColor(color);
378 }
379 }
380 palette_group.AddPalette(new_palette);
381 }
382 return palette_group;
383}
384
385using namespace palette_group_internal;
386
387// TODO: Refactor LoadAllPalettes to use group names, move to zelda3 namespace
388absl::Status LoadAllPalettes(const std::vector<uint8_t> &rom_data,
389 PaletteGroupMap &groups) {
393 RETURN_IF_ERROR(LoadHUDPalettes(rom_data, groups))
395 RETURN_IF_ERROR(LoadArmorPalettes(rom_data, groups))
396 RETURN_IF_ERROR(LoadSwordPalettes(rom_data, groups))
397 RETURN_IF_ERROR(LoadShieldPalettes(rom_data, groups))
398 RETURN_IF_ERROR(LoadSpriteAux1Palettes(rom_data, groups))
399 RETURN_IF_ERROR(LoadSpriteAux2Palettes(rom_data, groups))
400 RETURN_IF_ERROR(LoadSpriteAux3Palettes(rom_data, groups))
402 RETURN_IF_ERROR(LoadGrassColors(rom_data, groups))
403 RETURN_IF_ERROR(Load3DObjectPalettes(rom_data, groups))
405 return absl::OkStatus();
406}
407
408std::unordered_map<uint8_t, gfx::Paletteset> GfxContext::palettesets_;
409
410} // namespace yaze::gfx
static std::unordered_map< uint8_t, gfx::Paletteset > palettesets_
SNES Color container.
Definition snes_color.h:109
constexpr ImVec4 rgb() const
Get RGB values (WARNING: stored as 0-255 in ImVec4)
Definition snes_color.h:182
void set_snes(uint16_t val)
Set color from SNES 15-bit format.
constexpr uint16_t snes() const
Get SNES 15-bit color.
Definition snes_color.h:192
void set_rgb(const ImVec4 val)
Set color from ImVec4 (0.0-1.0 range)
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void AddColor(const SnesColor &color)
static constexpr size_t kMaxColors
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
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.
Definition editor.h:19
constexpr int kHardcodedGrassSpecial
snes_color ConvertSnesToRgb(uint16_t color_snes)
Convert SNES 15-bit color to RGB (0-255 range)
Definition snes_color.cc:19
constexpr int kHudPalettes
constexpr int kOverworldPaletteAux
SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t *rom)
Read a palette from ROM data.
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 int kCrystalPalette
const absl::flat_hash_map< std::string, uint32_t > kPaletteGroupAddressMap
uint16_t ConvertRgbToSnes(const snes_color &color)
Convert RGB (0-255) to SNES 15-bit color.
Definition snes_color.cc:33
constexpr int kShieldPalettes
constexpr int kSpritesPalettesAux2
constexpr int kOverworldPaletteAnimated
SnesColor ReadColorFromRom(int offset, const uint8_t *rom)
Definition snes_color.cc:48
constexpr int kOverworldMiniMapPalettes
constexpr int kOverworldPaletteMain
constexpr int kSwordPalettes
constexpr int kHardcodedGrassDW
constexpr int kGlobalSpritesLW
const absl::flat_hash_map< std::string, uint32_t > kPaletteGroupColorCounts
constexpr int kSpritesPalettesAux1
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)
SNES color in 15-bit RGB format (BGR555)
Definition yaze.h:208
uint16_t green
Definition yaze.h:210
uint16_t red
Definition yaze.h:209
uint16_t blue
Definition yaze.h:211
Represents a mapping of palette groups.
Represents a group of palettes.
void AddColor(SnesColor color)
void AddPalette(SnesPalette pal)