#include "app/editor/graphics_editor.h" #include #include #include #include #include "absl/status/status.h" #include "absl/status/statusor.h" #include "app/core/pipeline.h" #include "app/editor/palette_editor.h" #include "app/gfx/bitmap.h" #include "app/gfx/compression.h" #include "app/gfx/snes_tile.h" #include "app/gui/canvas.h" #include "app/gui/input.h" #include "app/gui/style.h" #include "app/rom.h" namespace yaze { namespace app { namespace editor { absl::Status GraphicsEditor::Update() { RETURN_IF_ERROR(DrawToolset()) if (open_memory_editor_) { ImGui::Begin("Memory Editor", &open_memory_editor_); RETURN_IF_ERROR(DrawMemoryEditor()) ImGui::End(); } BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags) SETUP_COLUMN("Graphics (BIN, CGX, SCR)") SETUP_COLUMN("Palette (COL)") SETUP_COLUMN("Maps and Animations (SCR, PNL)") SETUP_COLUMN("Preview") TABLE_HEADERS() NEXT_COLUMN() status_ = DrawCgxImport(); status_ = DrawClipboardImport(); status_ = DrawFileImport(); status_ = DrawExperimentalFeatures(); NEXT_COLUMN() status_ = DrawPaletteControls(); NEXT_COLUMN() core::BitmapCanvasPipeline(0x200, 0x200, 0x20, 6, scr_loaded_, cgx_bitmap_); NEXT_COLUMN() if (super_donkey_) { status_ = DrawGraphicsBin(); } else if (cgx_loaded_ && col_file_) { core::BitmapCanvasPipeline(0x100, 16384, 0x20, 5, cgx_loaded_, cgx_bitmap_); } else { core::BitmapCanvasPipeline(0x100, 16384, 0x20, 2, gfx_loaded_, bitmap_); } END_TABLE() CLEAR_AND_RETURN_STATUS(status_) return absl::OkStatus(); } absl::Status GraphicsEditor::DrawToolset() { if (ImGui::BeginTable("GraphicsToolset", 2, ImGuiTableFlags_SizingFixedFit, ImVec2(0, 0))) { for (const auto& name : kGfxToolsetColumnNames) ImGui::TableSetupColumn(name.data()); ImGui::TableNextColumn(); if (ImGui::Button(ICON_MD_MEMORY)) { if (!open_memory_editor_) { open_memory_editor_ = true; } else { open_memory_editor_ = false; } } TEXT_COLUMN("Open Memory Editor") // Separator ImGui::EndTable(); } return absl::OkStatus(); } absl::Status GraphicsEditor::DrawCgxImport() { gui::TextWithSeparators("Cgx Import"); ImGui::InputText("##CGXFile", cgx_file_name_, sizeof(cgx_file_name_)); ImGui::SameLine(); core::FileDialogPipeline( "ImportCgxKey", ".CGX,.cgx\0", "Open CGX", [&]() -> auto { strncpy(cgx_file_path_, ImGuiFileDialog::Instance()->GetFilePathName().c_str(), sizeof(cgx_file_path_)); strncpy(cgx_file_name_, ImGuiFileDialog::Instance()->GetCurrentFileName().c_str(), sizeof(cgx_file_name_)); status_ = temp_rom_.LoadFromFile(cgx_file_path_, /*z3_load=*/false); is_open_ = true; cgx_loaded_ = true; }); core::ButtonPipe("Copy File Path", [&]() -> auto { ImGui::SetClipboardText(cgx_file_path_); }); core::ButtonPipe("Decompress CGX Data", [&]() -> auto { cgx_viewer_.LoadCgx(temp_rom_); auto all_tiles_data = cgx_viewer_.GetCgxData(); cgx_bitmap_.Create(core::kTilesheetWidth, 8192, core::kTilesheetDepth, all_tiles_data.data(), all_tiles_data.size()); if (col_file_) { cgx_bitmap_.ApplyPalette(col_file_palette_); rom_.RenderBitmap(&cgx_bitmap_); } }); CLEAR_AND_RETURN_STATUS(status_) return absl::OkStatus(); } absl::Status GraphicsEditor::DrawFileImport() { static int size = 0; gui::TextWithSeparators("BIN Import"); ImGui::InputText("##ROMFile", file_path_, sizeof(file_path_)); ImGui::SameLine(); core::FileDialogPipeline( "ImportDlgKey", ".bin,.hex\0", "Open BIN", [&]() -> auto { strncpy(file_path_, ImGuiFileDialog::Instance()->GetFilePathName().c_str(), sizeof(file_path_)); status_ = temp_rom_.LoadFromFile(file_path_); is_open_ = true; }); core::ButtonPipe("Copy File Path", [&]() -> auto { ImGui::SetClipboardText(file_path_); }); gui::InputHex("BIN Offset", ¤t_offset_); gui::InputHex("BIN Size", &size); if (ImGui::Button("Decompress BIN")) { if (strlen(file_path_) > 0) { RETURN_IF_ERROR(DecompressImportData(size)) } else { return absl::InvalidArgumentError( "Please select a file before importing."); } } return absl::OkStatus(); } absl::Status GraphicsEditor::DrawPaletteControls() { gui::TextWithSeparators("COL Import"); ImGui::InputText("##ColFile", col_file_name_, sizeof(col_file_name_)); ImGui::SameLine(); core::FileDialogPipeline( "ImportColKey", ".COL,.col,.BAK,.bak\0", "Open COL", [&]() -> auto { strncpy(col_file_path_, ImGuiFileDialog::Instance()->GetFilePathName().c_str(), sizeof(col_file_path_)); strncpy(col_file_name_, ImGuiFileDialog::Instance()->GetCurrentFileName().c_str(), sizeof(col_file_name_)); status_ = temp_rom_.LoadFromFile(col_file_path_, /*z3_load=*/false); auto col_data_ = gfx::GetColFileData(temp_rom_.data()); col_file_palette_ = gfx::SNESPalette(col_data_); col_file_ = true; is_open_ = true; }); core::ButtonPipe("Copy File Path", [&]() -> auto { ImGui::SetClipboardText(col_file_path_); }); if (rom_.isLoaded()) { gui::TextWithSeparators("ROM Palette"); gui::InputHex("Palette Index", ¤t_palette_index_); ImGui::Combo("Palette", ¤t_palette_, kPaletteGroupAddressesKeys, IM_ARRAYSIZE(kPaletteGroupAddressesKeys)); } if (col_file_palette_.size() != 0) { palette_editor_.DrawPortablePalette(col_file_palette_); } return absl::OkStatus(); } absl::Status GraphicsEditor::DrawClipboardImport() { gui::TextWithSeparators("Clipboard Import"); core::ButtonPipe("Paste from Clipboard", [&]() -> auto { const char* text = ImGui::GetClipboardText(); if (text) { const auto clipboard_data = Bytes(text, text + strlen(text)); ImGui::MemFree((void*)text); status_ = temp_rom_.LoadFromBytes(clipboard_data); is_open_ = true; open_memory_editor_ = true; } }); gui::InputHex("Offset", &clipboard_offset_); gui::InputHex("Size", &clipboard_size_); gui::InputHex("Num Sheets", &num_sheets_to_load_); core::ButtonPipe("Decompress Clipboard Data", [&]() -> auto { if (temp_rom_.isLoaded()) { status_ = DecompressImportData(0x40000); } else { status_ = absl::InvalidArgumentError( "Please paste data into the clipboard before " "decompressing."); } }); int import_size = 0; int num_sheets = 0; return absl::OkStatus(); } absl::Status GraphicsEditor::DrawExperimentalFeatures() { gui::TextWithSeparators("Experimental"); if (ImGui::Button("Decompress Super Donkey Full")) { if (strlen(file_path_) > 0) { RETURN_IF_ERROR(DecompressSuperDonkey()) } else { return absl::InvalidArgumentError( "Please select `super_donkey_1.bin` before " "importing."); } } ImGui::SetItemTooltip( "Requires `super_donkey_1.bin` to be imported under the " "BIN import " "section."); return absl::OkStatus(); } absl::Status GraphicsEditor::DrawMemoryEditor() { std::string title = "Memory Editor"; if (is_open_) { static MemoryEditor mem_edit; mem_edit.DrawWindow(title.c_str(), temp_rom_.data(), temp_rom_.size()); } return absl::OkStatus(); } absl::Status GraphicsEditor::DrawGraphicsBin() { if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)3); ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { super_donkey_canvas_.DrawBackground( ImVec2(0x100 + 1, num_sheets_to_load_ * 0x40 + 1)); super_donkey_canvas_.DrawContextMenu(); if (super_donkey_) { for (const auto& [key, value] : graphics_bin_) { int offset = 0x40 * (key + 1); int top_left_y = super_donkey_canvas_.GetZeroPoint().y + 2; if (key >= 1) { top_left_y = super_donkey_canvas_.GetZeroPoint().y + 0x40 * key; } super_donkey_canvas_.GetDrawList()->AddImage( (void*)value.texture(), ImVec2(super_donkey_canvas_.GetZeroPoint().x + 2, top_left_y), ImVec2(super_donkey_canvas_.GetZeroPoint().x + 0x100, super_donkey_canvas_.GetZeroPoint().y + offset)); } } super_donkey_canvas_.DrawGrid(16.0f); super_donkey_canvas_.DrawOverlay(); } ImGui::EndChild(); return absl::OkStatus(); } absl::Status GraphicsEditor::DecompressImportData(int size) { ASSIGN_OR_RETURN(import_data_, gfx::lc_lz2::DecompressV2( temp_rom_.data(), current_offset_, size)) auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3); bitmap_.Create(core::kTilesheetWidth, 0x2000, core::kTilesheetDepth, converted_sheet.data(), size); if (rom_.isLoaded()) { auto palette_group = rom_.GetPaletteGroup("ow_main"); palette_ = palette_group.palettes[current_palette_]; if (col_file_) { bitmap_.ApplyPalette(col_file_palette_); } else { bitmap_.ApplyPalette(palette_); } } rom_.RenderBitmap(&bitmap_); gfx_loaded_ = true; return absl::OkStatus(); } absl::Status GraphicsEditor::DecompressSuperDonkey() { if (rom_.isLoaded()) { auto palette_group = rom_.GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]); palette_ = palette_group.palettes[current_palette_index_]; } int i = 0; for (const auto& offset : kSuperDonkeyTiles) { int offset_value = 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)) auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3); graphics_bin_[i] = gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight, core::kTilesheetDepth, converted_sheet.data(), 0x1000); if (col_file_) { graphics_bin_[i].ApplyPalette(col_file_palette_); } else { // ROM palette graphics_bin_[i].ApplyPalette(palette_); } rom_.RenderBitmap(&graphics_bin_[i]); i++; } for (const auto& offset : kSuperDonkeySprites) { int offset_value = 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)) auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3); graphics_bin_[i] = gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight, core::kTilesheetDepth, converted_sheet.data(), 0x1000); if (col_file_) { graphics_bin_[i].ApplyPalette(col_file_palette_); } else { // ROM palette graphics_bin_[i].ApplyPalette(palette_); } rom_.RenderBitmap(&graphics_bin_[i]); i++; } super_donkey_ = true; num_sheets_to_load_ = i; return absl::OkStatus(); } } // namespace editor } // namespace app } // namespace yaze