6#include "absl/status/status.h"
7#include "absl/status/statusor.h"
8#include "absl/strings/str_cat.h"
26#include "imgui/imgui.h"
27#include "imgui/misc/cpp/imgui_stdlib.h"
28#include "imgui_memory_editor.h"
37using ImGui::InputText;
39using ImGui::TableNextColumn;
42 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
43 ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
44 ImGuiTableFlags_SizingFixedFit;
50 card_registry->
RegisterCard({.card_id =
"graphics.sheet_editor", .display_name =
"Sheet Editor",
52 .shortcut_hint =
"Ctrl+Shift+1", .priority = 10});
53 card_registry->RegisterCard({.card_id =
"graphics.sheet_browser", .display_name =
"Sheet Browser",
55 .shortcut_hint =
"Ctrl+Shift+2", .priority = 20});
56 card_registry->RegisterCard({.card_id =
"graphics.player_animations", .display_name =
"Player Animations",
58 .shortcut_hint =
"Ctrl+Shift+3", .priority = 30});
59 card_registry->RegisterCard({.card_id =
"graphics.prototype_viewer", .display_name =
"Prototype Viewer",
61 .shortcut_hint =
"Ctrl+Shift+4", .priority = 40});
64 card_registry->ShowCard(
"graphics.sheet_editor");
72 if (
rom()->is_loaded()) {
82 int sheets_queued = 0;
84 if (!sheets[i].is_active() || !sheets[i].surface()) {
90 if (!sheets[i].texture()) {
97 LOG_INFO(
"GraphicsEditor",
"Queued texture creation for %d graphics sheets", sheets_queued);
100 return absl::OkStatus();
118 bool* sheet_editor_visible = card_registry->GetVisibilityFlag(
"graphics.sheet_editor");
119 if (sheet_editor_visible && *sheet_editor_visible) {
120 if (sheet_editor_card.
Begin(sheet_editor_visible)) {
123 sheet_editor_card.
End();
127 bool* sheet_browser_visible = card_registry->GetVisibilityFlag(
"graphics.sheet_browser");
128 if (sheet_browser_visible && *sheet_browser_visible) {
129 if (sheet_browser_card.
Begin(sheet_browser_visible)) {
135 sheet_browser_card.
End();
139 bool* player_anims_visible = card_registry->GetVisibilityFlag(
"graphics.player_animations");
140 if (player_anims_visible && *player_anims_visible) {
141 if (player_anims_card.
Begin(player_anims_visible)) {
144 player_anims_card.
End();
148 bool* prototype_visible = card_registry->GetVisibilityFlag(
"graphics.prototype_viewer");
149 if (prototype_visible && *prototype_visible) {
150 if (prototype_card.
Begin(prototype_visible)) {
153 prototype_card.
End();
157 return absl::OkStatus();
163 for (
const auto& name :
164 {
"Tilesheets",
"Current Graphics",
"Palette Controls"})
165 ImGui::TableSetupColumn(name);
167 ImGui::TableHeadersRow();
168 ImGui::TableNextColumn();
171 ImGui::TableNextColumn();
172 if (
rom()->is_loaded()) {
177 ImGui::TableNextColumn();
178 if (
rom()->is_loaded()) {
184 return absl::OkStatus();
203 if (ImGui::BeginTable(
"##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
205 for (
const auto& name :
206 {
"Select",
"Pencil",
"Fill",
"Copy Sheet",
"Paste Sheet",
"Zoom Out",
207 "Zoom In",
"Current Color",
"Tile Size"})
208 ImGui::TableSetupColumn(name);
229 status_ = absl::UnimplementedError(
"PNG export functionality removed");
235 status_ = absl::UnimplementedError(
"PNG import functionality removed");
256 auto palette = bitmap.palette();
259 for (
int i = 0; i < palette.size(); i++) {
260 if (i > 0 && i % 8 == 0) {
266 auto color = ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
267 palette[i].rgb().z / 255.0f, 1.0f);
270 if (ImGui::ColorButton(absl::StrFormat(
"Palette Color %d", i).c_str(),
271 color, ImGuiColorEditFlags_NoTooltip)) {
276 if (ImGui::IsItemHovered()) {
277 ImGui::SetTooltip(
"SNES Color: $%04X\nRGB: (%d, %d, %d)",
279 static_cast<int>(palette[i].rgb().x),
280 static_cast<int>(palette[i].rgb().y),
281 static_cast<int>(palette[i].rgb().z));
294 "##GfxEditChild", ImVec2(0, 0),
true,
295 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar);
296 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
298 static ImGuiSelectionBasicStorage selection;
299 ImGuiMultiSelectFlags flags =
300 ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
301 ImGuiMultiSelectIO* ms_io =
302 ImGui::BeginMultiSelect(flags, selection.Size,
kNumGfxSheets);
303 selection.ApplyRequests(ms_io);
304 ImGuiListClipper clipper;
306 if (ms_io->RangeSrcItem != -1)
307 clipper.IncludeItemByIndex(
308 (
int)ms_io->RangeSrcItem);
312 ImGui::BeginChild(absl::StrFormat(
"##GfxSheet%02X", key).c_str(),
313 ImVec2(0x100 + 1, 0x40 + 1),
true,
314 ImGuiWindowFlags_NoDecoration);
315 ImGui::PopStyleVar();
319 if (value.is_active()) {
321 if (!value.texture() && value.surface()) {
326 auto texture = value.texture();
329 (ImTextureID)(intptr_t)texture,
337 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
346 ImGui::CalcTextSize(absl::StrFormat(
"%02X", key).c_str());
347 ImVec2 rect_min(text_pos.x, text_pos.y);
348 ImVec2 rect_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
351 IM_COL32(0, 125, 0, 128));
354 text_pos, IM_COL32(125, 255, 125, 255),
355 absl::StrFormat(
"%02X", key).c_str());
362 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
365 ImGui::PopStyleVar();
366 ms_io = ImGui::EndMultiSelect();
367 selection.ApplyRequests(ms_io);
369 return absl::OkStatus();
375 static int next_tab_id = 0;
376 constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
377 ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
378 ImGuiTabBarFlags_FittingPolicyResizeDown |
379 ImGuiTabBarFlags_TabListPopupButton;
381 if (ImGui::BeginTabBar(
"##GfxEditTabBar", kGfxEditTabBarFlags)) {
382 if (ImGui::TabItemButton(
ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
383 ImGuiTabItemFlags_NoTooltip)) {
389 if (ImGui::BeginTabItem(absl::StrFormat(
"%d", sheet_id).c_str(), &open,
390 ImGuiTabItemFlags_None)) {
392 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
395 if (ImGui::IsItemHovered()) {
396 if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
402 const auto child_id =
403 absl::StrFormat(
"##GfxEditPaletteChildWindow%d", sheet_id);
404 ImGui::BeginChild(child_id.c_str(), ImVec2(0, 0),
true,
405 ImGuiWindowFlags_NoDecoration |
406 ImGuiWindowFlags_AlwaysVerticalScrollbar |
407 ImGuiWindowFlags_AlwaysHorizontalScrollbar);
412 auto draw_tile_event = [&]() {
445 int id_to_release = -1;
448 ImGui::SetNextWindowPos(ImGui::GetIO().MousePos, ImGuiCond_Once);
449 ImGui::SetNextWindowSize(ImVec2(0x100 + 1 * 16, 0x40 + 1 * 16),
451 ImGui::Begin(absl::StrFormat(
"##GfxEditPaletteChildWindow%d",
id).c_str(),
452 &
active, ImGuiWindowFlags_AlwaysUseWindowPadding);
467 if (id_to_release != -1) {
472 return absl::OkStatus();
476 if (
rom()->is_loaded()) {
483 ImGui::Text(
"Quick Presets:");
484 if (ImGui::Button(
"Overworld")) {
490 if (ImGui::Button(
"Dungeon")) {
496 if (ImGui::Button(
"Sprites")) {
504 if (ImGui::Button(
"Apply to Current Sheet") && !
open_sheets_.empty()) {
508 if (ImGui::Button(
"Apply to All Sheets")) {
512 if (sheet.is_active() && sheet.surface()) {
521 ImGui::SetNextItemWidth(150.f);
525 ImGui::SetNextItemWidth(100.f);
527 ImGui::SetNextItemWidth(100.f);
535 if (current.is_active() && current.surface()) {
543 return absl::OkStatus();
549 for (
const auto& name : {
"Canvas",
"Animation Steps",
"Properties"})
550 ImGui::TableSetupColumn(name);
552 ImGui::TableHeadersRow();
554 ImGui::TableNextColumn();
569 ImGui::TableNextColumn();
570 ImGui::Text(
"Placeholder");
572 ImGui::TableNextColumn();
573 if (ImGui::Button(
"Load Link Graphics (Experimental)")) {
574 if (
rom()->is_loaded()) {
588 return absl::OkStatus();
598 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
599 ImGuiTableFlags_Resizable |
600 ImGuiTableFlags_SizingStretchSame;
605 ImGui::TableSetupColumn(
"Tilemaps and Objects (SCR, PNL, OBJ)",
606 ImGuiTableColumnFlags_WidthFixed);
648 return absl::OkStatus();
658 if (ImGui::Button(
"Open CGX")) {
666 if (ImGui::Button(
"Copy CGX Path")) {
670 if (ImGui::Button(
"Load CGX Data")) {
682 return absl::OkStatus();
688 if (ImGui::Button(
"Open SCR")) {
698 if (ImGui::Button(
"Load Scr Data")) {
713 return absl::OkStatus();
721 if (ImGui::Button(
"Open COL")) {
731 auto col_file_palette_group_status =
733 if (col_file_palette_group_status.ok()) {
745 if (ImGui::Button(
"Copy Col Path")) {
749 if (
rom()->is_loaded()) {
761 return absl::OkStatus();
770 if (ImGui::Button(
"Open OBJ")) {
779 return absl::OkStatus();
788 if (ImGui::Button(
"Open Tilemap")) {
802 return absl::OkStatus();
811 if (ImGui::Button(
"Open BIN")) {
819 if (Button(
"Copy File Path")) {
826 if (Button(
"Decompress BIN")) {
828 return absl::InvalidArgumentError(
829 "Please select a file before decompressing.");
834 return absl::OkStatus();
839 if (Button(
"Paste From Clipboard")) {
840 const char* text = ImGui::GetClipboardText();
842 const auto clipboard_data =
843 std::vector<uint8_t>(text, text + strlen(text));
844 ImGui::MemFree((
void*)text);
854 if (Button(
"Decompress Clipboard Data")) {
858 status_ = absl::InvalidArgumentError(
859 "Please paste data into the clipboard before "
864 return absl::OkStatus();
869 if (Button(
"Decompress Super Donkey Full")) {
871 return absl::InvalidArgumentError(
872 "Please select `super_donkey_1.bin` before "
877 ImGui::SetItemTooltip(
878 "Requires `super_donkey_1.bin` to be imported under the "
879 "BIN import section.");
880 return absl::OkStatus();
884 std::string title =
"Memory Editor";
886 static MemoryEditor mem_edit;
890 return absl::OkStatus();
901 if (
rom()->is_loaded()) {
915 return absl::OkStatus();
922 std::stoi(offset,
nullptr, 16);
924 auto decompressed_data,
948 std::stoi(offset,
nullptr, 16);
950 auto decompressed_data,
973 return absl::OkStatus();
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
auto palette_group() const
absl::Status LoadFromData(const std::vector< uint8_t > &data, bool z3_load=true)
void RegisterCard(size_t session_id, const CardInfo &base_info)
Register a card for a specific session.
EditorDependencies dependencies_
absl::Status UpdateGfxEdit()
std::vector< uint8_t > scr_data_
uint64_t edit_palette_group_name_index_
std::array< gfx::Bitmap, kNumLinkSheets > link_sheets_
uint64_t clipboard_offset_
absl::Status UpdatePaletteColumn()
gfx::PaletteGroup col_file_palette_group_
std::string col_file_path_
absl::Status UpdateLinkGfxView()
std::string cgx_file_path_
absl::Status DrawMemoryEditor()
absl::Status Load() override
void Initialize() override
absl::Status UpdateGfxSheetList()
std::vector< uint8_t > decoded_cgx_
std::string scr_file_path_
GfxEditMode gfx_edit_mode_
std::set< uint16_t > child_window_sheets_
absl::Status DecompressSuperDonkey()
std::array< gfx::Bitmap, kNumGfxSheets > gfx_sheets_
gfx::SnesPalette col_file_palette_
std::string col_file_name_
absl::Status DrawCgxImport()
std::vector< uint8_t > cgx_data_
void DrawGfxEditToolset()
Draw the graphics editing toolset with enhanced ROM hacking features.
std::string obj_file_path_
uint64_t edit_palette_index_
absl::Status DrawFileImport()
std::vector< uint8_t > import_data_
uint64_t current_palette_index_
std::vector< SDL_Color > decoded_col_
absl::Status DrawTilemapImport()
gfx::SnesPalette z3_rom_palette_
std::set< uint16_t > open_sheets_
absl::Status Update() override
absl::Status DrawScrImport()
uint64_t edit_palette_sub_index_
std::string tilemap_file_path_
gui::Canvas current_sheet_canvas_
absl::Status UpdateScadView()
std::vector< uint8_t > extra_cgx_data_
gui::Canvas import_canvas_
std::vector< uint8_t > decoded_scr_data_
std::stack< uint16_t > release_queue_
std::string cgx_file_name_
absl::Status DrawObjImport()
absl::Status DrawClipboardImport()
gui::GfxSheetAssetBrowser asset_browser_
absl::Status UpdateGfxTabView()
uint64_t num_sheets_to_load_
absl::Status DecompressImportData(int size)
absl::Status DrawExperimentalFeatures()
gui::Canvas graphics_bin_canvas_
absl::Status DrawPaletteControls()
std::string scr_file_name_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
auto mutable_gfx_sheets()
Get mutable reference to all graphics sheets.
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
Represents a bitmap image optimized for SNES ROM hacking.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
void UpdateColorPainter(gfx::IRenderer *renderer, gfx::Bitmap &bitmap, const ImVec4 &color, const std::function< void()> &event, int tile_size, float scale=1.0f)
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_VIEW_LIST
#define ICON_MD_CONSTRUCTION
#define ICON_MD_FORMAT_COLOR_FILL
#define ICON_MD_CONTENT_PASTE
#define ICON_MD_SELECT_ALL
#define ICON_MD_CONTENT_COPY
#define LOG_INFO(category, format,...)
#define BEGIN_TABLE(l, n, f)
#define RETURN_IF_ERROR(expression)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
#define CLEAR_AND_RETURN_STATUS(status)
#define HOVER_HINT(string)
const std::string kSuperDonkeySprites[]
constexpr ImGuiTableFlags kGfxEditTableFlags
const std::string kSuperDonkeyTiles[]
constexpr int kNintendoMode1
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode)
Decompresses a buffer of data using the LC_LZ2 algorithm.
absl::Status LoadScr(std::string_view filename, uint8_t input_value, std::vector< uint8_t > &map_data)
Load Scr file (screen data)
std::vector< SDL_Color > DecodeColFile(const std::string_view filename)
Decode color file.
constexpr int kTilesheetHeight
constexpr int kTilesheetWidth
absl::Status LoadCgx(uint8_t bpp, std::string_view filename, std::vector< uint8_t > &cgx_data, std::vector< uint8_t > &cgx_loaded, std::vector< uint8_t > &cgx_header)
Load Cgx file (graphical content)
constexpr const char * kPaletteGroupAddressesKeys[]
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector< uint8_t > &map_bitmap_data, std::vector< uint8_t > &map_data, std::vector< uint8_t > &cgx_loaded)
Draw screen tilemap with graphical data.
constexpr int kTilesheetDepth
std::vector< uint8_t > SnesTo8bppSheet(std::span< uint8_t > sheet, int bpp, int num_sheets)
absl::StatusOr< PaletteGroup > CreatePaletteGroupFromColFile(std::vector< SnesColor > &palette_rows)
std::vector< SnesColor > GetColFileData(uint8_t *data)
void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id)
bool InputHex(const char *label, uint64_t *data)
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics, gfx::SnesPalette &palette)
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
void TextWithSeparators(const absl::string_view &text)
Main namespace for the application.
absl::StatusOr< std::array< gfx::Bitmap, kNumLinkSheets > > LoadLinkGraphics(const Rom &rom)
Loads the players 4bpp graphics sheet from Rom data.
constexpr uint32_t kNumGfxSheets
EditorCardRegistry * card_registry
void Draw(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)
void Initialize(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)