Files
yaze/src/app/gfx/snes_palette.h

426 lines
14 KiB
C++

#ifndef YAZE_APP_GFX_PALETTE_H
#define YAZE_APP_GFX_PALETTE_H
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/constants.h"
#include "app/gfx/snes_color.h"
#include "imgui/imgui.h"
#include "snes_color.h"
namespace yaze {
namespace gfx {
constexpr int kNumPalettes = 14;
enum PaletteCategory {
kSword,
kShield,
kClothes,
kWorldColors,
kAreaColors,
kGlobalSprites,
kSpritesAux1,
kSpritesAux2,
kSpritesAux3,
kDungeons,
kWorldMap,
kDungeonMap,
kTriforce,
kCrystal
};
static constexpr absl::string_view kPaletteCategoryNames[] = {
"Sword", "Shield", "Clothes", "World Colors",
"Area Colors", "Global Sprites", "Sprites Aux1", "Sprites Aux2",
"Sprites Aux3", "Dungeons", "World Map", "Dungeon Map",
"Triforce", "Crystal"};
static constexpr absl::string_view kPaletteGroupNames[] = {
"swords", "shields", "armors", "ow_main",
"ow_aux", "global_sprites", "sprites_aux1", "sprites_aux2",
"sprites_aux3", "dungeon_main", "ow_mini_map", "ow_mini_map",
"3d_object", "3d_object"};
constexpr const char *kPaletteGroupAddressesKeys[] = {
"ow_main", "ow_aux", "ow_animated", "hud",
"global_sprites", "armors", "swords", "shields",
"sprites_aux1", "sprites_aux2", "sprites_aux3", "dungeon_main",
"grass", "3d_object", "ow_mini_map",
};
constexpr int kOverworldPaletteMain = 0xDE6C8;
constexpr int kOverworldPaletteAux = 0xDE86C;
constexpr int kOverworldPaletteAnimated = 0xDE604;
constexpr int kGlobalSpritesLW = 0xDD218;
constexpr int kGlobalSpritePalettesDW = 0xDD290;
/** < Green, Blue, Red, Bunny, Electrocuted (15 colors each) */
constexpr int kArmorPalettes = 0xDD308;
constexpr int kSpritesPalettesAux1 = 0xDD39E; // 7 colors each
constexpr int kSpritesPalettesAux2 = 0xDD446; // 7 colors each
constexpr int kSpritesPalettesAux3 = 0xDD4E0; // 7 colors each
constexpr int kSwordPalettes = 0xDD630; // 3 colors each - 4 entries
constexpr int kShieldPalettes = 0xDD648; // 4 colors each - 3 entries
constexpr int kHudPalettes = 0xDD660;
constexpr int kDungeonMapPalettes = 0xDD70A; // 21 colors
// (15*6) colors each - 20 entries
constexpr int kDungeonMainPalettes = 0xDD734;
constexpr int kDungeonMapBgPalettes = 0xDE544; // 16*6
// Mirrored Value at 0x75645 : 0x75625
constexpr int kHardcodedGrassLW = 0x5FEA9;
constexpr int kHardcodedGrassDW = 0x05FEB3; // 0x7564F
constexpr int kHardcodedGrassSpecial = 0x75640;
constexpr int kOverworldMiniMapPalettes = 0x55B27;
constexpr int kTriforcePalette = 0x64425;
constexpr int kCrystalPalette = 0xF4CD3;
/** < 2 bytes for each overworld area (320) */
constexpr int CustomAreaSpecificBGPalette = 0x140000;
constexpr int CustomAreaSpecificBGASM = 0x140150;
// 1 byte, not 0 if enabled
constexpr int kCustomAreaSpecificBGEnabled = 0x140140;
uint32_t GetPaletteAddress(const std::string &group_name, size_t palette_index,
size_t color_index);
/**
* @brief Represents a palette of colors for the Super Nintendo Entertainment
* System (SNES).
*
* The `SnesPalette` class provides functionality to create, modify, and access
* colors in an SNES palette. It supports various constructors to initialize the
* palette with different types of data. The palette can be modified by adding
* or changing colors, and it can be cleared to remove all colors. Colors in the
* palette can be accessed using index-based access or through the `GetColor`
* method. The class also provides a method to create a sub-palette by selecting
* a range of colors from the original palette.
*/
class SnesPalette {
public:
template <typename T>
explicit SnesPalette(const std::vector<T> &data) {
for (const auto &item : data) {
colors.emplace_back(SnesColor(item));
}
}
SnesPalette() = default;
explicit SnesPalette(char *snesPal);
explicit SnesPalette(const unsigned char *snes_pal);
explicit SnesPalette(const std::vector<ImVec4> &);
explicit SnesPalette(const std::vector<snes_color> &);
explicit SnesPalette(const std::vector<SnesColor> &);
void Create(const std::vector<SnesColor> &cols) {
for (const auto &each : cols) {
colors.emplace_back(each);
}
}
void Create(std::ranges::range auto &&cols) {
std::copy(cols.begin(), cols.end(), std::back_inserter(colors));
}
void AddColor(const SnesColor &color) { colors.emplace_back(color); }
void AddColor(const snes_color &color) { colors.emplace_back(color); }
void AddColor(uint16_t color) { colors.emplace_back(color); }
absl::StatusOr<SnesColor> GetColor(int i) const {
if (i > colors.size()) {
return absl::InvalidArgumentError("SnesPalette: Index out of bounds");
}
return colors[i];
}
auto mutable_color(int i) { return &colors[i]; }
void clear() { colors.clear(); }
auto size() const { return colors.size(); }
auto empty() const { return colors.empty(); }
SnesColor &operator[](int i) {
if (i > colors.size()) {
std::cout << "SNESPalette: Index out of bounds" << std::endl;
return colors[0];
}
return colors[i];
}
void operator()(int i, const SnesColor &color) {
if (i >= colors.size()) {
std::cout << "SNESPalette: Index out of bounds" << std::endl;
}
colors[i] = color;
}
void operator()(int i, const ImVec4 &color) {
if (i >= colors.size()) {
std::cout << "SNESPalette: Index out of bounds" << std::endl;
return;
}
colors[i].set_rgb(color);
colors[i].set_modified(true);
}
SnesPalette sub_palette(int start, int end) const {
SnesPalette pal;
for (int i = start; i < end; i++) {
pal.AddColor(colors[i]);
}
return pal;
}
private:
std::vector<SnesColor> colors; /**< The colors in the palette. */
};
SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t *rom);
std::array<float, 4> ToFloatArray(const SnesColor &color);
/**
* @brief Represents a group of palettes.
*
* Supports adding palettes and colors, clearing the group, and accessing
* palettes and colors by index.
*/
struct PaletteGroup {
PaletteGroup() = default;
PaletteGroup(const std::string &name) : name_(name) {}
void AddPalette(SnesPalette pal) { palettes.emplace_back(pal); }
void AddColor(SnesColor color) {
if (palettes.empty()) {
palettes.emplace_back();
}
palettes[0].AddColor(color);
}
void clear() { palettes.clear(); }
auto name() const { return name_; }
auto size() const { return palettes.size(); }
auto palette(int i) const { return palettes[i]; }
auto mutable_palette(int i) { return &palettes[i]; }
SnesPalette operator[](int i) {
if (i > palettes.size()) {
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
return palettes[0];
}
return palettes[i];
}
const SnesPalette &operator[](int i) const {
if (i > palettes.size()) {
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
return palettes[0];
}
return palettes[i];
}
private:
std::string name_;
std::vector<SnesPalette> palettes;
};
/**
* @brief Represents a mapping of palette groups.
*
* Originally, this was an actual std::unordered_map but since the palette
* groups supported never change, it was changed to a struct with a method to
* get the group by name.
*/
struct PaletteGroupMap {
PaletteGroup overworld_main = {kPaletteGroupAddressesKeys[0]};
PaletteGroup overworld_aux = {kPaletteGroupAddressesKeys[1]};
PaletteGroup overworld_animated = {kPaletteGroupAddressesKeys[2]};
PaletteGroup hud = {kPaletteGroupAddressesKeys[3]};
PaletteGroup global_sprites = {kPaletteGroupAddressesKeys[4]};
PaletteGroup armors = {kPaletteGroupAddressesKeys[5]};
PaletteGroup swords = {kPaletteGroupAddressesKeys[6]};
PaletteGroup shields = {kPaletteGroupAddressesKeys[7]};
PaletteGroup sprites_aux1 = {kPaletteGroupAddressesKeys[8]};
PaletteGroup sprites_aux2 = {kPaletteGroupAddressesKeys[9]};
PaletteGroup sprites_aux3 = {kPaletteGroupAddressesKeys[10]};
PaletteGroup dungeon_main = {kPaletteGroupAddressesKeys[11]};
PaletteGroup grass = {kPaletteGroupAddressesKeys[12]};
PaletteGroup object_3d = {kPaletteGroupAddressesKeys[13]};
PaletteGroup overworld_mini_map = {kPaletteGroupAddressesKeys[14]};
auto get_group(const std::string &group_name) {
if (group_name == "ow_main") {
return &overworld_main;
} else if (group_name == "ow_aux") {
return &overworld_aux;
} else if (group_name == "ow_animated") {
return &overworld_animated;
} else if (group_name == "hud") {
return &hud;
} else if (group_name == "global_sprites") {
return &global_sprites;
} else if (group_name == "armors") {
return &armors;
} else if (group_name == "swords") {
return &swords;
} else if (group_name == "shields") {
return &shields;
} else if (group_name == "sprites_aux1") {
return &sprites_aux1;
} else if (group_name == "sprites_aux2") {
return &sprites_aux2;
} else if (group_name == "sprites_aux3") {
return &sprites_aux3;
} else if (group_name == "dungeon_main") {
return &dungeon_main;
} else if (group_name == "grass") {
return &grass;
} else if (group_name == "3d_object") {
return &object_3d;
} else if (group_name == "ow_mini_map") {
return &overworld_mini_map;
} else {
throw std::out_of_range("PaletteGroupMap: Group not found");
}
}
template <typename Func>
absl::Status for_each(Func &&func) {
RETURN_IF_ERROR(func(overworld_aux));
RETURN_IF_ERROR(func(overworld_animated));
RETURN_IF_ERROR(func(hud));
RETURN_IF_ERROR(func(global_sprites));
RETURN_IF_ERROR(func(armors));
RETURN_IF_ERROR(func(swords));
RETURN_IF_ERROR(func(shields));
RETURN_IF_ERROR(func(sprites_aux1));
RETURN_IF_ERROR(func(sprites_aux2));
RETURN_IF_ERROR(func(sprites_aux3));
RETURN_IF_ERROR(func(dungeon_main));
RETURN_IF_ERROR(func(grass));
RETURN_IF_ERROR(func(object_3d));
RETURN_IF_ERROR(func(overworld_mini_map));
return absl::OkStatus();
}
void clear() {
overworld_main.clear();
overworld_aux.clear();
overworld_animated.clear();
hud.clear();
global_sprites.clear();
armors.clear();
swords.clear();
shields.clear();
sprites_aux1.clear();
sprites_aux2.clear();
sprites_aux3.clear();
dungeon_main.clear();
grass.clear();
object_3d.clear();
overworld_mini_map.clear();
}
bool empty() {
return overworld_main.size() == 0 && overworld_aux.size() == 0 &&
overworld_animated.size() == 0 && hud.size() == 0 &&
global_sprites.size() == 0 && armors.size() == 0 &&
swords.size() == 0 && shields.size() == 0 &&
sprites_aux1.size() == 0 && sprites_aux2.size() == 0 &&
sprites_aux3.size() == 0 && dungeon_main.size() == 0 &&
grass.size() == 0 && object_3d.size() == 0 &&
overworld_mini_map.size() == 0;
}
};
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromColFile(
std::vector<SnesColor> &colors);
/**
* @brief Take a SNESPalette, divide it into palettes of 8 colors
*/
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
SnesPalette &palette, int num_colors = 8);
/**
* @brief Loads all the palettes for the game.
*
* This function loads all the palettes for the game, including overworld,
* HUD, armor, swords, shields, sprites, dungeon, grass, and 3D object
* palettes. It also adds the loaded palettes to their respective palette
* groups.
*
*/
absl::Status LoadAllPalettes(const std::vector<uint8_t> &rom_data,
PaletteGroupMap &groups);
/**
* @brief Represents a set of palettes used in a SNES graphics system.
*/
struct Paletteset {
/**
* @brief Default constructor for Paletteset.
*/
Paletteset() = default;
/**
* @brief Constructor for Paletteset.
* @param main The main palette.
* @param animated The animated palette.
* @param aux1 The first auxiliary palette.
* @param aux2 The second auxiliary palette.
* @param background The background color.
* @param hud The HUD palette.
* @param spr The sprite palette.
* @param spr2 The second sprite palette.
* @param comp The composite palette.
*/
Paletteset(gfx::SnesPalette main, gfx::SnesPalette animated,
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
gfx::SnesColor background, gfx::SnesPalette hud,
gfx::SnesPalette spr, gfx::SnesPalette spr2, gfx::SnesPalette comp)
: main_(main),
animated(animated),
aux1(aux1),
aux2(aux2),
background(background),
hud(hud),
spr(spr),
spr2(spr2),
composite(comp) {}
gfx::SnesPalette main_; /**< The main palette. */
gfx::SnesPalette animated; /**< The animated palette. */
gfx::SnesPalette aux1; /**< The first auxiliary palette. */
gfx::SnesPalette aux2; /**< The second auxiliary palette. */
gfx::SnesColor background; /**< The background color. */
gfx::SnesPalette hud; /**< The HUD palette. */
gfx::SnesPalette spr; /**< The sprite palette. */
gfx::SnesPalette spr2; /**< The second sprite palette. */
gfx::SnesPalette composite; /**< The composite palette. */
};
/**
* @brief Shared graphical context across editors.
*/
class GfxContext {
protected:
// Palettesets for the tile16 individual tiles
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
};
} // namespace gfx
} // namespace yaze
#endif // YAZE_APP_GFX_PALETTE_H