From c4ef29f3295d40e133d1e6b007249d6d9afadd13 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 26 Aug 2023 15:03:18 -0400 Subject: [PATCH] Add OverworldMap tile editing --- src/app/core/pipeline.cc | 14 ++--- src/app/core/pipeline.h | 6 +- src/app/editor/graphics_editor.cc | 95 ++++++++++++++++++++++-------- src/app/editor/graphics_editor.h | 25 +++++++- src/app/editor/overworld_editor.cc | 93 ++++++++++++++++++++++++----- src/app/editor/overworld_editor.h | 8 ++- src/app/editor/palette_editor.h | 49 +++++++++++++-- src/app/gfx/compression.cc | 5 ++ src/app/gfx/compression.h | 1 + src/app/gui/canvas.cc | 3 + src/app/gui/canvas.h | 2 + src/app/gui/color.cc | 3 + src/app/gui/color.h | 3 + src/app/gui/input.cc | 4 +- src/app/gui/input.h | 2 + src/app/gui/style.cc | 3 + src/app/gui/style.h | 2 + src/app/gui/widgets.cc | 2 + src/app/gui/widgets.h | 54 +++++++++++++++++ src/app/zelda3/overworld.cc | 93 ++++++++++++++++++++++++++++- src/app/zelda3/overworld.h | 17 ++++-- 21 files changed, 418 insertions(+), 66 deletions(-) diff --git a/src/app/core/pipeline.cc b/src/app/core/pipeline.cc index 27b455d3..8220e8a3 100644 --- a/src/app/core/pipeline.cc +++ b/src/app/core/pipeline.cc @@ -83,11 +83,11 @@ void ButtonPipe(absl::string_view button_text, std::function callback) { } } -void BitmapCanvasPipeline(int width, int height, int tile_size, bool is_loaded, - gfx::Bitmap& bitmap, bool scrollbar, int canvas_id) { - gui::Canvas canvas; - - auto draw_canvas = [&]() { +void BitmapCanvasPipeline(gui::Canvas& canvas, gfx::Bitmap& bitmap, int width, + int height, int tile_size, bool is_loaded, + bool scrollbar, int canvas_id) { + auto draw_canvas = [](gui::Canvas& canvas, gfx::Bitmap& bitmap, int width, + int height, int tile_size, bool is_loaded) { canvas.DrawBackground(ImVec2(width + 1, height + 1)); canvas.DrawContextMenu(); canvas.DrawBitmap(bitmap, 2, is_loaded); @@ -100,11 +100,11 @@ void BitmapCanvasPipeline(int width, int height, int tile_size, bool is_loaded, if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)canvas_id); ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { - draw_canvas(); + draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded); } ImGui::EndChild(); } else { - draw_canvas(); + draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded); } } diff --git a/src/app/core/pipeline.h b/src/app/core/pipeline.h index 70a5f497..ec2f887c 100644 --- a/src/app/core/pipeline.h +++ b/src/app/core/pipeline.h @@ -13,6 +13,7 @@ #include "app/core/constants.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" +#include "app/gui/canvas.h" #include "app/rom.h" namespace yaze { @@ -28,8 +29,9 @@ void GraphicsBinCanvasPipeline(int width, int height, int tile_size, void ButtonPipe(absl::string_view button_text, std::function callback); -void BitmapCanvasPipeline(int width, int height, int tile_size, bool is_loaded, - gfx::Bitmap& bitmap, bool scrollbar, int canvas_id); +void BitmapCanvasPipeline(gui::Canvas& canvas, gfx::Bitmap& bitmap, int width, + int height, int tile_size, bool is_loaded, + bool scrollbar, int canvas_id); void BuildAndRenderBitmapPipeline(int width, int height, int depth, Bytes data, ROM& z3_rom, gfx::Bitmap& bitmap, diff --git a/src/app/editor/graphics_editor.cc b/src/app/editor/graphics_editor.cc index ee327c6a..2f2f245f 100644 --- a/src/app/editor/graphics_editor.cc +++ b/src/app/editor/graphics_editor.cc @@ -19,6 +19,8 @@ #include "app/gui/style.h" #include "app/rom.h" +// + namespace yaze { namespace app { namespace editor { @@ -33,25 +35,24 @@ absl::Status GraphicsEditor::Update() { } BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags) - SETUP_COLUMN("Graphics (BIN, CGX, SCR)") + SETUP_COLUMN("File Import (BIN, CGX, ROM)") SETUP_COLUMN("Palette (COL)") - ImGui::TableSetupColumn("Maps and Animations (SCR, PNL)", + ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)", ImGuiTableColumnFlags_WidthFixed); - SETUP_COLUMN("Preview") + SETUP_COLUMN("Graphics Preview") TABLE_HEADERS() - NEXT_COLUMN() + NEXT_COLUMN() { + status_ = DrawCgxImport(); + status_ = DrawClipboardImport(); + status_ = DrawFileImport(); + status_ = DrawExperimentalFeatures(); + } - status_ = DrawCgxImport(); - status_ = DrawClipboardImport(); - status_ = DrawFileImport(); - status_ = DrawExperimentalFeatures(); + NEXT_COLUMN() { status_ = DrawPaletteControls(); } NEXT_COLUMN() - status_ = DrawPaletteControls(); - - NEXT_COLUMN() - core::BitmapCanvasPipeline(0x200, 0x200, 0x20, scr_loaded_, scr_bitmap_, - false, 0); + core::BitmapCanvasPipeline(scr_canvas_, scr_bitmap_, 0x200, 0x200, 0x20, + scr_loaded_, false, 0); status_ = DrawScrImport(); NEXT_COLUMN() @@ -69,12 +70,12 @@ absl::Status GraphicsEditor::Update() { super_donkey_, graphics_bin_); } else if (cgx_loaded_ && col_file_) { // Load the CGX graphics - core::BitmapCanvasPipeline(0x100, 16384, 0x20, cgx_loaded_, cgx_bitmap_, - true, 5); + core::BitmapCanvasPipeline(import_canvas_, cgx_bitmap_, 0x100, 16384, 0x20, + cgx_loaded_, true, 5); } else { // Load the BIN/Clipboard Graphics - core::BitmapCanvasPipeline(0x100, 16384, 0x20, gfx_loaded_, bitmap_, true, - 2); + core::BitmapCanvasPipeline(import_canvas_, bitmap_, 0x100, 16384, 0x20, + gfx_loaded_, true, 2); } END_TABLE() @@ -124,7 +125,7 @@ absl::Status GraphicsEditor::DrawCgxImport() { core::ButtonPipe("Copy CGX Path", [this]() { ImGui::SetClipboardText(cgx_file_path_); }); - core::ButtonPipe("Decompress CGX Data", [this]() { + core::ButtonPipe("Load CGX Data", [this]() { status_ = gfx::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_, decoded_cgx_, extra_cgx_data_); cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_); @@ -217,6 +218,49 @@ absl::Status GraphicsEditor::DrawPaletteControls() { return absl::OkStatus(); } +absl::Status GraphicsEditor::DrawObjImport() { + gui::TextWithSeparators("OBJ Import"); + + ImGui::InputText("##ObjFile", obj_file_path_, sizeof(obj_file_path_)); + ImGui::SameLine(); + + core::FileDialogPipeline( + "ImportObjKey", ".obj,.OBJ,.bak,.BAK\0", "Open OBJ", [this]() { + strncpy(file_path_, + ImGuiFileDialog::Instance()->GetFilePathName().c_str(), + sizeof(file_path_)); + status_ = temp_rom_.LoadFromFile(file_path_); + is_open_ = true; + }); + + return absl::OkStatus(); +} + +absl::Status GraphicsEditor::DrawTilemapImport() { + gui::TextWithSeparators("Tilemap Import"); + + ImGui::InputText("##TMapFile", tilemap_file_path_, + sizeof(tilemap_file_path_)); + ImGui::SameLine(); + + core::FileDialogPipeline( + "ImportTilemapKey", ".DAT,.dat,.BIN,.bin,.hex,.HEX\0", "Open Tilemap", + [this]() { + strncpy(tilemap_file_path_, + ImGuiFileDialog::Instance()->GetFilePathName().c_str(), + sizeof(tilemap_file_path_)); + status_ = tilemap_rom_.LoadFromFile(tilemap_file_path_); + + // Extract the high and low bytes from the file. + auto decomp_sheet = gfx::lc_lz2::DecompressV2( + tilemap_rom_.data(), gfx::lc_lz2::kNintendoMode1); + tilemap_loaded_ = true; + is_open_ = true; + }); + + return absl::OkStatus(); +} + absl::Status GraphicsEditor::DrawFileImport() { gui::TextWithSeparators("BIN Import"); @@ -290,8 +334,7 @@ absl::Status GraphicsEditor::DrawExperimentalFeatures() { } ImGui::SetItemTooltip( "Requires `super_donkey_1.bin` to be imported under the " - "BIN import " - "section."); + "BIN import section."); return absl::OkStatus(); } @@ -314,11 +357,11 @@ absl::Status GraphicsEditor::DecompressImportData(int size) { if (rom()->isLoaded()) { auto palette_group = rom()->GetPaletteGroup("ow_main"); - palette_ = palette_group[current_palette_]; + z3_rom_palette_ = palette_group[current_palette_]; if (col_file_) { bitmap_.ApplyPalette(col_file_palette_); } else { - bitmap_.ApplyPalette(palette_); + bitmap_.ApplyPalette(z3_rom_palette_); } } @@ -347,8 +390,8 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() { // ROM palette auto palette_group = rom()->GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]); - palette_ = palette_group[current_palette_index_]; - graphics_bin_[i].ApplyPalette(palette_); + z3_rom_palette_ = palette_group[current_palette_index_]; + graphics_bin_[i].ApplyPalette(z3_rom_palette_); } rom()->RenderBitmap(&graphics_bin_[i]); @@ -372,8 +415,8 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() { // ROM palette auto palette_group = rom()->GetPaletteGroup(kPaletteGroupAddressesKeys[current_palette_]); - palette_ = palette_group[current_palette_index_]; - graphics_bin_[i].ApplyPalette(palette_); + z3_rom_palette_ = palette_group[current_palette_index_]; + graphics_bin_[i].ApplyPalette(z3_rom_palette_); } rom()->RenderBitmap(&graphics_bin_[i]); diff --git a/src/app/editor/graphics_editor.h b/src/app/editor/graphics_editor.h index 262d04d2..edd792ef 100644 --- a/src/app/editor/graphics_editor.h +++ b/src/app/editor/graphics_editor.h @@ -15,6 +15,7 @@ #include "app/gui/canvas.h" #include "app/gui/input.h" #include "app/rom.h" +#include "app/zelda3/overworld.h" namespace yaze { namespace app { @@ -65,9 +66,13 @@ class GraphicsEditor : public SharedROM { private: absl::Status DrawToolset(); + absl::Status DrawCgxImport(); absl::Status DrawScrImport(); absl::Status DrawFileImport(); + absl::Status DrawObjImport(); + absl::Status DrawTilemapImport(); + absl::Status DrawPaletteControls(); absl::Status DrawClipboardImport(); absl::Status DrawExperimentalFeatures(); @@ -77,9 +82,9 @@ class GraphicsEditor : public SharedROM { absl::Status DecompressSuperDonkey(); + int current_palette_ = 0; uint64_t current_offset_ = 0; uint64_t current_size_ = 0; - int current_palette_ = 0; uint64_t current_palette_index_ = 0; int current_bpp_ = 0; @@ -95,12 +100,17 @@ class GraphicsEditor : public SharedROM { bool refresh_graphics_ = false; bool open_memory_editor_ = false; + bool gfx_loaded_ = false; bool is_open_ = false; bool super_donkey_ = false; + bool col_file_ = false; bool cgx_loaded_ = false; bool scr_loaded_ = false; + bool obj_loaded_ = false; + + bool tilemap_loaded_ = false; char file_path_[256] = ""; char col_file_path_[256] = ""; @@ -112,7 +122,14 @@ class GraphicsEditor : public SharedROM { char scr_file_path_[256] = ""; char scr_file_name_[256] = ""; + char obj_file_path_[256] = ""; + + char tilemap_file_path_[256] = ""; + char tilemap_file_name_[256] = ""; + ROM temp_rom_; + ROM tilemap_rom_; + Bytes import_data_; Bytes graphics_buffer_; @@ -124,6 +141,8 @@ class GraphicsEditor : public SharedROM { std::vector scr_data_; std::vector decoded_scr_data_; + zelda3::Overworld overworld_; + MemoryEditor cgx_memory_editor_; MemoryEditor col_memory_editor_; @@ -135,6 +154,8 @@ class GraphicsEditor : public SharedROM { gfx::Bitmap bitmap_; gui::Canvas import_canvas_; + gui::Canvas scr_canvas_; + gui::Canvas super_donkey_canvas_; gfx::BitmapTable graphics_bin_; @@ -142,7 +163,7 @@ class GraphicsEditor : public SharedROM { gfx::PaletteGroup col_file_palette_group_; - gfx::SNESPalette palette_; + gfx::SNESPalette z3_rom_palette_; gfx::SNESPalette col_file_palette_; absl::Status status_; diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index c9921417..80fcd47c 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -18,6 +18,7 @@ #include "app/gui/icons.h" #include "app/gui/input.h" #include "app/gui/style.h" +#include "app/gui/widgets.h" #include "app/rom.h" #include "app/zelda3/overworld.h" @@ -194,14 +195,68 @@ void OverworldEditor::DrawOverworldSprites() { // ---------------------------------------------------------------------------- -void OverworldEditor::DrawOverworldEdits() const { +void OverworldEditor::DrawOverworldEdits() { auto mouse_position = ow_map_canvas_.GetCurrentDrawnTilePosition(); auto canvas_size = ow_map_canvas_.GetCanvasSize(); int x = mouse_position.x / canvas_size.x; int y = mouse_position.y / canvas_size.y; auto index = x + (y * 64); - std::cout << "==> " << index << std::endl; + // Determine which overworld map the user is currently editing. + DetermineActiveMap(mouse_position); + + // Render the updated map bitmap. + RenderUpdatedMapBitmap(mouse_position, + tile16_individual_data_[current_tile16_]); + + // Queue up the raw ROM changes. + QueueROMChanges(index, current_tile16_); +} + +void OverworldEditor::RenderUpdatedMapBitmap(const ImVec2 &click_position, + const Bytes &tile_data) { + // Calculate the tile position relative to the current active map + constexpr int tile_size = 16; // Tile size is 16x16 pixels + + // Calculate the tile index for x and y based on the click_position + int tile_index_x = static_cast(click_position.x) / tile_size; + int tile_index_y = static_cast(click_position.y) / tile_size; + + // Calculate the pixel start position based on tile index and tile size + ImVec2 start_position; + start_position.x = tile_index_x * tile_size; + start_position.y = tile_index_y * tile_size; + + // Get the current map's bitmap from the BitmapTable + gfx::Bitmap ¤t_bitmap = maps_bmp_[current_map_]; + + // Update the bitmap's pixel data based on the start_position and tile_data + for (int y = 0; y < tile_size; ++y) { + for (int x = 0; x < tile_size; ++x) { + int pixel_index = (start_position.y + y) * current_bitmap.width() + + (start_position.x + x); + current_bitmap.WriteToPixel(pixel_index, tile_data[y * tile_size + x]); + } + } + + // Render the updated bitmap to the canvas + rom()->RenderBitmap(¤t_bitmap); +} + +void OverworldEditor::QueueROMChanges(int index, ushort new_tile16) { + // Store the changes made by the user to the ROM (or project file) +} + +void OverworldEditor::DetermineActiveMap(const ImVec2 &mouse_position) { + // Assuming each small map is 256x256 pixels (adjust as needed) + constexpr int small_map_size = 512; + + // Calculate which small map the mouse is currently over + int map_x = mouse_position.x / small_map_size; + int map_y = mouse_position.y / small_map_size; + + // Calculate the index of the map in the `maps_bmp_` vector + current_map_ = map_x + map_y * 8; } // ---------------------------------------------------------------------------- @@ -225,8 +280,7 @@ void OverworldEditor::DrawOverworldCanvas() { if (!blockset_canvas_.Points().empty()) { int x = blockset_canvas_.Points().front().x / 32; int y = blockset_canvas_.Points().front().y / 32; - current_tile16_ = x + (y * 0x10); - + current_tile16_ = x + (y * 8); if (ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_], 16)) { // Update the overworld map. @@ -272,8 +326,9 @@ void OverworldEditor::DrawTileSelector() { if (ImGui::BeginTabBar(kTileSelectorTab.data(), ImGuiTabBarFlags_FittingPolicyScroll)) { if (ImGui::BeginTabItem("Tile16")) { - core::BitmapCanvasPipeline(0x100, (8192 * 2), 0x20, map_blockset_loaded_, - tile16_blockset_bmp_, true, 1); + core::BitmapCanvasPipeline(blockset_canvas_, tile16_blockset_bmp_, 0x100, + (8192 * 2), 0x20, map_blockset_loaded_, true, + 1); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tile8")) { @@ -286,8 +341,9 @@ void OverworldEditor::DrawTileSelector() { ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Area Graphics")) { - core::BitmapCanvasPipeline(256, 0x10 * 0x40, 0x20, overworld_.isLoaded(), - current_gfx_bmp_, true, 3); + core::BitmapCanvasPipeline(current_gfx_canvas_, current_gfx_bmp_, 256, + 0x10 * 0x40, 0x20, overworld_.isLoaded(), true, + 3); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -319,17 +375,19 @@ absl::Status OverworldEditor::LoadGraphics() { // Copy the tile16 data into individual tiles. auto tile16_data = overworld_.Tile16Blockset(); + std::cout << tile16_data.size() << std::endl; + // Loop through the tiles and copy their pixel data into separate vectors for (int i = 0; i < 4096; i++) { // Create a new vector for the pixel data of the current tile - Bytes tile_data; - for (int j = 0; j < 16 * 16; j++) tile_data.push_back(0x00); + Bytes tile_data(16 * 16, 0x00); // More efficient initialization // Copy the pixel data for the current tile into the vector for (int ty = 0; ty < 16; ty++) { for (int tx = 0; tx < 16; tx++) { - int position = (tx + (ty * 0x10)); - uchar value = tile16_data[i + tx + (ty * 0x80)]; + int position = tx + (ty * 0x10); + uchar value = + tile16_data[(i % 8 * 16) + (i / 8 * 16 * 0x80) + (ty * 0x80) + tx]; tile_data[position] = value; } } @@ -389,20 +447,25 @@ absl::Status OverworldEditor::DrawExperimentalModal() { ImGui::InputText("##TilemapFile", &ow_tilemap_filename_); ImGui::SameLine(); core::FileDialogPipeline( - "ImportTilemapsKey", ".CGX,.cgx\0", "Tilemap Hex File", [this]() { + "ImportTilemapsKey", ".DAT,.dat\0", "Tilemap Hex File", [this]() { ow_tilemap_filename_ = ImGuiFileDialog::Instance()->GetFilePathName(); }); ImGui::InputText("##Tile32ConfigurationFile", &tile32_configuration_filename_); ImGui::SameLine(); - core::FileDialogPipeline("ImportTile32Key", ".CGX,.cgx\0", "Tile32 Hex File", + core::FileDialogPipeline("ImportTile32Key", ".DAT,.dat\0", "Tile32 Hex File", [this]() { tile32_configuration_filename_ = ImGuiFileDialog::Instance()->GetFilePathName(); }); - ImGui::Button("Load Prototype Overworld with ROM graphics"); + if (ImGui::Button("Load Prototype Overworld with ROM graphics")) { + if (rom()->isLoaded() && !all_gfx_loaded_) { + RETURN_IF_ERROR(LoadGraphics()) + all_gfx_loaded_ = true; + } + } gui::TextWithSeparators("Configuration"); diff --git a/src/app/editor/overworld_editor.h b/src/app/editor/overworld_editor.h index 8619b532..27c9d566 100644 --- a/src/app/editor/overworld_editor.h +++ b/src/app/editor/overworld_editor.h @@ -70,7 +70,13 @@ class OverworldEditor : public Editor, public SharedROM { void DrawOverworldEntrances(); void DrawOverworldMaps(); void DrawOverworldSprites(); - void DrawOverworldEdits() const; + + void DrawOverworldEdits(); + void RenderUpdatedMapBitmap(const ImVec2 &click_position, + const Bytes &tile_data); + void QueueROMChanges(int index, ushort new_tile16); + void DetermineActiveMap(const ImVec2 &mouse_position); + void DrawOverworldCanvas(); void DrawTile8Selector(); diff --git a/src/app/editor/palette_editor.h b/src/app/editor/palette_editor.h index 982afa4f..79cd6889 100644 --- a/src/app/editor/palette_editor.h +++ b/src/app/editor/palette_editor.h @@ -3,15 +3,12 @@ #include -#include - #include "absl/status/status.h" #include "app/gfx/snes_palette.h" #include "app/gui/canvas.h" #include "app/gui/icons.h" #include "app/rom.h" - namespace yaze { namespace app { namespace editor { @@ -33,6 +30,49 @@ struct PaletteChange { size_t paletteIndex; size_t colorIndex; gfx::SNESColor originalColor; + gfx::SNESColor newColor; +}; + +class PaletteEditorHistory { + public: + // Record a change in the palette editor + void RecordChange(const std::string& groupName, size_t paletteIndex, + size_t colorIndex, const gfx::SNESColor& originalColor, + const gfx::SNESColor& newColor) { + // Check size and remove the oldest if necessary + if (recentChanges.size() >= maxHistorySize) { + recentChanges.pop_front(); + } + + // Push the new change + recentChanges.push_back( + {groupName, paletteIndex, colorIndex, originalColor, newColor}); + } + + // Get recent changes for display in the palette editor + const std::deque& GetRecentChanges() const { + return recentChanges; + } + + // Restore the original color + gfx::SNESColor GetOriginalColor(const std::string& groupName, + size_t paletteIndex, + size_t colorIndex) const { + for (const auto& change : recentChanges) { + if (change.groupName == groupName && + change.paletteIndex == paletteIndex && + change.colorIndex == colorIndex) { + return change.originalColor; + } + } + // Handle error or return default (this is just an example, + // handle as appropriate for your application) + return gfx::SNESColor(); + } + + private: + std::deque recentChanges; + static const size_t maxHistorySize = 50; // or any other number you deem fit }; class PaletteEditor : public SharedROM { @@ -42,7 +82,6 @@ class PaletteEditor : public SharedROM { void EditColorInPalette(gfx::SNESPalette& palette, int index); void ResetColorToOriginal(gfx::SNESPalette& palette, int index, const gfx::SNESPalette& originalPalette); - void DisplayPalette(gfx::SNESPalette& palette, bool loaded); void DrawPortablePalette(gfx::SNESPalette& palette); @@ -60,7 +99,7 @@ class PaletteEditor : public SharedROM { } } - std::stack changeHistory; + PaletteEditorHistory history_; ImVec4 saved_palette_[256] = {}; ImVec4 current_color_; diff --git a/src/app/gfx/compression.cc b/src/app/gfx/compression.cc index 7ced2c97..950a4834 100644 --- a/src/app/gfx/compression.cc +++ b/src/app/gfx/compression.cc @@ -704,6 +704,11 @@ absl::StatusOr DecompressOverworld(const uchar* data, int pos, return DecompressV2(data, pos, size, kNintendoMode1); } +absl::StatusOr DecompressOverworld(const std::vector data, + int pos, int size) { + return DecompressV2(data.data(), pos, size, kNintendoMode1); +} + } // namespace lc_lz2 } // namespace gfx } // namespace app diff --git a/src/app/gfx/compression.h b/src/app/gfx/compression.h index d435594e..1bfb816d 100644 --- a/src/app/gfx/compression.h +++ b/src/app/gfx/compression.h @@ -107,6 +107,7 @@ absl::StatusOr DecompressV2(const uchar* data, int offset, int size = 0x800, int mode = 1); absl::StatusOr DecompressGraphics(const uchar* data, int pos, int size); absl::StatusOr DecompressOverworld(const uchar* data, int pos, int size); +absl::StatusOr DecompressOverworld(const std::vector data, int pos, int size); // Compression V1 diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index b2e64b5b..02c33e3e 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -9,6 +9,8 @@ #include "app/rom.h" namespace yaze { +namespace app { + namespace gui { constexpr uint32_t kRectangleColor = IM_COL32(32, 32, 32, 255); @@ -209,4 +211,5 @@ void Canvas::DrawOverlay() { } } // namespace gui +} // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/gui/canvas.h b/src/app/gui/canvas.h index b5e1952e..dca3a2a9 100644 --- a/src/app/gui/canvas.h +++ b/src/app/gui/canvas.h @@ -10,6 +10,7 @@ #include "app/rom.h" namespace yaze { +namespace app { namespace gui { using app::gfx::Bitmap; @@ -83,6 +84,7 @@ class Canvas { }; } // namespace gui +} // namespace app } // namespace yaze #endif \ No newline at end of file diff --git a/src/app/gui/color.cc b/src/app/gui/color.cc index 3420cab6..498abfd6 100644 --- a/src/app/gui/color.cc +++ b/src/app/gui/color.cc @@ -9,6 +9,8 @@ #include "app/gfx/snes_palette.h" namespace yaze { +namespace app { + namespace gui { void DisplayPalette(app::gfx::SNESPalette& palette, bool loaded) { @@ -83,4 +85,5 @@ void DisplayPalette(app::gfx::SNESPalette& palette, bool loaded) { } } // namespace gui +} // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/gui/color.h b/src/app/gui/color.h index fc9c5bc4..a3605098 100644 --- a/src/app/gui/color.h +++ b/src/app/gui/color.h @@ -10,11 +10,14 @@ #include "app/gfx/snes_palette.h" namespace yaze { +namespace app { + namespace gui { void DisplayPalette(app::gfx::SNESPalette& palette, bool loaded); } // namespace gui +} // namespace app } // namespace yaze #endif \ No newline at end of file diff --git a/src/app/gui/input.cc b/src/app/gui/input.cc index e5c52c3d..879d2488 100644 --- a/src/app/gui/input.cc +++ b/src/app/gui/input.cc @@ -6,6 +6,7 @@ #include "absl/strings/string_view.h" namespace yaze { +namespace app { namespace gui { const int kStepOneHex = 0x01; @@ -67,4 +68,5 @@ void ItemLabel(absl::string_view title, ItemLabelFlags flags) { } } // namespace gui -} // namespace yaze +} // namespace app +} // namespace yaze \ No newline at end of file diff --git a/src/app/gui/input.h b/src/app/gui/input.h index e7555f02..a1c5c8e8 100644 --- a/src/app/gui/input.h +++ b/src/app/gui/input.h @@ -10,6 +10,7 @@ #include "absl/strings/string_view.h" namespace yaze { +namespace app { namespace gui { constexpr ImVec2 kDefaultModalSize = ImVec2(200, 0); @@ -27,6 +28,7 @@ using ItemLabelFlags = enum ItemLabelFlag { IMGUI_API void ItemLabel(absl::string_view title, ItemLabelFlags flags); } // namespace gui +} // namespace app } // namespace yaze #endif \ No newline at end of file diff --git a/src/app/gui/style.cc b/src/app/gui/style.cc index f49d58d7..c90df451 100644 --- a/src/app/gui/style.cc +++ b/src/app/gui/style.cc @@ -4,6 +4,8 @@ #include "imgui/imgui_internal.h" namespace yaze { +namespace app { + namespace gui { void TextWithSeparators(const absl::string_view &text) { @@ -111,4 +113,5 @@ void ColorsYaze() { colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); } } // namespace gui +} // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/gui/style.h b/src/app/gui/style.h index 9a1a97ba..ca6e4249 100644 --- a/src/app/gui/style.h +++ b/src/app/gui/style.h @@ -7,6 +7,7 @@ #include "absl/strings/string_view.h" namespace yaze { +namespace app { namespace gui { void TextWithSeparators(const absl::string_view& text); @@ -14,6 +15,7 @@ void TextWithSeparators(const absl::string_view& text); void ColorsYaze(); } // namespace gui +} // namespace app } // namespace yaze #endif \ No newline at end of file diff --git a/src/app/gui/widgets.cc b/src/app/gui/widgets.cc index 6d0a96dc..b07cd4d0 100644 --- a/src/app/gui/widgets.cc +++ b/src/app/gui/widgets.cc @@ -7,6 +7,7 @@ #include "app/core/constants.h" namespace yaze { +namespace app { namespace gui { namespace widgets { @@ -93,4 +94,5 @@ TextEditor::LanguageDefinition GetAssemblyLanguageDef() { } // namespace widgets } // namespace gui +} // namespace app } // namespace yaze diff --git a/src/app/gui/widgets.h b/src/app/gui/widgets.h index 69bf15b9..eda06c34 100644 --- a/src/app/gui/widgets.h +++ b/src/app/gui/widgets.h @@ -2,18 +2,72 @@ #define YAZE_GUI_WIDGETS_H #include +#include +#include + +#include #include "absl/status/status.h" #include "app/core/constants.h" +#include "app/gfx/bitmap.h" namespace yaze { +namespace app { namespace gui { namespace widgets { TextEditor::LanguageDefinition GetAssemblyLanguageDef(); +class BitmapViewer { + public: + BitmapViewer() : current_bitmap_index_(0) {} + + void Display(const std::vector& bitmaps) { + if (bitmaps.empty()) { + ImGui::Text("No bitmaps available."); + return; + } + + // Display the current bitmap index and total count. + ImGui::Text("Viewing Bitmap %d / %zu", current_bitmap_index_ + 1, + bitmaps.size()); + + // Buttons to navigate through bitmaps. + if (ImGui::Button("<- Prev")) { + if (current_bitmap_index_ > 0) { + --current_bitmap_index_; + } + } + ImGui::SameLine(); + if (ImGui::Button("Next ->")) { + if (current_bitmap_index_ < bitmaps.size() - 1) { + ++current_bitmap_index_; + } + } + + // Display the current bitmap. + const gfx::Bitmap& current_bitmap = bitmaps[current_bitmap_index_]; + // Assuming Bitmap has a function to get its texture ID, and width and + // height. + ImTextureID tex_id = current_bitmap.texture(); + ImVec2 size(current_bitmap.width(), current_bitmap.height()); + ImGui::Image(tex_id, size); + + // Scroll if the image is larger than the display area. + if (ImGui::BeginChild("BitmapScrollArea", ImVec2(0, 0), false, + ImGuiWindowFlags_HorizontalScrollbar)) { + ImGui::Image(tex_id, size); + ImGui::EndChild(); + } + } + + private: + int current_bitmap_index_; +}; + } // namespace widgets } // namespace gui +} // namespace app } // namespace yaze #endif \ No newline at end of file diff --git a/src/app/zelda3/overworld.cc b/src/app/zelda3/overworld.cc index 2e9ec0d8..f946bcca 100644 --- a/src/app/zelda3/overworld.cc +++ b/src/app/zelda3/overworld.cc @@ -2,10 +2,13 @@ #include +#include #include #include +#include #include +#include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "app/core/constants.h" #include "app/gfx/bitmap.h" @@ -35,6 +38,57 @@ uint GetOwMapGfxLowPtr(const uchar *rom, int index, uint32_t map_low_ptr) { return core::SnesToPc(p2); } +absl::flat_hash_map parseFile(const std::string &filename) { + absl::flat_hash_map resultMap; + + std::ifstream file(filename); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << filename << std::endl; + return resultMap; + } + + std::string line; + int currentKey; + bool isHigh = true; + + while (getline(file, line)) { + // Skip empty or whitespace-only lines + if (line.find_first_not_of(" \t\r\n") == std::string::npos) { + continue; + } + + // If the line starts with "MAPDTH" or "MAPDTL", extract the ID. + if (line.find("MAPDTH") == 0) { + auto num_str = line.substr(6); // Extract ID after "MAPDTH" + currentKey = std::stoi(num_str); + isHigh = true; + } else if (line.find("MAPDTL") == 0) { + auto num_str = line.substr(6); // Extract ID after "MAPDTH" + currentKey = std::stoi(num_str); + isHigh = false; + } else { + // Check if the currentKey is already in the map. If not, initialize it. + if (resultMap.find(currentKey) == resultMap.end()) { + resultMap[currentKey] = MapData(); + } + + // Split the line by commas and convert to uint8_t. + std::stringstream ss(line); + std::string valueStr; + while (getline(ss, valueStr, ',')) { + uint8_t value = std::stoi(valueStr, nullptr, 16); + if (isHigh) { + resultMap[currentKey].highData.push_back(value); + } else { + resultMap[currentKey].lowData.push_back(value); + } + } + } + } + + return resultMap; +} + } // namespace absl::Status Overworld::Load(ROM &rom) { @@ -463,6 +517,39 @@ absl::Status Overworld::DecompressAllMapTiles() { return absl::OkStatus(); } +absl::Status Overworld::DecompressProtoMapTiles(const std::string &filename) { + proto_map_data_ = parseFile(filename); + int sx = 0; + int sy = 0; + int c = 0; + for (int i = 0; i < proto_map_data_.size(); i++) { + int ttpos = 0; + + ASSIGN_OR_RETURN(auto bytes, gfx::lc_lz2::DecompressOverworld( + proto_map_data_[i].lowData, 0, + proto_map_data_[i].lowData.size())) + ASSIGN_OR_RETURN(auto bytes2, gfx::lc_lz2::DecompressOverworld( + proto_map_data_[i].highData, 0, + proto_map_data_[i].highData.size())) + OrganizeMapTiles(bytes, bytes2, i, sx, sy, ttpos); + + sx++; + if (sx >= 8) { + sy++; + sx = 0; + } + + c++; + if (c >= 64) { + sx = 0; + sy = 0; + c = 0; + } + } + + return absl::OkStatus(); +} + // ---------------------------------------------------------------------------- void Overworld::FetchLargeMaps() { @@ -619,13 +706,13 @@ void Overworld::LoadSpritesFromMap(int spriteStart, int spriteCount, } } -absl::Status Overworld::LoadPrototype(ROM &rom, std::vector &tilemap, - std::vector tile32) { +absl::Status Overworld::LoadPrototype(ROM &rom, + const std::string &tilemap_filename) { rom_ = rom; AssembleMap32Tiles(); AssembleMap16Tiles(); - RETURN_IF_ERROR(DecompressAllMapTiles()) + RETURN_IF_ERROR(DecompressProtoMapTiles(tilemap_filename)) for (int map_index = 0; map_index < kNumOverworldMaps; ++map_index) overworld_maps_.emplace_back(map_index, rom_, tiles16); diff --git a/src/app/zelda3/overworld.h b/src/app/zelda3/overworld.h index 742feff0..f48f9f6f 100644 --- a/src/app/zelda3/overworld.h +++ b/src/app/zelda3/overworld.h @@ -7,6 +7,7 @@ #include #include +#include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "app/core/constants.h" #include "app/gfx/bitmap.h" @@ -171,6 +172,11 @@ constexpr int LimitOfMap32 = 8864; constexpr int NumberOfOWSprites = 352; constexpr int NumberOfMap32 = Map32PerScreen * kNumOverworldMaps; +struct MapData { + std::vector highData; + std::vector lowData; +}; + class Overworld { public: absl::Status Load(ROM &rom); @@ -189,6 +195,9 @@ class Overworld { auto AreaPalette() const { return overworld_maps_[current_map_].AreaPalette(); } + auto AreaPaletteById(int id) const { + return overworld_maps_[id].AreaPalette(); + } auto BitmapData() const { return overworld_maps_[current_map_].BitmapData(); } auto Tile16Blockset() const { return overworld_maps_[current_map_].Tile16Blockset(); @@ -197,8 +206,7 @@ class Overworld { auto isLoaded() const { return is_loaded_; } void SetCurrentMap(int i) { current_map_ = i; } - absl::Status LoadPrototype(ROM &rom_, std::vector &tilemap, - std::vector tile32); + absl::Status LoadPrototype(ROM &rom_, const std::string &tilemap_filename); private: enum Dimension { @@ -216,13 +224,12 @@ class Overworld { void OrganizeMapTiles(Bytes &bytes, Bytes &bytes2, int i, int sx, int sy, int &ttpos); absl::Status DecompressAllMapTiles(); + absl::Status DecompressProtoMapTiles(const std::string &filename); void FetchLargeMaps(); void LoadEntrances(); void LoadSprites(); void LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex); - void LoadOverworldMap(); - int game_state_ = 0; int current_map_ = 0; uchar map_parent_[160]; @@ -238,6 +245,8 @@ class Overworld { std::vector all_entrances_; std::vector all_holes_; std::vector> all_sprites_; + + absl::flat_hash_map proto_map_data_; }; } // namespace zelda3