backend-infra-engineer: Release v0.3.0 snapshot

This commit is contained in:
scawful
2025-09-27 00:25:45 -04:00
parent 8ce29e1436
commit e32ac75b9c
346 changed files with 55946 additions and 11764 deletions

View File

@@ -2,7 +2,7 @@
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/arena.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
@@ -112,7 +112,7 @@ void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
BeginGroup();
for (int i = 0; i < 8; i++) {
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
auto sheet = rom()->gfx_sheets().at(sheet_id);
auto &sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 22);
}
@@ -165,7 +165,7 @@ void GfxGroupEditor::DrawRoomsetViewer() {
BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
auto sheet = rom()->gfx_sheets().at(sheet_id);
auto &sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 23);
}
@@ -203,7 +203,8 @@ void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
auto sheet = rom()->gfx_sheets().at(115 + sheet_id);
auto &sheet =
gfx::Arena::Get().mutable_gfx_sheets()->at(115 + sheet_id);
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 24);
}
@@ -235,6 +236,9 @@ void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
} // namespace
void GfxGroupEditor::DrawPaletteViewer() {
if (!rom()->is_loaded()) {
return;
}
gui::InputHexByte("Selected Paletteset", &selected_paletteset_);
if (selected_paletteset_ >= 71) {
selected_paletteset_ = 71;

View File

@@ -13,7 +13,7 @@ namespace editor {
* @class GfxGroupEditor
* @brief Manage graphics group configurations in a Rom.
*/
class GfxGroupEditor : public SharedRom {
class GfxGroupEditor {
public:
absl::Status Update();
@@ -27,6 +27,8 @@ class GfxGroupEditor : public SharedRom {
void SetSelectedSpriteset(uint8_t spriteset) {
selected_spriteset_ = spriteset;
}
void set_rom(Rom* rom) { rom_ = rom; }
Rom* rom() const { return rom_; }
private:
uint8_t selected_blockset_ = 0;
@@ -39,6 +41,7 @@ class GfxGroupEditor : public SharedRom {
gui::Canvas spriteset_canvas_;
gfx::SnesPalette palette_;
Rom* rom_ = nullptr;
};
} // namespace editor

View File

@@ -4,10 +4,11 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "app/core/platform/clipboard.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/platform/renderer.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/core/window.h"
#include "app/gfx/arena.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/compression.h"
#include "app/gfx/scad_format.h"
@@ -41,15 +42,20 @@ constexpr ImGuiTableFlags kGfxEditTableFlags =
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
ImGuiTableFlags_SizingFixedFit;
void GraphicsEditor::Initialize() {}
absl::Status GraphicsEditor::Load() { return absl::OkStatus(); }
absl::Status GraphicsEditor::Update() {
if (ImGui::BeginTabBar("##TabBar")) {
status_ = UpdateGfxEdit();
TAB_ITEM("Sheet Browser")
if (asset_browser_.Initialized == false) {
asset_browser_.Initialize(rom()->gfx_sheets());
if (ImGui::BeginTabItem("Sheet Browser")) {
if (asset_browser_.Initialized == false) {
asset_browser_.Initialize(gfx::Arena::Get().gfx_sheets());
}
asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
ImGui::EndTabItem();
}
asset_browser_.Draw(rom()->gfx_sheets());
END_TAB_ITEM()
status_ = UpdateScadView();
status_ = UpdateLinkGfxView();
ImGui::EndTabBar();
@@ -67,17 +73,16 @@ absl::Status GraphicsEditor::UpdateGfxEdit() {
ImGui::TableSetupColumn(name);
ImGui::TableHeadersRow();
NEXT_COLUMN();
ImGui::TableNextColumn();
status_ = UpdateGfxSheetList();
NEXT_COLUMN();
ImGui::TableNextColumn();
if (rom()->is_loaded()) {
DrawGfxEditToolset();
status_ = UpdateGfxTabView();
}
NEXT_COLUMN();
ImGui::TableNextColumn();
if (rom()->is_loaded()) {
status_ = UpdatePaletteColumn();
}
@@ -116,25 +121,35 @@ void GraphicsEditor::DrawGfxEditToolset() {
TableNextColumn();
if (Button(ICON_MD_CONTENT_COPY)) {
#if YAZE_LIB_PNG == 1
std::vector<uint8_t> png_data =
rom()->gfx_sheets().at(current_sheet_).GetPngData();
gfx::Arena::Get().gfx_sheets().at(current_sheet_).GetPngData();
core::CopyImageToClipboard(png_data);
#else
// PNG support disabled - show message or alternative action
status_ = absl::UnimplementedError("PNG export not available in this build");
#endif
}
HOVER_HINT("Copy to Clipboard");
TableNextColumn();
if (Button(ICON_MD_CONTENT_PASTE)) {
#if YAZE_LIB_PNG == 1
std::vector<uint8_t> png_data;
int width, height;
core::GetImageFromClipboard(png_data, width, height);
if (png_data.size() > 0) {
rom()
->mutable_gfx_sheets()
gfx::Arena::Get()
.mutable_gfx_sheets()
->at(current_sheet_)
.Create(width, height, 8, png_data);
Renderer::GetInstance().UpdateBitmap(
&rom()->mutable_gfx_sheets()->at(current_sheet_));
Renderer::Get().UpdateBitmap(
&gfx::Arena::Get().mutable_gfx_sheets()->at(current_sheet_));
}
#else
// PNG support disabled - show message or alternative action
status_ = absl::UnimplementedError("PNG import not available in this build");
#endif
}
HOVER_HINT("Paste from Clipboard");
@@ -153,9 +168,9 @@ void GraphicsEditor::DrawGfxEditToolset() {
}
TableNextColumn();
auto bitmap = rom()->gfx_sheets()[current_sheet_];
auto bitmap = gfx::Arena::Get().gfx_sheets()[current_sheet_];
auto palette = bitmap.palette();
for (int i = 0; i < 8; i++) {
for (int i = 0; i < palette.size(); i++) {
ImGui::SameLine();
auto color =
ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
@@ -192,7 +207,7 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
(int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
int key = 0;
for (auto& value : rom()->gfx_sheets()) {
for (auto& value : gfx::Arena::Get().gfx_sheets()) {
ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
ImVec2(0x100 + 1, 0x40 + 1), true,
ImGuiWindowFlags_NoDecoration);
@@ -281,17 +296,18 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
gfx::Bitmap& current_bitmap = rom()->mutable_gfx_sheets()->at(sheet_id);
gfx::Bitmap& current_bitmap =
gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
auto draw_tile_event = [&]() {
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, &current_bitmap,
current_color_);
Renderer::GetInstance().UpdateBitmap(&current_bitmap);
Renderer::Get().UpdateBitmap(&current_bitmap);
};
current_sheet_canvas_.UpdateColorPainter(
rom()->mutable_gfx_sheets()->at(sheet_id), current_color_,
draw_tile_event, tile_size_, current_scale_);
gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
current_color_, draw_tile_event, tile_size_, current_scale_);
ImGui::EndChild();
ImGui::EndTabItem();
@@ -323,7 +339,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
current_sheet_ = id;
// ImVec2(0x100, 0x40),
current_sheet_canvas_.UpdateColorPainter(
rom()->mutable_gfx_sheets()->at(id), current_color_,
gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
[&]() {
},
@@ -359,13 +375,12 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
palette);
if (refresh_graphics_ && !open_sheets_.empty()) {
RETURN_IF_ERROR(
rom()
->mutable_gfx_sheets()
->data()[current_sheet_]
.ApplyPaletteWithTransparent(palette, edit_palette_sub_index_));
Renderer::GetInstance().UpdateBitmap(
&rom()->mutable_gfx_sheets()->data()[current_sheet_]);
gfx::Arena::Get()
.mutable_gfx_sheets()
->data()[current_sheet_]
.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
Renderer::Get().UpdateBitmap(
&gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_]);
refresh_graphics_ = false;
}
}
@@ -382,29 +397,29 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
ImGui::TableHeadersRow();
NEXT_COLUMN();
ImGui::TableNextColumn();
link_canvas_.DrawBackground();
link_canvas_.DrawGrid(16.0f);
int i = 0;
for (auto link_sheet : *rom()->mutable_link_graphics()) {
for (auto& link_sheet : link_sheets_) {
int x_offset = 0;
int y_offset = gfx::kTilesheetHeight * i * 4;
link_canvas_.DrawContextMenu(&link_sheet);
link_canvas_.DrawContextMenu();
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
i++;
}
link_canvas_.DrawOverlay();
link_canvas_.DrawGrid();
NEXT_COLUMN();
ImGui::TableNextColumn();
ImGui::Text("Placeholder");
NEXT_COLUMN();
ImGui::TableNextColumn();
if (ImGui::Button("Load Link Graphics (Experimental)")) {
if (rom()->is_loaded()) {
// Load Links graphics from the ROM
RETURN_IF_ERROR(rom()->LoadLinkGraphics());
ASSIGN_OR_RETURN(link_sheets_, LoadLinkGraphics(*rom()));
// Split it into the pose data frames
// Create an animation step display for the poses
@@ -461,9 +476,9 @@ absl::Status GraphicsEditor::UpdateScadView() {
// TODO: Implement the Super Donkey 1 graphics decompression
// if (refresh_graphics_) {
// for (int i = 0; i < kNumGfxSheets; i++) {
// status_ = graphics_bin_[i].ApplyPalette(
// status_ = graphics_bin_[i].SetPalette(
// col_file_palette_group_[current_palette_index_]);
// Renderer::GetInstance().UpdateBitmap(&graphics_bin_[i]);
// Renderer::Get().UpdateBitmap(&graphics_bin_[i]);
// }
// refresh_graphics_ = false;
// }
@@ -488,16 +503,15 @@ absl::Status GraphicsEditor::UpdateScadView() {
absl::Status GraphicsEditor::DrawToolset() {
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
"#memoryEditor",
"##separator_gfx1",
};
if (ImGui::BeginTable("GraphicsToolset", 2, ImGuiTableFlags_SizingFixedFit,
if (ImGui::BeginTable("GraphicsToolset", 1, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
for (const auto& name : kGfxToolsetColumnNames)
ImGui::TableSetupColumn(name.data());
TableNextColumn();
if (Button(ICON_MD_MEMORY)) {
if (Button(absl::StrCat(ICON_MD_MEMORY, "Open Memory Editor").c_str())) {
if (!open_memory_editor_) {
open_memory_editor_ = true;
} else {
@@ -505,8 +519,6 @@ absl::Status GraphicsEditor::DrawToolset() {
}
}
TEXT_COLUMN("Open Memory Editor") // Separator
ImGui::EndTable();
}
return absl::OkStatus();
@@ -532,13 +544,13 @@ absl::Status GraphicsEditor::DrawCgxImport() {
}
if (ImGui::Button("Load CGX Data")) {
status_ = gfx::scad_format::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
decoded_cgx_, extra_cgx_data_);
status_ = gfx::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
decoded_cgx_, extra_cgx_data_);
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
if (col_file_) {
cgx_bitmap_.ApplyPalette(decoded_col_);
Renderer::GetInstance().RenderBitmap(&cgx_bitmap_);
cgx_bitmap_.SetPalette(decoded_col_);
Renderer::Get().RenderBitmap(&cgx_bitmap_);
}
}
@@ -559,17 +571,16 @@ absl::Status GraphicsEditor::DrawScrImport() {
InputInt("SCR Mod", &scr_mod_value_);
if (ImGui::Button("Load Scr Data")) {
status_ =
gfx::scad_format::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
status_ = gfx::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
decoded_scr_data_.resize(0x100 * 0x100);
status_ = gfx::scad_format::DrawScrWithCgx(current_bpp_, scr_data_,
decoded_scr_data_, decoded_cgx_);
status_ = gfx::DrawScrWithCgx(current_bpp_, scr_data_, decoded_scr_data_,
decoded_cgx_);
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
if (scr_loaded_) {
scr_bitmap_.ApplyPalette(decoded_col_);
Renderer::GetInstance().RenderBitmap(&scr_bitmap_);
scr_bitmap_.SetPalette(decoded_col_);
Renderer::Get().RenderBitmap(&scr_bitmap_);
}
}
@@ -599,7 +610,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
col_file_palette_ = gfx::SnesPalette(col_data_);
// gigaleak dev format based code
decoded_col_ = gfx::scad_format::DecodeColFile(col_file_path_);
decoded_col_ = gfx::DecodeColFile(col_file_path_);
col_file_ = true;
is_open_ = true;
}
@@ -705,7 +716,7 @@ absl::Status GraphicsEditor::DrawClipboardImport() {
const auto clipboard_data =
std::vector<uint8_t>(text, text + strlen(text));
ImGui::MemFree((void*)text);
status_ = temp_rom_.LoadFromBytes(clipboard_data);
status_ = temp_rom_.LoadFromData(clipboard_data);
is_open_ = true;
open_memory_editor_ = true;
}
@@ -755,7 +766,7 @@ absl::Status GraphicsEditor::DrawMemoryEditor() {
absl::Status GraphicsEditor::DecompressImportData(int size) {
ASSIGN_OR_RETURN(import_data_, gfx::lc_lz2::DecompressV2(
temp_rom_.data(), current_offset_, size))
temp_rom_.data(), current_offset_, size));
auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
bin_bitmap_.Create(gfx::kTilesheetWidth, 0x2000, gfx::kTilesheetDepth,
@@ -765,13 +776,13 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
auto palette_group = rom()->palette_group().overworld_animated;
z3_rom_palette_ = palette_group[current_palette_];
if (col_file_) {
status_ = bin_bitmap_.ApplyPalette(col_file_palette_);
bin_bitmap_.SetPalette(col_file_palette_);
} else {
status_ = bin_bitmap_.ApplyPalette(z3_rom_palette_);
bin_bitmap_.SetPalette(z3_rom_palette_);
}
}
Renderer::GetInstance().RenderBitmap(&bin_bitmap_);
Renderer::Get().RenderBitmap(&bin_bitmap_);
gfx_loaded_ = true;
return absl::OkStatus();
@@ -784,12 +795,12 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
std::stoi(offset, nullptr, 16); // convert hex string to int
ASSIGN_OR_RETURN(
auto decompressed_data,
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000));
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
gfx::kTilesheetDepth, converted_sheet);
if (col_file_) {
status_ = gfx_sheets_[i].ApplyPalette(
gfx_sheets_[i].SetPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
@@ -797,10 +808,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
auto palette_group = rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
gfx_sheets_[i].SetPalette(z3_rom_palette_);
}
Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
Renderer::Get().RenderBitmap(&gfx_sheets_[i]);
i++;
}
@@ -809,22 +820,22 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
std::stoi(offset, nullptr, 16); // convert hex string to int
ASSIGN_OR_RETURN(
auto decompressed_data,
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000));
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
gfx::kTilesheetDepth, converted_sheet);
if (col_file_) {
status_ = gfx_sheets_[i].ApplyPalette(
gfx_sheets_[i].SetPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group = rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
gfx_sheets_[i].SetPalette(z3_rom_palette_);
}
Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
Renderer::Get().RenderBitmap(&gfx_sheets_[i]);
i++;
}
super_donkey_ = true;

View File

@@ -54,18 +54,28 @@ const std::string kSuperDonkeySprites[] = {
* drawing toolsets, palette controls, clipboard imports, experimental features,
* and memory editor.
*/
class GraphicsEditor : public SharedRom, public Editor {
class GraphicsEditor : public Editor {
public:
GraphicsEditor() { type_ = EditorType::kGraphics; }
explicit GraphicsEditor(Rom* rom = nullptr) : rom_(rom) {
type_ = EditorType::kGraphics;
}
void Initialize() override;
absl::Status Load() override;
absl::Status Save() override { return absl::UnimplementedError("Save"); }
absl::Status Update() override;
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
absl::Status Find() override { return absl::UnimplementedError("Find"); }
// Set the ROM pointer
void set_rom(Rom* rom) { rom_ = rom; }
// Get the ROM pointer
Rom* rom() const { return rom_; }
private:
enum class GfxEditMode {
@@ -159,7 +169,7 @@ class GraphicsEditor : public SharedRom, public Editor {
Rom temp_rom_;
Rom tilemap_rom_;
zelda3::Overworld overworld_;
zelda3::Overworld overworld_{&temp_rom_};
MemoryEditor cgx_memory_editor_;
MemoryEditor col_memory_editor_;
PaletteEditor palette_editor_;
@@ -176,6 +186,7 @@ class GraphicsEditor : public SharedRom, public Editor {
gfx::Bitmap bin_bitmap_;
gfx::Bitmap link_full_sheet_;
std::array<gfx::Bitmap, kNumGfxSheets> gfx_sheets_;
std::array<gfx::Bitmap, kNumLinkSheets> link_sheets_;
gfx::PaletteGroup col_file_palette_group_;
gfx::SnesPalette z3_rom_palette_;
@@ -192,6 +203,8 @@ class GraphicsEditor : public SharedRom, public Editor {
ImVec2(gfx::kTilesheetWidth * 4, gfx::kTilesheetHeight * 0x10 * 4),
gui::CanvasGridSize::k16x16};
absl::Status status_;
Rom* rom_;
};
} // namespace editor

View File

@@ -4,7 +4,6 @@
#include "absl/strings/str_cat.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/color.h"
#include "app/gui/style.h"
#include "imgui/imgui.h"
namespace yaze {
@@ -37,11 +36,8 @@ using ImGui::SetClipboardText;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetColumnIndex;
using ImGui::TableSetupColumn;
using ImGui::Text;
using ImGui::TreeNode;
using ImGui::TreePop;
using namespace gfx;
@@ -51,12 +47,14 @@ constexpr ImGuiTableFlags kPaletteTableFlags =
constexpr ImGuiColorEditFlags kPalNoAlpha = ImGuiColorEditFlags_NoAlpha;
constexpr ImGuiColorEditFlags kPalButtonFlags2 = ImGuiColorEditFlags_NoAlpha |
ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip;
constexpr ImGuiColorEditFlags kPalButtonFlags = ImGuiColorEditFlags_NoAlpha |
ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip;
constexpr ImGuiColorEditFlags kColorPopupFlags =
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha;
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha |
ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV |
ImGuiColorEditFlags_DisplayHex;
namespace {
int CustomFormatString(char* buf, size_t buf_size, const char* fmt, ...) {
@@ -94,7 +92,7 @@ absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
static bool init = false;
if (loaded && !init) {
for (int n = 0; n < palette.size(); n++) {
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
auto color = palette[n];
current_palette[n].x = color.rgb().x / 255;
current_palette[n].y = color.rgb().y / 255;
current_palette[n].z = color.rgb().z / 255;
@@ -146,7 +144,7 @@ absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
PushID(n);
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
if (ColorButton("##palette", current_palette[n], kPalButtonFlags2,
if (ColorButton("##palette", current_palette[n], kPalButtonFlags,
ImVec2(20, 20)))
color = ImVec4(current_palette[n].x, current_palette[n].y,
current_palette[n].z, color.w); // Preserve alpha!
@@ -170,7 +168,9 @@ absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
return absl::OkStatus();
}
absl::Status PaletteEditor::Update() {
void PaletteEditor::Initialize() {}
absl::Status PaletteEditor::Load() {
if (rom()->is_loaded()) {
// Initialize the labels
for (int i = 0; i < kNumPalettes; i++) {
@@ -181,71 +181,166 @@ absl::Status PaletteEditor::Update() {
} else {
return absl::NotFoundError("ROM not open, no palettes to display");
}
return absl::OkStatus();
}
if (BeginTable("paletteEditorTable", 2, kPaletteTableFlags, ImVec2(0, 0))) {
TableSetupColumn("Palette Groups", ImGuiTableColumnFlags_WidthStretch,
GetContentRegionAvail().x);
TableSetupColumn("Palette Sets and Metadata",
ImGuiTableColumnFlags_WidthStretch,
GetContentRegionAvail().x);
absl::Status PaletteEditor::Update() {
static int current_palette_group = 0;
if (BeginTable("paletteGroupsTable", 3, kPaletteTableFlags)) {
TableSetupColumn("Categories", ImGuiTableColumnFlags_WidthFixed, 200);
TableSetupColumn("Palette Editor", ImGuiTableColumnFlags_WidthStretch);
TableSetupColumn("Quick Access", ImGuiTableColumnFlags_WidthStretch);
TableHeadersRow();
TableNextRow();
TableNextColumn();
DrawModifiedColors();
DrawCustomPalette();
Separator();
gui::SnesColorEdit4("Current Color Picker", &current_color_,
ImGuiColorEditFlags_NoAlpha);
Separator();
DisplayCategoryTable();
static int selected_category = 0;
BeginChild("CategoryList", ImVec2(0, GetContentRegionAvail().y), true);
for (int i = 0; i < kNumPalettes; i++) {
const bool is_selected = (selected_category == i);
if (Selectable(std::string(kPaletteCategoryNames[i]).c_str(),
is_selected)) {
selected_category = i;
}
}
EndChild();
TableNextColumn();
gfx_group_editor_.DrawPaletteViewer();
BeginChild("PaletteEditor", ImVec2(0, 0), true);
Text("%s", std::string(kPaletteCategoryNames[selected_category]).c_str());
Separator();
static bool in_use = false;
ImGui::Checkbox("Palette in use? ", &in_use);
Separator();
static std::string palette_notes = "Notes about the palette";
ImGui::InputTextMultiline("Notes", palette_notes.data(), 1024,
ImVec2(-1, ImGui::GetTextLineHeight() * 4),
ImGuiInputTextFlags_AllowTabInput);
if (rom()->is_loaded()) {
status_ = DrawPaletteGroup(selected_category, true);
}
EndChild();
TableNextColumn();
DrawQuickAccessTab();
EndTable();
}
CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus();
}
void PaletteEditor::DrawQuickAccessTab() {
BeginChild("QuickAccessPalettes", ImVec2(0, 0), true);
Text("Custom Palette");
DrawCustomPalette();
Separator();
// Current color picker with more options
BeginGroup();
Text("Current Color");
gui::SnesColorEdit4("##CurrentColorPicker", &current_color_,
kColorPopupFlags);
char buf[64];
auto col = current_color_.rgb();
int cr = F32_TO_INT8_SAT(col.x / 255.0f);
int cg = F32_TO_INT8_SAT(col.y / 255.0f);
int cb = F32_TO_INT8_SAT(col.z / 255.0f);
CustomFormatString(buf, IM_ARRAYSIZE(buf), "RGB: %d, %d, %d", cr, cg, cb);
Text("%s", buf);
CustomFormatString(buf, IM_ARRAYSIZE(buf), "SNES: $%04X",
current_color_.snes());
Text("%s", buf);
if (Button("Copy to Clipboard")) {
SetClipboardText(buf);
}
EndGroup();
Separator();
// Recently used colors
Text("Recently Used Colors");
for (int i = 0; i < recently_used_colors_.size(); i++) {
PushID(i);
if (i % 8 != 0) SameLine();
ImVec4 displayColor =
gui::ConvertSnesColorToImVec4(recently_used_colors_[i]);
if (ImGui::ColorButton("##recent", displayColor)) {
// Set as current color
current_color_ = recently_used_colors_[i];
}
PopID();
}
EndChild();
}
void PaletteEditor::DrawCustomPalette() {
if (BeginChild("ColorPalette", ImVec2(0, 40), true,
if (BeginChild("ColorPalette", ImVec2(0, 40), ImGuiChildFlags_None,
ImGuiWindowFlags_HorizontalScrollbar)) {
for (int i = 0; i < custom_palette_.size(); i++) {
PushID(i);
SameLine(0.0f, GetStyle().ItemSpacing.y);
gui::SnesColorEdit4("##customPalette", &custom_palette_[i],
ImGuiColorEditFlags_NoInputs);
// Accept a drag drop target which adds a color to the custom_palette_
if (i > 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
// Add a context menu to each color
ImVec4 displayColor = gui::ConvertSnesColorToImVec4(custom_palette_[i]);
bool open_color_picker = ImGui::ColorButton(
absl::StrFormat("##customPal%d", i).c_str(), displayColor);
if (open_color_picker) {
current_color_ = custom_palette_[i];
edit_palette_index_ = i;
ImGui::OpenPopup("CustomPaletteColorEdit");
}
if (BeginPopupContextItem()) {
// Edit color directly in the popup
SnesColor original_color = custom_palette_[i];
if (gui::SnesColorEdit4("Edit Color", &custom_palette_[i],
kColorPopupFlags)) {
// Color was changed, add to recently used
AddRecentlyUsedColor(custom_palette_[i]);
}
if (Button("Delete", ImVec2(-1, 0))) {
custom_palette_.erase(custom_palette_.begin() + i);
}
}
// Handle drag/drop for palette rearrangement
if (BeginDragDropTarget()) {
if (const ImGuiPayload* payload =
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) {
ImVec4 color = ImVec4(0, 0, 0, 1.0f);
memcpy((float*)&color, payload->Data, sizeof(float));
custom_palette_.push_back(SnesColor(color));
ImVec4 color;
memcpy((float*)&color, payload->Data, sizeof(float) * 3);
color.w = 1.0f; // Set alpha to 1.0
custom_palette_[i] = SnesColor(color);
AddRecentlyUsedColor(custom_palette_[i]);
}
EndDragDropTarget();
}
PopID();
}
SameLine();
if (ImGui::Button("Add Color")) {
if (ImGui::Button("+")) {
custom_palette_.push_back(SnesColor(0x7FFF));
}
SameLine();
if (ImGui::Button("Export to Clipboard")) {
if (ImGui::Button("Clear")) {
custom_palette_.clear();
}
SameLine();
if (ImGui::Button("Export")) {
std::string clipboard;
for (const auto& color : custom_palette_) {
clipboard += absl::StrFormat("$%04X,", color.snes());
@@ -254,89 +349,20 @@ void PaletteEditor::DrawCustomPalette() {
}
}
EndChild();
}
void PaletteEditor::DisplayCategoryTable() {
if (BeginTable("Category Table", 8,
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_SizingStretchSame |
ImGuiTableFlags_Hideable,
ImVec2(0, 0))) {
TableSetupColumn("Weapons and Gear");
TableSetupColumn("Overworld and Area Colors");
TableSetupColumn("Global Sprites");
TableSetupColumn("Sprites Aux1");
TableSetupColumn("Sprites Aux2");
TableSetupColumn("Sprites Aux3");
TableSetupColumn("Maps and Items");
TableSetupColumn("Dungeons");
TableHeadersRow();
TableNextRow();
TableSetColumnIndex(0);
if (TreeNode("Sword")) {
status_ = DrawPaletteGroup(PaletteCategory::kSword);
TreePop();
// Color picker popup for custom palette editing
if (ImGui::BeginPopup("CustomPaletteColorEdit")) {
if (edit_palette_index_ >= 0 &&
edit_palette_index_ < custom_palette_.size()) {
SnesColor original_color = custom_palette_[edit_palette_index_];
if (gui::SnesColorEdit4(
"Edit Color", &custom_palette_[edit_palette_index_],
kColorPopupFlags | ImGuiColorEditFlags_PickerHueWheel)) {
// Color was changed, add to recently used
AddRecentlyUsedColor(custom_palette_[edit_palette_index_]);
}
}
if (TreeNode("Shield")) {
status_ = DrawPaletteGroup(PaletteCategory::kShield);
TreePop();
}
if (TreeNode("Clothes")) {
status_ = DrawPaletteGroup(PaletteCategory::kClothes, true);
TreePop();
}
TableSetColumnIndex(1);
gui::BeginChildWithScrollbar("##WorldPaletteScrollRegion");
if (TreeNode("World Colors")) {
status_ = DrawPaletteGroup(PaletteCategory::kWorldColors);
TreePop();
}
if (TreeNode("Area Colors")) {
status_ = DrawPaletteGroup(PaletteCategory::kAreaColors);
TreePop();
}
EndChild();
TableSetColumnIndex(2);
status_ = DrawPaletteGroup(PaletteCategory::kGlobalSprites, true);
TableSetColumnIndex(3);
status_ = DrawPaletteGroup(PaletteCategory::kSpritesAux1);
TableSetColumnIndex(4);
status_ = DrawPaletteGroup(PaletteCategory::kSpritesAux2);
TableSetColumnIndex(5);
status_ = DrawPaletteGroup(PaletteCategory::kSpritesAux3);
TableSetColumnIndex(6);
gui::BeginChildWithScrollbar("##MapPaletteScrollRegion");
if (TreeNode("World Map")) {
status_ = DrawPaletteGroup(PaletteCategory::kWorldMap, true);
TreePop();
}
if (TreeNode("Dungeon Map")) {
status_ = DrawPaletteGroup(PaletteCategory::kDungeonMap);
TreePop();
}
if (TreeNode("Triforce")) {
status_ = DrawPaletteGroup(PaletteCategory::kTriforce);
TreePop();
}
if (TreeNode("Crystal")) {
status_ = DrawPaletteGroup(PaletteCategory::kCrystal);
TreePop();
}
EndChild();
TableSetColumnIndex(7);
gui::BeginChildWithScrollbar("##DungeonPaletteScrollRegion");
status_ = DrawPaletteGroup(PaletteCategory::kDungeons, true);
EndChild();
EndTable();
ImGui::EndPopup();
}
}
@@ -350,26 +376,30 @@ absl::Status PaletteEditor::DrawPaletteGroup(int category, bool right_side) {
rom()->mutable_palette_group()->get_group(palette_group_name.data());
const auto size = palette_group->size();
static bool edit_color = false;
for (int j = 0; j < size; j++) {
gfx::SnesPalette* palette = palette_group->mutable_palette(j);
auto pal_size = palette->size();
BeginGroup();
PushID(j);
BeginGroup();
rom()->resource_label()->SelectableLabelWithNameEdit(
false, palette_group_name.data(), /*key=*/std::to_string(j),
"Unnamed Palette");
EndGroup();
for (int n = 0; n < pal_size; n++) {
PushID(n);
if (!right_side) {
if ((n % 7) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
} else {
if ((n % 15) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
}
if (n > 0 && n % 8 != 0) SameLine(0.0f, 2.0f);
auto popup_id =
absl::StrCat(kPaletteCategoryNames[category].data(), j, "_", n);
// Small icon of the color in the palette
if (gui::SnesColorButton(popup_id, *palette->mutable_color(n),
kPalNoAlpha)) {
ASSIGN_OR_RETURN(current_color_, palette->GetColor(n));
ImVec4 displayColor = gui::ConvertSnesColorToImVec4((*palette)[n]);
if (ImGui::ColorButton(popup_id.c_str(), displayColor)) {
current_color_ = (*palette)[n];
AddRecentlyUsedColor(current_color_);
}
if (BeginPopupContextItem(popup_id.c_str())) {
@@ -377,49 +407,64 @@ absl::Status PaletteEditor::DrawPaletteGroup(int category, bool right_side) {
}
PopID();
}
SameLine();
rom()->resource_label()->SelectableLabelWithNameEdit(
false, palette_group_name.data(), /*key=*/std::to_string(j),
"Unnamed Palette");
if (right_side) Separator();
PopID();
EndGroup();
if (j < size - 1) {
Separator();
}
}
return absl::OkStatus();
}
void PaletteEditor::DrawModifiedColors() {
if (BeginChild("ModifiedColors", ImVec2(0, 100), true,
ImGuiWindowFlags_HorizontalScrollbar)) {
for (int i = 0; i < history_.size(); i++) {
PushID(i);
gui::SnesColorEdit4("Original ", &history_.GetOriginalColor(i),
ImGuiColorEditFlags_NoInputs);
SameLine(0.0f, GetStyle().ItemSpacing.y);
gui::SnesColorEdit4("Modified ", &history_.GetModifiedColor(i),
ImGuiColorEditFlags_NoInputs);
PopID();
}
void PaletteEditor::AddRecentlyUsedColor(const SnesColor& color) {
// Check if color already exists in recently used
auto it = std::find_if(
recently_used_colors_.begin(), recently_used_colors_.end(),
[&color](const SnesColor& c) { return c.snes() == color.snes(); });
// If found, remove it to re-add at front
if (it != recently_used_colors_.end()) {
recently_used_colors_.erase(it);
}
// Add at front
recently_used_colors_.insert(recently_used_colors_.begin(), color);
// Limit size
if (recently_used_colors_.size() > 16) {
recently_used_colors_.pop_back();
}
EndChild();
}
absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
int j, int n) {
auto col = gfx::ToFloatArray(palette[n]);
auto original_color = palette[n];
if (gui::SnesColorEdit4("Edit Color", &palette[n], kColorPopupFlags)) {
history_.RecordChange(/*group_name=*/std::string(kPaletteGroupNames[i]),
/*palette_index=*/j, /*color_index=*/n,
original_color, palette[n]);
palette[n].set_modified(true);
// Add to recently used colors
AddRecentlyUsedColor(palette[n]);
}
// Color information display
char buf[64];
int cr = F32_TO_INT8_SAT(col[0]);
int cg = F32_TO_INT8_SAT(col[1]);
int cb = F32_TO_INT8_SAT(col[2]);
Text("RGB: %d, %d, %d", cr, cg, cb);
Text("SNES: $%04X", palette[n].snes());
Separator();
if (Button("Copy as..", ImVec2(-1, 0))) OpenPopup("Copy");
if (BeginPopup("Copy")) {
int cr = F32_TO_INT8_SAT(col[0]);
int cg = F32_TO_INT8_SAT(col[1]);
int cb = 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);
@@ -438,6 +483,11 @@ absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
EndPopup();
}
// Add a button to add this color to custom palette
if (Button("Add to Custom Palette", ImVec2(-1, 0))) {
custom_palette_.push_back(palette[n]);
}
EndPopup();
return absl::OkStatus();
}
@@ -449,11 +499,14 @@ absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
}
// Get the current color
ASSIGN_OR_RETURN(auto color, palette.GetColor(index));
auto color = palette[index];
auto currentColor = color.rgb();
if (ColorPicker4("Color Picker", (float*)&palette[index])) {
// The color was modified, update it in the palette
palette(index, currentColor);
palette[index] = gui::ConvertImVec4ToSnesColor(currentColor);
// Add to recently used colors
AddRecentlyUsedColor(palette[index]);
}
return absl::OkStatus();
}
@@ -464,9 +517,9 @@ absl::Status PaletteEditor::ResetColorToOriginal(
if (index >= palette.size() || index >= originalPalette.size()) {
return absl::InvalidArgumentError("Index out of bounds");
}
ASSIGN_OR_RETURN(auto color, originalPalette.GetColor(index));
auto color = originalPalette[index];
auto originalColor = color.rgb();
palette(index, originalColor);
palette[index] = gui::ConvertImVec4ToSnesColor(originalColor);
return absl::OkStatus();
}

View File

@@ -6,10 +6,10 @@
#include <vector>
#include "absl/status/status.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/editor.h"
#include "app/gfx/snes_palette.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/gfx/snes_color.h"
#include "app/gfx/snes_palette.h"
#include "app/rom.h"
#include "imgui/imgui.h"
@@ -17,6 +17,7 @@ namespace yaze {
namespace editor {
namespace palette_internal {
struct PaletteChange {
std::string group_name;
size_t palette_index;
@@ -76,32 +77,36 @@ absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded);
* @class PaletteEditor
* @brief Allows the user to view and edit in game palettes.
*/
class PaletteEditor : public SharedRom, public Editor {
class PaletteEditor : public Editor {
public:
PaletteEditor() {
explicit PaletteEditor(Rom* rom = nullptr) : rom_(rom) {
type_ = EditorType::kPalette;
custom_palette_.push_back(gfx::SnesColor(0x7FFF));
}
void Initialize() override;
absl::Status Load() override;
absl::Status Update() override;
absl::Status Cut() override { return absl::OkStatus(); }
absl::Status Copy() override { return absl::OkStatus(); }
absl::Status Paste() override { return absl::OkStatus(); }
absl::Status Undo() override { return absl::OkStatus(); }
absl::Status Redo() override { return absl::OkStatus(); }
absl::Status Find() override { return absl::OkStatus(); }
absl::Status Save() override { return absl::UnimplementedError("Save"); }
void DisplayCategoryTable();
void DrawQuickAccessTab();
void DrawCustomPalette();
absl::Status DrawPaletteGroup(int category, bool right_side = false);
absl::Status EditColorInPalette(gfx::SnesPalette& palette, int index);
absl::Status ResetColorToOriginal(gfx::SnesPalette& palette, int index,
const gfx::SnesPalette& originalPalette);
absl::Status DrawPaletteGroup(int category, bool right_side = false);
void DrawCustomPalette();
void AddRecentlyUsedColor(const gfx::SnesColor& color);
void DrawModifiedColors();
void set_rom(Rom* rom) { rom_ = rom; }
Rom* rom() const { return rom_; }
private:
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
@@ -112,10 +117,15 @@ class PaletteEditor : public SharedRom, public Editor {
GfxGroupEditor gfx_group_editor_;
std::vector<gfx::SnesColor> custom_palette_;
std::vector<gfx::SnesColor> recently_used_colors_;
int edit_palette_index_ = -1;
ImVec4 saved_palette_[256] = {};
palette_internal::PaletteEditorHistory history_;
Rom* rom_;
};
} // namespace editor

View File

@@ -6,17 +6,18 @@
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "app/core/constants.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/platform/renderer.h"
#include "app/core/window.h"
#include "app/gfx/arena.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/tilesheet.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "imgui/imgui.h"
#include "util/hex.h"
#include "util/macro.h"
namespace yaze {
namespace editor {
@@ -25,12 +26,42 @@ using core::Renderer;
constexpr uint32_t kRedPen = 0xFF0000FF;
void ScreenEditor::Initialize() {}
absl::Status ScreenEditor::Load() {
ASSIGN_OR_RETURN(dungeon_maps_,
zelda3::LoadDungeonMaps(*rom(), dungeon_map_labels_));
RETURN_IF_ERROR(zelda3::LoadDungeonMapTile16(
tile16_blockset_, *rom(), rom()->graphics_buffer(), false));
// TODO: Load roomset gfx based on dungeon ID
sheets_.try_emplace(0, gfx::Arena::Get().gfx_sheets()[212]);
sheets_.try_emplace(1, gfx::Arena::Get().gfx_sheets()[213]);
sheets_.try_emplace(2, gfx::Arena::Get().gfx_sheets()[214]);
sheets_.try_emplace(3, gfx::Arena::Get().gfx_sheets()[215]);
/**
int current_tile8 = 0;
int tile_data_offset = 0;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 32; j++) {
std::vector<uint8_t> tile_data(64, 0); // 8x8 tile (64 bytes
int tile_index = current_tile8 + j;
int x = (j % 8) * 8;
int y = (j / 8) * 8;
sheets_[i].Get8x8Tile(tile_index, x, y, tile_data, tile_data_offset);
tile8_individual_.emplace_back(gfx::Bitmap(8, 8, 4, tile_data));
tile8_individual_.back().SetPalette(*rom()->mutable_dungeon_palette(3));
Renderer::Get().RenderBitmap(&tile8_individual_.back());
}
tile_data_offset = 0;
}
*/
return absl::OkStatus();
}
absl::Status ScreenEditor::Update() {
if (ImGui::BeginTabBar("##ScreenEditorTabBar")) {
if (ImGui::BeginTabItem("Dungeon Maps")) {
if (rom()->is_loaded()) {
DrawDungeonMapsEditor();
}
DrawDungeonMapsEditor();
ImGui::EndTabItem();
}
DrawInventoryMenuEditor();
@@ -43,44 +74,44 @@ absl::Status ScreenEditor::Update() {
}
void ScreenEditor::DrawInventoryMenuEditor() {
TAB_ITEM("Inventory Menu")
if (ImGui::BeginTabItem("Inventory Menu")) {
static bool create = false;
if (!create && rom()->is_loaded()) {
status_ = inventory_.Create();
palette_ = inventory_.palette();
create = true;
}
static bool create = false;
if (!create && rom()->is_loaded()) {
status_ = inventory_.Create();
palette_ = inventory_.Palette();
create = true;
DrawInventoryToolset();
if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn("Canvas");
ImGui::TableSetupColumn("Tiles");
ImGui::TableSetupColumn("Palette");
ImGui::TableHeadersRow();
ImGui::TableNextColumn();
screen_canvas_.DrawBackground();
screen_canvas_.DrawContextMenu();
screen_canvas_.DrawBitmap(inventory_.bitmap(), 2, create);
screen_canvas_.DrawGrid(32.0f);
screen_canvas_.DrawOverlay();
ImGui::TableNextColumn();
tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
tilesheet_canvas_.DrawContextMenu();
tilesheet_canvas_.DrawBitmap(inventory_.tilesheet(), 2, create);
tilesheet_canvas_.DrawGrid(16.0f);
tilesheet_canvas_.DrawOverlay();
ImGui::TableNextColumn();
gui::DisplayPalette(palette_, create);
ImGui::EndTable();
}
ImGui::Separator();
ImGui::EndTabItem();
}
DrawInventoryToolset();
if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn("Canvas");
ImGui::TableSetupColumn("Tiles");
ImGui::TableSetupColumn("Palette");
ImGui::TableHeadersRow();
ImGui::TableNextColumn();
screen_canvas_.DrawBackground();
screen_canvas_.DrawContextMenu();
screen_canvas_.DrawBitmap(inventory_.Bitmap(), 2, create);
screen_canvas_.DrawGrid(32.0f);
screen_canvas_.DrawOverlay();
ImGui::TableNextColumn();
tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
tilesheet_canvas_.DrawContextMenu();
tilesheet_canvas_.DrawBitmap(inventory_.Tilesheet(), 2, create);
tilesheet_canvas_.DrawGrid(16.0f);
tilesheet_canvas_.DrawOverlay();
ImGui::TableNextColumn();
status_ = gui::DisplayPalette(palette_, create);
ImGui::EndTable();
}
ImGui::Separator();
END_TAB_ITEM()
}
void ScreenEditor::DrawInventoryToolset() {
@@ -95,189 +126,82 @@ void ScreenEditor::DrawInventoryToolset() {
ImGui::TableSetupColumn("#bg3Tool");
ImGui::TableSetupColumn("#itemTool");
BUTTON_COLUMN(ICON_MD_UNDO)
BUTTON_COLUMN(ICON_MD_REDO)
TEXT_COLUMN(ICON_MD_MORE_VERT)
BUTTON_COLUMN(ICON_MD_ZOOM_OUT)
BUTTON_COLUMN(ICON_MD_ZOOM_IN)
TEXT_COLUMN(ICON_MD_MORE_VERT)
BUTTON_COLUMN(ICON_MD_DRAW)
BUTTON_COLUMN(ICON_MD_BUILD)
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_UNDO)) {
// status_ = inventory_.Undo();
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_REDO)) {
// status_ = inventory_.Redo();
}
ImGui::TableNextColumn();
ImGui::Text(ICON_MD_MORE_VERT);
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_ZOOM_OUT)) {
screen_canvas_.ZoomOut();
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_ZOOM_IN)) {
screen_canvas_.ZoomIn();
}
ImGui::TableNextColumn();
ImGui::Text(ICON_MD_MORE_VERT);
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_DRAW)) {
current_mode_ = EditingMode::DRAW;
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_BUILD)) {
// current_mode_ = EditingMode::BUILD;
}
ImGui::EndTable();
}
}
absl::Status ScreenEditor::LoadDungeonMaps() {
std::vector<std::array<uint8_t, 25>> current_floor_rooms_d;
std::vector<std::array<uint8_t, 25>> current_floor_gfx_d;
int total_floors_d;
uint8_t nbr_floor_d;
uint8_t nbr_basement_d;
void ScreenEditor::DrawDungeonMapScreen(int i) {
auto &current_dungeon = dungeon_maps_[selected_dungeon];
for (int d = 0; d < 14; d++) {
current_floor_rooms_d.clear();
current_floor_gfx_d.clear();
ASSIGN_OR_RETURN(
int ptr,
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
ASSIGN_OR_RETURN(
int ptr_gfx,
rom()->ReadWord(zelda3::screen::kDungeonMapGfxPtr + (d * 2)));
ptr |= 0x0A0000; // Add bank to the short ptr
ptr_gfx |= 0x0A0000; // Add bank to the short ptr
int pc_ptr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
int pc_ptr_gfx =
core::SnesToPc(ptr_gfx); // Contains data for the next 25 rooms
floor_number = i;
screen_canvas_.DrawBackground(ImVec2(325, 325));
screen_canvas_.DrawTileSelector(64.f);
ASSIGN_OR_RETURN(
ushort boss_room_d,
rom()->ReadWord(zelda3::screen::kDungeonMapBossRooms + (d * 2)));
auto boss_room = current_dungeon.boss_room;
for (int j = 0; j < zelda3::kNumRooms; j++) {
if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
int tile16_id = current_dungeon.floor_gfx[floor_number][j];
int posX = ((j % 5) * 32);
int posY = ((j / 5) * 32);
ASSIGN_OR_RETURN(
nbr_basement_d,
rom()->ReadByte(zelda3::screen::kDungeonMapFloors + (d * 2)));
nbr_basement_d &= 0x0F;
gfx::RenderTile16(tile16_blockset_, tile16_id);
screen_canvas_.DrawBitmap(tile16_blockset_.tile_bitmaps[tile16_id],
(posX * 2), (posY * 2), 4.0f);
ASSIGN_OR_RETURN(
nbr_floor_d,
rom()->ReadByte(zelda3::screen::kDungeonMapFloors + (d * 2)));
nbr_floor_d &= 0xF0;
nbr_floor_d = nbr_floor_d >> 4;
total_floors_d = nbr_basement_d + nbr_floor_d;
dungeon_map_labels_.emplace_back();
// for each floor in the dungeon
for (int i = 0; i < total_floors_d; i++) {
dungeon_map_labels_[d].emplace_back();
std::array<uint8_t, 25> rdata;
std::array<uint8_t, 25> gdata;
// for each room on the floor
for (int j = 0; j < 25; j++) {
gdata[j] = 0xFF;
rdata[j] = rom()->data()[pc_ptr + j + (i * 25)]; // Set the rooms
if (rdata[j] == 0x0F) {
gdata[j] = 0xFF;
} else {
gdata[j] = rom()->data()[pc_ptr_gfx++];
}
std::string label = core::HexByte(rdata[j]);
dungeon_map_labels_[d][i][j] = label;
if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64, 64,
kRedPen);
}
current_floor_gfx_d.push_back(gdata); // Add new floor gfx data
current_floor_rooms_d.push_back(rdata); // Add new floor data
}
dungeon_maps_.emplace_back(boss_room_d, nbr_floor_d, nbr_basement_d,
current_floor_rooms_d, current_floor_gfx_d);
}
return absl::OkStatus();
}
absl::Status ScreenEditor::SaveDungeonMaps() {
for (int d = 0; d < 14; d++) {
int ptr = zelda3::screen::kDungeonMapRoomsPtr + (d * 2);
int ptr_gfx = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
int pc_ptr = core::SnesToPc(ptr);
int pc_ptr_gfx = core::SnesToPc(ptr_gfx);
const int nbr_floors = dungeon_maps_[d].nbr_of_floor;
const int nbr_basements = dungeon_maps_[d].nbr_of_basement;
for (int i = 0; i < nbr_floors + nbr_basements; i++) {
for (int j = 0; j < 25; j++) {
RETURN_IF_ERROR(rom()->WriteByte(pc_ptr + j + (i * 25),
dungeon_maps_[d].floor_rooms[i][j]));
RETURN_IF_ERROR(rom()->WriteByte(pc_ptr_gfx + j + (i * 25),
dungeon_maps_[d].floor_gfx[i][j]));
pc_ptr_gfx++;
}
std::string label =
dungeon_map_labels_[selected_dungeon][floor_number][j];
screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
std::string gfx_id = util::HexByte(tile16_id);
screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
}
}
return absl::OkStatus();
}
screen_canvas_.DrawGrid(64.f, 5);
screen_canvas_.DrawOverlay();
absl::Status ScreenEditor::LoadDungeonMapTile16(
const std::vector<uint8_t>& gfx_data, bool bin_mode) {
tile16_sheet_.Init(256, 192, gfx::TileType::Tile16);
for (int i = 0; i < 186; i++) {
int addr = zelda3::screen::kDungeonMapTile16;
if (rom()->data()[zelda3::screen::kDungeonMapExpCheck] != 0xB9) {
addr = zelda3::screen::kDungeonMapTile16Expanded;
}
ASSIGN_OR_RETURN(auto tl, rom()->ReadWord(addr + (i * 8)));
gfx::TileInfo t1 = gfx::WordToTileInfo(tl); // Top left
ASSIGN_OR_RETURN(auto tr, rom()->ReadWord(addr + 2 + (i * 8)));
gfx::TileInfo t2 = gfx::WordToTileInfo(tr); // Top right
ASSIGN_OR_RETURN(auto bl, rom()->ReadWord(addr + 4 + (i * 8)));
gfx::TileInfo t3 = gfx::WordToTileInfo(bl); // Bottom left
ASSIGN_OR_RETURN(auto br, rom()->ReadWord(addr + 6 + (i * 8)));
gfx::TileInfo t4 = gfx::WordToTileInfo(br); // Bottom right
int sheet_offset = 212;
if (bin_mode) {
sheet_offset = 0;
}
tile16_sheet_.ComposeTile16(gfx_data, t1, t2, t3, t4, sheet_offset);
if (!screen_canvas_.points().empty()) {
int x = screen_canvas_.points().front().x / 64;
int y = screen_canvas_.points().front().y / 64;
selected_room = x + (y * 5);
}
RETURN_IF_ERROR(tile16_sheet_.mutable_bitmap()->ApplyPalette(
*rom()->mutable_dungeon_palette(3)));
Renderer::GetInstance().RenderBitmap(&*tile16_sheet_.mutable_bitmap().get());
for (int i = 0; i < tile16_sheet_.num_tiles(); ++i) {
auto tile = tile16_sheet_.GetTile16(i);
tile16_individual_[i] = tile;
RETURN_IF_ERROR(
tile16_individual_[i].ApplyPalette(*rom()->mutable_dungeon_palette(3)));
Renderer::GetInstance().RenderBitmap(&tile16_individual_[i]);
}
return absl::OkStatus();
}
absl::Status ScreenEditor::SaveDungeonMapTile16() {
for (int i = 0; i < 186; i++) {
int addr = zelda3::screen::kDungeonMapTile16;
if (rom()->data()[zelda3::screen::kDungeonMapExpCheck] != 0xB9) {
addr = zelda3::screen::kDungeonMapTile16Expanded;
}
gfx::TileInfo t1 = tile16_sheet_.tile_info()[i].tiles[0];
gfx::TileInfo t2 = tile16_sheet_.tile_info()[i].tiles[1];
gfx::TileInfo t3 = tile16_sheet_.tile_info()[i].tiles[2];
gfx::TileInfo t4 = tile16_sheet_.tile_info()[i].tiles[3];
auto tl = gfx::TileInfoToWord(t1);
RETURN_IF_ERROR(rom()->WriteWord(addr + (i * 8), tl));
auto tr = gfx::TileInfoToWord(t2);
RETURN_IF_ERROR(rom()->WriteWord(addr + 2 + (i * 8), tr));
auto bl = gfx::TileInfoToWord(t3);
RETURN_IF_ERROR(rom()->WriteWord(addr + 4 + (i * 8), bl));
auto br = gfx::TileInfoToWord(t4);
RETURN_IF_ERROR(rom()->WriteWord(addr + 6 + (i * 8), br));
}
return absl::OkStatus();
}
void ScreenEditor::DrawDungeonMapsTabs() {
auto& current_dungeon = dungeon_maps_[selected_dungeon];
auto &current_dungeon = dungeon_maps_[selected_dungeon];
if (ImGui::BeginTabBar("##DungeonMapTabs")) {
auto nbr_floors =
current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
@@ -288,49 +212,8 @@ void ScreenEditor::DrawDungeonMapsTabs() {
tab_name = absl::StrFormat("Floor %d",
i - current_dungeon.nbr_of_basement + 1);
}
if (ImGui::BeginTabItem(tab_name.c_str())) {
floor_number = i;
screen_canvas_.DrawBackground(ImVec2(325, 325));
screen_canvas_.DrawTileSelector(64.f);
auto boss_room = current_dungeon.boss_room;
for (int j = 0; j < 25; j++) {
if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
int tile16_id = current_dungeon.floor_gfx[floor_number][j];
int posX = ((j % 5) * 32);
int posY = ((j / 5) * 32);
if (tile16_individual_.count(tile16_id) == 0) {
tile16_individual_[tile16_id] =
tile16_sheet_.GetTile16(tile16_id);
Renderer::GetInstance().RenderBitmap(
&tile16_individual_[tile16_id]);
}
screen_canvas_.DrawBitmap(tile16_individual_[tile16_id], (posX * 2),
(posY * 2), 4.0f);
if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64,
64, kRedPen);
}
std::string label =
dungeon_map_labels_[selected_dungeon][floor_number][j];
screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
std::string gfx_id = core::HexByte(tile16_id);
screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
}
}
screen_canvas_.DrawGrid(64.f, 5);
screen_canvas_.DrawOverlay();
if (!screen_canvas_.points().empty()) {
int x = screen_canvas_.points().front().x / 64;
int y = screen_canvas_.points().front().y / 64;
selected_room = x + (y * 5);
}
if (ImGui::BeginTabItem(tab_name.data())) {
DrawDungeonMapScreen(i);
ImGui::EndTabItem();
}
}
@@ -343,9 +226,8 @@ void ScreenEditor::DrawDungeonMapsTabs() {
gui::InputHexWord("Boss Room", &current_dungeon.boss_room);
const ImVec2 button_size = ImVec2(130, 0);
const auto button_size = ImVec2(130, 0);
// Add Floor Button
if (ImGui::Button("Add Floor", button_size) &&
current_dungeon.nbr_of_floor < 8) {
current_dungeon.nbr_of_floor++;
@@ -358,7 +240,6 @@ void ScreenEditor::DrawDungeonMapsTabs() {
dungeon_map_labels_[selected_dungeon].pop_back();
}
// Add Basement Button
if (ImGui::Button("Add Basement", button_size) &&
current_dungeon.nbr_of_basement < 8) {
current_dungeon.nbr_of_basement++;
@@ -380,56 +261,74 @@ void ScreenEditor::DrawDungeonMapsTabs() {
}
}
void ScreenEditor::DrawDungeonMapsEditor() {
if (!dungeon_maps_loaded_) {
if (!LoadDungeonMaps().ok()) {
ImGui::Text("Failed to load dungeon maps");
void ScreenEditor::DrawDungeonMapsRoomGfx() {
if (ImGui::BeginChild("##DungeonMapTiles", ImVec2(0, 0), true)) {
tilesheet_canvas_.DrawBackground(ImVec2((256 * 2) + 2, (192 * 2) + 4));
tilesheet_canvas_.DrawContextMenu();
if (tilesheet_canvas_.DrawTileSelector(32.f)) {
selected_tile16_ = tilesheet_canvas_.points().front().x / 32 +
(tilesheet_canvas_.points().front().y / 32) * 16;
gfx::RenderTile16(tile16_blockset_, selected_tile16_);
std::ranges::copy(tile16_blockset_.tile_info[selected_tile16_],
current_tile16_info.begin());
}
tilesheet_canvas_.DrawBitmap(tile16_blockset_.atlas, 1, 1, 2.0f);
tilesheet_canvas_.DrawGrid(32.f);
tilesheet_canvas_.DrawOverlay();
if (!tilesheet_canvas_.points().empty() &&
!screen_canvas_.points().empty()) {
dungeon_maps_[selected_dungeon].floor_gfx[floor_number][selected_room] =
selected_tile16_;
tilesheet_canvas_.mutable_points()->clear();
}
if (LoadDungeonMapTile16(rom()->graphics_buffer()).ok()) {
// TODO: Load roomset gfx based on dungeon ID
sheets_.emplace(0, rom()->gfx_sheets()[212]);
sheets_.emplace(1, rom()->gfx_sheets()[213]);
sheets_.emplace(2, rom()->gfx_sheets()[214]);
sheets_.emplace(3, rom()->gfx_sheets()[215]);
int current_tile8 = 0;
int tile_data_offset = 0;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 32; j++) {
std::vector<uint8_t> tile_data(64, 0); // 8x8 tile (64 bytes
int tile_index = current_tile8 + j;
int x = (j % 8) * 8;
int y = (j / 8) * 8;
sheets_[i].Get8x8Tile(tile_index, 0, 0, tile_data, tile_data_offset);
tile8_individual_.emplace_back(gfx::Bitmap(8, 8, 4, tile_data));
RETURN_VOID_IF_ERROR(tile8_individual_.back().ApplyPalette(
*rom()->mutable_dungeon_palette(3)));
Renderer::GetInstance().RenderBitmap(&tile8_individual_.back());
}
tile_data_offset = 0;
}
dungeon_maps_loaded_ = true;
} else {
ImGui::Text("Failed to load dungeon map tile16");
ImGui::Separator();
current_tile_canvas_.DrawBackground(); // ImVec2(64 * 2 + 2, 64 * 2 + 4));
current_tile_canvas_.DrawContextMenu();
if (current_tile_canvas_.DrawTilePainter(tile8_individual_[selected_tile8_],
16)) {
// Modify the tile16 based on the selected tile and current_tile16_info
gfx::ModifyTile16(tile16_blockset_, rom()->graphics_buffer(),
current_tile16_info[0], current_tile16_info[1],
current_tile16_info[2], current_tile16_info[3], 212,
selected_tile16_);
gfx::UpdateTile16(tile16_blockset_, selected_tile16_);
}
current_tile_canvas_.DrawBitmap(
tile16_blockset_.tile_bitmaps[selected_tile16_], 2, 4.0f);
current_tile_canvas_.DrawGrid(16.f);
current_tile_canvas_.DrawOverlay();
gui::InputTileInfo("TL", &current_tile16_info[0]);
ImGui::SameLine();
gui::InputTileInfo("TR", &current_tile16_info[1]);
gui::InputTileInfo("BL", &current_tile16_info[2]);
ImGui::SameLine();
gui::InputTileInfo("BR", &current_tile16_info[3]);
if (ImGui::Button("Modify Tile16")) {
gfx::ModifyTile16(tile16_blockset_, rom()->graphics_buffer(),
current_tile16_info[0], current_tile16_info[1],
current_tile16_info[2], current_tile16_info[3], 212,
selected_tile16_);
gfx::UpdateTile16(tile16_blockset_, selected_tile16_);
}
}
ImGui::EndChild();
}
if (ImGui::BeginTable("##DungeonMapToolset", 2,
ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupColumn("Draw Mode");
ImGui::TableSetupColumn("Edit Mode");
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_DRAW)) {
current_mode_ = EditingMode::DRAW;
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_EDIT)) {
current_mode_ = EditingMode::EDIT;
}
ImGui::EndTable();
void ScreenEditor::DrawDungeonMapsEditor() {
if (ImGui::Button(ICON_MD_DRAW)) {
current_mode_ = EditingMode::DRAW;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_EDIT)) {
current_mode_ = EditingMode::EDIT;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SAVE)) {
PRINT_IF_ERROR(zelda3::SaveDungeonMapTile16(tile16_blockset_, *rom()));
}
static std::vector<std::string> dungeon_names = {
@@ -449,7 +348,6 @@ void ScreenEditor::DrawDungeonMapsEditor() {
ImGui::TableSetupColumn("Tiles Gfx");
ImGui::TableHeadersRow();
// Dungeon column
ImGui::TableNextColumn();
for (int i = 0; i < dungeon_names.size(); i++) {
rom()->resource_label()->SelectableLabelWithNameEdit(
@@ -460,66 +358,11 @@ void ScreenEditor::DrawDungeonMapsEditor() {
}
}
// Map column
ImGui::TableNextColumn();
DrawDungeonMapsTabs();
ImGui::TableNextColumn();
if (ImGui::BeginChild("##DungeonMapTiles", ImVec2(0, 0), true)) {
tilesheet_canvas_.DrawBackground(ImVec2((256 * 2) + 2, (192 * 2) + 4));
tilesheet_canvas_.DrawContextMenu();
tilesheet_canvas_.DrawTileSelector(32.f);
tilesheet_canvas_.DrawBitmap(*tile16_sheet_.bitmap(), 2, true);
tilesheet_canvas_.DrawGrid(32.f);
tilesheet_canvas_.DrawOverlay();
if (!tilesheet_canvas_.points().empty()) {
selected_tile16_ = tilesheet_canvas_.points().front().x / 32 +
(tilesheet_canvas_.points().front().y / 32) * 16;
current_tile16_info = tile16_sheet_.tile_info().at(selected_tile16_);
// Draw the selected tile
if (!screen_canvas_.points().empty()) {
dungeon_maps_[selected_dungeon]
.floor_gfx[floor_number][selected_room] = selected_tile16_;
tilesheet_canvas_.mutable_points()->clear();
}
}
ImGui::Separator();
current_tile_canvas_
.DrawBackground(); // ImVec2(64 * 2 + 2, 64 * 2 + 4));
current_tile_canvas_.DrawContextMenu();
if (current_tile_canvas_.DrawTilePainter(
tile8_individual_[selected_tile8_], 16)) {
// Modify the tile16 based on the selected tile and current_tile16_info
}
current_tile_canvas_.DrawBitmap(tile16_individual_[selected_tile16_], 2,
4.0f);
current_tile_canvas_.DrawGrid(16.f);
current_tile_canvas_.DrawOverlay();
gui::InputTileInfo("TL", &current_tile16_info.tiles[0]);
ImGui::SameLine();
gui::InputTileInfo("TR", &current_tile16_info.tiles[1]);
gui::InputTileInfo("BL", &current_tile16_info.tiles[2]);
ImGui::SameLine();
gui::InputTileInfo("BR", &current_tile16_info.tiles[3]);
if (ImGui::Button("Modify Tile16")) {
tile16_sheet_.ModifyTile16(
rom()->graphics_buffer(), current_tile16_info.tiles[0],
current_tile16_info.tiles[1], current_tile16_info.tiles[2],
current_tile16_info.tiles[3], selected_tile16_, 212);
tile16_individual_[selected_tile16_] =
tile16_sheet_.GetTile16(selected_tile16_);
RETURN_VOID_IF_ERROR(tile16_individual_[selected_tile16_].ApplyPalette(
*rom()->mutable_dungeon_palette(3)));
Renderer::GetInstance().RenderBitmap(
&tile16_individual_[selected_tile16_]);
}
}
ImGui::EndChild();
DrawDungeonMapsRoomGfx();
ImGui::TableNextColumn();
tilemap_canvas_.DrawBackground();
@@ -533,7 +376,6 @@ void ScreenEditor::DrawDungeonMapsEditor() {
tilemap_canvas_.DrawOverlay();
ImGui::Text("Selected tile8: %d", selected_tile8_);
ImGui::Separator();
ImGui::Text("For use with custom inserted graphics assembly patches.");
if (ImGui::Button("Load GFX from BIN file")) LoadBinaryGfx();
@@ -550,20 +392,18 @@ void ScreenEditor::LoadBinaryGfx() {
// Read the gfx data into a buffer
std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
gfx_bin_data_ = converted_bin;
tile16_sheet_.clear();
if (LoadDungeonMapTile16(converted_bin, true).ok()) {
if (auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
zelda3::LoadDungeonMapTile16(tile16_blockset_, *rom(), converted_bin,
true)
.ok()) {
sheets_.clear();
std::vector<std::vector<uint8_t>> gfx_sheets;
for (int i = 0; i < 4; i++) {
gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
converted_bin.begin() + ((i + 1) * 0x1000));
sheets_.emplace(i, gfx::Bitmap(128, 32, 8, gfx_sheets[i]));
status_ = sheets_[i].ApplyPalette(*rom()->mutable_dungeon_palette(3));
if (status_.ok()) {
Renderer::GetInstance().RenderBitmap(&sheets_[i]);
}
sheets_[i].SetPalette(*rom()->mutable_dungeon_palette(3));
Renderer::Get().RenderBitmap(&sheets_[i]);
}
binary_gfx_loaded_ = true;
} else {

View File

@@ -7,7 +7,7 @@
#include "app/editor/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/tilesheet.h"
#include "app/gfx/tilemap.h"
#include "app/gui/canvas.h"
#include "app/rom.h"
#include "app/zelda3/screen/dungeon_map.h"
@@ -28,26 +28,28 @@ namespace editor {
*
* The screens that can be edited include the title screen, naming screen,
* overworld map, inventory menu, and more.
*
* The class inherits from the SharedRom class.
*/
class ScreenEditor : public SharedRom, public Editor {
class ScreenEditor : public Editor {
public:
ScreenEditor() {
explicit ScreenEditor(Rom* rom = nullptr) : rom_(rom) {
screen_canvas_.SetCanvasSize(ImVec2(512, 512));
type_ = EditorType::kScreen;
}
void Initialize() override;
absl::Status Load() override;
absl::Status Update() override;
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
absl::Status Find() override { return absl::UnimplementedError("Find"); }
absl::Status Save() override { return absl::UnimplementedError("Save"); }
void set_rom(Rom* rom) { rom_ = rom; }
Rom* rom() const { return rom_; }
absl::Status SaveDungeonMaps();
std::vector<zelda3::DungeonMap> dungeon_maps_;
private:
void DrawTitleScreenEditor();
@@ -58,12 +60,14 @@ class ScreenEditor : public SharedRom, public Editor {
void DrawToolset();
void DrawInventoryToolset();
absl::Status LoadDungeonMaps();
absl::Status LoadDungeonMapTile16(const std::vector<uint8_t> &gfx_data,
absl::Status LoadDungeonMapTile16(const std::vector<uint8_t>& gfx_data,
bool bin_mode = false);
absl::Status SaveDungeonMapTile16();
void DrawDungeonMapScreen(int i);
void DrawDungeonMapsTabs();
void DrawDungeonMapsEditor();
void DrawDungeonMapsRoomGfx();
void LoadBinaryGfx();
@@ -71,11 +75,9 @@ class ScreenEditor : public SharedRom, public Editor {
EditingMode current_mode_ = EditingMode::DRAW;
bool dungeon_maps_loaded_ = false;
bool binary_gfx_loaded_ = false;
uint8_t selected_room = 0;
uint8_t boss_room = 0;
int selected_tile16_ = 0;
int selected_tile8_ = 0;
@@ -85,20 +87,13 @@ class ScreenEditor : public SharedRom, public Editor {
bool copy_button_pressed = false;
bool paste_button_pressed = false;
std::array<uint16_t, 4> current_tile16_data_;
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
std::vector<gfx::Bitmap> tile8_individual_;
std::vector<uint8_t> all_gfx_;
std::vector<uint8_t> gfx_bin_data_;
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
absl::Status status_;
zelda3::DungeonMapLabels dungeon_map_labels_;
gfx::SnesPalette palette_;
gfx::BitmapTable sheets_;
gfx::Tilesheet tile16_sheet_;
gfx::InternalTile16 current_tile16_info;
gfx::Tilemap tile16_blockset_;
std::array<gfx::TileInfo, 4> current_tile16_info;
gui::Canvas current_tile_canvas_{"##CurrentTileCanvas", ImVec2(32, 32),
gui::CanvasGridSize::k16x16, 2.0f};
@@ -107,7 +102,9 @@ class ScreenEditor : public SharedRom, public Editor {
gui::Canvas tilemap_canvas_{"##TilemapCanvas", ImVec2(128 + 2, (192) + 4),
gui::CanvasGridSize::k8x8, 2.f};
zelda3::screen::Inventory inventory_;
zelda3::Inventory inventory_;
Rom* rom_;
absl::Status status_;
};
} // namespace editor

View File

@@ -1,396 +0,0 @@
#include "tile16_editor.h"
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/platform/renderer.h"
#include "app/editor/editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/tilesheet.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
using core::Renderer;
using ImGui::BeginChild;
using ImGui::BeginMenu;
using ImGui::BeginMenuBar;
using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
using ImGui::BeginTable;
using ImGui::Button;
using ImGui::Checkbox;
using ImGui::Combo;
using ImGui::EndChild;
using ImGui::EndMenu;
using ImGui::EndMenuBar;
using ImGui::EndTabBar;
using ImGui::EndTabItem;
using ImGui::EndTable;
using ImGui::GetContentRegionAvail;
using ImGui::Separator;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
using ImGui::Text;
absl::Status Tile16Editor::InitBlockset(
const gfx::Bitmap& tile16_blockset_bmp, const gfx::Bitmap& current_gfx_bmp,
const std::vector<gfx::Bitmap>& tile16_individual,
std::array<uint8_t, 0x200>& all_tiles_types) {
all_tiles_types_ = all_tiles_types;
tile16_blockset_bmp_ = tile16_blockset_bmp;
tile16_individual_ = tile16_individual;
current_gfx_bmp_ = current_gfx_bmp;
RETURN_IF_ERROR(LoadTile8());
ImVector<std::string> tile16_names;
for (int i = 0; i < 0x200; ++i) {
std::string str = core::HexByte(all_tiles_types_[i]);
tile16_names.push_back(str);
}
*tile8_source_canvas_.mutable_labels(0) = tile16_names;
*tile8_source_canvas_.custom_labels_enabled() = true;
return absl::OkStatus();
}
absl::Status Tile16Editor::Update() {
if (!map_blockset_loaded_) {
return absl::InvalidArgumentError("Blockset not initialized, open a ROM.");
}
RETURN_IF_ERROR(DrawMenu());
if (BeginTabBar("Tile16 Editor Tabs")) {
DrawTile16Editor();
RETURN_IF_ERROR(UpdateTile16Transfer());
EndTabBar();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::DrawMenu() {
if (BeginMenuBar()) {
if (BeginMenu("View")) {
Checkbox("Show Collision Types",
tile8_source_canvas_.custom_labels_enabled());
EndMenu();
}
EndMenuBar();
}
return absl::OkStatus();
}
void Tile16Editor::DrawTile16Editor() {
if (BeginTabItem("Tile16 Editing")) {
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
TableSetupColumn("Blockset", ImGuiTableColumnFlags_WidthFixed,
GetContentRegionAvail().x);
TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch,
GetContentRegionAvail().x);
TableHeadersRow();
TableNextRow();
TableNextColumn();
status_ = UpdateBlockset();
if (!status_.ok()) {
EndTable();
}
TableNextColumn();
status_ = UpdateTile16Edit();
if (status_ != absl::OkStatus()) {
EndTable();
}
status_ = DrawTileEditControls();
EndTable();
}
EndTabItem();
}
}
absl::Status Tile16Editor::UpdateBlockset() {
gui::BeginPadding(2);
gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
blockset_canvas_.DrawBackground();
gui::EndPadding();
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
EndChild();
if (!blockset_canvas_.points().empty()) {
notify_tile16.mutable_get() = blockset_canvas_.GetTileIdFromMousePos();
notify_tile16.apply_changes();
if (notify_tile16.modified()) {
current_tile16_ = notify_tile16.get();
current_tile16_bmp_ = tile16_individual_[notify_tile16];
auto ow_main_pal_group = rom()->palette_group().overworld_main;
RETURN_IF_ERROR(current_tile16_bmp_.ApplyPalette(
ow_main_pal_group[current_palette_]));
Renderer::GetInstance().RenderBitmap(&current_tile16_bmp_);
}
}
return absl::OkStatus();
}
absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
constexpr int tile8_size = 8;
constexpr int tile16_size = 16;
// Calculate the tile index for x and y based on the click_position
// Adjusting for Tile16 (16x16) which contains 4 Tile8 (8x8)
int tile_index_x = static_cast<int>(click_position.x) / tile8_size;
int tile_index_y = static_cast<int>(click_position.y) / tile8_size;
std::cout << "Tile Index X: " << tile_index_x << std::endl;
std::cout << "Tile Index Y: " << tile_index_y << std::endl;
// Calculate the pixel start position within the Tile16
ImVec2 start_position;
start_position.x = tile_index_x * 0x40;
start_position.y = tile_index_y * 0x40;
std::cout << "Start Position X: " << start_position.x << std::endl;
std::cout << "Start Position Y: " << start_position.y << std::endl;
// Draw the Tile8 to the correct position within the Tile16
for (int y = 0; y < tile8_size; ++y) {
for (int x = 0; x < tile8_size; ++x) {
int pixel_index =
(start_position.y + y) * tile16_size + ((start_position.x) + x);
int gfx_pixel_index = y * tile8_size + x;
current_tile16_bmp_.WriteToPixel(
pixel_index,
current_gfx_individual_[current_tile8_].data()[gfx_pixel_index]);
}
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateTile16Edit() {
auto ow_main_pal_group = rom()->palette_group().overworld_main;
if (BeginChild("Tile8 Selector", ImVec2(GetContentRegionAvail().x, 0x175),
true)) {
tile8_source_canvas_.DrawBackground();
tile8_source_canvas_.DrawContextMenu(&current_gfx_bmp_);
if (tile8_source_canvas_.DrawTileSelector(32)) {
RETURN_IF_ERROR(
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
Renderer::GetInstance().UpdateBitmap(
&current_gfx_individual_[current_tile8_]);
}
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 4.0f);
tile8_source_canvas_.DrawGrid();
tile8_source_canvas_.DrawOverlay();
}
EndChild();
// The user selected a tile8
if (!tile8_source_canvas_.points().empty()) {
uint16_t x = tile8_source_canvas_.points().front().x / 16;
uint16_t y = tile8_source_canvas_.points().front().y / 16;
current_tile8_ = x + (y * 8);
RETURN_IF_ERROR(
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
Renderer::GetInstance().UpdateBitmap(
&current_gfx_individual_[current_tile8_]);
}
if (BeginChild("Tile16 Editor Options",
ImVec2(GetContentRegionAvail().x, 0x50), true)) {
tile16_edit_canvas_.DrawBackground();
tile16_edit_canvas_.DrawContextMenu(&current_tile16_bmp_);
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
if (!tile8_source_canvas_.points().empty()) {
if (tile16_edit_canvas_.DrawTilePainter(
current_gfx_individual_[current_tile8_], 16, 2.0f)) {
RETURN_IF_ERROR(
DrawToCurrentTile16(tile16_edit_canvas_.drawn_tile_position()));
Renderer::GetInstance().UpdateBitmap(&current_tile16_bmp_);
}
}
tile16_edit_canvas_.DrawGrid();
tile16_edit_canvas_.DrawOverlay();
}
EndChild();
return absl::OkStatus();
}
absl::Status Tile16Editor::DrawTileEditControls() {
Separator();
Text("Tile16 ID: %d", current_tile16_);
Text("Tile8 ID: %d", current_tile8_);
Text("Options:");
gui::InputHexByte("Palette", &notify_palette.mutable_get());
notify_palette.apply_changes();
if (notify_palette.modified()) {
auto palette = palettesets_[current_palette_].main_;
auto value = notify_palette.get();
if (notify_palette.get() > 0x04 && notify_palette.get() < 0x06) {
palette = palettesets_[current_palette_].aux1;
value -= 0x04;
} else if (notify_palette.get() > 0x06) {
palette = palettesets_[current_palette_].aux2;
value -= 0x06;
}
if (value > 0x00) {
RETURN_IF_ERROR(
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value));
Renderer::GetInstance().UpdateBitmap(&current_gfx_bmp_);
RETURN_IF_ERROR(
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value));
Renderer::GetInstance().UpdateBitmap(&current_tile16_bmp_);
}
}
Checkbox("X Flip", &x_flip);
Checkbox("Y Flip", &y_flip);
Checkbox("Priority Tile", &priority_tile);
return absl::OkStatus();
}
absl::Status Tile16Editor::LoadTile8() {
auto ow_main_pal_group = rom()->palette_group().overworld_main;
current_gfx_individual_.reserve(1024);
for (int index = 0; index < 1024; index++) {
std::vector<uint8_t> tile_data(0x40, 0x00);
// Copy the pixel data for the current tile into the vector
for (int ty = 0; ty < 8; ty++) {
for (int tx = 0; tx < 8; tx++) {
// Current Gfx Data is 16 sheets of 8x8 tiles ordered 16 wide by 4 tall
// Calculate the position in the tile data vector
int position = tx + (ty * 0x08);
// Calculate the position in the current gfx data
int num_columns = current_gfx_bmp_.width() / 8;
int x = (index % num_columns) * 8 + tx;
int y = (index / num_columns) * 8 + ty;
int gfx_position = x + (y * 0x100);
// Get the pixel value from the current gfx data
uint8_t value = current_gfx_bmp_.data()[gfx_position];
if (value & 0x80) {
value -= 0x88;
}
tile_data[position] = value;
}
}
current_gfx_individual_.emplace_back();
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
RETURN_IF_ERROR(current_gfx_individual_[index].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
Renderer::GetInstance().RenderBitmap(&current_gfx_individual_[index]);
}
map_blockset_loaded_ = true;
return absl::OkStatus();
}
absl::Status Tile16Editor::SetCurrentTile(int id) {
current_tile16_ = id;
current_tile16_bmp_ = tile16_individual_[id];
auto ow_main_pal_group = rom()->palette_group().overworld_main;
RETURN_IF_ERROR(
current_tile16_bmp_.ApplyPalette(ow_main_pal_group[current_palette_]));
Renderer::GetInstance().RenderBitmap(&current_tile16_bmp_);
return absl::OkStatus();
}
#pragma mark - Tile16Transfer
absl::Status Tile16Editor::UpdateTile16Transfer() {
if (BeginTabItem("Tile16 Transfer")) {
if (BeginTable("#Tile16TransferTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
GetContentRegionAvail().x / 2);
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
GetContentRegionAvail().x / 2);
TableHeadersRow();
TableNextRow();
TableNextColumn();
RETURN_IF_ERROR(UpdateBlockset());
TableNextColumn();
RETURN_IF_ERROR(UpdateTransferTileCanvas());
EndTable();
}
EndTabItem();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateTransferTileCanvas() {
// Create a button for loading another ROM
if (Button("Load ROM")) {
auto file_name = core::FileDialogWrapper::ShowOpenFileDialog();
transfer_status_ = transfer_rom_.LoadFromFile(file_name);
transfer_started_ = true;
}
// TODO: Implement tile16 transfer
if (transfer_started_ && !transfer_blockset_loaded_) {
PRINT_IF_ERROR(transfer_rom_.LoadAllGraphicsData())
// Load the Link to the Past overworld.
PRINT_IF_ERROR(transfer_overworld_.Load(transfer_rom_))
transfer_overworld_.set_current_map(0);
palette_ = transfer_overworld_.current_area_palette();
// Create the tile16 blockset image
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
0x80, 0x2000, 0x80, transfer_overworld_.tile16_blockset_data(),
transfer_blockset_bmp_, palette_));
transfer_blockset_loaded_ = true;
}
// Create a canvas for holding the tiles which will be exported
gui::BitmapCanvasPipeline(transfer_canvas_, transfer_blockset_bmp_, 0x100,
(8192 * 2), 0x20, transfer_blockset_loaded_, true,
3);
return absl::OkStatus();
}
} // namespace editor
} // namespace yaze

View File

@@ -1,103 +0,0 @@
#ifndef YAZE_APP_EDITOR_TILE16EDITOR_H
#define YAZE_APP_EDITOR_TILE16EDITOR_H
#include "absl/status/status.h"
#include "app/core/common.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
/**
* @brief Popup window to edit Tile16 data
*/
class Tile16Editor : public gfx::GfxContext, public SharedRom {
public:
absl::Status InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
const gfx::Bitmap& current_gfx_bmp,
const std::vector<gfx::Bitmap>& tile16_individual,
std::array<uint8_t, 0x200>& all_tiles_types);
absl::Status Update();
absl::Status DrawMenu();
void DrawTile16Editor();
absl::Status UpdateTile16Transfer();
absl::Status UpdateBlockset();
absl::Status DrawToCurrentTile16(ImVec2 pos);
absl::Status UpdateTile16Edit();
absl::Status DrawTileEditControls();
absl::Status UpdateTransferTileCanvas();
absl::Status LoadTile8();
absl::Status SetCurrentTile(int id);
private:
bool map_blockset_loaded_ = false;
bool transfer_started_ = false;
bool transfer_blockset_loaded_ = false;
int current_tile16_ = 0;
int current_tile8_ = 0;
uint8_t current_palette_ = 0;
core::NotifyValue<uint32_t> notify_tile16;
core::NotifyValue<uint8_t> notify_palette;
// Various options for the Tile16 Editor
bool x_flip;
bool y_flip;
bool priority_tile;
int tile_size;
std::array<uint8_t, 0x200> all_tiles_types_;
// Tile16 blockset for selecting the tile to edit
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100, 0x4000),
gui::CanvasGridSize::k32x32};
gfx::Bitmap tile16_blockset_bmp_;
// Canvas for editing the selected tile
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", ImVec2(0x40, 0x40),
gui::CanvasGridSize::k64x64};
gfx::Bitmap current_tile16_bmp_;
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
gui::Canvas tile8_source_canvas_{
"Tile8SourceCanvas",
ImVec2(gfx::kTilesheetWidth * 4, gfx::kTilesheetHeight * 0x10 * 4),
gui::CanvasGridSize::k32x32};
gfx::Bitmap current_gfx_bmp_;
gui::Canvas transfer_canvas_;
gfx::Bitmap transfer_blockset_bmp_;
std::vector<gfx::Bitmap> tile16_individual_;
std::vector<gfx::Bitmap> current_gfx_individual_;
PaletteEditor palette_editor_;
gfx::SnesPalette palette_;
zelda3::Overworld transfer_overworld_;
absl::Status status_;
Rom transfer_rom_;
absl::Status transfer_status_;
};
} // namespace editor
} // namespace yaze
#endif // YAZE_APP_EDITOR_TILE16EDITOR_H