8#include "absl/status/status.h"
9#include "absl/status/statusor.h"
10#include "absl/strings/str_cat.h"
11#include "imgui/imgui.h"
12#include "imgui/misc/cpp/imgui_stdlib.h"
44using ImGui::InputText;
71 panel_manager->RegisterEditorPanel(
72 std::make_unique<GraphicsSheetBrowserPanel>([
this]() {
78 panel_manager->RegisterEditorPanel(
79 std::make_unique<GraphicsPixelEditorPanel>([
this]() {
85 panel_manager->RegisterEditorPanel(
86 std::make_unique<GraphicsPaletteControlsPanel>([
this]() {
92 panel_manager->RegisterEditorPanel(
93 std::make_unique<GraphicsLinkSpritePanel>([
this]() {
99 panel_manager->RegisterEditorPanel(
100 std::make_unique<GraphicsPolyhedralPanel>([
this]() {
106 panel_manager->RegisterEditorPanel(
107 std::make_unique<GraphicsGfxGroupPanel>([
this]() {
114 panel_manager->RegisterEditorPanel(
115 std::make_unique<GraphicsPalettesetPanel>([
this]() {
122 panel_manager->RegisterEditorPanel(
123 std::make_unique<GraphicsPrototypeViewerPanel>([
this]() {
133 if (
rom()->is_loaded()) {
141 LOG_INFO(
"GraphicsEditor",
"Initializing textures for %d graphics sheets",
144 int sheets_queued = 0;
146 if (!sheets[i].is_active() || !sheets[i].surface()) {
152 if (!sheets[i].texture()) {
155 if (sheets[i].palette().empty()) {
160 sheets[i].SetPaletteWithTransparent(
161 game_data()->palette_groups.dungeon_main.palette(0), 0);
163 }
else if (i >= 113 && i <= 127) {
166 sheets[i].SetPaletteWithTransparent(
167 game_data()->palette_groups.sprites_aux1.palette(0), 0);
172 sheets[i].SetPaletteWithTransparent(
173 game_data()->palette_groups.hud.palette(0), 0);
184 LOG_INFO(
"GraphicsEditor",
"Queued texture creation for %d graphics sheets",
193 return absl::OkStatus();
198 return absl::FailedPreconditionError(
"ROM not loaded");
203 LOG_INFO(
"GraphicsEditor",
"No modified sheets to save");
204 return absl::OkStatus();
207 LOG_INFO(
"GraphicsEditor",
"Saving %zu modified graphics sheets",
215 auto& sheet = sheets[sheet_id];
216 if (!sheet.is_active())
continue;
220 bool compressed =
true;
223 if (sheet_id == 113 || sheet_id == 114 || sheet_id >= 218) {
228 if (sheet_id >= 115 && sheet_id <= 126) {
235 std::vector<uint8_t> final_data;
238 int compressed_size = 0;
240 indexed_data.data(),
static_cast<int>(indexed_data.size()),
241 &compressed_size, 1);
242 final_data.assign(compressed_data.begin(),
243 compressed_data.begin() + compressed_size);
245 final_data = std::move(indexed_data);
250 auto version_constants = zelda3::kVersionConstantsMap.at(
game_data()->version);
252 rom_->
data(),
static_cast<uint8_t
>(sheet_id),
253 version_constants.kOverworldGfxPtr1,
254 version_constants.kOverworldGfxPtr2,
255 version_constants.kOverworldGfxPtr3,
rom_->
size());
258 for (
size_t i = 0; i < final_data.size(); i++) {
262 LOG_INFO(
"GraphicsEditor",
"Saved sheet %02X (%zu bytes, %s) at offset %06X",
263 sheet_id, final_data.size(), compressed ?
"compressed" :
"raw",
270 return absl::OkStatus();
281 return absl::OkStatus();
286 if (ImGui::GetIO().WantTextInput) {
291 if (ImGui::IsKeyPressed(ImGuiKey_V,
false)) {
294 if (ImGui::IsKeyPressed(ImGuiKey_B,
false)) {
297 if (ImGui::IsKeyPressed(ImGuiKey_E,
false)) {
300 if (ImGui::IsKeyPressed(ImGuiKey_G,
false) && !ImGui::GetIO().KeyCtrl) {
303 if (ImGui::IsKeyPressed(ImGuiKey_I,
false)) {
306 if (ImGui::IsKeyPressed(ImGuiKey_L,
false) && !ImGui::GetIO().KeyCtrl) {
309 if (ImGui::IsKeyPressed(ImGuiKey_R,
false) && !ImGui::GetIO().KeyCtrl) {
314 if (ImGui::IsKeyPressed(ImGuiKey_Equal,
false) ||
315 ImGui::IsKeyPressed(ImGuiKey_KeypadAdd,
false)) {
318 if (ImGui::IsKeyPressed(ImGuiKey_Minus,
false) ||
319 ImGui::IsKeyPressed(ImGuiKey_KeypadSubtract,
false)) {
324 if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_G,
false)) {
329 if (ImGui::IsKeyPressed(ImGuiKey_PageDown,
false)) {
332 if (ImGui::IsKeyPressed(ImGuiKey_PageUp,
false)) {
344 constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
345 ImGuiTableFlags_Resizable |
346 ImGuiTableFlags_SizingStretchSame;
351 ImGui::TableSetupColumn(
"Tilemaps and Objects (SCR, PNL, OBJ)",
352 ImGuiTableColumnFlags_WidthFixed);
376 ImGui::Image((ImTextureID)(intptr_t)
gfx_sheets_[i].texture(),
378 if ((i + 1) % 4 != 0) {
406 if (ImGui::Button(
"Open CGX")) {
414 if (ImGui::Button(
"Copy CGX Path")) {
418 if (ImGui::Button(
"Load CGX Data")) {
430 return absl::OkStatus();
436 if (ImGui::Button(
"Open SCR")) {
446 if (ImGui::Button(
"Load Scr Data")) {
461 return absl::OkStatus();
469 if (ImGui::Button(
"Open COL")) {
478 auto col_file_palette_group_status =
480 if (col_file_palette_group_status.ok()) {
492 if (ImGui::Button(
"Copy Col Path")) {
496 if (
rom()->is_loaded()) {
500 IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
508 return absl::OkStatus();
517 if (ImGui::Button(
"Open OBJ")) {
526 return absl::OkStatus();
535 if (ImGui::Button(
"Open Tilemap")) {
550 return absl::OkStatus();
559 if (ImGui::Button(
"Open BIN")) {
567 if (Button(
"Copy File Path")) {
574 if (Button(
"Decompress BIN")) {
576 return absl::InvalidArgumentError(
577 "Please select a file before decompressing.");
582 return absl::OkStatus();
587 if (Button(
"Paste From Clipboard")) {
588 const char* text = ImGui::GetClipboardText();
590 const auto clipboard_data =
591 std::vector<uint8_t>(text, text + strlen(text));
592 ImGui::MemFree((
void*)text);
602 if (Button(
"Decompress Clipboard Data")) {
606 status_ = absl::InvalidArgumentError(
607 "Please paste data into the clipboard before "
612 return absl::OkStatus();
617 if (Button(
"Decompress Super Donkey Full")) {
619 return absl::InvalidArgumentError(
620 "Please select `super_donkey_1.bin` before "
625 ImGui::SetItemTooltip(
626 "Requires `super_donkey_1.bin` to be imported under the "
627 "BIN import section.");
628 return absl::OkStatus();
632 std::string title =
"Memory Editor";
638 return absl::OkStatus();
664 return absl::OkStatus();
671 std::stoi(offset,
nullptr, 16);
673 auto decompressed_data,
685 return absl::FailedPreconditionError(
"GameData not available");
700 std::stoi(offset,
nullptr, 16);
702 auto decompressed_data,
728 return absl::OkStatus();
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
absl::Status WriteByte(int addr, uint8_t value)
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
zelda3::GameData * game_data() const
EditorDependencies dependencies_
std::set< uint16_t > modified_sheets
uint16_t current_sheet_id
bool HasUnsavedChanges() const
Check if any sheets have unsaved changes.
void SetTool(PixelTool tool)
Set the current editing tool.
void ClearModifiedSheets()
Clear modification tracking (after save)
std::vector< uint8_t > scr_data_
GraphicsEditorState state_
absl::Status Save() override
uint64_t clipboard_offset_
gfx::PaletteGroup col_file_palette_group_
std::string col_file_path_
std::string cgx_file_path_
absl::Status DrawMemoryEditor()
absl::Status Load() override
std::unique_ptr< GfxGroupEditor > gfx_group_panel_
zelda3::GameData * game_data_
std::unique_ptr< PaletteControlsPanel > palette_controls_panel_
void Initialize() override
std::vector< uint8_t > decoded_cgx_
std::string scr_file_path_
absl::Status DecompressSuperDonkey()
gfx::SnesPalette col_file_palette_
std::string col_file_name_
void HandleEditorShortcuts()
absl::Status DrawCgxImport()
std::vector< uint8_t > cgx_data_
std::unique_ptr< PixelEditorPanel > pixel_editor_panel_
std::unique_ptr< PolyhedralEditorPanel > polyhedral_panel_
std::unique_ptr< PalettesetEditorPanel > paletteset_panel_
std::string obj_file_path_
void DrawPrototypeViewer()
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_
absl::Status Update() override
absl::Status DrawScrImport()
std::string tilemap_file_path_
std::vector< uint8_t > extra_cgx_data_
std::array< gfx::Bitmap, zelda3::kNumGfxSheets > gfx_sheets_
gui::Canvas import_canvas_
std::vector< uint8_t > decoded_scr_data_
std::string cgx_file_name_
std::unique_ptr< LinkSpritePanel > link_sprite_panel_
absl::Status DrawObjImport()
absl::Status DrawClipboardImport()
uint64_t num_sheets_to_load_
absl::Status DecompressImportData(int size)
absl::Status DrawExperimentalFeatures()
absl::Status DrawPaletteControls()
std::unique_ptr< SheetBrowserPanel > sheet_browser_panel_
std::string scr_file_name_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
std::array< gfx::Bitmap, 223 > & gfx_sheets()
Get reference to all graphics sheets.
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 using SNES palette format.
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define LOG_INFO(category, format,...)
#define BEGIN_TABLE(l, n, f)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
#define CLEAR_AND_RETURN_STATUS(status)
#define HOVER_HINT(string)
const std::string kSuperDonkeySprites[]
const std::string kSuperDonkeyTiles[]
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode, size_t rom_size)
Decompresses a buffer of data using the LC_LZ2 algorithm.
constexpr int kNintendoMode1
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 > Bpp8SnesToIndexed(std::vector< uint8_t > data, uint64_t bpp)
std::vector< uint8_t > SnesTo8bppSheet(std::span< uint8_t > sheet, int bpp, int num_sheets)
std::vector< uint8_t > HyruleMagicCompress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
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)
void TextWithSeparators(const absl::string_view &text)
constexpr uint32_t kNumGfxSheets
uint32_t GetGraphicsAddress(const uint8_t *data, uint8_t addr, uint32_t ptr1, uint32_t ptr2, uint32_t ptr3, size_t rom_size)
Gets the graphics address for a sheet index.
#define RETURN_IF_ERROR(expr)
PanelManager * panel_manager
PaletteGroup overworld_animated
PaletteGroup * get_group(const std::string &group_name)
gfx::PaletteGroupMap palette_groups