From 9ea107bc8dd6d11e7c1cb05181df8c12b7e91a7b Mon Sep 17 00:00:00 2001 From: scawful Date: Fri, 4 Aug 2023 22:51:12 -0400 Subject: [PATCH] Update SNESPalette, SNESColor, add tests --- src/app/core/constants.h | 9 ++ src/app/core/pipeline.cc | 2 +- src/app/editor/graphics_editor.cc | 6 +- src/app/editor/overworld_editor.cc | 4 +- src/app/editor/palette_editor.cc | 52 +++++----- src/app/gfx/bitmap.cc | 12 +-- src/app/gfx/snes_palette.cc | 161 ++++++++++++----------------- src/app/gfx/snes_palette.h | 153 ++++++++++++++++++--------- src/app/gui/color.cc | 8 +- src/app/rom.cc | 25 +---- src/app/rom.h | 3 - src/app/yaze.cc | 9 +- src/app/zelda3/overworld.cc | 23 ++--- test/CMakeLists.txt | 2 +- test/compression_test.cc | 40 +++---- test/rom_test.cc | 12 --- test/snes_palette_test.cc | 41 ++++++++ 17 files changed, 299 insertions(+), 263 deletions(-) delete mode 100644 test/rom_test.cc create mode 100644 test/snes_palette_test.cc diff --git a/src/app/core/constants.h b/src/app/core/constants.h index b28292c0..b4ca40a1 100644 --- a/src/app/core/constants.h +++ b/src/app/core/constants.h @@ -54,6 +54,15 @@ } \ } +#define EXIT_IF_ERROR(expression) \ + { \ + auto error = expression; \ + if (!error.ok()) { \ + std::cout << error.ToString() << std::endl; \ + return EXIT_FAILURE; \ + } \ + } + #define RETURN_IF_ERROR(expression) \ { \ auto error = expression; \ diff --git a/src/app/core/pipeline.cc b/src/app/core/pipeline.cc index 19412370..97407dc6 100644 --- a/src/app/core/pipeline.cc +++ b/src/app/core/pipeline.cc @@ -30,7 +30,7 @@ void SelectablePalettePipeline(uint64_t& palette_id, bool& refresh_graphics, ImGui::PushID(n); if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); - if (ImGui::ColorButton("##palette", palette[n].RGB(), + if (ImGui::ColorButton("##palette", palette[n].GetRGB(), ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, diff --git a/src/app/editor/graphics_editor.cc b/src/app/editor/graphics_editor.cc index 8fabd06d..1a5c2c4a 100644 --- a/src/app/editor/graphics_editor.cc +++ b/src/app/editor/graphics_editor.cc @@ -276,7 +276,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) { if (rom_.isLoaded()) { auto palette_group = rom_.GetPaletteGroup("ow_main"); - palette_ = palette_group.palettes[current_palette_]; + palette_ = palette_group[current_palette_]; if (col_file_) { bitmap_.ApplyPalette(col_file_palette_); } else { @@ -309,7 +309,7 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() { // ROM palette auto palette_group = rom_.GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]); - palette_ = palette_group.palettes[current_palette_index_]; + palette_ = palette_group[current_palette_index_]; graphics_bin_[i].ApplyPalette(palette_); } @@ -334,7 +334,7 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() { // ROM palette auto palette_group = rom_.GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]); - palette_ = palette_group.palettes[current_palette_index_]; + palette_ = palette_group[current_palette_index_]; graphics_bin_[i].ApplyPalette(palette_); } diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index 5dd217e1..ec828a20 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -352,11 +352,11 @@ absl::Status OverworldEditor::LoadGraphics() { absl::Status OverworldEditor::LoadSpriteGraphics() { // Render the sprites for each Overworld map for (int i = 0; i < 3; i++) - for (auto &sprite : overworld_.Sprites(i)) { + for (auto const &sprite : overworld_.Sprites(i)) { int width = sprite.Width(); int height = sprite.Height(); int depth = 0x40; - auto spr_gfx = sprite.PreviewGraphics().data(); + auto spr_gfx = sprite.PreviewGraphics(); sprite_previews_[sprite.id()].Create(width, height, depth, spr_gfx); sprite_previews_[sprite.id()].ApplyPalette(palette_); rom_.RenderBitmap(&(sprite_previews_[sprite.id()])); diff --git a/src/app/editor/palette_editor.cc b/src/app/editor/palette_editor.cc index b79f0cab..1628c525 100644 --- a/src/app/editor/palette_editor.cc +++ b/src/app/editor/palette_editor.cc @@ -33,13 +33,6 @@ namespace yaze { namespace app { namespace editor { -namespace { -void DrawPaletteTooltips(gfx::SNESPalette& palette, int size) {} - -using namespace ImGui; - -} // namespace - absl::Status PaletteEditor::Update() { for (int i = 0; i < kNumPalettes; ++i) { if (ImGui::TreeNode(kPaletteCategoryNames[i].data())) { @@ -57,7 +50,7 @@ void PaletteEditor::DrawPaletteGroup(int i) { ImGui::Text("%d", j); auto palette = palettes[j]; - auto pal_size = palette.size_; + auto pal_size = palette.size(); for (int n = 0; n < pal_size; n++) { ImGui::PushID(n); @@ -65,38 +58,39 @@ void PaletteEditor::DrawPaletteGroup(int i) { std::string popupId = kPaletteCategoryNames[i].data() + std::to_string(j) + "_" + std::to_string(n); - if (ImGui::ColorButton(popupId.c_str(), palette[n].RGB(), + if (ImGui::ColorButton(popupId.c_str(), palette[n].GetRGB(), palette_button_flags)) { - if (ImGui::ColorEdit4(popupId.c_str(), palette[n].ToFloatArray(), + static auto float_array = gfx::ToFloatArray(palette[n]); + if (ImGui::ColorEdit4(popupId.c_str(), float_array.data(), palette_button_flags)) - current_color_ = - ImVec4(palette[n].rgb.x, palette[n].rgb.y, palette[n].rgb.z, - palette[n].rgb.w); // Prese rve alpha! + current_color_ = ImVec4(palette[n].GetRGB().x, palette[n].GetRGB().y, + palette[n].GetRGB().z, + palette[n].GetRGB().w); // Prese rve alpha! } if (ImGui::BeginPopupContextItem(popupId.c_str())) { - auto col = palette[n].ToFloatArray(); + auto col = gfx::ToFloatArray(palette[n]); if (ImGui::ColorEdit4( - "Edit Color", col, + "Edit Color", col.data(), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha)) { rom_.UpdatePaletteColor(kPaletteGroupNames[i].data(), j, n, palette[n]); } - if (Button("Copy as..", ImVec2(-1, 0))) OpenPopup("Copy"); - if (BeginPopup("Copy")) { + if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy"); + if (ImGui::BeginPopup("Copy")) { int cr = IM_F32_TO_INT8_SAT(col[0]); int cg = IM_F32_TO_INT8_SAT(col[1]); int cb = IM_F32_TO_INT8_SAT(col[2]); char buf[64]; CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff)", col[0], col[1], col[2]); - if (Selectable(buf)) SetClipboardText(buf); + if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf); CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d)", cr, cg, cb); - if (Selectable(buf)) SetClipboardText(buf); + if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf); CustomFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb); - if (Selectable(buf)) SetClipboardText(buf); - EndPopup(); + if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf); + ImGui::EndPopup(); } ImGui::EndPopup(); @@ -116,10 +110,10 @@ void PaletteEditor::DisplayPalette(gfx::SNESPalette& palette, bool loaded) { // Generate a default palette. The palette will persist and can be edited. static bool init = false; if (loaded && !init) { - for (int n = 0; n < palette.size_; n++) { - saved_palette_[n].x = palette.GetColor(n).rgb.x / 255; - saved_palette_[n].y = palette.GetColor(n).rgb.y / 255; - saved_palette_[n].z = palette.GetColor(n).rgb.z / 255; + for (int n = 0; n < palette.size(); n++) { + saved_palette_[n].x = palette.GetColor(n).GetRGB().x / 255; + saved_palette_[n].y = palette.GetColor(n).GetRGB().y / 255; + saved_palette_[n].z = palette.GetColor(n).GetRGB().z / 255; saved_palette_[n].w = 255; // Alpha } init = true; @@ -193,10 +187,10 @@ void PaletteEditor::DisplayPalette(gfx::SNESPalette& palette, bool loaded) { void PaletteEditor::DrawPortablePalette(gfx::SNESPalette& palette) { static bool init = false; if (!init) { - for (int n = 0; n < palette.size_; n++) { - saved_palette_[n].x = palette.GetColor(n).rgb.x / 255; - saved_palette_[n].y = palette.GetColor(n).rgb.y / 255; - saved_palette_[n].z = palette.GetColor(n).rgb.z / 255; + for (int n = 0; n < palette.size(); n++) { + saved_palette_[n].x = palette.GetColor(n).GetRGB().x / 255; + saved_palette_[n].y = palette.GetColor(n).GetRGB().y / 255; + saved_palette_[n].z = palette.GetColor(n).GetRGB().z / 255; saved_palette_[n].w = 255; // Alpha } init = true; diff --git a/src/app/gfx/bitmap.cc b/src/app/gfx/bitmap.cc index e6480a81..cda07eaa 100644 --- a/src/app/gfx/bitmap.cc +++ b/src/app/gfx/bitmap.cc @@ -128,17 +128,17 @@ void Bitmap::UpdateTexture(std::shared_ptr renderer) { void Bitmap::ApplyPalette(const SNESPalette &palette) { palette_ = palette; SDL_UnlockSurface(surface_.get()); - for (int i = 0; i < palette.size_; ++i) { - if (palette.GetColor(i).transparent) { + for (int i = 0; i < palette.size(); ++i) { + if (palette.GetColor(i).isTransparent()) { surface_->format->palette->colors[i].r = 0; surface_->format->palette->colors[i].g = 0; surface_->format->palette->colors[i].b = 0; surface_->format->palette->colors[i].a = 0; } else { - surface_->format->palette->colors[i].r = palette.GetColor(i).rgb.x; - surface_->format->palette->colors[i].g = palette.GetColor(i).rgb.y; - surface_->format->palette->colors[i].b = palette.GetColor(i).rgb.z; - surface_->format->palette->colors[i].a = palette.GetColor(i).rgb.w; + surface_->format->palette->colors[i].r = palette.GetColor(i).GetRGB().x; + surface_->format->palette->colors[i].g = palette.GetColor(i).GetRGB().y; + surface_->format->palette->colors[i].b = palette.GetColor(i).GetRGB().z; + surface_->format->palette->colors[i].a = palette.GetColor(i).GetRGB().w; } } SDL_LockSurface(surface_.get()); diff --git a/src/app/gfx/snes_palette.cc b/src/app/gfx/snes_palette.cc index d52d2284..5e60e36f 100644 --- a/src/app/gfx/snes_palette.cc +++ b/src/app/gfx/snes_palette.cc @@ -16,57 +16,62 @@ namespace yaze { namespace app { namespace gfx { -ushort ConvertRGBtoSNES(const snes_color color) { - uchar red = color.red / 8; - uchar green = color.green / 8; - uchar blue = color.blue / 8; - return blue * 1024 + green * 32 + red; +constexpr uint16_t SNES_RED_MASK = 32; +constexpr uint16_t SNES_GREEN_MASK = 32; +constexpr uint16_t SNES_BLUE_MASK = 32; + +constexpr uint16_t SNES_GREEN_SHIFT = 32; +constexpr uint16_t SNES_BLUE_SHIFT = 1024; + +uint16_t ConvertRGBtoSNES(const snes_color& color) { + uint16_t red = color.red / 8; + uint16_t green = color.green / 8; + uint16_t blue = color.blue / 8; + return (blue * SNES_BLUE_SHIFT) + (green * SNES_GREEN_SHIFT) + red; } -snes_color ConvertSNEStoRGB(const ushort color) { - snes_color toret; +snes_color ConvertSNEStoRGB(uint16_t color_snes) { + snes_color result; - toret.red = ((color) % 32) * 8; - toret.green = ((color / 32) % 32) * 8; - toret.blue = ((color / 1024) % 32) * 8; + result.red = (color_snes % SNES_RED_MASK) * 8; + result.green = ((color_snes / SNES_GREEN_MASK) % SNES_GREEN_MASK) * 8; + result.blue = ((color_snes / SNES_BLUE_SHIFT) % SNES_BLUE_MASK) * 8; - toret.red = toret.red + toret.red / 32; - toret.green = toret.green + toret.green / 32; - toret.blue = toret.blue + toret.blue / 32; - return toret; + result.red += result.red / SNES_RED_MASK; + result.green += result.green / SNES_GREEN_MASK; + result.blue += result.blue / SNES_BLUE_MASK; + + return result; } -snes_palette* Extract(const char* data, const unsigned int offset, - const unsigned int palette_size) { - snes_palette* toret = nullptr; // palette_create(palette_size, 0) - unsigned colnum = 0; - for (int i = 0; i < palette_size * 2; i += 2) { - unsigned short snes_color; - snes_color = ((uchar)data[offset + i + 1]) << 8; - snes_color = snes_color | ((uchar)data[offset + i]); - toret->colors[colnum] = ConvertSNEStoRGB(snes_color); - colnum++; +std::vector Extract(const char* data, unsigned int offset, + unsigned int palette_size) { + std::vector palette(palette_size); + for (unsigned int i = 0; i < palette_size * 2; i += 2) { + uint16_t snes_color = (static_cast(data[offset + i + 1]) << 8) | + static_cast(data[offset + i]); + palette[i / 2] = ConvertSNEStoRGB(snes_color); } - return toret; + return palette; } -char* Convert(const snes_palette pal) { - char* toret = (char*)malloc(pal.size * 2); - for (unsigned int i = 0; i < pal.size; i++) { - unsigned short snes_data = ConvertRGBtoSNES(pal.colors[i]); - toret[i * 2] = snes_data & 0xFF; - toret[i * 2 + 1] = snes_data >> 8; +std::vector Convert(const std::vector& palette) { + std::vector data(palette.size() * 2); + for (unsigned int i = 0; i < palette.size(); i++) { + uint16_t snes_data = ConvertRGBtoSNES(palette[i]); + data[i * 2] = snes_data & 0xFF; + data[i * 2 + 1] = snes_data >> 8; } - return toret; + return data; } -SNESColor GetCgxColor(short color) { +SNESColor GetCgxColor(uint16_t color) { ImVec4 rgb; rgb.x = (color & 0x1F) * 8; rgb.y = ((color & 0x3E0) >> 5) * 8; rgb.z = ((color & 0x7C00) >> 10) * 8; SNESColor toret; - toret.setRgb(rgb); + toret.SetRGB(rgb); return toret; } @@ -76,7 +81,7 @@ std::vector GetColFileData(uchar* data) { colors.resize(256); for (int i = 0; i < 512; i += 2) { - colors[i / 2] = GetCgxColor((short)((data[i + 1] << 8) + data[i])); + colors[i / 2] = GetCgxColor((uint16_t)((data[i + 1] << 8) + data[i])); } return colors; @@ -84,43 +89,6 @@ std::vector GetColFileData(uchar* data) { // ============================================================================ -SNESColor::SNESColor() : rgb(ImVec4(0.f, 0.f, 0.f, 0.f)) {} - -SNESColor::SNESColor(snes_color val) { - rgb.x = val.red; - rgb.y = val.green; - rgb.z = val.blue; -} - -SNESColor::SNESColor(ImVec4 val) : rgb(val) { - snes_color col; - col.red = (uchar)val.x; - col.blue = (uchar)val.y; - col.green = (uchar)val.z; - snes = ConvertRGBtoSNES(col); -} - -void SNESColor::setRgb(ImVec4 val) { - rgb = val; - snes_color col; - col.red = val.x; - col.blue = val.y; - col.green = val.z; - snes = ConvertRGBtoSNES(col); -} - -void SNESColor::setSNES(snes_color val) { - rgb = ImVec4(val.red, val.green, val.blue, 255.f); -} - -void SNESColor::setSNES(uint16_t val) { - snes = val; - snes_color col = ConvertSNEStoRGB(val); - rgb = ImVec4(col.red, col.green, col.blue, 0.f); -} - -// ============================================================================ - SNESPalette::SNESPalette(uint8_t mSize) : size_(mSize) { for (unsigned int i = 0; i < mSize; i++) { SNESColor col; @@ -132,10 +100,10 @@ SNESPalette::SNESPalette(char* data) : size_(sizeof(data) / 2) { assert((sizeof(data) % 4 == 0) && (sizeof(data) <= 32)); for (unsigned i = 0; i < sizeof(data); i += 2) { SNESColor col; - col.snes = static_cast(data[i + 1]) << 8; - col.snes = col.snes | static_cast(data[i]); - snes_color mColor = ConvertSNEStoRGB(col.snes); - col.rgb = ImVec4(mColor.red, mColor.green, mColor.blue, 1.f); + col.SetSNES(static_cast(data[i + 1]) << 8); + col.SetSNES(col.GetSNES() | static_cast(data[i])); + snes_color mColor = ConvertSNEStoRGB(col.GetSNES()); + col.SetRGB(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f)); colors.push_back(col); } } @@ -145,10 +113,10 @@ SNESPalette::SNESPalette(const unsigned char* snes_pal) assert((sizeof(snes_pal) % 4 == 0) && (sizeof(snes_pal) <= 32)); for (unsigned i = 0; i < sizeof(snes_pal); i += 2) { SNESColor col; - col.snes = snes_pal[i + 1] << (uint16_t)8; - col.snes = col.snes | snes_pal[i]; - snes_color mColor = ConvertSNEStoRGB(col.snes); - col.rgb = ImVec4(mColor.red, mColor.green, mColor.blue, 1.f); + col.SetSNES(snes_pal[i + 1] << (uint16_t)8); + col.SetSNES(col.GetSNES() | snes_pal[i]); + snes_color mColor = ConvertSNEStoRGB(col.GetSNES()); + col.SetRGB(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f)); colors.push_back(col); } } @@ -156,7 +124,7 @@ SNESPalette::SNESPalette(const unsigned char* snes_pal) SNESPalette::SNESPalette(const std::vector& cols) { for (const auto& each : cols) { SNESColor scol; - scol.setRgb(each); + scol.SetRGB(each); colors.push_back(scol); } size_ = cols.size(); @@ -165,7 +133,7 @@ SNESPalette::SNESPalette(const std::vector& cols) { SNESPalette::SNESPalette(const std::vector& cols) { for (const auto& each : cols) { SNESColor scol; - scol.setSNES(each); + scol.SetSNES(ConvertRGBtoSNES(each)); colors.push_back(scol); } size_ = cols.size(); @@ -178,19 +146,11 @@ SNESPalette::SNESPalette(const std::vector& cols) { size_ = cols.size(); } -void SNESPalette::Create(const std::vector& cols) { - for (const auto each : cols) { - colors.push_back(each); - } - size_ = cols.size(); -} - char* SNESPalette::encode() { auto data = new char[size_ * 2]; for (unsigned int i = 0; i < size_; i++) { - std::cout << colors[i].snes << std::endl; - data[i * 2] = (char)(colors[i].snes & 0xFF); - data[i * 2 + 1] = (char)(colors[i].snes >> 8); + data[i * 2] = (char)(colors[i].GetSNES() & 0xFF); + data[i * 2 + 1] = (char)(colors[i].GetSNES() >> 8); } return data; } @@ -201,9 +161,9 @@ SDL_Palette* SNESPalette::GetSDL_Palette() { auto color = std::vector(size_); for (int i = 0; i < size_; i++) { - color[i].r = (uint8_t)colors[i].rgb.x * 100; - color[i].g = (uint8_t)colors[i].rgb.y * 100; - color[i].b = (uint8_t)colors[i].rgb.z * 100; + color[i].r = (uint8_t)colors[i].GetRGB().x * 100; + color[i].g = (uint8_t)colors[i].GetRGB().y * 100; + color[i].b = (uint8_t)colors[i].GetRGB().z * 100; color[i].a = 0; std::cout << "Color " << i << " added (R:" << color[i].r << " G:" << color[i].g << " B:" << color[i].b << ")" << std::endl; @@ -212,6 +172,15 @@ SDL_Palette* SNESPalette::GetSDL_Palette() { return sdl_palette.get(); } +std::array ToFloatArray(const SNESColor& color) { + std::array colorArray; + colorArray[0] = color.GetRGB().x / 255.0f; + colorArray[1] = color.GetRGB().y / 255.0f; + colorArray[2] = color.GetRGB().z / 255.0f; + colorArray[3] = color.GetRGB().w; + return colorArray; +} + PaletteGroup::PaletteGroup(uint8_t mSize) : size_(mSize) {} PaletteGroup CreatePaletteGroupFromColFile( @@ -221,7 +190,7 @@ PaletteGroup CreatePaletteGroupFromColFile( for (int i = 0; i < palette_rows.size(); i += 8) { SNESPalette palette; for (int j = 0; j < 8; j++) { - palette.AddColor(palette_rows[i + j]); + palette.AddColor(palette_rows[i + j].GetRomRGB()); } toret.AddPalette(palette); } diff --git a/src/app/gfx/snes_palette.h b/src/app/gfx/snes_palette.h index dd2e0840..63566968 100644 --- a/src/app/gfx/snes_palette.h +++ b/src/app/gfx/snes_palette.h @@ -11,6 +11,7 @@ #include #include +#include "absl/base/casts.h" #include "app/core/constants.h" namespace yaze { @@ -18,9 +19,9 @@ namespace app { namespace gfx { struct snes_color { - uchar red; - uchar blue; - uchar green; + uint16_t red; + uint16_t blue; + uint16_t green; }; using snes_color = struct snes_color; @@ -31,47 +32,80 @@ struct snes_palette { }; using snes_palette = struct snes_palette; -ushort ConvertRGBtoSNES(const snes_color color); -snes_color ConvertSNEStoRGB(const ushort snes_color); -snes_palette* Extract(const char* data, const unsigned int offset, - const unsigned int palette_size); -char* Convert(const snes_palette pal); +uint16_t ConvertRGBtoSNES(const snes_color& color); +snes_color ConvertSNEStoRGB(uint16_t snes_color); +std::vector Extract(const char* data, unsigned int offset, + unsigned int palette_size); +std::vector Convert(const std::vector& palette); struct SNESColor { - SNESColor(); - explicit SNESColor(ImVec4); - explicit SNESColor(snes_color); + SNESColor() : rgb(0.f, 0.f, 0.f, 0.f), snes(0) {} - void setRgb(ImVec4); - void setSNES(snes_color); - void setSNES(uint16_t); + explicit SNESColor(const ImVec4 val) : rgb(val) { + snes_color color; + color.red = val.x / 255; + color.green = val.y / 255; + color.blue = val.z / 255; + snes = ConvertRGBtoSNES(color); + } + + explicit SNESColor(const snes_color val) + : rgb(val.red, val.green, val.blue, 255.f), + snes(ConvertRGBtoSNES(val)), + rom_color(val) {} + + ImVec4 GetRGB() const { return rgb; } + void SetRGB(const ImVec4 val) { + rgb.x = val.x / 255; + rgb.y = val.y / 255; + rgb.z = val.z / 255; + snes_color color; + color.red = val.x; + color.green = val.y; + color.blue = val.z; + rom_color = color; + snes = ConvertRGBtoSNES(color); + modified = true; + } + + // Used for indexed Bitmaps from CGX files + snes_color GetRomRGB() const { return rom_color; } + + uint16_t GetSNES() const { return snes; } + + void SetSNES(uint16_t val) { + snes = val; + snes_color col = ConvertSNEStoRGB(val); + rgb = ImVec4(col.red, col.green, col.blue, 0.f); + modified = true; + } + + bool isModified() const { return modified; } + bool isTransparent() const { return transparent; } void setTransparent(bool t) { transparent = t; } + void setModified(bool m) { modified = m; } - auto RGB() const { - return ImVec4(rgb.x / 255, rgb.y / 255, rgb.z / 255, rgb.w); - } - - float* ToFloatArray() { - static std::vector colorArray(4); - colorArray[0] = rgb.x / 255.0f; - colorArray[1] = rgb.y / 255.0f; - colorArray[2] = rgb.z / 255.0f; - colorArray[3] = rgb.w; - return colorArray.data(); - } - + private: + ImVec4 rgb; + uint16_t snes; + snes_color rom_color; bool modified = false; bool transparent = false; - uint16_t snes = 0; - ImVec4 rgb; }; -SNESColor GetCgxColor(short color); +SNESColor GetCgxColor(uint16_t color); std::vector GetColFileData(uchar* data); class SNESPalette { public: + template + explicit SNESPalette(const std::vector& data) { + for (const auto& item : data) { + colors.push_back(SNESColor(item)); + } + } + SNESPalette() = default; explicit SNESPalette(uint8_t mSize); explicit SNESPalette(char* snesPal); @@ -80,70 +114,92 @@ class SNESPalette { explicit SNESPalette(const std::vector&); explicit SNESPalette(const std::vector&); - void Create(const std::vector&); + char* encode(); + SDL_Palette* GetSDL_Palette(); + + void Create(const std::vector& cols) { + for (const auto each : cols) { + colors.push_back(each); + } + size_ = cols.size(); + } + void AddColor(SNESColor color) { colors.push_back(color); size_++; } - auto GetColor(int i) const { return colors[i]; } + + void AddColor(snes_color color) { + colors.emplace_back(color); + size_++; + } + + auto GetColor(int i) const { + if (i > size_) { + throw std::out_of_range("SNESPalette: Index out of bounds"); + } + return colors[i]; + } + void Clear() { colors.clear(); size_ = 0; } + auto size() const { return colors.size(); } + SNESColor operator[](int i) { if (i > size_) { - std::cout << "SNESPalette: Index out of bounds" << std::endl; - return colors[0]; + throw std::out_of_range("SNESPalette: Index out of bounds"); } return colors[i]; } void operator()(int i, const SNESColor& color) { if (i >= size_) { - std::cout << "SNESPalette: Index out of bounds" << std::endl; - return; + throw std::out_of_range("SNESPalette: Index out of bounds"); } colors[i] = color; } void operator()(int i, const ImVec4& color) { if (i >= size_) { - std::cout << "SNESPalette: Index out of bounds" << std::endl; - return; + throw std::out_of_range("SNESPalette: Index out of bounds"); } - colors[i].rgb.x = color.x; - colors[i].rgb.y = color.y; - colors[i].rgb.z = color.z; - colors[i].rgb.w = color.w; - colors[i].modified = true; + colors[i].SetRGB(color); + colors[i].setModified(true); } - char* encode(); - SDL_Palette* GetSDL_Palette(); - + private: int size_ = 0; - auto size() const { return colors.size(); } std::vector colors; }; +std::array ToFloatArray(const SNESColor& color); + struct PaletteGroup { PaletteGroup() = default; explicit PaletteGroup(uint8_t mSize); + void AddPalette(SNESPalette pal) { palettes.emplace_back(pal); size_ = palettes.size(); } + void AddColor(SNESColor color) { if (size_ == 0) { palettes.emplace_back(); } palettes[0].AddColor(color); } + void Clear() { palettes.clear(); size_ = 0; } + + auto size() const { return palettes.size(); } + SNESPalette operator[](int i) { if (i > size_) { std::cout << "PaletteGroup: Index out of bounds" << std::endl; @@ -151,8 +207,9 @@ struct PaletteGroup { } return palettes[i]; } + + private: int size_ = 0; - auto size() const { return palettes.size(); } std::vector palettes; }; diff --git a/src/app/gui/color.cc b/src/app/gui/color.cc index a7fd6807..3420cab6 100644 --- a/src/app/gui/color.cc +++ b/src/app/gui/color.cc @@ -21,10 +21,10 @@ void DisplayPalette(app::gfx::SNESPalette& palette, bool loaded) { static bool init = false; static ImVec4 saved_palette[32] = {}; if (loaded && !init) { - for (int n = 0; n < palette.size_; n++) { - saved_palette[n].x = palette.GetColor(n).rgb.x / 255; - saved_palette[n].y = palette.GetColor(n).rgb.y / 255; - saved_palette[n].z = palette.GetColor(n).rgb.z / 255; + for (int n = 0; n < palette.size(); n++) { + saved_palette[n].x = palette.GetColor(n).GetRGB().x / 255; + saved_palette[n].y = palette.GetColor(n).GetRGB().y / 255; + saved_palette[n].z = palette.GetColor(n).GetRGB().z / 255; saved_palette[n].w = 255; // Alpha } init = true; diff --git a/src/app/rom.cc b/src/app/rom.cc index 7147139d..c246bb82 100644 --- a/src/app/rom.cc +++ b/src/app/rom.cc @@ -470,9 +470,9 @@ void ROM::SaveAllPalettes() { for (size_t j = 0; j < palette.size(); ++j) { gfx::SNESColor color = palette[j]; // If the color is modified, save the color to the ROM - if (color.modified) { + if (color.isModified()) { WriteColor(GetPaletteAddress(groupName, i, j), color); - color.modified = false; // Reset the modified flag after saving + color.setModified(false); // Reset the modified flag after saving } } } @@ -561,7 +561,7 @@ gfx::SNESPalette ROM::ReadPalette(int offset, int num_colors) { new_color.red = (color & 0x1F) * 8; new_color.green = ((color >> 5) & 0x1F) * 8; new_color.blue = ((color >> 10) & 0x1F) * 8; - colors[color_offset].setSNES(new_color); + colors[color_offset].SetSNES(gfx::ConvertRGBtoSNES(new_color)); color_offset++; offset += 2; } @@ -582,8 +582,8 @@ void ROM::WriteShort(int addr, int value) { // ============================================================================ void ROM::WriteColor(uint32_t address, const gfx::SNESColor& color) { - uint16_t bgr = ((color.snes >> 10) & 0x1F) | ((color.snes & 0x1F) << 10) | - (color.snes & 0x7C00); + uint16_t bgr = ((color.GetSNES() >> 10) & 0x1F) | + ((color.GetSNES() & 0x1F) << 10) | (color.GetSNES() & 0x7C00); // Write the 16-bit color value to the ROM at the specified address rom_data_[address] = static_cast(bgr & 0xFF); @@ -607,20 +607,5 @@ uint32_t ROM::GetPaletteAddress(const std::string& groupName, return address; } -// ============================================================================ - -absl::Status ROM::ApplyAssembly(const absl::string_view& filename, - uint32_t size) { - // int count = 0; - // auto patch = filename.data(); - // auto data = (char*)rom_data_.data(); - // if (int size = size_; !asar_patch(patch, data, patch_size, &size)) { - // auto asar_error = asar_geterrors(&count); - // auto full_error = asar_error->fullerrdata; - // return absl::InternalError(absl::StrCat("ASAR Error: ", full_error)); - // } - return absl::OkStatus(); -} - } // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/rom.h b/src/app/rom.h index 3078edd8..92166395 100644 --- a/src/app/rom.h +++ b/src/app/rom.h @@ -69,9 +69,6 @@ const absl::flat_hash_map paletteGroupColorCounts = { class ROM { public: - // Assembly functions - absl::Status ApplyAssembly(const absl::string_view& filename, uint32_t size); - // Compression function absl::StatusOr Compress(const int start, const int length, int mode = 1, bool check = false); diff --git a/src/app/yaze.cc b/src/app/yaze.cc index 27f4e68c..8fc85f3d 100644 --- a/src/app/yaze.cc +++ b/src/app/yaze.cc @@ -10,16 +10,13 @@ int main(int argc, char** argv) { absl::InitializeSymbolizer(argv[0]); absl::FailureSignalHandlerOptions options; + options.symbolize_stacktrace = true; + options.alarm_on_failure_secs = true; absl::InstallFailureSignalHandler(options); yaze::app::core::Controller controller; - auto entry_status = controller.OnEntry(); - if (!entry_status.ok()) { - // TODO(@scawful): log the specific error - return EXIT_FAILURE; - } - + EXIT_IF_ERROR(controller.OnEntry()) while (controller.IsActive()) { controller.OnInput(); controller.OnLoad(); diff --git a/src/app/zelda3/overworld.cc b/src/app/zelda3/overworld.cc index c6f83f3a..07c3962f 100644 --- a/src/app/zelda3/overworld.cc +++ b/src/app/zelda3/overworld.cc @@ -11,17 +11,17 @@ namespace { uint GetOwMapGfxHighPtr(const uchar *rom, int index) { int map_high_ptr = core::compressedAllMap32PointersHigh; - int p1 = (rom[(map_high_ptr) + 2 + (3 * index)] << 16) + - (rom[(map_high_ptr) + 1 + (3 * index)] << 8) + - (rom[(map_high_ptr + (3 * index))]); + int p1 = (rom[map_high_ptr + 2 + (3 * index)] << 16) + + (rom[map_high_ptr + 1 + (3 * index)] << 8) + + (rom[map_high_ptr + (3 * index)]); return core::SnesToPc(p1); } uint GetOwMapGfxLowPtr(const uchar *rom, int index) { int map_low_ptr = core::compressedAllMap32PointersLow; - int p2 = (rom[(map_low_ptr) + 2 + (3 * index)] << 16) + - (rom[(map_low_ptr) + 1 + (3 * index)] << 8) + - (rom[(map_low_ptr + (3 * index))]); + int p2 = (rom[map_low_ptr + 2 + (3 * index)] << 16) + + (rom[map_low_ptr + 1 + (3 * index)] << 8) + + (rom[map_low_ptr + (3 * index)]); return core::SnesToPc(p2); } @@ -360,13 +360,13 @@ void Overworld::AssembleMap32Tiles() { void Overworld::AssembleMap16Tiles() { int tpos = core::map16Tiles; for (int i = 0; i < 4096; i += 1) { - auto t0 = gfx::GetTilesInfo((rom_.toint16(tpos))); + auto t0 = gfx::GetTilesInfo(rom_.toint16(tpos)); tpos += 2; - auto t1 = gfx::GetTilesInfo((rom_.toint16(tpos))); + auto t1 = gfx::GetTilesInfo(rom_.toint16(tpos)); tpos += 2; - auto t2 = gfx::GetTilesInfo((rom_.toint16(tpos))); + auto t2 = gfx::GetTilesInfo(rom_.toint16(tpos)); tpos += 2; - auto t3 = gfx::GetTilesInfo((rom_.toint16(tpos))); + auto t3 = gfx::GetTilesInfo(rom_.toint16(tpos)); tpos += 2; tiles16.emplace_back(t0, t1, t2, t3); } @@ -393,8 +393,7 @@ void Overworld::OrganizeMapTiles(Bytes &bytes, Bytes &bytes2, int i, int sx, for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { auto tidD = (ushort)((bytes2[ttpos] << 8) + bytes[ttpos]); - int tpos = tidD; - if (tpos < tiles32.size()) { + if (int tpos = tidD; tpos < tiles32.size()) { if (i < 64) { AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.light_world); } else if (i < 128 && i >= 64) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 45f4d5b4..a31c1b85 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,7 +14,7 @@ add_executable( yaze_test yaze_test.cc compression_test.cc - rom_test.cc + snes_palette_test.cc ../src/app/rom.cc ../src/app/gfx/bitmap.cc ../src/app/gfx/snes_tile.cc diff --git a/test/compression_test.cc b/test/compression_test.cc index 6e079af1..fe5e05d6 100644 --- a/test/compression_test.cc +++ b/test/compression_test.cc @@ -59,7 +59,7 @@ std::shared_ptr ExpectNewCompressionPieceOk( } // namespace -TEST(GFXTest, NewDecompressionPieceOk) { +TEST(LC_LZ2_CompressionTest, NewDecompressionPieceOk) { char command = 1; int length = 1; char args[] = "aaa"; @@ -81,7 +81,7 @@ TEST(GFXTest, NewDecompressionPieceOk) { } } -TEST(GFXTest, DecompressionValidCommand) { +TEST(LC_LZ2_CompressionTest, DecompressionValidCommand) { ROM rom; Bytes simple_copy_input = {BUILD_HEADER(0x00, 0x02), 0x2A, 0x45, 0xFF}; uchar simple_copy_output[2] = {0x2A, 0x45}; @@ -89,7 +89,7 @@ TEST(GFXTest, DecompressionValidCommand) { EXPECT_THAT(simple_copy_output, ElementsAreArray(decomp_result.data(), 2)); } -TEST(GFXTest, DecompressionMixingCommand) { +TEST(LC_LZ2_CompressionTest, DecompressionMixingCommand) { ROM rom; uchar random1_i[11] = {BUILD_HEADER(0x01, 0x03), 0x2A, @@ -107,7 +107,7 @@ TEST(GFXTest, DecompressionMixingCommand) { EXPECT_THAT(random1_o, ElementsAreArray(decomp_result.data(), 9)); } -TEST(GFXTest, CompressionSingleSet) { +TEST(LC_LZ2_CompressionTest, CompressionSingleSet) { ROM rom; uchar single_set[5] = {0x2A, 0x2A, 0x2A, 0x2A, 0x2A}; uchar single_set_expected[3] = {BUILD_HEADER(1, 5), 0x2A, 0xFF}; @@ -116,7 +116,7 @@ TEST(GFXTest, CompressionSingleSet) { EXPECT_THAT(single_set_expected, ElementsAreArray(comp_result.data(), 3)); } -TEST(GFXTest, CompressionSingleWord) { +TEST(LC_LZ2_CompressionTest, CompressionSingleWord) { ROM rom; uchar single_word[6] = {0x2A, 0x01, 0x2A, 0x01, 0x2A, 0x01}; uchar single_word_expected[4] = {BUILD_HEADER(0x02, 0x06), 0x2A, 0x01, 0xFF}; @@ -125,7 +125,7 @@ TEST(GFXTest, CompressionSingleWord) { EXPECT_THAT(single_word_expected, ElementsAreArray(comp_result.data(), 4)); } -TEST(GFXTest, CompressionSingleIncrement) { +TEST(LC_LZ2_CompressionTest, CompressionSingleIncrement) { ROM rom; uchar single_inc[3] = {0x01, 0x02, 0x03}; uchar single_inc_expected[3] = {BUILD_HEADER(0x03, 0x03), 0x01, 0xFF}; @@ -133,7 +133,7 @@ TEST(GFXTest, CompressionSingleIncrement) { EXPECT_THAT(single_inc_expected, ElementsAreArray(comp_result.data(), 3)); } -TEST(GFXTest, CompressionSingleCopy) { +TEST(LC_LZ2_CompressionTest, CompressionSingleCopy) { ROM rom; uchar single_copy[4] = {0x03, 0x0A, 0x07, 0x14}; uchar single_copy_expected[6] = { @@ -143,7 +143,7 @@ TEST(GFXTest, CompressionSingleCopy) { } /* Hiding tests until I figure out a better PR to address the bug -TEST(GFXTest, CompressionSingleCopyRepeat) { +TEST(LC_LZ2_CompressionTest, CompressionSingleCopyRepeat) { ROM rom; uchar single_copy_repeat[8] = {0x03, 0x0A, 0x07, 0x14, 0x03, 10, 0x07, 0x14}; uchar single_copy_repeat_expected[9] = { @@ -154,7 +154,7 @@ TEST(GFXTest, CompressionSingleCopyRepeat) { ElementsAreArray(comp_result.data(), 9)); } -TEST(GFXTest, CompressionSingleOverflowIncrement) { +TEST(LC_LZ2_CompressionTest, CompressionSingleOverflowIncrement) { ROM rom; uchar overflow_inc[4] = {0xFE, 0xFF, 0x00, 0x01}; uchar overflow_inc_expected[3] = {BUILD_HEADER(0x03, 0x04), 0xFE, 0xFF}; @@ -163,7 +163,7 @@ TEST(GFXTest, CompressionSingleOverflowIncrement) { EXPECT_THAT(overflow_inc_expected, ElementsAreArray(comp_result.data(), 3)); } -TEST(GFXTest, CompressionMixedRepeatIncrement) { +TEST(LC_LZ2_CompressionTest, CompressionMixedRepeatIncrement) { ROM rom; uchar to_compress_string[28] = {0x05, 0x05, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x05, 0x02, 0x05, 0x02, @@ -183,7 +183,7 @@ TEST(GFXTest, CompressionMixedRepeatIncrement) { } */ -TEST(GFXTest, CompressionMixedIncrementIntraCopyOffset) { +TEST(LC_LZ2_CompressionTest, CompressionMixedIncrementIntraCopyOffset) { ROM rom; uchar to_compress_string[] = {0x05, 0x05, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x05, 0x02, 0x05, 0x02, @@ -207,7 +207,7 @@ TEST(GFXTest, CompressionMixedIncrementIntraCopyOffset) { ElementsAreArray(comp_result.data(), 9)); } -TEST(GFXTest, CompressionMixedIncrementIntraCopySource) { +TEST(LC_LZ2_CompressionTest, CompressionMixedIncrementIntraCopySource) { ROM rom; uchar to_compress_string[] = {0x05, 0x05, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x05, 0x02, 0x05, 0x02, @@ -236,7 +236,7 @@ TEST(GFXTest, CompressionMixedIncrementIntraCopySource) { EXPECT_THAT(all_expected, ElementsAreArray(comp_result.data(), 16)); } -TEST(GFXTest, LengthBorderCompression) { +TEST(LC_LZ2_CompressionTest, LengthBorderCompression) { ROM rom; uchar buffer[3000]; @@ -270,7 +270,7 @@ TEST(GFXTest, LengthBorderCompression) { ElementsAreArray(comp_result.data(), 9)); } -TEST(GFXTest, CompressionExtendedWordCopy) { +TEST(LC_LZ2_CompressionTest, CompressionExtendedWordCopy) { ROM rom; uchar buffer[3000]; for (unsigned int i = 0; i < 3000; i += 2) { @@ -286,7 +286,7 @@ TEST(GFXTest, CompressionExtendedWordCopy) { } /* Extended Header Command is currently unimplemented -TEST(GFXTest, ExtendedHeaderDecompress) { +TEST(LC_LZ2_CompressionTest, ExtendedHeaderDecompress) { ROM rom; Bytes extendedcmd_i = {0b11100100, 0x8F, 0x2A, 0xFF}; uchar extendedcmd_o[50]; @@ -298,7 +298,7 @@ TEST(GFXTest, ExtendedHeaderDecompress) { ASSERT_THAT(extendedcmd_o, ElementsAreArray(decomp_result.data(), 50)); } -TEST(GFXTest, ExtendedHeaderDecompress2) { +TEST(LC_LZ2_CompressionTest, ExtendedHeaderDecompress2) { ROM rom; Bytes extendedcmd_i = {0b11100101, 0x8F, 0x2A, 0xFF}; uchar extendedcmd_o[50]; @@ -313,7 +313,7 @@ TEST(GFXTest, ExtendedHeaderDecompress2) { } */ -TEST(GFXTest, CompressionDecompressionEmptyData) { +TEST(LC_LZ2_CompressionTest, CompressionDecompressionEmptyData) { ROM rom; uchar empty_input[0] = {}; auto comp_result = ExpectCompressOk(rom, empty_input, 0); @@ -323,7 +323,7 @@ TEST(GFXTest, CompressionDecompressionEmptyData) { EXPECT_EQ(0, decomp_result.size()); } -// TEST(GFXTest, CompressionDecompressionSingleByte) { +// TEST(LC_LZ2_CompressionTest, CompressionDecompressionSingleByte) { // ROM rom; // uchar single_byte[1] = {0x2A}; // uchar single_byte_expected[3] = {BUILD_HEADER(0x00, 0x01), 0x2A, 0xFF}; @@ -335,7 +335,7 @@ TEST(GFXTest, CompressionDecompressionEmptyData) { // EXPECT_THAT(single_byte, ElementsAreArray(decomp_result.data(), 1)); // } -// TEST(GFXTest, CompressionDecompressionAllBitsSet) { +// TEST(LC_LZ2_CompressionTest, CompressionDecompressionAllBitsSet) { // ROM rom; // uchar all_bits_set[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // uchar all_bits_set_expected[3] = {BUILD_HEADER(0x01, 0x05), 0xFF, 0xFF}; @@ -348,7 +348,7 @@ TEST(GFXTest, CompressionDecompressionEmptyData) { // EXPECT_THAT(all_bits_set, ElementsAreArray(decomp_result.data(), 5)); // } -// TEST(GFXTest, DecompressionInvalidData) { +// TEST(LC_LZ2_CompressionTest, DecompressionInvalidData) { // ROM rom; // Bytes invalid_input = {0xFF, 0xFF}; // Invalid command diff --git a/test/rom_test.cc b/test/rom_test.cc deleted file mode 100644 index 82d133ba..00000000 --- a/test/rom_test.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include "app/rom.h" - -#include -#include - -#include - -#include "absl/status/statusor.h" - -namespace yaze_test { -namespace rom_test {} // namespace rom_test -} // namespace yaze_test \ No newline at end of file diff --git a/test/snes_palette_test.cc b/test/snes_palette_test.cc new file mode 100644 index 00000000..5bb2f06d --- /dev/null +++ b/test/snes_palette_test.cc @@ -0,0 +1,41 @@ +#include "app/gfx/snes_palette.h" + +#include +#include + +namespace yaze_test { +namespace gfx_test { + +TEST(SNESColorTest, ConvertRGBtoSNES) { + yaze::app::gfx::snes_color color = {132, 132, 132}; + uint16_t snes = yaze::app::gfx::ConvertRGBtoSNES(color); + ASSERT_EQ(snes, 0x4210); +} + +TEST(SNESColorTest, ConvertSNEStoRGB) { + uint16_t snes = 0x4210; + yaze::app::gfx::snes_color color = yaze::app::gfx::ConvertSNEStoRGB(snes); + ASSERT_EQ(color.red, 132); + ASSERT_EQ(color.green, 132); + ASSERT_EQ(color.blue, 132); +} + +TEST(SNESPaletteTest, AddColor) { + yaze::app::gfx::SNESPalette palette; + yaze::app::gfx::SNESColor color; + palette.AddColor(color); + ASSERT_EQ(palette.size(), 1); +} + +TEST(SNESPaletteTest, GetColorOutOfBounds) { + yaze::app::gfx::SNESPalette palette; + std::vector colors(5); + palette.Create(colors); + + // Now try to get a color at an out-of-bounds index + ASSERT_THROW(palette.GetColor(10), std::exception); + ASSERT_THROW(palette[10], std::exception); +} + +} // namespace gfx_test +} // namespace yaze_test \ No newline at end of file