5#include "absl/status/status.h"
6#include "absl/status/statusor.h"
7#include "absl/strings/str_cat.h"
25#include "imgui/imgui.h"
26#include "imgui/misc/cpp/imgui_stdlib.h"
27#include "imgui_memory_editor.h"
36using ImGui::InputText;
38using ImGui::TableNextColumn;
41 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
42 ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
43 ImGuiTableFlags_SizingFixedFit;
49 card_manager.RegisterCard({
50 .card_id =
"graphics.sheet_editor",
51 .display_name =
"Sheet Editor",
53 .category =
"Graphics",
54 .shortcut_hint =
"Ctrl+Shift+1",
59 card_manager.RegisterCard({
60 .card_id =
"graphics.sheet_browser",
61 .display_name =
"Sheet Browser",
63 .category =
"Graphics",
64 .shortcut_hint =
"Ctrl+Shift+2",
69 card_manager.RegisterCard({
70 .card_id =
"graphics.player_animations",
71 .display_name =
"Player Animations",
73 .category =
"Graphics",
74 .shortcut_hint =
"Ctrl+Shift+3",
79 card_manager.RegisterCard({
80 .card_id =
"graphics.prototype_viewer",
81 .display_name =
"Prototype Viewer",
83 .category =
"Graphics",
84 .shortcut_hint =
"Ctrl+Shift+4",
95 if (
rom()->is_loaded()) {
105 int sheets_queued = 0;
107 if (!sheets[i].is_active() || !sheets[i].surface()) {
113 if (!sheets[i].texture()) {
120 LOG_INFO(
"GraphicsEditor",
"Queued texture creation for %d graphics sheets", sheets_queued);
123 return absl::OkStatus();
140 sheet_editor_card.
End();
150 sheet_browser_card.
End();
157 player_anims_card.
End();
164 prototype_card.
End();
168 return absl::OkStatus();
195 for (
const auto& name :
196 {
"Tilesheets",
"Current Graphics",
"Palette Controls"})
197 ImGui::TableSetupColumn(name);
199 ImGui::TableHeadersRow();
200 ImGui::TableNextColumn();
203 ImGui::TableNextColumn();
204 if (
rom()->is_loaded()) {
209 ImGui::TableNextColumn();
210 if (
rom()->is_loaded()) {
216 return absl::OkStatus();
235 if (ImGui::BeginTable(
"##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
237 for (
const auto& name :
238 {
"Select",
"Pencil",
"Fill",
"Copy Sheet",
"Paste Sheet",
"Zoom Out",
239 "Zoom In",
"Current Color",
"Tile Size"})
240 ImGui::TableSetupColumn(name);
261 status_ = absl::UnimplementedError(
"PNG export functionality removed");
267 status_ = absl::UnimplementedError(
"PNG import functionality removed");
288 auto palette = bitmap.palette();
291 for (
int i = 0; i < palette.size(); i++) {
292 if (i > 0 && i % 8 == 0) {
298 auto color = ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
299 palette[i].rgb().z / 255.0f, 1.0f);
302 if (ImGui::ColorButton(absl::StrFormat(
"Palette Color %d", i).c_str(),
303 color, ImGuiColorEditFlags_NoTooltip)) {
308 if (ImGui::IsItemHovered()) {
309 ImGui::SetTooltip(
"SNES Color: $%04X\nRGB: (%d, %d, %d)",
311 static_cast<int>(palette[i].rgb().x),
312 static_cast<int>(palette[i].rgb().y),
313 static_cast<int>(palette[i].rgb().z));
326 "##GfxEditChild", ImVec2(0, 0),
true,
327 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar);
328 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
330 static ImGuiSelectionBasicStorage selection;
331 ImGuiMultiSelectFlags flags =
332 ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
333 ImGuiMultiSelectIO* ms_io =
334 ImGui::BeginMultiSelect(flags, selection.Size,
kNumGfxSheets);
335 selection.ApplyRequests(ms_io);
336 ImGuiListClipper clipper;
338 if (ms_io->RangeSrcItem != -1)
339 clipper.IncludeItemByIndex(
340 (
int)ms_io->RangeSrcItem);
344 ImGui::BeginChild(absl::StrFormat(
"##GfxSheet%02X", key).c_str(),
345 ImVec2(0x100 + 1, 0x40 + 1),
true,
346 ImGuiWindowFlags_NoDecoration);
347 ImGui::PopStyleVar();
351 if (value.is_active()) {
353 if (!value.texture() && value.surface()) {
358 auto texture = value.texture();
361 (ImTextureID)(intptr_t)texture,
369 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
378 ImGui::CalcTextSize(absl::StrFormat(
"%02X", key).c_str());
379 ImVec2 rect_min(text_pos.x, text_pos.y);
380 ImVec2 rect_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
383 IM_COL32(0, 125, 0, 128));
386 text_pos, IM_COL32(125, 255, 125, 255),
387 absl::StrFormat(
"%02X", key).c_str());
394 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
397 ImGui::PopStyleVar();
398 ms_io = ImGui::EndMultiSelect();
399 selection.ApplyRequests(ms_io);
401 return absl::OkStatus();
407 static int next_tab_id = 0;
408 constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
409 ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
410 ImGuiTabBarFlags_FittingPolicyResizeDown |
411 ImGuiTabBarFlags_TabListPopupButton;
413 if (ImGui::BeginTabBar(
"##GfxEditTabBar", kGfxEditTabBarFlags)) {
414 if (ImGui::TabItemButton(
ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
415 ImGuiTabItemFlags_NoTooltip)) {
421 if (ImGui::BeginTabItem(absl::StrFormat(
"%d", sheet_id).c_str(), &open,
422 ImGuiTabItemFlags_None)) {
424 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
427 if (ImGui::IsItemHovered()) {
428 if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
434 const auto child_id =
435 absl::StrFormat(
"##GfxEditPaletteChildWindow%d", sheet_id);
436 ImGui::BeginChild(child_id.c_str(), ImVec2(0, 0),
true,
437 ImGuiWindowFlags_NoDecoration |
438 ImGuiWindowFlags_AlwaysVerticalScrollbar |
439 ImGuiWindowFlags_AlwaysHorizontalScrollbar);
444 auto draw_tile_event = [&]() {
477 int id_to_release = -1;
480 ImGui::SetNextWindowPos(ImGui::GetIO().MousePos, ImGuiCond_Once);
481 ImGui::SetNextWindowSize(ImVec2(0x100 + 1 * 16, 0x40 + 1 * 16),
483 ImGui::Begin(absl::StrFormat(
"##GfxEditPaletteChildWindow%d",
id).c_str(),
484 &
active, ImGuiWindowFlags_AlwaysUseWindowPadding);
499 if (id_to_release != -1) {
504 return absl::OkStatus();
508 if (
rom()->is_loaded()) {
515 ImGui::Text(
"Quick Presets:");
516 if (ImGui::Button(
"Overworld")) {
522 if (ImGui::Button(
"Dungeon")) {
528 if (ImGui::Button(
"Sprites")) {
536 if (ImGui::Button(
"Apply to Current Sheet") && !
open_sheets_.empty()) {
540 if (ImGui::Button(
"Apply to All Sheets")) {
544 if (sheet.is_active() && sheet.surface()) {
553 ImGui::SetNextItemWidth(150.f);
557 ImGui::SetNextItemWidth(100.f);
559 ImGui::SetNextItemWidth(100.f);
567 if (current.is_active() && current.surface()) {
575 return absl::OkStatus();
581 for (
const auto& name : {
"Canvas",
"Animation Steps",
"Properties"})
582 ImGui::TableSetupColumn(name);
584 ImGui::TableHeadersRow();
586 ImGui::TableNextColumn();
601 ImGui::TableNextColumn();
602 ImGui::Text(
"Placeholder");
604 ImGui::TableNextColumn();
605 if (ImGui::Button(
"Load Link Graphics (Experimental)")) {
606 if (
rom()->is_loaded()) {
620 return absl::OkStatus();
632 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
633 ImGuiTableFlags_Resizable |
634 ImGuiTableFlags_SizingStretchSame;
639 ImGui::TableSetupColumn(
"Tilemaps and Objects (SCR, PNL, OBJ)",
640 ImGuiTableColumnFlags_WidthFixed);
682 return absl::OkStatus();
692 if (ImGui::Button(
"Open CGX")) {
700 if (ImGui::Button(
"Copy CGX Path")) {
704 if (ImGui::Button(
"Load CGX Data")) {
716 return absl::OkStatus();
722 if (ImGui::Button(
"Open SCR")) {
732 if (ImGui::Button(
"Load Scr Data")) {
747 return absl::OkStatus();
755 if (ImGui::Button(
"Open COL")) {
765 auto col_file_palette_group_status =
767 if (col_file_palette_group_status.ok()) {
779 if (ImGui::Button(
"Copy Col Path")) {
783 if (
rom()->is_loaded()) {
795 return absl::OkStatus();
804 if (ImGui::Button(
"Open OBJ")) {
813 return absl::OkStatus();
822 if (ImGui::Button(
"Open Tilemap")) {
836 return absl::OkStatus();
845 if (ImGui::Button(
"Open BIN")) {
853 if (Button(
"Copy File Path")) {
860 if (Button(
"Decompress BIN")) {
862 return absl::InvalidArgumentError(
863 "Please select a file before decompressing.");
868 return absl::OkStatus();
873 if (Button(
"Paste From Clipboard")) {
874 const char* text = ImGui::GetClipboardText();
876 const auto clipboard_data =
877 std::vector<uint8_t>(text, text + strlen(text));
878 ImGui::MemFree((
void*)text);
888 if (Button(
"Decompress Clipboard Data")) {
892 status_ = absl::InvalidArgumentError(
893 "Please paste data into the clipboard before "
898 return absl::OkStatus();
903 if (Button(
"Decompress Super Donkey Full")) {
905 return absl::InvalidArgumentError(
906 "Please select `super_donkey_1.bin` before "
911 ImGui::SetItemTooltip(
912 "Requires `super_donkey_1.bin` to be imported under the "
913 "BIN import section.");
914 return absl::OkStatus();
918 std::string title =
"Memory Editor";
920 static MemoryEditor mem_edit;
924 return absl::OkStatus();
935 if (
rom()->is_loaded()) {
949 return absl::OkStatus();
956 std::stoi(offset,
nullptr, 16);
958 auto decompressed_data,
982 std::stoi(offset,
nullptr, 16);
984 auto decompressed_data,
1007 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)
std::string MakeCardTitle(const std::string &base_title) const
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_
bool show_prototype_viewer_
void DrawGfxEditToolset()
Draw the graphics editing toolset with enhanced ROM hacking features.
bool show_player_animations_
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)
static EditorCardManager & Get()
Draggable, dockable card for editor sub-windows.
bool Begin(bool *p_open=nullptr)
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 VerticalSpacing(float pixels)
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
void Draw(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)
void Initialize(const std::array< gfx::Bitmap, kNumGfxSheets > &bmp_manager)