Refactor ROM handling and remove SharedRom singleton for improved architecture
- Eliminated the SharedRom class to enhance modularity and reduce global state management. - Updated various classes to directly manage ROM instances, improving clarity and encapsulation. - Added new functions for loading messages and colors from ROM, enhancing functionality. - Refactored Canvas and Editor classes to utilize direct ROM references, streamlining interactions. - Improved documentation and comments for better code understanding and maintainability.
This commit is contained in:
@@ -9,7 +9,6 @@ The goal of yaze is to build a cross platform editor for the Legend of Zelda: A
|
||||
- **yaze**: Desktop application for Windows/macOS/Linux
|
||||
- **z3ed**: Command Line Interface
|
||||
- **yaze_c**: C Library
|
||||
- **yaze_py**: Python Module
|
||||
- **yaze_test**: Unit test executable
|
||||
- **yaze_ios**: iOS application
|
||||
|
||||
@@ -23,7 +22,6 @@ The goal of yaze is to build a cross platform editor for the Legend of Zelda: A
|
||||
- **app**: Contains the GUI editor `yaze`
|
||||
- **app/emu**: Contains a standalone Snes emulator application `yaze_emu`
|
||||
- **cli**: Contains the command line interface `z3ed`
|
||||
- **cli/python**: Contains the Python module `yaze_py`
|
||||
- **ios**: Contains the iOS application `yaze_ios`
|
||||
- **lib**: Contains the dependencies as git submodules
|
||||
- **test**: Contains testing interface `yaze_test`
|
||||
@@ -37,11 +35,10 @@ See [build-instructions.md](docs/build-instructions.md) for more information.
|
||||
- **ImGui**: GUI library
|
||||
- **Abseil**: C++ library
|
||||
- **libpng**: Image library
|
||||
- **Boost**: Python library
|
||||
|
||||
## Flow of Control
|
||||
|
||||
- app/yaze.cc
|
||||
- app/main.cc
|
||||
- Initializes `absl::FailureSignalHandler` for stack tracing.
|
||||
- Runs the `core::Controller` loop.
|
||||
- app/core/controller.cc
|
||||
@@ -75,8 +72,6 @@ See [build-instructions.md](docs/build-instructions.md) for more information.
|
||||
|
||||
The Rom class provides methods to manipulate and access data from a ROM.
|
||||
|
||||
Currently implemented as a singleton with SharedRom which is not great but has helped with development velocity. Potential room for improvement is to refactor the editors to take the ROM as a parameter.
|
||||
|
||||
## Bitmap
|
||||
|
||||
- app/gfx/bitmap.cc
|
||||
|
||||
49
incl/yaze.h
49
incl/yaze.h
@@ -35,8 +35,6 @@ struct yaze_project {
|
||||
const char* name;
|
||||
const char* filepath;
|
||||
const char* rom_filename;
|
||||
const char* code_folder;
|
||||
const char* labels_filename;
|
||||
};
|
||||
|
||||
yaze_project yaze_load_project(const char* filename);
|
||||
@@ -93,14 +91,61 @@ typedef struct snes_tile32 {
|
||||
uint16_t t3;
|
||||
} snes_tile32;
|
||||
|
||||
/**
|
||||
* @brief Get a color from a palette set.
|
||||
*
|
||||
* @details This function gets a color from a palette set and returns it as a
|
||||
* snes_color object.
|
||||
*
|
||||
* @param rom The ROM to get the color from.
|
||||
* @param palette_set The palette set to get the color from.
|
||||
* @param palette The palette to get the color from.
|
||||
* @param color The color to get from the palette.
|
||||
* @return The color from the palette set.
|
||||
*/
|
||||
snes_color yaze_get_color_from_paletteset(const zelda3_rom* rom,
|
||||
int palette_set, int palette,
|
||||
int color);
|
||||
|
||||
/**
|
||||
* @brief Load the overworld from the ROM.
|
||||
*
|
||||
* @param rom The ROM to load the overworld from.
|
||||
* @return The status of the operation. If the operation is successful, the
|
||||
* overworld object will be populated with the overworld from the ROM.
|
||||
*/
|
||||
zelda3_overworld* yaze_load_overworld(const zelda3_rom* rom);
|
||||
|
||||
/**
|
||||
* @brief Load all rooms from the ROM.
|
||||
*
|
||||
* @details This function loads all rooms from the ROM and returns them as an
|
||||
* array of rooms.
|
||||
*
|
||||
* @param rom The ROM to load rooms from.
|
||||
* @return The status of the operation. If the operation is successful, the
|
||||
* rooms array will be populated with the rooms from the ROM.
|
||||
*/
|
||||
zelda3_dungeon_room* yaze_load_all_rooms(const zelda3_rom* rom);
|
||||
|
||||
/**
|
||||
* @brief Load all messages from the ROM.
|
||||
*
|
||||
* @details This function loads all messages from the ROM and returns them as an
|
||||
* array of messages.
|
||||
*
|
||||
* @param rom The ROM to load messages from.
|
||||
* @param messages Pointer to an array of messages.
|
||||
* @return The status of the operation. If the operation is successful, the
|
||||
* messages array will be populated with the messages from the ROM.
|
||||
*/
|
||||
yaze_status yaze_load_messages(zelda3_rom* rom, zelda3_message** messages);
|
||||
|
||||
/**
|
||||
* @brief Function pointer to initialize the extension.
|
||||
*
|
||||
* @param context The editor context.
|
||||
*/
|
||||
typedef void (*yaze_initialize_func)(yaze_editor_context* context);
|
||||
typedef void (*yaze_cleanup_func)(void);
|
||||
|
||||
|
||||
15
incl/zelda.h
15
incl/zelda.h
@@ -86,7 +86,7 @@ const static zelda3_version_pointers zelda3_jp_pointers = {
|
||||
|
||||
typedef struct zelda3_rom {
|
||||
const char* filename;
|
||||
const uint8_t* data;
|
||||
uint8_t* data;
|
||||
size_t size;
|
||||
void* impl; // yaze::Rom*
|
||||
} zelda3_rom;
|
||||
@@ -95,6 +95,19 @@ zelda3_rom* yaze_load_rom(const char* filename);
|
||||
void yaze_unload_rom(zelda3_rom* rom);
|
||||
void yaze_save_rom(zelda3_rom* rom, const char* filename);
|
||||
|
||||
/**
|
||||
* @brief Primitive of a message.
|
||||
*
|
||||
*/
|
||||
typedef struct zelda3_message {
|
||||
uint8_t id;
|
||||
uint8_t address;
|
||||
uint8_t *raw_string;
|
||||
uint8_t *contents_parsed;
|
||||
uint8_t *data;
|
||||
uint8_t *data_parsed;
|
||||
} zelda3_message;
|
||||
|
||||
/**
|
||||
* @brief Primitive of an overworld map.
|
||||
*/
|
||||
|
||||
@@ -206,15 +206,16 @@
|
||||
}
|
||||
|
||||
- (void)openFileAction:(id)sender {
|
||||
if (!yaze::SharedRom::shared_rom_
|
||||
->LoadFromFile(yaze::core::FileDialogWrapper::ShowOpenFileDialog())
|
||||
.ok()) {
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:@"Error"];
|
||||
[alert setInformativeText:@"Failed to load file."];
|
||||
[alert addButtonWithTitle:@"OK"];
|
||||
[alert runModal];
|
||||
}
|
||||
// TODO: Re-implmenent this without the SharedRom singleton
|
||||
// if (!yaze::SharedRom::shared_rom_
|
||||
// ->LoadFromFile(yaze::core::FileDialogWrapper::ShowOpenFileDialog())
|
||||
// .ok()) {
|
||||
// NSAlert *alert = [[NSAlert alloc] init];
|
||||
// [alert setMessageText:@"Error"];
|
||||
// [alert setInformativeText:@"Failed to load file."];
|
||||
// [alert addButtonWithTitle:@"OK"];
|
||||
// [alert runModal];
|
||||
// }
|
||||
}
|
||||
|
||||
- (void)cutAction:(id)sender {
|
||||
@@ -238,12 +239,12 @@ extern "C" void yaze_initialize_cococa() {
|
||||
|
||||
extern "C" int yaze_run_cocoa_app_delegate(const char *filename) {
|
||||
yaze_initialize_cococa();
|
||||
yaze::core::Controller controller;
|
||||
EXIT_IF_ERROR(controller.OnEntry(filename));
|
||||
while (controller.IsActive()) {
|
||||
auto controller = std::make_unique<yaze::core::Controller>();
|
||||
EXIT_IF_ERROR(controller->OnEntry(filename));
|
||||
while (controller->IsActive()) {
|
||||
@autoreleasepool {
|
||||
controller.OnInput();
|
||||
if (auto status = controller.OnLoad(); !status.ok()) {
|
||||
controller->OnInput();
|
||||
if (auto status = controller->OnLoad(); !status.ok()) {
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:@"Error"];
|
||||
[alert setInformativeText:[NSString stringWithUTF8String:status.message().data()]];
|
||||
@@ -251,10 +252,10 @@ extern "C" int yaze_run_cocoa_app_delegate(const char *filename) {
|
||||
[alert runModal];
|
||||
break;
|
||||
}
|
||||
controller.DoRender();
|
||||
controller->DoRender();
|
||||
}
|
||||
}
|
||||
controller.OnExit();
|
||||
controller->OnExit();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace editor {
|
||||
* @class GfxGroupEditor
|
||||
* @brief Manage graphics group configurations in a Rom.
|
||||
*/
|
||||
class GfxGroupEditor : public SharedRom {
|
||||
class GfxGroupEditor {
|
||||
public:
|
||||
absl::Status Update();
|
||||
|
||||
@@ -27,6 +27,8 @@ class GfxGroupEditor : public SharedRom {
|
||||
void SetSelectedSpriteset(uint8_t spriteset) {
|
||||
selected_spriteset_ = spriteset;
|
||||
}
|
||||
void set_rom(Rom* rom) { rom_ = rom; }
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
private:
|
||||
uint8_t selected_blockset_ = 0;
|
||||
@@ -39,6 +41,7 @@ class GfxGroupEditor : public SharedRom {
|
||||
gui::Canvas spriteset_canvas_;
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
Rom* rom_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -586,7 +586,9 @@ absl::Status OverworldEditor::CheckForCurrentMap() {
|
||||
}
|
||||
const int current_highlighted_map = current_map_;
|
||||
|
||||
current_parent_ = overworld_.overworld_map(current_map_)->parent();
|
||||
if (!current_map_lock_) {
|
||||
current_parent_ = overworld_.overworld_map(current_map_)->parent();
|
||||
}
|
||||
|
||||
if (overworld_.overworld_map(current_map_)->is_large_map() ||
|
||||
overworld_.overworld_map(current_map_)->large_index() != 0) {
|
||||
@@ -613,6 +615,11 @@ absl::Status OverworldEditor::CheckForCurrentMap() {
|
||||
maps_bmp_[current_map_].set_modified(false);
|
||||
}
|
||||
|
||||
// If double clicked, toggle the current map
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Right)) {
|
||||
current_map_lock_ = !current_map_lock_;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ constexpr ImVec2 kBlocksetCanvasSize(0x100 + 1, 0x4000 + 1);
|
||||
constexpr ImVec2 kGraphicsBinCanvasSize(0x100 + 1, kNumSheetsToLoad * 0x40 + 1);
|
||||
|
||||
constexpr ImGuiTableFlags kOWMapFlags =
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable;
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp;
|
||||
constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit;
|
||||
constexpr ImGuiTableFlags kOWEditFlags =
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||
@@ -75,6 +76,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
public:
|
||||
explicit OverworldEditor(Rom* rom) : rom_(rom) {
|
||||
type_ = EditorType::kOverworld;
|
||||
gfx_group_editor_.set_rom(rom);
|
||||
}
|
||||
|
||||
void Initialize() override;
|
||||
@@ -212,12 +214,13 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
bool overworld_canvas_fullscreen_ = false;
|
||||
bool middle_mouse_dragging_ = false;
|
||||
bool is_dragging_entity_ = false;
|
||||
bool current_map_lock_ = false;
|
||||
|
||||
gfx::Tilemap tile16_blockset_;
|
||||
|
||||
Rom* rom_;
|
||||
|
||||
Tile16Editor tile16_editor_{&tile16_blockset_};
|
||||
Tile16Editor tile16_editor_{rom_, &tile16_blockset_};
|
||||
GfxGroupEditor gfx_group_editor_;
|
||||
PaletteEditor palette_editor_;
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
||||
}
|
||||
|
||||
absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
static const auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
|
||||
// Create a more organized layout with tabs
|
||||
if (BeginTabBar("Tile16EditorTabs")) {
|
||||
|
||||
@@ -22,9 +22,10 @@ namespace editor {
|
||||
/**
|
||||
* @brief Popup window to edit Tile16 data
|
||||
*/
|
||||
class Tile16Editor : public gfx::GfxContext, public SharedRom {
|
||||
class Tile16Editor : public gfx::GfxContext {
|
||||
public:
|
||||
Tile16Editor(gfx::Tilemap *tile16_blockset) : tile16_blockset_(tile16_blockset) {}
|
||||
Tile16Editor(Rom *rom, gfx::Tilemap *tile16_blockset)
|
||||
: rom_(rom), tile16_blockset_(tile16_blockset) {}
|
||||
absl::Status Initialize(const gfx::Bitmap &tile16_blockset_bmp,
|
||||
const gfx::Bitmap ¤t_gfx_bmp,
|
||||
std::array<uint8_t, 0x200> &all_tiles_types);
|
||||
@@ -52,7 +53,11 @@ class Tile16Editor : public gfx::GfxContext, public SharedRom {
|
||||
absl::Status LoadTile16FromScratchSpace(int slot);
|
||||
absl::Status ClearScratchSpace(int slot);
|
||||
|
||||
void set_rom(Rom *rom) { rom_ = rom; }
|
||||
Rom *rom() const { return rom_; }
|
||||
|
||||
private:
|
||||
Rom *rom_ = nullptr;
|
||||
bool map_blockset_loaded_ = false;
|
||||
bool transfer_started_ = false;
|
||||
bool transfer_blockset_loaded_ = false;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/emu/cpu/internal/opcodes.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
|
||||
@@ -250,7 +250,7 @@ struct BackgroundLayer {
|
||||
bool enabled; // Whether the background layer is enabled
|
||||
};
|
||||
|
||||
class Ppu : public SharedRom {
|
||||
class Ppu {
|
||||
public:
|
||||
// Initializes the PPU with the necessary resources and dependencies
|
||||
Ppu(Memory& memory) : memory_(memory) {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/platform/renderer.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
namespace yaze::gui {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
@@ -43,8 +40,8 @@ constexpr ImGuiButtonFlags kMouseFlags =
|
||||
|
||||
namespace {
|
||||
ImVec2 AlignPosToGrid(ImVec2 pos, float scale) {
|
||||
return ImVec2(std::floor((double)pos.x / scale) * scale,
|
||||
std::floor((double)pos.y / scale) * scale);
|
||||
return ImVec2(std::floor(pos.x / scale) * scale,
|
||||
std::floor(pos.y / scale) * scale);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -62,15 +59,14 @@ void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
||||
DrawOverlay();
|
||||
}
|
||||
|
||||
void Canvas::UpdateInfoGrid(ImVec2 bg_size, int tile_size, float scale,
|
||||
float grid_size, int label_id) {
|
||||
void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
|
||||
enable_custom_labels_ = true;
|
||||
DrawBackground(bg_size);
|
||||
DrawInfoGrid(grid_size, 8, label_id);
|
||||
DrawOverlay();
|
||||
}
|
||||
|
||||
void Canvas::DrawBackground(ImVec2 canvas_size, bool can_drag) {
|
||||
void Canvas::DrawBackground(ImVec2 canvas_size) {
|
||||
draw_list_ = GetWindowDrawList();
|
||||
canvas_p0_ = GetCursorScreenPos();
|
||||
if (!custom_canvas_size_) canvas_sz_ = GetContentRegionAvail();
|
||||
@@ -145,59 +141,59 @@ void Canvas::DrawContextMenu() {
|
||||
Text("Pitch: %d", bitmap_->surface()->pitch);
|
||||
Text("BitsPerPixel: %d", bitmap_->surface()->format->BitsPerPixel);
|
||||
Text("BytesPerPixel: %d", bitmap_->surface()->format->BytesPerPixel);
|
||||
EndMenu();
|
||||
}
|
||||
if (BeginMenu("Bitmap Format")) {
|
||||
if (MenuItem("Indexed")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::kIndexed);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
if (MenuItem("4BPP")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::k4bpp);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
if (MenuItem("8BPP")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::k8bpp);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
|
||||
EndMenu();
|
||||
}
|
||||
if (BeginMenu("View Palette")) {
|
||||
DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette", true, 8);
|
||||
EndMenu();
|
||||
}
|
||||
|
||||
if (BeginMenu("Bitmap Palette")) {
|
||||
if (rom()->is_loaded()) {
|
||||
gui::TextWithSeparators("ROM Palette");
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
ImGui::Combo("Palette Group", (int *)&edit_palette_group_name_index_,
|
||||
gfx::kPaletteGroupAddressesKeys,
|
||||
IM_ARRAYSIZE(gfx::kPaletteGroupAddressesKeys));
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHexWord("Palette Group Index", &edit_palette_index_);
|
||||
|
||||
auto palette_group = rom()->mutable_palette_group()->get_group(
|
||||
gfx::kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
auto palette = palette_group->mutable_palette(edit_palette_index_);
|
||||
|
||||
if (ImGui::BeginChild("Palette", ImVec2(0, 300), true)) {
|
||||
gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
||||
refresh_graphics_, *palette);
|
||||
|
||||
if (refresh_graphics_) {
|
||||
bitmap_->SetPaletteWithTransparent(*palette,
|
||||
edit_palette_sub_index_);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
MenuItem("Data", nullptr, &show_bitmap_data);
|
||||
if (BeginMenu("Format")) {
|
||||
if (MenuItem("Indexed")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::kIndexed);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
if (MenuItem("4BPP")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::k4bpp);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
if (MenuItem("8BPP")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::k8bpp);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
|
||||
EndMenu();
|
||||
}
|
||||
if (BeginMenu("Change Palette")) {
|
||||
Text("Work in progress");
|
||||
// TODO: Get ROM data for change palette
|
||||
// gui::TextWithSeparators("ROM Palette");
|
||||
// ImGui::SetNextItemWidth(100.f);
|
||||
// ImGui::Combo("Palette Group", (int *)&edit_palette_group_name_index_,
|
||||
// gfx::kPaletteGroupAddressesKeys,
|
||||
// IM_ARRAYSIZE(gfx::kPaletteGroupAddressesKeys));
|
||||
// ImGui::SetNextItemWidth(100.f);
|
||||
// gui::InputHexWord("Palette Group Index", &edit_palette_index_);
|
||||
|
||||
// auto palette_group = rom()->mutable_palette_group()->get_group(
|
||||
// gfx::kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
// auto palette = palette_group->mutable_palette(edit_palette_index_);
|
||||
|
||||
// if (ImGui::BeginChild("Palette", ImVec2(0, 300), true)) {
|
||||
// gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
||||
// refresh_graphics_, *palette);
|
||||
|
||||
// if (refresh_graphics_) {
|
||||
// bitmap_->SetPaletteWithTransparent(*palette,
|
||||
// edit_palette_sub_index_);
|
||||
// Renderer::Get().UpdateBitmap(bitmap_);
|
||||
// refresh_graphics_ = false;
|
||||
// }
|
||||
// ImGui::EndChild();
|
||||
// }
|
||||
EndMenu();
|
||||
}
|
||||
if (BeginMenu("View Palette")) {
|
||||
DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette", true,
|
||||
8);
|
||||
EndMenu();
|
||||
}
|
||||
EndMenu();
|
||||
}
|
||||
MenuItem("Bitmap Data", nullptr, &show_bitmap_data);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (BeginMenu("Grid Tile Size")) {
|
||||
@@ -255,12 +251,8 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) {
|
||||
origin.y + paint_pos.y + scaled_size));
|
||||
}
|
||||
|
||||
if (IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
// Draw the currently selected tile on the overworld here
|
||||
// Save the coordinates of the selected tile.
|
||||
drawn_tile_pos_ = paint_pos;
|
||||
return true;
|
||||
} else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (IsMouseClicked(ImGuiMouseButton_Left) &&
|
||||
ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
// Draw the currently selected tile on the overworld here
|
||||
// Save the coordinates of the selected tile.
|
||||
drawn_tile_pos_ = paint_pos;
|
||||
@@ -308,10 +300,8 @@ bool Canvas::DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile) {
|
||||
ImVec2(origin.x + paint_pos.x + scaled_size,
|
||||
origin.y + paint_pos.y + scaled_size));
|
||||
|
||||
if (IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
drawn_tile_pos_ = paint_pos;
|
||||
return true;
|
||||
} else if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
if (IsMouseClicked(ImGuiMouseButton_Left) ||
|
||||
ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
drawn_tile_pos_ = paint_pos;
|
||||
return true;
|
||||
}
|
||||
@@ -407,9 +397,7 @@ bool Canvas::DrawTileSelector(int size, int size_y) {
|
||||
if (!points_.empty()) {
|
||||
points_.clear();
|
||||
}
|
||||
ImVec2 painter_pos;
|
||||
painter_pos.x = std::floor((double)mouse_pos.x / size) * size;
|
||||
painter_pos.y = std::floor((double)mouse_pos.y / size) * size;
|
||||
ImVec2 painter_pos = AlignPosToGrid(mouse_pos, size);
|
||||
|
||||
points_.push_back(painter_pos);
|
||||
points_.push_back(ImVec2(painter_pos.x + size, painter_pos.y + size_y));
|
||||
@@ -464,50 +452,48 @@ void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
|
||||
dragging = true;
|
||||
}
|
||||
|
||||
if (dragging) {
|
||||
if (dragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
||||
// Release dragging mode
|
||||
if (!ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
||||
dragging = false;
|
||||
dragging = false;
|
||||
|
||||
// Calculate the bounds of the rectangle in terms of 16x16 tile indices
|
||||
constexpr int tile16_size = 16;
|
||||
int start_x = std::floor(drag_start_pos.x / scaled_size) * tile16_size;
|
||||
int start_y = std::floor(drag_start_pos.y / scaled_size) * tile16_size;
|
||||
int end_x = std::floor(drag_end_pos.x / scaled_size) * tile16_size;
|
||||
int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
|
||||
// Calculate the bounds of the rectangle in terms of 16x16 tile indices
|
||||
constexpr int tile16_size = 16;
|
||||
int start_x = std::floor(drag_start_pos.x / scaled_size) * tile16_size;
|
||||
int start_y = std::floor(drag_start_pos.y / scaled_size) * tile16_size;
|
||||
int end_x = std::floor(drag_end_pos.x / scaled_size) * tile16_size;
|
||||
int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
|
||||
|
||||
// Swap the start and end positions if they are in the wrong order
|
||||
if (start_x > end_x) std::swap(start_x, end_x);
|
||||
if (start_y > end_y) std::swap(start_y, end_y);
|
||||
// Swap the start and end positions if they are in the wrong order
|
||||
if (start_x > end_x) std::swap(start_x, end_x);
|
||||
if (start_y > end_y) std::swap(start_y, end_y);
|
||||
|
||||
selected_tiles_.clear();
|
||||
// Number of tiles per local map (since each tile is 16x16)
|
||||
constexpr int tiles_per_local_map = small_map_size / 16;
|
||||
selected_tiles_.clear();
|
||||
// Number of tiles per local map (since each tile is 16x16)
|
||||
constexpr int tiles_per_local_map = small_map_size / 16;
|
||||
|
||||
// Loop through the tiles in the rectangle and store their positions
|
||||
for (int y = start_y; y <= end_y; y += tile16_size) {
|
||||
for (int x = start_x; x <= end_x; x += tile16_size) {
|
||||
// Determine which local map (512x512) the tile is in
|
||||
int local_map_x = x / small_map_size;
|
||||
int local_map_y = y / small_map_size;
|
||||
// Loop through the tiles in the rectangle and store their positions
|
||||
for (int y = start_y; y <= end_y; y += tile16_size) {
|
||||
for (int x = start_x; x <= end_x; x += tile16_size) {
|
||||
// Determine which local map (512x512) the tile is in
|
||||
int local_map_x = x / small_map_size;
|
||||
int local_map_y = y / small_map_size;
|
||||
|
||||
// Calculate the tile's position within its local map
|
||||
int tile16_x = (x % small_map_size) / tile16_size;
|
||||
int tile16_y = (y % small_map_size) / tile16_size;
|
||||
// Calculate the tile's position within its local map
|
||||
int tile16_x = (x % small_map_size) / tile16_size;
|
||||
int tile16_y = (y % small_map_size) / tile16_size;
|
||||
|
||||
// Calculate the index within the overall map structure
|
||||
int index_x = local_map_x * tiles_per_local_map + tile16_x;
|
||||
int index_y = local_map_y * tiles_per_local_map + tile16_y;
|
||||
// Calculate the index within the overall map structure
|
||||
int index_x = local_map_x * tiles_per_local_map + tile16_x;
|
||||
int index_y = local_map_y * tiles_per_local_map + tile16_y;
|
||||
|
||||
selected_tiles_.push_back(ImVec2(index_x, index_y));
|
||||
}
|
||||
selected_tiles_.push_back(ImVec2(index_x, index_y));
|
||||
}
|
||||
// Clear and add the calculated rectangle points
|
||||
selected_points_.clear();
|
||||
selected_points_.push_back(drag_start_pos);
|
||||
selected_points_.push_back(drag_end_pos);
|
||||
select_rect_active_ = true;
|
||||
}
|
||||
// Clear and add the calculated rectangle points
|
||||
selected_points_.clear();
|
||||
selected_points_.push_back(drag_start_pos);
|
||||
selected_points_.push_back(drag_end_pos);
|
||||
select_rect_active_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,25 +695,27 @@ void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
||||
DrawGridLines(grid_step);
|
||||
DrawCustomHighlight(grid_step);
|
||||
|
||||
if (enable_custom_labels_) {
|
||||
// Draw the contents of labels on the grid
|
||||
for (float x = fmodf(scrolling_.x, grid_step);
|
||||
x < canvas_sz_.x * global_scale_; x += grid_step) {
|
||||
for (float y = fmodf(scrolling_.y, grid_step);
|
||||
y < canvas_sz_.y * global_scale_; y += grid_step) {
|
||||
int tile_x = (x - scrolling_.x) / grid_step;
|
||||
int tile_y = (y - scrolling_.y) / grid_step;
|
||||
int tile_id = tile_x + (tile_y * tile_id_offset);
|
||||
if (!enable_custom_labels_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tile_id >= labels_[label_id].size()) {
|
||||
break;
|
||||
}
|
||||
std::string label = labels_[label_id][tile_id];
|
||||
draw_list_->AddText(
|
||||
ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
|
||||
canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
|
||||
kWhiteColor, label.data());
|
||||
// Draw the contents of labels on the grid
|
||||
for (float x = fmodf(scrolling_.x, grid_step);
|
||||
x < canvas_sz_.x * global_scale_; x += grid_step) {
|
||||
for (float y = fmodf(scrolling_.y, grid_step);
|
||||
y < canvas_sz_.y * global_scale_; y += grid_step) {
|
||||
int tile_x = (x - scrolling_.x) / grid_step;
|
||||
int tile_y = (y - scrolling_.y) / grid_step;
|
||||
int tile_id = tile_x + (tile_y * tile_id_offset);
|
||||
|
||||
if (tile_id >= labels_[label_id].size()) {
|
||||
break;
|
||||
}
|
||||
std::string label = labels_[label_id][tile_id];
|
||||
draw_list_->AddText(
|
||||
ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
|
||||
canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
|
||||
kWhiteColor, label.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -773,25 +761,26 @@ void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_custom_labels_) {
|
||||
// Draw the contents of labels on the grid
|
||||
for (float x = fmodf(scrolling_.x, grid_step);
|
||||
x < canvas_sz_.x * global_scale_; x += grid_step) {
|
||||
for (float y = fmodf(scrolling_.y, grid_step);
|
||||
y < canvas_sz_.y * global_scale_; y += grid_step) {
|
||||
int tile_x = (x - scrolling_.x) / grid_step;
|
||||
int tile_y = (y - scrolling_.y) / grid_step;
|
||||
int tile_id = tile_x + (tile_y * tile_id_offset);
|
||||
if (!enable_custom_labels_) {
|
||||
return;
|
||||
}
|
||||
// Draw the contents of labels on the grid
|
||||
for (float x = fmodf(scrolling_.x, grid_step);
|
||||
x < canvas_sz_.x * global_scale_; x += grid_step) {
|
||||
for (float y = fmodf(scrolling_.y, grid_step);
|
||||
y < canvas_sz_.y * global_scale_; y += grid_step) {
|
||||
int tile_x = (x - scrolling_.x) / grid_step;
|
||||
int tile_y = (y - scrolling_.y) / grid_step;
|
||||
int tile_id = tile_x + (tile_y * tile_id_offset);
|
||||
|
||||
if (tile_id >= labels_[current_labels_].size()) {
|
||||
break;
|
||||
}
|
||||
std::string label = labels_[current_labels_][tile_id];
|
||||
draw_list_->AddText(
|
||||
ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
|
||||
canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
|
||||
kWhiteColor, label.data());
|
||||
if (tile_id >= labels_[current_labels_].size()) {
|
||||
break;
|
||||
}
|
||||
std::string label = labels_[current_labels_][tile_id];
|
||||
draw_list_->AddText(
|
||||
ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
|
||||
canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
|
||||
kWhiteColor, label.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -909,8 +898,8 @@ void GraphicsBinCanvasPipeline(int width, int height, int tile_size,
|
||||
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) {
|
||||
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);
|
||||
@@ -932,5 +921,4 @@ void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
} // namespace yaze::gui
|
||||
|
||||
@@ -34,28 +34,28 @@ enum class CanvasGridSize { k8x8, k16x16, k32x32, k64x64 };
|
||||
* on a canvas. It supports features such as bitmap drawing, context menu
|
||||
* handling, tile painting, custom grid, and more.
|
||||
*/
|
||||
class Canvas : public SharedRom {
|
||||
class Canvas {
|
||||
public:
|
||||
Canvas() = default;
|
||||
explicit Canvas(const std::string &id) : canvas_id_(id) {
|
||||
context_id_ = id + "Context";
|
||||
}
|
||||
explicit Canvas(const std::string &id, ImVec2 canvas_size)
|
||||
: canvas_id_(id), custom_canvas_size_(true), canvas_sz_(canvas_size) {
|
||||
: custom_canvas_size_(true), canvas_sz_(canvas_size), canvas_id_(id) {
|
||||
context_id_ = id + "Context";
|
||||
}
|
||||
explicit Canvas(const std::string &id, ImVec2 canvas_size,
|
||||
CanvasGridSize grid_size)
|
||||
: canvas_id_(id), custom_canvas_size_(true), canvas_sz_(canvas_size) {
|
||||
: custom_canvas_size_(true), canvas_sz_(canvas_size), canvas_id_(id) {
|
||||
context_id_ = id + "Context";
|
||||
SetCanvasGridSize(grid_size);
|
||||
}
|
||||
explicit Canvas(const std::string &id, ImVec2 canvas_size,
|
||||
CanvasGridSize grid_size, float global_scale)
|
||||
: canvas_id_(id),
|
||||
custom_canvas_size_(true),
|
||||
: custom_canvas_size_(true),
|
||||
global_scale_(global_scale),
|
||||
canvas_sz_(canvas_size),
|
||||
global_scale_(global_scale) {
|
||||
canvas_id_(id) {
|
||||
context_id_ = id + "Context";
|
||||
SetCanvasGridSize(grid_size);
|
||||
}
|
||||
@@ -81,12 +81,12 @@ class Canvas : public SharedRom {
|
||||
const std::function<void()> &event, int tile_size,
|
||||
float scale = 1.0f);
|
||||
|
||||
void UpdateInfoGrid(ImVec2 bg_size, int tile_size, float scale = 1.0f,
|
||||
float grid_size = 64.0f, int label_id = 0);
|
||||
void UpdateInfoGrid(ImVec2 bg_size, float grid_size = 64.0f,
|
||||
int label_id = 0);
|
||||
|
||||
// Background for the Canvas represents region without any content drawn to
|
||||
// it, but can be controlled by the user.
|
||||
void DrawBackground(ImVec2 canvas_size = ImVec2(0, 0), bool drag = false);
|
||||
void DrawBackground(ImVec2 canvas_size = ImVec2(0, 0));
|
||||
|
||||
// Context Menu refers to what happens when the right mouse button is pressed
|
||||
// This routine also handles the scrolling for the canvas.
|
||||
@@ -111,8 +111,8 @@ class Canvas : public SharedRom {
|
||||
|
||||
// Draws the contents of the Bitmap image to the Canvas
|
||||
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale);
|
||||
void DrawBitmap(Bitmap &bitmap, int x_offset, int y_offset, float scale = 1.0f,
|
||||
int alpha = 255);
|
||||
void DrawBitmap(Bitmap &bitmap, int x_offset, int y_offset,
|
||||
float scale = 1.0f, int alpha = 255);
|
||||
void DrawBitmap(Bitmap &bitmap, ImVec2 dest_pos, ImVec2 dest_size,
|
||||
ImVec2 src_pos, ImVec2 src_size);
|
||||
void DrawBitmapTable(const BitmapTable &gfx_bin);
|
||||
@@ -206,6 +206,9 @@ class Canvas : public SharedRom {
|
||||
|
||||
auto hover_mouse_pos() const { return mouse_pos_in_canvas_; }
|
||||
|
||||
void set_rom(Rom *rom) { rom_ = rom; }
|
||||
Rom *rom() const { return rom_; }
|
||||
|
||||
private:
|
||||
bool draggable_ = false;
|
||||
bool is_hovered_ = false;
|
||||
@@ -228,6 +231,7 @@ class Canvas : public SharedRom {
|
||||
uint64_t edit_palette_sub_index_ = 0;
|
||||
|
||||
Bitmap *bitmap_ = nullptr;
|
||||
Rom *rom_ = nullptr;
|
||||
|
||||
ImDrawList *draw_list_ = nullptr;
|
||||
|
||||
|
||||
@@ -649,6 +649,4 @@ absl::Status Rom::WriteColor(uint32_t address, const gfx::SnesColor &color) {
|
||||
return WriteShort(address, bgr);
|
||||
}
|
||||
|
||||
std::shared_ptr<Rom> SharedRom::shared_rom_ = nullptr;
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
@@ -272,40 +272,6 @@ absl::StatusOr<std::array<gfx::Bitmap, kNumLinkSheets>> LoadLinkGraphics(
|
||||
|
||||
absl::StatusOr<gfx::Bitmap> LoadFontGraphics(const Rom& rom);
|
||||
|
||||
/**
|
||||
* @brief A class to hold a shared pointer to a Rom object.
|
||||
*/
|
||||
class SharedRom {
|
||||
public:
|
||||
SharedRom() = default;
|
||||
virtual ~SharedRom() = default;
|
||||
|
||||
std::shared_ptr<Rom> shared_rom() {
|
||||
if (!shared_rom_) {
|
||||
shared_rom_ = std::make_shared<Rom>();
|
||||
}
|
||||
return shared_rom_;
|
||||
}
|
||||
|
||||
auto rom() {
|
||||
if (!shared_rom_) {
|
||||
shared_rom_ = std::make_shared<Rom>();
|
||||
}
|
||||
Rom* rom = shared_rom_.get();
|
||||
return rom;
|
||||
}
|
||||
|
||||
static void set_rom(Rom* rom) {
|
||||
if (!shared_rom_) {
|
||||
shared_rom_ = std::make_shared<Rom>();
|
||||
}
|
||||
shared_rom_ = std::shared_ptr<Rom>(rom);
|
||||
}
|
||||
|
||||
// private:
|
||||
static std::shared_ptr<Rom> shared_rom_;
|
||||
};
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
|
||||
@@ -77,14 +77,6 @@
|
||||
|
||||
// Enable native IME.
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
if (!_controller->CreateWindow().ok()) {
|
||||
printf("Error creating window: %s\n", SDL_GetError());
|
||||
abort();
|
||||
}
|
||||
if (!_controller->CreateRenderer().ok()) {
|
||||
printf("Error creating renderer: %s\n", SDL_GetError());
|
||||
abort();
|
||||
}
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(_controller->window(),
|
||||
yaze::core::Renderer::Get().renderer());
|
||||
@@ -93,7 +85,6 @@
|
||||
if (!LoadPackageFonts().ok()) {
|
||||
abort();
|
||||
}
|
||||
_controller->Initialize("");
|
||||
_controller->set_active(true);
|
||||
|
||||
_hoverGestureRecognizer =
|
||||
@@ -357,9 +348,10 @@
|
||||
rom_data.resize(size);
|
||||
std::copy(bytes, bytes + size, rom_data.begin());
|
||||
|
||||
PRINT_IF_ERROR(yaze::SharedRom::shared_rom_->LoadFromData(rom_data));
|
||||
// TODO: Re-implmenent this without the SharedRom singleton
|
||||
// PRINT_IF_ERROR(yaze::SharedRom::shared_rom_->LoadFromData(rom_data));
|
||||
std::string filename = std::string([selectedFileURL.path UTF8String]);
|
||||
yaze::SharedRom::shared_rom_->set_filename(filename);
|
||||
// yaze::SharedRom::shared_rom_->set_filename(filename);
|
||||
[selectedFileURL stopAccessingSecurityScopedResource];
|
||||
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user