backend-infra-engineer: Pre-0.2.2 snapshot (2023)

This commit is contained in:
scawful
2023-12-29 22:43:40 -05:00
parent e7470bdfac
commit d94b7a3e81
174 changed files with 31731 additions and 4836 deletions

View File

@@ -0,0 +1,35 @@
#include "app/editor/context/gfx_context.h"
#include <imgui/imgui.h>
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/gui/pipeline.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
namespace yaze {
namespace app {
namespace editor {
absl::Status GfxContext::Update() { return absl::OkStatus(); }
gfx::Bitmap GfxContext::current_ow_gfx_bmp_;
gfx::SNESPalette GfxContext::current_ow_palette_;
gfx::Bitmap GfxContext::tile16_blockset_bmp_;
gfx::Bitmap GfxContext::tile8_blockset_bmp_;
std::vector<gfx::Bitmap> GfxContext::tile16_individual_bmp_;
std::vector<gfx::Bitmap> GfxContext::tile8_individual_bmp_;
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,56 @@
#ifndef YAZE_APP_EDITOR_VRAM_CONTEXT_H
#define YAZE_APP_EDITOR_VRAM_CONTEXT_H
#include <imgui/imgui.h>
#include <cmath>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/gui/pipeline.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
// Create a class which manages the current VRAM state of Link to the Past,
// including static members for the Bitmaps and Palettes as well as the current
// blockset as to update all of the overworld maps with the new blockset when it
// is changed. This class will also manage the current tile16 and tile8
// selection, as well as the current palette selection.
namespace yaze {
namespace app {
namespace editor {
class GfxContext {
public:
absl::Status Update();
protected:
static gfx::Bitmap current_ow_gfx_bmp_;
static gfx::SNESPalette current_ow_palette_;
static gfx::Bitmap tile16_blockset_bmp_;
static gfx::Bitmap tile8_blockset_bmp_;
// Bitmaps for the tile16 individual tiles
static std::vector<gfx::Bitmap> tile16_individual_bmp_;
// Bitmaps for the tile8 individual tiles
static std::vector<gfx::Bitmap> tile8_individual_bmp_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_VRAM_CONTEXT_H

View File

@@ -1,59 +1,378 @@
#include "dungeon_editor.h"
#include "gui/icons.h"
#include <imgui/imgui.h>
#include "app/core/common.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/object_names.h"
#include "app/zelda3/dungeon/room_names.h"
#include "zelda3/dungeon/room.h"
namespace yaze {
namespace app {
namespace editor {
void DungeonEditor::Update() {
constexpr ImGuiTableFlags kDungeonObjectTableFlags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
absl::Status DungeonEditor::Update() {
if (!is_loaded_ && rom()->isLoaded()) {
for (int i = 0; i < 0x100; i++) {
rooms_.emplace_back(zelda3::dungeon::Room(i));
rooms_[i].LoadHeader();
rooms_[i].LoadRoomFromROM();
if (flags()->kDrawDungeonRoomGraphics) {
rooms_[i].LoadRoomGraphics();
}
}
graphics_bin_ = rom()->graphics_bin();
full_palette_ =
rom()->palette_group("dungeon_main")[current_palette_group_id_];
current_palette_group_ =
gfx::CreatePaletteGroupFromLargePalette(full_palette_);
// Create a vector of pointers to the current block bitmaps
for (int block : rooms_[current_room_id_].blocks()) {
room_gfx_sheets_.emplace_back(&graphics_bin_[block]);
}
is_loaded_ = true;
}
if (refresh_graphics_) {
for (int block : rooms_[current_room_id_].blocks()) {
graphics_bin_[block].ApplyPalette(
current_palette_group_[current_palette_id_]);
rom()->UpdateBitmap(&graphics_bin_[block]);
}
refresh_graphics_ = false;
}
DrawToolset();
if (palette_showing_) {
ImGui::Begin("Palette Editor", &palette_showing_, 0);
current_palette_ =
rom()->palette_group("dungeon_main")[current_palette_group_id_];
gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
current_palette_);
ImGui::End();
}
if (ImGui::BeginTable("#DungeonEditTable", 3, kDungeonTableFlags,
ImVec2(0, 0))) {
TableSetupColumn("Room Selector");
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Object Selector");
TableHeadersRow();
TableNextRow();
TableNextColumn();
DrawRoomSelector();
TableNextColumn();
DrawDungeonTabView();
TableNextColumn();
DrawTileSelector();
ImGui::EndTable();
}
return absl::OkStatus();
}
void DungeonEditor::DrawToolset() {
if (ImGui::BeginTable("DWToolset", 13, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
TableSetupColumn("#undoTool");
TableSetupColumn("#redoTool");
TableSetupColumn("#separator");
TableSetupColumn("#anyTool");
TableSetupColumn("#bg1Tool");
TableSetupColumn("#bg2Tool");
TableSetupColumn("#bg3Tool");
TableSetupColumn("#separator");
TableSetupColumn("#spriteTool");
TableSetupColumn("#itemTool");
TableSetupColumn("#doorTool");
TableSetupColumn("#blockTool");
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_UNDO)) {
PRINT_IF_ERROR(Undo());
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_REDO)) {
PRINT_IF_ERROR(Redo());
}
ImGui::TableNextColumn();
ImGui::Text(ICON_MD_MORE_VERT);
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_FILTER_NONE,
background_type_ == kBackgroundAny)) {
background_type_ = kBackgroundAny;
}
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_FILTER_1,
background_type_ == kBackground1)) {
background_type_ = kBackground1;
}
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_FILTER_2,
background_type_ == kBackground2)) {
background_type_ = kBackground2;
}
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_FILTER_3,
background_type_ == kBackground3)) {
background_type_ = kBackground3;
}
ImGui::TableNextColumn();
ImGui::Text(ICON_MD_MORE_VERT);
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_PEST_CONTROL, placement_type_ == kSprite)) {
placement_type_ = kSprite;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Sprites");
}
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_GRASS, placement_type_ == kItem)) {
placement_type_ = kItem;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Items");
}
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_SENSOR_DOOR, placement_type_ == kDoor)) {
placement_type_ = kDoor;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Doors");
}
ImGui::TableNextColumn();
if (ImGui::RadioButton(ICON_MD_SQUARE, placement_type_ == kBlock)) {
placement_type_ = kBlock;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Blocks");
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_PALETTE)) {
palette_showing_ = !palette_showing_;
}
ImGui::EndTable();
}
}
void DungeonEditor::DrawRoomSelector() {
if (rom()->isLoaded()) {
gui::InputHexWord("Room ID", &current_room_id_);
gui::InputHex("Palette ID", &current_palette_id_);
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
int i = 0;
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
ImGui::Selectable(each_room_name.data(), current_room_id_ == i,
ImGuiSelectableFlags_AllowDoubleClick);
if (ImGui::IsItemClicked()) {
active_rooms_.push_back(i);
}
i += 1;
}
}
ImGui::EndChild();
}
}
void DungeonEditor::DrawDungeonTabView() {
static int next_tab_id = 0;
if (ImGui::BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
// TODO: Manage the room that is being added to the tab bar.
if (ImGui::TabItemButton("+", kDungeonTabFlags)) {
active_rooms_.push_back(next_tab_id++); // Add new tab
}
// Submit our regular tabs
for (int n = 0; n < active_rooms_.Size;) {
bool open = true;
if (ImGui::BeginTabItem(
zelda3::dungeon::kRoomNames[active_rooms_[n]].data(), &open,
ImGuiTabItemFlags_None)) {
DrawDungeonCanvas(active_rooms_[n]);
ImGui::EndTabItem();
}
if (!open)
active_rooms_.erase(active_rooms_.Data + n);
else
n++;
}
ImGui::EndTabBar();
}
ImGui::Separator();
}
void DungeonEditor::DrawDungeonCanvas(int room_id) {
ImGui::BeginGroup();
gui::InputHexByte("Layout", &rooms_[room_id].layout);
ImGui::SameLine();
gui::InputHexByte("Blockset", &rooms_[room_id].blockset);
ImGui::SameLine();
gui::InputHexByte("Spriteset", &rooms_[room_id].spriteset);
ImGui::SameLine();
gui::InputHexByte("Palette", &rooms_[room_id].palette);
gui::InputHexByte("Floor1", &rooms_[room_id].floor1);
ImGui::SameLine();
gui::InputHexByte("Floor2", &rooms_[room_id].floor2);
ImGui::SameLine();
gui::InputHexWord("Message ID", &rooms_[room_id].message_id_);
ImGui::SameLine();
ImGui::EndGroup();
canvas_.DrawBackground();
canvas_.DrawContextMenu();
canvas_.DrawGrid();
canvas_.DrawOverlay();
}
void DungeonEditor::DrawToolset() {
if (ImGui::BeginTable("DWToolset", 9, toolset_table_flags_, ImVec2(0, 0))) {
ImGui::TableSetupColumn("#undoTool");
ImGui::TableSetupColumn("#redoTool");
ImGui::TableSetupColumn("#history");
ImGui::TableSetupColumn("#separator");
ImGui::TableSetupColumn("#bg1Tool");
ImGui::TableSetupColumn("#bg2Tool");
ImGui::TableSetupColumn("#bg3Tool");
ImGui::TableSetupColumn("#itemTool");
ImGui::TableSetupColumn("#spriteTool");
void DungeonEditor::DrawRoomGraphics() {
const auto height = 0x40;
room_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
room_gfx_canvas_.DrawContextMenu();
room_gfx_canvas_.DrawTileSelector(32);
if (is_loaded_) {
auto blocks = rooms_[current_room_id_].blocks();
int current_block = 0;
for (int block : blocks) {
int offset = height * (current_block + 1);
int top_left_y = room_gfx_canvas_.zero_point().y + 2;
if (current_block >= 1) {
top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
}
room_gfx_canvas_.GetDrawList()->AddImage(
(void*)graphics_bin_[block].texture(),
ImVec2(room_gfx_canvas_.zero_point().x + 2, top_left_y),
ImVec2(room_gfx_canvas_.zero_point().x + 0x100,
room_gfx_canvas_.zero_point().y + offset));
current_block += 1;
}
}
room_gfx_canvas_.DrawGrid(32.0f);
room_gfx_canvas_.DrawOverlay();
}
void DungeonEditor::DrawTileSelector() {
if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
if (ImGui::BeginTabItem("Room Graphics")) {
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)3);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawRoomGraphics();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Object Renderer")) {
DrawObjectRenderer();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
void DungeonEditor::DrawObjectRenderer() {
if (ImGui::BeginTable("DungeonObjectEditorTable", 2, kDungeonObjectTableFlags,
ImVec2(0, 0))) {
TableSetupColumn("Dungeon Objects", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Canvas");
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_UNDO);
ImGui::BeginChild("DungeonObjectButtons", ImVec2(250, 0), true);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_REDO);
int selected_object = 0;
int i = 0;
for (const auto object_name : zelda3::dungeon::Type1RoomObjectNames) {
if (ImGui::Selectable(object_name.data(), selected_object == i)) {
selected_object = i;
current_object_ = i;
object_renderer_.LoadObject(i,
rooms_[current_room_id_].mutable_blocks());
rom()->RenderBitmap(object_renderer_.bitmap());
object_loaded_ = true;
}
i += 1;
}
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_MANAGE_HISTORY);
ImGui::EndChild();
// Right side of the table - Canvas
ImGui::TableNextColumn();
ImGui::Text(ICON_MD_MORE_VERT);
ImGui::BeginChild("DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1),
true);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_FILTER_1);
object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
object_canvas_.DrawContextMenu();
object_canvas_.DrawTileSelector(32);
if (object_loaded_) {
object_canvas_.DrawBitmap(*object_renderer_.bitmap(), 0, 0);
}
object_canvas_.DrawGrid(32.0f);
object_canvas_.DrawOverlay();
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_FILTER_2);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_FILTER_3);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_GRASS);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_PEST_CONTROL_RODENT);
ImGui::EndChild();
ImGui::EndTable();
}
// if (object_loaded_) {
// ImGui::Begin("Memory Viewer", &object_loaded_, 0);
// auto memory = object_renderer_.memory();
// static MemoryEditor mem_edit;
// mem_edit.DrawContents((void*)object_renderer_.memory_ptr(),
// memory.size());
// ImGui::End();
// }
}
} // namespace editor

View File

@@ -3,22 +3,97 @@
#include <imgui/imgui.h>
#include "gui/canvas.h"
#include "gui/icons.h"
#include "app/core/common.h"
#include "app/core/editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_object.h"
namespace yaze {
namespace app {
namespace editor {
class DungeonEditor {
constexpr ImGuiTabItemFlags kDungeonTabFlags =
ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip;
constexpr ImGuiTabBarFlags kDungeonTabBarFlags =
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
ImGuiTabBarFlags_FittingPolicyResizeDown |
ImGuiTabBarFlags_TabListPopupButton;
constexpr ImGuiTableFlags kDungeonTableFlags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV;
class DungeonEditor : public Editor,
public SharedROM,
public core::ExperimentFlags {
public:
void Update();
absl::Status Update() override;
absl::Status Cut() override { return absl::OkStatus(); }
absl::Status Copy() override { return absl::OkStatus(); }
absl::Status Paste() override { return absl::OkStatus(); }
absl::Status Undo() override { return absl::OkStatus(); }
absl::Status Redo() override { return absl::OkStatus(); }
private:
void DrawToolset();
void DrawRoomSelector();
void DrawDungeonTabView();
void DrawDungeonCanvas(int room_id);
void DrawRoomGraphics();
void DrawTileSelector();
void DrawObjectRenderer();
enum BackgroundType {
kNoBackground,
kBackground1,
kBackground2,
kBackground3,
kBackgroundAny,
};
enum PlacementType { kNoType, kSprite, kItem, kDoor, kBlock };
int background_type_ = kNoBackground;
int placement_type_ = kNoType;
int current_object_ = 0;
bool is_loaded_ = false;
bool object_loaded_ = false;
bool palette_showing_ = false;
bool refresh_graphics_ = false;
bool show_object_render_ = false;
uint16_t current_room_id_ = 0;
uint64_t current_palette_id_ = 0;
uint64_t current_palette_group_id_ = 0;
ImVector<int> active_rooms_;
PaletteEditor palette_editor_;
gfx::SNESPalette current_palette_;
gfx::SNESPalette full_palette_;
gfx::PaletteGroup current_palette_group_;
gui::Canvas canvas_;
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
gui::Canvas room_gfx_canvas_;
gui::Canvas object_canvas_;
gfx::Bitmap room_gfx_bmp_;
gfx::BitmapTable graphics_bin_;
std::vector<gfx::Bitmap*> room_gfx_sheets_;
std::vector<zelda3::dungeon::Room> rooms_;
std::vector<gfx::BitmapManager> room_graphics_;
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
};
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,768 @@
#include "app/editor/graphics_editor.h"
#include <ImGuiFileDialog/ImGuiFileDialog.h>
#include <imgui/imgui.h>
#include <imgui/misc/cpp/imgui_stdlib.h>
#include <imgui_memory_editor.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/platform/clipboard.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/compression.h"
#include "app/gfx/scad_format.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/gui/style.h"
#include "app/rom.h"
namespace yaze {
namespace app {
namespace editor {
using ImGui::Button;
using ImGui::InputInt;
using ImGui::InputText;
using ImGui::SameLine;
constexpr ImGuiTableFlags kGfxEditTableFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
ImGuiTableFlags_SizingFixedFit;
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
ImGuiTabBarFlags_FittingPolicyResizeDown |
ImGuiTabBarFlags_TabListPopupButton;
absl::Status GraphicsEditor::Update() {
TAB_BAR("##TabBar")
status_ = UpdateGfxEdit();
status_ = UpdateScadView();
status_ = UpdateLinkGfxView();
END_TAB_BAR()
CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdateGfxEdit() {
TAB_ITEM("Graphics Editor")
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
ImVec2(0, 0))) {
for (const auto& name : kGfxEditColumnNames)
ImGui::TableSetupColumn(name.data());
ImGui::TableHeadersRow();
NEXT_COLUMN();
status_ = UpdateGfxSheetList();
NEXT_COLUMN();
if (rom()->isLoaded()) {
DrawGfxEditToolset();
status_ = UpdateGfxTabView();
}
NEXT_COLUMN();
if (rom()->isLoaded()) {
status_ = UpdatePaletteColumn();
}
}
ImGui::EndTable();
END_TAB_ITEM()
return absl::OkStatus();
}
void GraphicsEditor::DrawGfxEditToolset() {
if (ImGui::BeginTable("##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
for (const auto& name :
{"Select", "Pencil", "Fill", "Copy Sheet", "Paste Sheet", "Zoom Out",
"Zoom In", "Current Color", "Tile Size"})
ImGui::TableSetupColumn(name);
ImGui::TableNextColumn();
if (Button(ICON_MD_SELECT_ALL)) {
gfx_edit_mode_ = GfxEditMode::kSelect;
}
ImGui::TableNextColumn();
if (Button(ICON_MD_DRAW)) {
gfx_edit_mode_ = GfxEditMode::kPencil;
}
ImGui::TableNextColumn();
if (Button(ICON_MD_FORMAT_COLOR_FILL)) {
gfx_edit_mode_ = GfxEditMode::kFill;
}
ImGui::TableNextColumn();
if (Button(ICON_MD_CONTENT_COPY)) {
std::vector<uint8_t> png_data =
rom()->bitmap_manager().GetBitmap(current_sheet_)->GetPngData();
CopyImageToClipboard(png_data);
}
ImGui::TableNextColumn();
if (Button(ICON_MD_CONTENT_PASTE)) {
std::vector<uint8_t> png_data;
int width, height;
GetImageFromClipboard(png_data, width, height);
if (png_data.size() > 0) {
rom()
->bitmap_manager()
.GetBitmap(current_sheet_)
->LoadFromPngData(png_data, width, height);
rom()->UpdateBitmap(rom()
->mutable_bitmap_manager()
->mutable_bitmap(current_sheet_)
.get());
}
}
HOVER_HINT("Paste from Clipboard");
ImGui::TableNextColumn();
if (Button(ICON_MD_ZOOM_OUT)) {
if (current_scale_ >= 0.0f) {
current_scale_ -= 1.0f;
}
}
ImGui::TableNextColumn();
if (Button(ICON_MD_ZOOM_IN)) {
if (current_scale_ <= 16.0f) {
current_scale_ += 1.0f;
}
}
ImGui::TableNextColumn();
auto bitmap = rom()->bitmap_manager()[current_sheet_];
auto palette = bitmap->palette();
for (int i = 0; i < 8; i++) {
ImGui::SameLine();
auto color =
ImVec4(palette[i].GetRGB().x / 255.0f, palette[i].GetRGB().y / 255.0f,
palette[i].GetRGB().z / 255.0f, 255.0f);
if (ImGui::ColorButton(absl::StrFormat("Palette Color %d", i).c_str(),
color)) {
current_color_ = color;
}
}
ImGui::TableNextColumn();
gui::InputHexByte("Tile Size", &tile_size_, 0x01);
ImGui::EndTable();
}
}
absl::Status GraphicsEditor::UpdateGfxSheetList() {
ImGui::BeginChild(
"##GfxEditChild", ImVec2(0, 0), true,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
for (auto& [key, value] : rom()->bitmap_manager()) {
ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
ImVec2(0x100 + 1, 0x40 + 1), true,
ImGuiWindowFlags_NoDecoration);
ImGui::PopStyleVar();
gui::Canvas graphics_bin_canvas_;
auto select_tile_event = [&]() {
if (value.get()->IsActive()) {
auto texture = value.get()->texture();
graphics_bin_canvas_.GetDrawList()->AddImage(
(void*)texture,
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
graphics_bin_canvas_.zero_point().y + 2),
ImVec2(graphics_bin_canvas_.zero_point().x +
value.get()->width() * sheet_scale_,
graphics_bin_canvas_.zero_point().y +
value.get()->height() * sheet_scale_));
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
current_sheet_ = key;
open_sheets_.insert(key);
}
// Add a slightly transparent rectangle behind the text
ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
graphics_bin_canvas_.zero_point().y + 2);
ImVec2 text_size =
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
ImVec2 rent_min(text_pos.x, text_pos.y);
ImVec2 rent_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
graphics_bin_canvas_.GetDrawList()->AddRectFilled(
rent_min, rent_max, IM_COL32(0, 125, 0, 128));
graphics_bin_canvas_.GetDrawList()->AddText(
text_pos, IM_COL32(125, 255, 125, 255),
absl::StrFormat("%02X", key).c_str());
}
};
graphics_bin_canvas_.UpdateEvent(
select_tile_event, ImVec2(0x100 + 1, 0x40 + 1), 0x20, sheet_scale_,
/*grid_size=*/16.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::EndChild();
}
ImGui::PopStyleVar();
ImGui::EndChild();
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdateGfxTabView() {
static int next_tab_id = 0;
if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
if (ImGui::TabItemButton(
"+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) {
open_sheets_.insert(next_tab_id++);
}
for (auto& sheet_id : open_sheets_) {
bool open = true;
if (ImGui::BeginTabItem(absl::StrFormat("%d", sheet_id).c_str(), &open,
ImGuiTabItemFlags_None)) {
current_sheet_ = sheet_id;
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
release_queue_.push(sheet_id);
}
if (ImGui::IsItemHovered()) {
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
release_queue_.push(sheet_id);
child_window_sheets_.insert(sheet_id);
}
}
const auto child_id =
absl::StrFormat("##GfxEditPaletteChildWindow%d", sheet_id);
ImGui::BeginChild(child_id.c_str(), ImVec2(0, 0), true,
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
auto draw_tile_event = [&]() {
gfx::Bitmap& current_bitmap =
*rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id);
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, current_bitmap,
current_color_);
rom()->UpdateBitmap(&current_bitmap);
};
auto size = ImVec2(0x80, 0x20);
current_sheet_canvas_.UpdateColorPainter(
*rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
size, tile_size_, current_scale_, 8.0f);
ImGui::EndChild();
ImGui::EndTabItem();
}
if (!open) release_queue_.push(sheet_id);
}
ImGui::EndTabBar();
}
// Release any tabs that were closed
while (!release_queue_.empty()) {
auto sheet_id = release_queue_.top();
open_sheets_.erase(sheet_id);
release_queue_.pop();
}
// Draw any child windows that were created
if (!child_window_sheets_.empty()) {
int id_to_release = -1;
for (const auto& id : child_window_sheets_) {
bool active = true;
ImGui::SetNextWindowPos(ImGui::GetIO().MousePos, ImGuiCond_Once);
ImGui::SetNextWindowSize(ImVec2(0x100 + 1 * 16, 0x40 + 1 * 16),
ImGuiCond_Once);
ImGui::Begin(absl::StrFormat("##GfxEditPaletteChildWindow%d", id).c_str(),
&active, ImGuiWindowFlags_AlwaysUseWindowPadding);
current_sheet_ = id;
current_sheet_canvas_.UpdateColorPainter(
*rom()->bitmap_manager()[id], current_color_,
[&]() {
},
ImVec2(0x100, 0x40), tile_size_, current_scale_, 8.0f);
ImGui::End();
if (active == false) {
id_to_release = id;
}
}
if (id_to_release != -1) {
child_window_sheets_.erase(id_to_release);
}
}
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdatePaletteColumn() {
auto palette_group = rom()->palette_group(
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
auto palette = palette_group[edit_palette_index_];
if (rom()->isLoaded()) {
gui::TextWithSeparators("ROM Palette");
ImGui::SetNextItemWidth(100.f);
ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
kPaletteGroupAddressesKeys,
IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
ImGui::SetNextItemWidth(100.f);
gui::InputHex("Palette Group Index", &edit_palette_index_);
}
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
palette);
if (refresh_graphics_) {
rom()->bitmap_manager()[current_sheet_]->ApplyPaletteWithTransparent(
palette, edit_palette_sub_index_);
rom()->UpdateBitmap(
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_).get());
refresh_graphics_ = false;
}
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdateLinkGfxView() {
TAB_ITEM("Player Animations")
const auto link_gfx_offset = 0x80000;
const auto link_gfx_length = 0x7000;
// Load Links graphics from the ROM
RETURN_IF_ERROR(rom()->LoadLinkGraphics());
// Split it into the pose data frames
// Create an animation step display for the poses
// Allow the user to modify the frames used in an anim step
// LinkOAM_AnimationSteps:
// #_0D85FB
END_TAB_ITEM()
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdateScadView() {
TAB_ITEM("Prototype")
RETURN_IF_ERROR(DrawToolset())
if (open_memory_editor_) {
ImGui::Begin("Memory Editor", &open_memory_editor_);
RETURN_IF_ERROR(DrawMemoryEditor())
ImGui::End();
}
BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
SETUP_COLUMN("File Import (BIN, CGX, ROM)")
SETUP_COLUMN("Palette (COL)")
ImGui::TableSetupColumn("Tilemaps and Objects (SCR, PNL, OBJ)",
ImGuiTableColumnFlags_WidthFixed);
SETUP_COLUMN("Graphics Preview")
TABLE_HEADERS()
NEXT_COLUMN() {
status_ = DrawCgxImport();
status_ = DrawClipboardImport();
status_ = DrawFileImport();
status_ = DrawExperimentalFeatures();
}
NEXT_COLUMN() { status_ = DrawPaletteControls(); }
NEXT_COLUMN()
gui::BitmapCanvasPipeline(scr_canvas_, scr_bitmap_, 0x200, 0x200, 0x20,
scr_loaded_, false, 0);
status_ = DrawScrImport();
NEXT_COLUMN()
if (super_donkey_) {
if (refresh_graphics_) {
for (int i = 0; i < graphics_bin_.size(); i++) {
graphics_bin_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
rom()->UpdateBitmap(&graphics_bin_[i]);
}
refresh_graphics_ = false;
}
// Load the full graphics space from `super_donkey_1.bin`
gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
super_donkey_, graphics_bin_);
} else if (cgx_loaded_ && col_file_) {
// Load the CGX graphics
gui::BitmapCanvasPipeline(import_canvas_, cgx_bitmap_, 0x100, 16384, 0x20,
cgx_loaded_, true, 5);
} else {
// Load the BIN/Clipboard Graphics
gui::BitmapCanvasPipeline(import_canvas_, bin_bitmap_, 0x100, 16384, 0x20,
gfx_loaded_, true, 2);
}
END_TABLE()
END_TAB_ITEM()
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawToolset() {
if (ImGui::BeginTable("GraphicsToolset", 2, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
for (const auto& name : kGfxToolsetColumnNames)
ImGui::TableSetupColumn(name.data());
ImGui::TableNextColumn();
if (Button(ICON_MD_MEMORY)) {
if (!open_memory_editor_) {
open_memory_editor_ = true;
} else {
open_memory_editor_ = false;
}
}
TEXT_COLUMN("Open Memory Editor") // Separator
ImGui::EndTable();
}
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawCgxImport() {
gui::TextWithSeparators("Cgx Import");
InputInt("BPP", &current_bpp_);
InputText("##CGXFile", cgx_file_name_, sizeof(cgx_file_name_));
SameLine();
gui::FileDialogPipeline("ImportCgxKey", ".CGX,.cgx\0", "Open CGX", [this]() {
strncpy(cgx_file_path_,
ImGuiFileDialog::Instance()->GetFilePathName().c_str(),
sizeof(cgx_file_path_));
strncpy(cgx_file_name_,
ImGuiFileDialog::Instance()->GetCurrentFileName().c_str(),
sizeof(cgx_file_name_));
is_open_ = true;
cgx_loaded_ = true;
});
gui::ButtonPipe("Copy CGX Path",
[this]() { ImGui::SetClipboardText(cgx_file_path_); });
gui::ButtonPipe("Load CGX Data", [this]() {
status_ = gfx::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
decoded_cgx_, extra_cgx_data_);
cgx_bitmap_.InitializeFromData(0x80, 0x200, 8, decoded_cgx_);
if (col_file_) {
cgx_bitmap_.ApplyPalette(decoded_col_);
rom()->RenderBitmap(&cgx_bitmap_);
}
});
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawScrImport() {
InputText("##ScrFile", scr_file_name_, sizeof(scr_file_name_));
gui::FileDialogPipeline(
"ImportScrKey", ".SCR,.scr,.BAK\0", "Open SCR", [this]() {
strncpy(scr_file_path_,
ImGuiFileDialog::Instance()->GetFilePathName().c_str(),
sizeof(scr_file_path_));
strncpy(scr_file_name_,
ImGuiFileDialog::Instance()->GetCurrentFileName().c_str(),
sizeof(scr_file_name_));
is_open_ = true;
scr_loaded_ = true;
});
InputInt("SCR Mod", &scr_mod_value_);
gui::ButtonPipe("Load Scr Data", [this]() {
status_ = gfx::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
decoded_scr_data_.resize(0x100 * 0x100);
status_ = gfx::DrawScrWithCgx(current_bpp_, scr_data_, decoded_scr_data_,
decoded_cgx_);
scr_bitmap_.InitializeFromData(0x100, 0x100, 8, decoded_scr_data_);
if (scr_loaded_) {
scr_bitmap_.ApplyPalette(decoded_col_);
rom()->RenderBitmap(&scr_bitmap_);
}
});
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawPaletteControls() {
gui::TextWithSeparators("COL Import");
InputText("##ColFile", col_file_name_, sizeof(col_file_name_));
SameLine();
gui::FileDialogPipeline(
"ImportColKey", ".COL,.col,.BAK,.bak\0", "Open COL", [this]() {
strncpy(col_file_path_,
ImGuiFileDialog::Instance()->GetFilePathName().c_str(),
sizeof(col_file_path_));
strncpy(col_file_name_,
ImGuiFileDialog::Instance()->GetCurrentFileName().c_str(),
sizeof(col_file_name_));
status_ = temp_rom_.LoadFromFile(col_file_path_,
/*z3_load=*/false);
auto col_data_ = gfx::GetColFileData(temp_rom_.data());
if (col_file_palette_group_.size() != 0) {
col_file_palette_group_.Clear();
}
col_file_palette_group_ = gfx::CreatePaletteGroupFromColFile(col_data_);
col_file_palette_ = gfx::SNESPalette(col_data_);
// gigaleak dev format based code
decoded_col_ = gfx::DecodeColFile(col_file_path_);
col_file_ = true;
is_open_ = true;
});
gui::ButtonPipe("Copy COL Path",
[this]() { ImGui::SetClipboardText(col_file_path_); });
if (rom()->isLoaded()) {
gui::TextWithSeparators("ROM Palette");
gui::InputHex("Palette Index", &current_palette_index_);
ImGui::Combo("Palette", &current_palette_, kPaletteGroupAddressesKeys,
IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
}
if (col_file_palette_.size() != 0) {
gui::SelectablePalettePipeline(current_palette_index_, refresh_graphics_,
col_file_palette_);
}
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawObjImport() {
gui::TextWithSeparators("OBJ Import");
InputText("##ObjFile", obj_file_path_, sizeof(obj_file_path_));
SameLine();
gui::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");
InputText("##TMapFile", tilemap_file_path_, sizeof(tilemap_file_path_));
SameLine();
gui::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");
InputText("##ROMFile", file_path_, sizeof(file_path_));
SameLine();
gui::FileDialogPipeline("ImportDlgKey", ".bin,.hex\0", "Open BIN", [this]() {
strncpy(file_path_, ImGuiFileDialog::Instance()->GetFilePathName().c_str(),
sizeof(file_path_));
status_ = temp_rom_.LoadFromFile(file_path_);
is_open_ = true;
});
gui::ButtonPipe("Copy File Path",
[this]() { ImGui::SetClipboardText(file_path_); });
gui::InputHex("BIN Offset", &current_offset_);
gui::InputHex("BIN Size", &bin_size_);
if (Button("Decompress BIN")) {
if (strlen(file_path_) > 0) {
RETURN_IF_ERROR(DecompressImportData(bin_size_))
} else {
return absl::InvalidArgumentError(
"Please select a file before importing.");
}
}
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawClipboardImport() {
gui::TextWithSeparators("Clipboard Import");
gui::ButtonPipe("Paste from Clipboard", [this]() {
const char* text = ImGui::GetClipboardText();
if (text) {
const auto clipboard_data = Bytes(text, text + strlen(text));
ImGui::MemFree((void*)text);
status_ = temp_rom_.LoadFromBytes(clipboard_data);
is_open_ = true;
open_memory_editor_ = true;
}
});
gui::InputHex("Offset", &clipboard_offset_);
gui::InputHex("Size", &clipboard_size_);
gui::InputHex("Num Sheets", &num_sheets_to_load_);
gui::ButtonPipe("Decompress Clipboard Data", [this]() {
if (temp_rom_.isLoaded()) {
status_ = DecompressImportData(0x40000);
} else {
status_ = absl::InvalidArgumentError(
"Please paste data into the clipboard before "
"decompressing.");
}
});
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawExperimentalFeatures() {
gui::TextWithSeparators("Experimental");
if (Button("Decompress Super Donkey Full")) {
if (strlen(file_path_) > 0) {
RETURN_IF_ERROR(DecompressSuperDonkey())
} else {
return absl::InvalidArgumentError(
"Please select `super_donkey_1.bin` before "
"importing.");
}
}
ImGui::SetItemTooltip(
"Requires `super_donkey_1.bin` to be imported under the "
"BIN import section.");
return absl::OkStatus();
}
absl::Status GraphicsEditor::DrawMemoryEditor() {
std::string title = "Memory Editor";
if (is_open_) {
static MemoryEditor mem_edit;
mem_edit.DrawWindow(title.c_str(), temp_rom_.data(), temp_rom_.size());
}
return absl::OkStatus();
}
absl::Status GraphicsEditor::DecompressImportData(int size) {
ASSIGN_OR_RETURN(import_data_, gfx::lc_lz2::DecompressV2(
temp_rom_.data(), current_offset_, size))
auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
bin_bitmap_.Create(core::kTilesheetWidth, 0x2000, core::kTilesheetDepth,
converted_sheet);
if (rom()->isLoaded()) {
auto palette_group = rom()->palette_group("ow_main");
z3_rom_palette_ = palette_group[current_palette_];
if (col_file_) {
bin_bitmap_.ApplyPalette(col_file_palette_);
} else {
bin_bitmap_.ApplyPalette(z3_rom_palette_);
}
}
rom()->RenderBitmap(&bin_bitmap_);
gfx_loaded_ = true;
return absl::OkStatus();
}
absl::Status GraphicsEditor::DecompressSuperDonkey() {
int i = 0;
for (const auto& offset : kSuperDonkeyTiles) {
int offset_value =
std::stoi(offset, nullptr, 16); // convert hex string to int
ASSIGN_OR_RETURN(
auto decompressed_data,
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
graphics_bin_[i] =
gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
core::kTilesheetDepth, converted_sheet);
if (col_file_) {
graphics_bin_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group =
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = palette_group[current_palette_index_];
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
}
rom()->RenderBitmap(&graphics_bin_[i]);
i++;
}
for (const auto& offset : kSuperDonkeySprites) {
int offset_value =
std::stoi(offset, nullptr, 16); // convert hex string to int
ASSIGN_OR_RETURN(
auto decompressed_data,
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
graphics_bin_[i] =
gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
core::kTilesheetDepth, converted_sheet);
if (col_file_) {
graphics_bin_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group =
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = palette_group[current_palette_index_];
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
}
rom()->RenderBitmap(&graphics_bin_[i]);
i++;
}
super_donkey_ = true;
num_sheets_to_load_ = i;
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,193 @@
#ifndef YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
#define YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
#include <ImGuiFileDialog/ImGuiFileDialog.h>
#include <imgui/imgui.h>
#include <imgui/misc/cpp/imgui_stdlib.h>
#include <imgui_memory_editor.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
namespace yaze {
namespace app {
namespace editor {
// "99973","A3D80",
const std::string kSuperDonkeyTiles[] = {
"97C05", "98219", "9871E", "98C00", "99084", "995AF", "99DE0", "9A27E",
"9A741", "9AC31", "9B07E", "9B55C", "9B963", "9BB99", "9C009", "9C4B4",
"9C92B", "9CDD6", "9D2C2", "9E037", "9E527", "9EA56", "9EF65", "9FCD1",
"A0193", "A059E", "A0B17", "A0FB6", "A14A5", "A1988", "A1E66", "A232B",
"A27F0", "A2B6E", "A302C", "A3453", "A38CA", "A42BB", "A470C", "A4BA9",
"A5089", "A5385", "A5742", "A5BCC", "A6017", "A6361", "A66F8"};
const std::string kSuperDonkeySprites[] = {
"A8E5D", "A9435", "A9934", "A9D83", "AA2F1", "AA6D4", "AABE4", "AB127",
"AB65A", "ABBDD", "AC38D", "AC797", "ACCC8", "AD0AE", "AD245", "AD554",
"ADAAC", "ADECC", "AE453", "AE9D2", "AEF40", "AF3C9", "AF92E", "AFE9D",
"B03D2", "B09AC", "B0F0C", "B1430", "B1859", "B1E01", "B229A", "B2854",
"B2D27", "B31D7", "B3B58", "B40B5", "B45A5", "B4D64", "B5031", "B555F",
"B5F30", "B6858", "B70DD", "B7526", "B79EC", "B7C83", "B80F7", "B85CC",
"B8A3F", "B8F97", "B94F2", "B9A20", "B9E9A", "BA3A2", "BA8F6", "BACDC",
"BB1F9", "BB781", "BBCCA", "BC26D", "BC7D4", "BCBB0", "BD082", "BD5FC",
"BE115", "BE5C2", "BEB63", "BF0CB", "BF607", "BFA55", "BFD71", "C017D",
"C0567", "C0981", "C0BA7", "C116D", "C166A", "C1FE0", "C24CE", "C2B19"};
constexpr const char* kPaletteGroupAddressesKeys[] = {
"ow_main", "ow_aux", "ow_animated", "hud",
"global_sprites", "armors", "swords", "shields",
"sprites_aux1", "sprites_aux2", "sprites_aux3", "dungeon_main",
"grass", "3d_object", "ow_mini_map",
};
static constexpr std::string_view kGfxEditColumnNames[] = {
"Tilesheets", "Current Graphics", "Palette Controls"};
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
"#memoryEditor",
"##separator_gfx1",
};
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
class GraphicsEditor : public SharedROM {
public:
absl::Status Update();
private:
enum class GfxEditMode {
kSelect,
kPencil,
kFill,
};
// Graphics Editor Tab
absl::Status UpdateGfxEdit();
absl::Status UpdateGfxSheetList();
absl::Status UpdateGfxTabView();
absl::Status UpdatePaletteColumn();
void DrawGfxEditToolset();
// Link Graphics Edit Tab
absl::Status UpdateLinkGfxView();
// Prototype Graphics Viewer
absl::Status UpdateScadView();
// Import Functions
absl::Status DrawCgxImport();
absl::Status DrawScrImport();
absl::Status DrawFileImport();
absl::Status DrawObjImport();
absl::Status DrawTilemapImport();
// Other Functions
absl::Status DrawToolset();
absl::Status DrawPaletteControls();
absl::Status DrawClipboardImport();
absl::Status DrawExperimentalFeatures();
absl::Status DrawMemoryEditor();
absl::Status DecompressImportData(int size);
absl::Status DecompressSuperDonkey();
// Member Variables
ImVec4 current_color_;
uint16_t current_sheet_ = 0;
uint8_t tile_size_ = 0x01;
std::set<uint16_t> open_sheets_;
std::set<uint16_t> child_window_sheets_;
std::stack<uint16_t> release_queue_;
uint64_t edit_palette_group_name_index_ = 0;
uint64_t edit_palette_group_index_ = 0;
uint64_t edit_palette_index_ = 0;
uint64_t edit_palette_sub_index_ = 0;
float sheet_scale_ = 2.0f;
float current_scale_ = 4.0f;
// Prototype Graphics Viewer
int current_palette_ = 0;
uint64_t current_offset_ = 0;
uint64_t current_size_ = 0;
uint64_t current_palette_index_ = 0;
int current_bpp_ = 0;
int scr_mod_value_ = 0;
uint64_t num_sheets_to_load_ = 1;
uint64_t bin_size_ = 0;
uint64_t clipboard_offset_ = 0;
uint64_t clipboard_size_ = 0;
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] = "";
char col_file_name_[256] = "";
char cgx_file_path_[256] = "";
char cgx_file_name_[256] = "";
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] = "";
GfxEditMode gfx_edit_mode_ = GfxEditMode::kSelect;
ROM temp_rom_;
ROM tilemap_rom_;
zelda3::Overworld overworld_;
MemoryEditor cgx_memory_editor_;
MemoryEditor col_memory_editor_;
PaletteEditor palette_editor_;
Bytes import_data_;
Bytes graphics_buffer_;
std::vector<uint8_t> decoded_cgx_;
std::vector<uint8_t> cgx_data_;
std::vector<uint8_t> extra_cgx_data_;
std::vector<SDL_Color> decoded_col_;
std::vector<uint8_t> scr_data_;
std::vector<uint8_t> decoded_scr_data_;
gfx::Bitmap cgx_bitmap_;
gfx::Bitmap scr_bitmap_;
gfx::Bitmap bin_bitmap_;
gfx::Bitmap link_full_sheet_;
gfx::BitmapTable graphics_bin_;
gfx::BitmapTable clipboard_graphics_bin_;
gfx::BitmapTable link_graphics_;
gfx::PaletteGroup col_file_palette_group_;
gfx::SNESPalette z3_rom_palette_;
gfx::SNESPalette col_file_palette_;
gfx::SNESPalette link_palette_;
gui::Canvas import_canvas_;
gui::Canvas scr_canvas_;
gui::Canvas super_donkey_canvas_;
gui::Canvas current_sheet_canvas_;
absl::Status status_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_GRAPHICS_EDITOR_H

View File

@@ -7,18 +7,27 @@
#include <imgui_memory_editor.h>
#include "absl/status/status.h"
#include "app/core/common.h"
#include "app/core/constants.h"
#include "app/editor/assembly_editor.h"
#include "app/core/platform/file_dialog.h"
#include "app/editor/dungeon_editor.h"
#include "app/editor/music_editor.h"
#include "app/editor/graphics_editor.h"
#include "app/editor/modules/assembly_editor.h"
#include "app/editor/modules/music_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/screen_editor.h"
#include "app/editor/sprite_editor.h"
#include "app/emu/emulator.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/gui/style.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "gui/widgets.h"
namespace yaze {
namespace app {
@@ -32,9 +41,9 @@ constexpr ImGuiWindowFlags kMainEditorFlags =
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar;
void NewMasterFrame() {
const ImGuiIO &io = ImGui::GetIO();
const ImGuiIO& io = ImGui::GetIO();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowPos(gui::kZeroPos);
ImVec2 dimensions(io.DisplaySize.x, io.DisplaySize.y);
ImGui::SetNextWindowSize(dimensions, ImGuiCond_Always);
@@ -44,8 +53,8 @@ void NewMasterFrame() {
}
}
bool BeginCentered(const char *name) {
ImGuiIO const &io = ImGui::GetIO();
bool BeginCentered(const char* name) {
ImGuiIO const& io = ImGui::GetIO();
ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGuiWindowFlags flags =
@@ -54,30 +63,65 @@ bool BeginCentered(const char *name) {
return ImGui::Begin(name, nullptr, flags);
}
void DisplayStatus(absl::Status &status) {
if (BeginCentered("StatusWindow")) {
ImGui::Text("%s", status.ToString().c_str());
ImGui::Spacing();
ImGui::NextColumn();
ImGui::Columns(1);
ImGui::Separator();
ImGui::NewLine();
ImGui::SameLine(270);
if (ImGui::Button("OK", ImVec2(200, 0))) {
status = absl::OkStatus();
class RecentFilesManager {
public:
RecentFilesManager(const std::string& filename) : filename_(filename) {}
void AddFile(const std::string& filePath) {
// Add a file to the list, avoiding duplicates
auto it = std::find(recentFiles_.begin(), recentFiles_.end(), filePath);
if (it == recentFiles_.end()) {
recentFiles_.push_back(filePath);
}
ImGui::End();
}
}
void Save() {
std::ofstream file(filename_);
if (!file.is_open()) {
return; // Handle the error appropriately
}
for (const auto& filePath : recentFiles_) {
file << filePath << std::endl;
}
}
void Load() {
std::ifstream file(filename_);
if (!file.is_open()) {
return; // Handle the error appropriately
}
recentFiles_.clear();
std::string line;
while (std::getline(file, line)) {
if (!line.empty()) {
recentFiles_.push_back(line);
}
}
}
const std::vector<std::string>& GetRecentFiles() const {
return recentFiles_;
}
private:
std::string filename_;
std::vector<std::string> recentFiles_;
};
} // namespace
using ImGui::BeginMenu;
using ImGui::MenuItem;
using ImGui::Text;
void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer) {
sdl_renderer_ = renderer;
rom_.SetupRenderer(renderer);
rom()->SetupRenderer(renderer);
}
void MasterEditor::UpdateScreen() {
absl::Status MasterEditor::Update() {
NewMasterFrame();
DrawYazeMenu();
@@ -86,34 +130,72 @@ void MasterEditor::UpdateScreen() {
DrawAboutPopup();
DrawInfoPopup();
if (rom()->isLoaded() && !rom_assets_loaded_) {
// Initialize overworld graphics, maps, and palettes
RETURN_IF_ERROR(overworld_editor_.LoadGraphics());
rom_assets_loaded_ = true;
}
TAB_BAR("##TabBar")
DrawOverworldEditor();
DrawDungeonEditor();
DrawMusicEditor();
DrawSpriteEditor();
DrawScreenEditor();
DrawPaletteEditor();
gui::RenderTabItem("Overworld", [&]() {
current_editor_ = &overworld_editor_;
status_ = overworld_editor_.Update();
});
gui::RenderTabItem("Dungeon", [&]() {
current_editor_ = &dungeon_editor_;
status_ = dungeon_editor_.Update();
});
gui::RenderTabItem("Graphics",
[&]() { status_ = graphics_editor_.Update(); });
gui::RenderTabItem("Sprites", [&]() { status_ = sprite_editor_.Update(); });
gui::RenderTabItem("Palettes", [&]() { status_ = palette_editor_.Update(); });
gui::RenderTabItem("Screens", [&]() { screen_editor_.Update(); });
gui::RenderTabItem("Music", [&]() { music_editor_.Update(); });
END_TAB_BAR()
ImGui::End();
return absl::OkStatus();
}
void MasterEditor::DrawFileDialog() {
if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) {
if (ImGuiFileDialog::Instance()->IsOk()) {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
status_ = rom_.LoadFromFile(filePathName);
overworld_editor_.SetupROM(rom_);
screen_editor_.SetupROM(rom_);
palette_editor_.SetupROM(rom_);
}
ImGuiFileDialog::Instance()->Close();
}
gui::FileDialogPipeline("ChooseFileDlgKey", ".sfc,.smc", std::nullopt, [&]() {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
status_ = rom()->LoadFromFile(filePathName);
static RecentFilesManager manager("recent_files.txt");
// Load existing recent files
manager.Load();
// Add a new file
manager.AddFile(filePathName);
// Save the updated list
manager.Save();
});
}
void MasterEditor::DrawStatusPopup() {
if (!status_.ok()) {
DisplayStatus(status_);
show_status_ = true;
prev_status_ = status_;
}
if (show_status_ && (BeginCentered("StatusWindow"))) {
Text("%s", prev_status_.ToString().c_str());
ImGui::Spacing();
ImGui::NextColumn();
ImGui::Columns(1);
ImGui::Separator();
ImGui::NewLine();
ImGui::SameLine(270);
if (ImGui::Button("OK", gui::kDefaultModalSize)) {
show_status_ = false;
}
ImGui::End();
}
}
@@ -121,13 +203,13 @@ void MasterEditor::DrawAboutPopup() {
if (about_) ImGui::OpenPopup("About");
if (ImGui::BeginPopupModal("About", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Yet Another Zelda3 Editor - v0.02");
ImGui::Text("Written by: scawful");
Text("Yet Another Zelda3 Editor - v0.05");
Text("Written by: scawful");
ImGui::Spacing();
ImGui::Text("Special Thanks: Zarby89, JaredBrian");
Text("Special Thanks: Zarby89, JaredBrian");
ImGui::Separator();
if (ImGui::Button("Close", ImVec2(200, 0))) {
if (ImGui::Button("Close", gui::kDefaultModalSize)) {
about_ = false;
ImGui::CloseCurrentPopup();
}
@@ -139,10 +221,10 @@ void MasterEditor::DrawInfoPopup() {
if (rom_info_) ImGui::OpenPopup("ROM Information");
if (ImGui::BeginPopupModal("ROM Information", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Title: %s", rom_.GetTitle());
ImGui::Text("ROM Size: %ld", rom_.size());
Text("Title: %s", rom()->title());
Text("ROM Size: %ld", rom()->size());
if (ImGui::Button("Close", ImVec2(200, 0))) {
if (ImGui::Button("Close", gui::kDefaultModalSize)) {
rom_info_ = false;
ImGui::CloseCurrentPopup();
}
@@ -151,52 +233,145 @@ void MasterEditor::DrawInfoPopup() {
}
void MasterEditor::DrawYazeMenu() {
static bool show_display_settings = false;
static bool show_command_line_interface = false;
MENU_BAR()
DrawFileMenu();
DrawEditMenu();
DrawViewMenu();
DrawHelpMenu();
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetStyle().ItemSpacing.x -
ImGui::CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 150);
// Modify the style of the button to have no background color
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
if (ImGui::Button(ICON_MD_DISPLAY_SETTINGS)) {
show_display_settings = !show_display_settings;
}
if (ImGui::Button(ICON_MD_TERMINAL)) {
show_command_line_interface = !show_command_line_interface;
}
ImGui::PopStyleColor();
Text("%s", absl::StrCat("yaze v", core::kYazeVersion).c_str());
END_MENU_BAR()
if (show_display_settings) {
ImGui::Begin("Display Settings", &show_display_settings,
ImGuiWindowFlags_None);
gui::DrawDisplaySettings();
ImGui::End();
}
if (show_command_line_interface) {
ImGui::Begin("Command Line Interface", &show_command_line_interface,
ImGuiWindowFlags_None);
Text("Enter a command:");
ImGui::End();
}
}
void MasterEditor::DrawFileMenu() const {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open", "Ctrl+O")) {
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
".sfc,.smc", ".");
void MasterEditor::DrawFileMenu() {
static bool save_as_menu = false;
if (BeginMenu("File")) {
if (MenuItem("Open", "Ctrl+O")) {
if (flags()->kNewFileDialogWrapper) {
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
PRINT_IF_ERROR(rom()->LoadFromFile(file_name));
static RecentFilesManager manager("recent_files.txt");
manager.Load();
manager.AddFile(file_name);
manager.Save();
} else {
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
".sfc,.smc", ".");
}
}
MENU_ITEM2("Save", "Ctrl+S") {}
MENU_ITEM("Save As..") {}
if (BeginMenu("Open Recent")) {
static RecentFilesManager manager("recent_files.txt");
manager.Load();
if (manager.GetRecentFiles().empty()) {
MenuItem("No Recent Files", nullptr, false, false);
} else {
for (const auto& filePath : manager.GetRecentFiles()) {
if (MenuItem(filePath.c_str())) {
status_ = rom()->LoadFromFile(filePath);
}
}
}
ImGui::EndMenu();
}
MENU_ITEM2("Save", "Ctrl+S") { status_ = rom()->SaveToFile(backup_rom_); }
MENU_ITEM("Save As..") { save_as_menu = true; }
if (rom()->isLoaded()) {
MENU_ITEM("Reload") { status_ = rom()->Reload(); }
MENU_ITEM("Close") { status_ = rom()->Close(); }
}
ImGui::Separator();
// TODO: Make these options matter
if (ImGui::BeginMenu("Options")) {
static bool enabled = true;
ImGui::MenuItem("Enabled", "", &enabled);
ImGui::BeginChild("child", ImVec2(0, 60), true);
for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i);
ImGui::EndChild();
static float f = 0.5f;
static int n = 0;
ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
ImGui::InputFloat("Input", &f, 0.1f);
ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
if (BeginMenu("Options")) {
MenuItem("Backup ROM", "", &backup_rom_);
ImGui::Separator();
Text("Experiment Flags");
ImGui::Checkbox("Enable Texture Streaming",
&mutable_flags()->kLoadTexturesAsStreaming);
ImGui::Checkbox("Enable Overworld Sprites",
&mutable_flags()->kDrawOverworldSprites);
ImGui::Checkbox("Use Bitmap Manager",
&mutable_flags()->kUseBitmapManager);
ImGui::Checkbox("Log Instructions to Debugger",
&mutable_flags()->kLogInstructions);
ImGui::Checkbox("Use New ImGui Input",
&mutable_flags()->kUseNewImGuiInput);
ImGui::Checkbox("Save All Palettes", &mutable_flags()->kSaveAllPalettes);
ImGui::Checkbox("Save With Change Queue",
&mutable_flags()->kSaveWithChangeQueue);
ImGui::Checkbox("Draw Dungeon Room Graphics",
&mutable_flags()->kDrawDungeonRoomGraphics);
ImGui::EndMenu();
}
ImGui::Separator();
if (MenuItem("Quit", "Ctrl+Q")) {
// TODO: Implement quit confirmation dialog.
}
ImGui::EndMenu();
}
if (save_as_menu) {
static std::string save_as_filename = "";
ImGui::Begin("Save As..", &save_as_menu, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::InputText("Filename", &save_as_filename);
if (ImGui::Button("Save", gui::kDefaultModalSize)) {
status_ = rom()->SaveToFile(backup_rom_, save_as_filename);
save_as_menu = false;
}
ImGui::SameLine();
if (ImGui::Button("Cancel", gui::kDefaultModalSize)) {
save_as_menu = false;
}
ImGui::End();
}
}
void MasterEditor::DrawEditMenu() {
if (ImGui::BeginMenu("Edit")) {
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = overworld_editor_.Undo(); }
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = overworld_editor_.Redo(); }
if (BeginMenu("Edit")) {
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = current_editor_->Undo(); }
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = current_editor_->Redo(); }
ImGui::Separator();
MENU_ITEM2("Cut", "Ctrl+X") { status_ = overworld_editor_.Cut(); }
MENU_ITEM2("Copy", "Ctrl+C") { status_ = overworld_editor_.Copy(); }
MENU_ITEM2("Paste", "Ctrl+V") { status_ = overworld_editor_.Paste(); }
MENU_ITEM2("Cut", "Ctrl+X") { status_ = current_editor_->Cut(); }
MENU_ITEM2("Copy", "Ctrl+C") { status_ = current_editor_->Copy(); }
MENU_ITEM2("Paste", "Ctrl+V") { status_ = current_editor_->Paste(); }
ImGui::Separator();
MENU_ITEM2("Find", "Ctrl+F") {}
ImGui::Separator();
@@ -207,11 +382,18 @@ void MasterEditor::DrawEditMenu() {
void MasterEditor::DrawViewMenu() {
static bool show_imgui_metrics = false;
static bool show_imgui_style_editor = false;
static bool show_memory_editor = false;
static bool show_asm_editor = false;
static bool show_imgui_demo = false;
static bool show_memory_viewer = false;
static bool show_palette_editor = false;
static bool show_emulator = false;
if (show_emulator) {
ImGui::Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
emulator_.Run();
ImGui::End();
}
if (show_imgui_metrics) {
ImGui::ShowMetricsWindow(&show_imgui_metrics);
@@ -219,7 +401,7 @@ void MasterEditor::DrawViewMenu() {
if (show_memory_editor) {
static MemoryEditor mem_edit;
mem_edit.DrawWindow("Memory Editor", (void *)&rom_, rom_.size());
mem_edit.DrawWindow("Memory Editor", (void*)&(*rom()), rom()->size());
}
if (show_imgui_demo) {
@@ -227,12 +409,12 @@ void MasterEditor::DrawViewMenu() {
}
if (show_asm_editor) {
assembly_editor_.Update();
assembly_editor_.Update(show_asm_editor);
}
if (show_imgui_style_editor) {
ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor);
ImGui::ShowStyleEditor();
if (show_palette_editor) {
ImGui::Begin("Palette Editor", &show_palette_editor);
status_ = palette_editor_.Update();
ImGui::End();
}
@@ -250,7 +432,7 @@ void MasterEditor::DrawViewMenu() {
ImGui::TableNextRow();
for (int column = 0; column < 3; column++) {
ImGui::TableNextColumn();
ImGui::Text("Cell %d,%d", column, row);
Text("Cell %d,%d", column, row);
}
}
ImGui::EndTable();
@@ -259,62 +441,42 @@ void MasterEditor::DrawViewMenu() {
ImGui::End();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("HEX Editor", nullptr, &show_memory_editor);
ImGui::MenuItem("ASM Editor", nullptr, &show_asm_editor);
ImGui::MenuItem("Memory Viewer", nullptr, &show_memory_viewer);
ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo);
ImGui::Separator();
if (ImGui::BeginMenu("GUI Tools")) {
ImGui::MenuItem("Metrics (ImGui)", nullptr, &show_imgui_metrics);
ImGui::MenuItem("Style Editor (ImGui)", nullptr,
&show_imgui_style_editor);
ImGui::EndMenu();
}
if (BeginMenu("View")) {
MenuItem("Emulator", nullptr, &show_emulator);
MenuItem("HEX Editor", nullptr, &show_memory_editor);
MenuItem("ASM Editor", nullptr, &show_asm_editor);
MenuItem("Palette Editor", nullptr, &show_palette_editor);
MenuItem("Memory Viewer", nullptr, &show_memory_viewer);
MenuItem("ImGui Demo", nullptr, &show_imgui_demo);
MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
ImGui::EndMenu();
}
}
void MasterEditor::DrawHelpMenu() {
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About")) about_ = true;
static bool open_rom_help = false;
if (BeginMenu("Help")) {
if (MenuItem("How to open a ROM")) open_rom_help = true;
if (MenuItem("About")) about_ = true;
ImGui::EndMenu();
}
}
void MasterEditor::DrawOverworldEditor() {
TAB_ITEM("Overworld")
status_ = overworld_editor_.Update();
END_TAB_ITEM()
}
if (open_rom_help) ImGui::OpenPopup("Open a ROM");
if (ImGui::BeginPopupModal("Open a ROM", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
Text("File -> Open");
Text("Select a ROM file to open");
Text("Supported ROMs (headered or unheadered):");
Text("The Legend of Zelda: A Link to the Past");
Text("US Version 1.0");
Text("JP Version 1.0");
void MasterEditor::DrawDungeonEditor() {
TAB_ITEM("Dungeon")
dungeon_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawPaletteEditor() {
TAB_ITEM("Palettes")
status_ = palette_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawScreenEditor() {
TAB_ITEM("Screens")
screen_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawMusicEditor() {
TAB_ITEM("Music")
music_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawSpriteEditor() {
TAB_ITEM("Sprites")
END_TAB_ITEM()
if (ImGui::Button("Close", gui::kDefaultModalSize)) {
open_rom_help = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
} // namespace editor

View File

@@ -8,28 +8,40 @@
#include <imgui_memory_editor.h>
#include "absl/status/status.h"
#include "app/core/common.h"
#include "app/core/constants.h"
#include "app/editor/assembly_editor.h"
#include "app/gui/pipeline.h"
#include "app/editor/context/gfx_context.h"
#include "app/editor/dungeon_editor.h"
#include "app/editor/music_editor.h"
#include "app/editor/graphics_editor.h"
#include "app/editor/modules/assembly_editor.h"
#include "app/editor/modules/music_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/palette_editor.h"
#include "app/editor/screen_editor.h"
#include "app/editor/sprite_editor.h"
#include "app/emu/emulator.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
namespace yaze {
namespace app {
namespace editor {
class MasterEditor {
class MasterEditor : public SharedROM,
public GfxContext,
public core::ExperimentFlags {
public:
MasterEditor() { current_editor_ = &overworld_editor_; }
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer);
void UpdateScreen();
absl::Status Update();
void Shutdown() { overworld_editor_.Shutdown(); }
private:
void DrawFileDialog();
@@ -38,31 +50,34 @@ class MasterEditor {
void DrawInfoPopup();
void DrawYazeMenu();
void DrawFileMenu() const;
void DrawFileMenu();
void DrawEditMenu();
void DrawViewMenu();
void DrawHelpMenu();
void DrawOverworldEditor();
void DrawDungeonEditor();
void DrawPaletteEditor();
void DrawMusicEditor();
void DrawScreenEditor();
void DrawSpriteEditor();
bool about_ = false;
bool rom_info_ = false;
bool backup_rom_ = true;
bool show_status_ = false;
bool rom_assets_loaded_ = false;
absl::Status status_;
absl::Status prev_status_;
std::shared_ptr<SDL_Renderer> sdl_renderer_;
absl::Status status_;
emu::Emulator emulator_;
AssemblyEditor assembly_editor_;
DungeonEditor dungeon_editor_;
GraphicsEditor graphics_editor_;
MusicEditor music_editor_;
OverworldEditor overworld_editor_;
PaletteEditor palette_editor_;
ScreenEditor screen_editor_;
MusicEditor music_editor_;
ROM rom_;
SpriteEditor sprite_editor_;
Editor *current_editor_ = nullptr;
};
} // namespace editor

View File

@@ -1,19 +1,19 @@
#include "assembly_editor.h"
#include "app/gui/widgets.h"
#include "core/constants.h"
#include "gui/widgets.h"
namespace yaze {
namespace app {
namespace editor {
AssemblyEditor::AssemblyEditor() {
text_editor_.SetLanguageDefinition(gui::widgets::GetAssemblyLanguageDef());
text_editor_.SetLanguageDefinition(gui::GetAssemblyLanguageDef());
text_editor_.SetPalette(TextEditor::GetDarkPalette());
}
void AssemblyEditor::Update() {
ImGui::Begin("Assembly Editor", &file_is_loaded_);
void AssemblyEditor::Update(bool& is_loaded) {
ImGui::Begin("Assembly Editor", &is_loaded);
MENU_BAR()
DrawFileMenu();
DrawEditMenu();
@@ -32,8 +32,6 @@ void AssemblyEditor::Update() {
ImGui::End();
}
void AssemblyEditor::InlineUpdate() {
ChangeActiveFile("assets/asm/template_song.asm");
auto cpos = text_editor_.GetCursorPosition();
@@ -48,10 +46,49 @@ void AssemblyEditor::InlineUpdate() {
text_editor_.Render("##asm_editor", ImVec2(0, 0));
}
void AssemblyEditor::ChangeActiveFile(const std::string& filename) {
void AssemblyEditor::ChangeActiveFile(const std::string_view& filename) {
current_file_ = filename;
}
void AssemblyEditor::DrawFileView() {
ImGui::BeginTable("##table_view", 4,
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg);
// Table headers
ImGui::TableSetupColumn("Files", ImGuiTableColumnFlags_WidthFixed, 150.0f);
ImGui::TableSetupColumn("Line", ImGuiTableColumnFlags_WidthFixed, 60.0f);
ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 100.0f);
ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableHeadersRow();
// Table data
ImGui::TableNextRow();
ImGui::TableNextColumn();
// TODO: Add tree view of files
ImGui::TableNextColumn();
// TODO: Add line number
ImGui::TableNextColumn();
// TODO: Add address per line
ImGui::TableNextColumn();
auto cpos = text_editor_.GetCursorPosition();
SetEditorText();
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
cpos.mColumn + 1, text_editor_.GetTotalLines(),
text_editor_.IsOverwrite() ? "Ovr" : "Ins",
text_editor_.CanUndo() ? "*" : " ",
text_editor_.GetLanguageDefinition().mName.c_str(),
current_file_.c_str());
text_editor_.Render("##asm_editor");
ImGui::EndTable();
}
void AssemblyEditor::DrawFileMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open", "Ctrl+O")) {

View File

@@ -16,13 +16,16 @@ class AssemblyEditor {
public:
AssemblyEditor();
void Update();
void Update(bool &is_loaded);
void InlineUpdate();
void ChangeActiveFile(const std::string &);
void ChangeActiveFile(const std::string_view &);
private:
void DrawFileMenu();
void DrawEditMenu();
void DrawFileView();
void SetEditorText();
bool file_is_loaded_ = false;

View File

@@ -0,0 +1,182 @@
#include "gfx_group_editor.h"
#include <imgui/imgui.h>
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
namespace yaze {
namespace app {
namespace editor {
using ImGui::SameLine;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
absl::Status GfxGroupEditor::Update() {
if (ImGui::BeginTabBar("GfxGroupEditor")) {
if (ImGui::BeginTabItem("Main")) {
gui::InputHexByte("Selected Blockset", &selected_blockset_);
ImGui::Text("Values");
if (ImGui::BeginTable("##BlocksetTable", 2, ImGuiTableFlags_Borders,
ImVec2(0, 0))) {
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
TableHeadersRow();
TableNextRow();
TableNextColumn();
{
ImGui::BeginGroup();
for (int i = 0; i < 8; i++) {
ImGui::SetNextItemWidth(100.f);
gui::InputHexByte(("##blockset0" + std::to_string(i)).c_str(),
&rom()->main_blockset_ids[selected_blockset_][i]);
if (i != 3 && i != 7) {
SameLine();
}
}
ImGui::EndGroup();
}
TableNextColumn();
{
ImGui::BeginGroup();
for (int i = 0; i < 8; i++) {
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
auto &sheet = *rom()->bitmap_manager()[sheet_id];
if (sheet_id != last_sheet_id_) {
last_sheet_id_ = sheet_id;
auto palette_group = rom()->palette_group("ow_main");
auto palette = palette_group[preview_palette_id_];
sheet.ApplyPalette(palette);
rom()->UpdateBitmap(&sheet);
}
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 22);
}
ImGui::EndGroup();
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Rooms")) {
gui::InputHexByte("Selected Blockset", &selected_roomset_);
ImGui::Text("Values - Overwrites 4 of main blockset");
if (ImGui::BeginTable("##Roomstable", 2, ImGuiTableFlags_Borders,
ImVec2(0, 0))) {
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
TableHeadersRow();
TableNextRow();
TableNextColumn();
{
ImGui::BeginGroup();
for (int i = 0; i < 4; i++) {
ImGui::SetNextItemWidth(100.f);
gui::InputHexByte(("##roomset0" + std::to_string(i)).c_str(),
&rom()->room_blockset_ids[selected_roomset_][i]);
if (i != 3 && i != 7) {
SameLine();
}
}
ImGui::EndGroup();
}
TableNextColumn();
{
ImGui::BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
auto &sheet = *rom()->bitmap_manager()[sheet_id];
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 23);
}
ImGui::EndGroup();
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Sprites")) {
gui::InputHexByte("Selected Spriteset", &selected_spriteset_);
ImGui::Text("Values");
if (ImGui::BeginTable("##SpritesTable", 2, ImGuiTableFlags_Borders,
ImVec2(0, 0))) {
TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed, 256);
TableHeadersRow();
TableNextRow();
TableNextColumn();
{
ImGui::BeginGroup();
for (int i = 0; i < 4; i++) {
ImGui::SetNextItemWidth(100.f);
gui::InputHexByte(("##spriteset0" + std::to_string(i)).c_str(),
&rom()->spriteset_ids[selected_spriteset_][i]);
if (i != 3 && i != 7) {
SameLine();
}
}
ImGui::EndGroup();
}
TableNextColumn();
{
ImGui::BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
auto sheet = *rom()->bitmap_manager()[sheet_id];
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256,
0x10 * 0x04, 0x20, true, false, 24);
}
ImGui::EndGroup();
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Palettes")) {
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::Separator();
ImGui::Text("Palette: ");
ImGui::InputInt("##PreviewPaletteID", &preview_palette_id_);
return absl::OkStatus();
}
void GfxGroupEditor::InitBlockset(gfx::Bitmap tile16_blockset) {
tile16_blockset_bmp_ = tile16_blockset;
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,57 @@
#ifndef YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
#define YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
#include <imgui/imgui.h>
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/gui/pipeline.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
namespace yaze {
namespace app {
namespace editor {
class GfxGroupEditor : public SharedROM {
public:
absl::Status Update();
void InitBlockset(gfx::Bitmap tile16_blockset);
private:
int preview_palette_id_ = 0;
int last_sheet_id_ = 0;
uint8_t selected_blockset_ = 0;
uint8_t selected_roomset_ = 0;
uint8_t selected_spriteset_ = 0;
gui::Canvas blockset_canvas_;
gui::Canvas roomset_canvas_;
gui::Canvas spriteset_canvas_;
gfx::SNESPalette palette_;
gfx::PaletteGroup palette_group_;
gfx::Bitmap tile16_blockset_bmp_;
std::vector<Bytes> tile16_individual_data_;
std::vector<gfx::Bitmap> tile16_individual_;
gui::BitmapViewer gfx_group_viewer_;
zelda3::Overworld overworld_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H

View File

@@ -4,13 +4,13 @@
#include <imgui/imgui.h>
#include "absl/strings/str_format.h"
#include "app/editor/assembly_editor.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "snes_spc/demo/demo_util.h"
#include "snes_spc/demo/wave_writer.h"
#include "snes_spc/snes_spc/spc.h"
#include "app/editor/modules/assembly_editor.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
// #include "snes_spc/demo/demo_util.h"
// #include "snes_spc/demo/wave_writer.h"
// #include "snes_spc/snes_spc/spc.h"
namespace yaze {
namespace app {
@@ -21,46 +21,46 @@ namespace {
#define BUF_SIZE 2048
void PlaySPC() {
/* Create emulator and filter */
SNES_SPC* snes_spc = spc_new();
SPC_Filter* filter = spc_filter_new();
if (!snes_spc || !filter) error("Out of memory");
// /* Create emulator and filter */
// SNES_SPC* snes_spc = spc_new();
// SPC_Filter* filter = spc_filter_new();
// if (!snes_spc || !filter) error("Out of memory");
/* Load SPC */
{
/* Load file into memory */
long spc_size;
void* spc = load_file("assets/music/hyrule_field.spc", &spc_size);
// /* Load SPC */
// {
// /* Load file into memory */
// long spc_size;
// void* spc = load_file("assets/music/hyrule_field.spc", &spc_size);
/* Load SPC data into emulator */
error(spc_load_spc(snes_spc, spc, spc_size));
free(spc); /* emulator makes copy of data */
// /* Load SPC data into emulator */
// error(spc_load_spc(snes_spc, spc, spc_size));
// free(spc); /* emulator makes copy of data */
/* Most SPC files have garbage data in the echo buffer, so clear that */
spc_clear_echo(snes_spc);
// /* Most SPC files have garbage data in the echo buffer, so clear that */
// spc_clear_echo(snes_spc);
/* Clear filter before playing */
spc_filter_clear(filter);
}
// /* Clear filter before playing */
// spc_filter_clear(filter);
// }
/* Record 20 seconds to wave file */
wave_open(spc_sample_rate, "out.wav");
wave_enable_stereo();
while (wave_sample_count() < 30 * spc_sample_rate * 2) {
/* Play into buffer */
short buf[BUF_SIZE];
error(spc_play(snes_spc, BUF_SIZE, buf));
// /* Record 20 seconds to wave file */
// wave_open(spc_sample_rate, "out.wav");
// wave_enable_stereo();
// while (wave_sample_count() < 30 * spc_sample_rate * 2) {
// /* Play into buffer */
// short buf[BUF_SIZE];
// error(spc_play(snes_spc, BUF_SIZE, buf));
/* Filter samples */
spc_filter_run(filter, buf, BUF_SIZE);
// /* Filter samples */
// spc_filter_run(filter, buf, BUF_SIZE);
wave_write(buf, BUF_SIZE);
}
// wave_write(buf, BUF_SIZE);
// }
/* Cleanup */
spc_filter_delete(filter);
spc_delete(snes_spc);
wave_close();
// /* Cleanup */
// spc_filter_delete(filter);
// spc_delete(snes_spc);
// wave_close();
}
} // namespace
@@ -223,10 +223,10 @@ void MusicEditor::DrawToolset() {
if (is_playing) {
if (!has_loaded_song) {
PlaySPC();
current_song_ = Mix_LoadMUS("out.wav");
Mix_PlayMusic(current_song_, -1);
has_loaded_song = true;
// PlaySPC();
// current_song_ = Mix_LoadMUS("out.wav");
// Mix_PlayMusic(current_song_, -1);
// has_loaded_song = true;
}
// // If there is no music playing
@@ -252,11 +252,13 @@ void MusicEditor::DrawToolset() {
ImGui::Combo("#songs_in_game", &selected_option, kGameSongs, 30);
gui::ItemLabel("Controls: ", gui::ItemLabelFlags::Left);
if (ImGui::BeginTable("SongToolset", 5, toolset_table_flags_, ImVec2(0, 0))) {
if (ImGui::BeginTable("SongToolset", 6, toolset_table_flags_, ImVec2(0, 0))) {
ImGui::TableSetupColumn("#play");
ImGui::TableSetupColumn("#rewind");
ImGui::TableSetupColumn("#fastforward");
ImGui::TableSetupColumn("#volume");
ImGui::TableSetupColumn("#debug");
ImGui::TableSetupColumn("#slider");
ImGui::TableNextColumn();
@@ -271,6 +273,9 @@ void MusicEditor::DrawToolset() {
BUTTON_COLUMN(ICON_MD_FAST_REWIND)
BUTTON_COLUMN(ICON_MD_FAST_FORWARD)
BUTTON_COLUMN(ICON_MD_VOLUME_UP)
if (ImGui::Button(ICON_MD_ACCESS_TIME)) {
music_tracker_.LoadSongs(*rom());
}
ImGui::TableNextColumn();
ImGui::SliderInt("Volume", &current_volume, 0, 100);
ImGui::EndTable();

View File

@@ -5,13 +5,15 @@
#include <imgui/imgui.h>
#include "absl/strings/str_format.h"
#include "app/editor/assembly_editor.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "snes_spc/demo/demo_util.h"
#include "snes_spc/demo/wave_writer.h"
#include "snes_spc/snes_spc/spc.h"
#include "app/editor/modules/assembly_editor.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/music/tracker.h"
// #include "snes_spc/demo/demo_util.h"
// #include "snes_spc/demo/wave_writer.h"
// #include "snes_spc/snes_spc/spc.h"
namespace yaze {
namespace app {
@@ -52,7 +54,7 @@ static const char* kGameSongs[] = {"Title",
static constexpr absl::string_view kSongNotes[] = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
class MusicEditor {
class MusicEditor : public SharedROM {
public:
void Update();
@@ -63,7 +65,9 @@ class MusicEditor {
void DrawSongToolset();
void DrawToolset();
Mix_Music* current_song_ = NULL;
zelda3::Tracker music_tracker_;
// Mix_Music* current_song_ = NULL;
AssemblyEditor assembly_editor_;
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;

View File

@@ -0,0 +1,288 @@
#include "palette_editor.h"
#include <imgui/imgui.h>
#include "absl/status/status.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
static inline float ImSaturate(float f) {
return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
}
#define IM_F32_TO_INT8_SAT(_VAL) \
((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
int CustomFormatString(char* buf, size_t buf_size, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
#ifdef IMGUI_USE_STB_SPRINTF
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
#else
int w = vsnprintf(buf, buf_size, fmt, args);
#endif
va_end(args);
if (buf == nullptr) return w;
if (w == -1 || w >= (int)buf_size) w = (int)buf_size - 1;
buf[w] = 0;
return w;
}
namespace yaze {
namespace app {
namespace editor {
absl::Status PaletteEditor::Update() {
if (ImGui::BeginTable("paletteEditorTable", 2,
ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame,
ImVec2(0, 0))) {
ImGui::TableSetupColumn("Palette Groups",
ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
ImGui::TableSetupColumn("Editor", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
for (int category = 0; category < kNumPalettes; ++category) {
if (ImGui::TreeNode(kPaletteCategoryNames[category].data())) {
status_ = DrawPaletteGroup(category);
ImGui::TreePop();
}
}
ImGui::TableNextColumn();
ImGui::Text("Test Column");
ImGui::EndTable();
}
CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus();
}
void PaletteEditor::EditColorInPalette(gfx::SNESPalette& palette, int index) {
if (index >= palette.size()) {
// Handle error: the index is out of bounds
return;
}
// Get the current color
auto currentColor = palette.GetColor(index).GetRGB();
if (ImGui::ColorPicker4("Color Picker", (float*)&palette[index])) {
// The color was modified, update it in the palette
palette(index, currentColor);
}
}
void PaletteEditor::ResetColorToOriginal(
gfx::SNESPalette& palette, int index,
const gfx::SNESPalette& originalPalette) {
if (index >= palette.size() || index >= originalPalette.size()) {
// Handle error: the index is out of bounds
return;
}
auto originalColor = originalPalette.GetColor(index).GetRGB();
palette(index, originalColor);
}
absl::Status PaletteEditor::DrawPaletteGroup(int category) {
if (!rom()->isLoaded()) {
return absl::NotFoundError("ROM not open, no palettes to display");
}
const auto size =
rom()->palette_group(kPaletteGroupNames[category].data()).size();
auto palettes = rom()->palette_group(kPaletteGroupNames[category].data());
static bool edit_color = false;
for (int j = 0; j < size; j++) {
ImGui::Text("%d", j);
auto palette = palettes[j];
auto pal_size = palette.size();
for (int n = 0; n < pal_size; n++) {
ImGui::PushID(n);
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
auto popup_id =
absl::StrCat(kPaletteCategoryNames[category].data(), j, "_", n);
// Small icon of the color in the palette
if (gui::SNESColorButton(popup_id, palette[n], palette_button_flags)) {
edit_color = true;
}
if (ImGui::BeginPopupContextItem(popup_id.c_str())) {
RETURN_IF_ERROR(HandleColorPopup(palette, category, j, n))
}
if (edit_color) {
// The color button was clicked, open the popup
if (ImGui::ColorEdit4(popup_id.c_str(),
gfx::ToFloatArray(palette[n]).data(),
palette_button_flags)) {
EditColorInPalette(palette, n);
}
}
ImGui::PopID();
}
}
return absl::OkStatus();
}
absl::Status PaletteEditor::HandleColorPopup(gfx::SNESPalette& palette, int i,
int j, int n) {
auto col = gfx::ToFloatArray(palette[n]);
if (ImGui::ColorEdit4("Edit Color", col.data(), color_popup_flags)) {
RETURN_IF_ERROR(rom()->UpdatePaletteColor(kPaletteGroupNames[i].data(), j,
n, palette[n]))
}
if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy");
if (ImGui::BeginPopup("Copy")) {
int cr = IM_F32_TO_INT8_SAT(col[0]);
int cg = IM_F32_TO_INT8_SAT(col[1]);
int cb = IM_F32_TO_INT8_SAT(col[2]);
char buf[64];
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff)", col[0],
col[1], col[2]);
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d)", cr, cg, cb);
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
CustomFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb);
if (ImGui::Selectable(buf)) ImGui::SetClipboardText(buf);
ImGui::EndPopup();
}
ImGui::EndPopup();
return absl::OkStatus();
}
void PaletteEditor::DisplayPalette(gfx::SNESPalette& palette, bool loaded) {
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
ImGuiColorEditFlags_NoDragDrop |
ImGuiColorEditFlags_NoOptions;
// Generate a default palette. The palette will persist and can be edited.
static bool init = false;
if (loaded && !init) {
InitializeSavedPalette(palette);
init = true;
}
static ImVec4 backup_color;
bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags);
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
open_popup |= ImGui::Button("Palette");
if (open_popup) {
ImGui::OpenPopup("mypicker");
backup_color = color;
}
if (ImGui::BeginPopup("mypicker")) {
TEXT_WITH_SEPARATOR("Current Overworld Palette");
ImGui::ColorPicker4("##picker", (float*)&color,
misc_flags | ImGuiColorEditFlags_NoSidePreview |
ImGuiColorEditFlags_NoSmallPreview);
ImGui::SameLine();
ImGui::BeginGroup(); // Lock X position
ImGui::Text("Current ==>");
ImGui::SameLine();
ImGui::Text("Previous");
if (ImGui::Button("Update Map Palette")) {
}
ImGui::ColorButton(
"##current", color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40));
ImGui::SameLine();
if (ImGui::ColorButton(
"##previous", backup_color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40)))
color = backup_color;
// List of Colors in Overworld Palette
ImGui::Separator();
ImGui::Text("Palette");
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
ImGui::PushID(n);
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
if (ImGui::ColorButton("##palette", saved_palette_[n],
palette_button_flags_2, ImVec2(20, 20)))
color = ImVec4(saved_palette_[n].x, saved_palette_[n].y,
saved_palette_[n].z, color.w); // Preserve alpha!
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
ImGui::EndDragDropTarget();
}
ImGui::PopID();
}
ImGui::EndGroup();
ImGui::EndPopup();
}
}
void PaletteEditor::DrawPortablePalette(gfx::SNESPalette& palette) {
static bool init = false;
if (!init) {
InitializeSavedPalette(palette);
init = true;
}
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)100);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
ImGui::BeginGroup(); // Lock X position
ImGui::Text("Palette");
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
ImGui::PushID(n);
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
if (ImGui::ColorButton("##palette", saved_palette_[n],
palette_button_flags_2, ImVec2(20, 20)))
ImVec4(saved_palette_[n].x, saved_palette_[n].y, saved_palette_[n].z,
1.0f); // Preserve alpha!
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
ImGui::EndDragDropTarget();
}
ImGui::PopID();
}
ImGui::EndGroup();
}
ImGui::EndChild();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,122 @@
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
#include <imgui/imgui.h>
#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 {
constexpr int kNumPalettes = 11;
static constexpr absl::string_view kPaletteCategoryNames[] = {
"Sword", "Shield", "Clothes", "World Colors",
"Area Colors", "Enemies", "Dungeons", "World Map",
"Dungeon Map", "Triforce", "Crystal"};
static constexpr absl::string_view kPaletteGroupNames[] = {
"swords", "shields", "armors", "ow_main",
"ow_aux", "global_sprites", "dungeon_main", "ow_mini_map",
"ow_mini_map", "3d_object", "3d_object"};
struct PaletteChange {
std::string groupName;
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<PaletteChange>& 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<PaletteChange> recentChanges;
static const size_t maxHistorySize = 50; // or any other number you deem fit
};
class PaletteEditor : public SharedROM {
public:
absl::Status Update();
absl::Status DrawPaletteGroups();
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);
private:
absl::Status DrawPaletteGroup(int category);
absl::Status HandleColorPopup(gfx::SNESPalette& palette, int i, int j, int n);
void InitializeSavedPalette(const gfx::SNESPalette& palette) {
for (int n = 0; n < palette.size(); n++) {
saved_palette_[n].x = palette.GetColor(n).GetRGB().x / 255;
saved_palette_[n].y = palette.GetColor(n).GetRGB().y / 255;
saved_palette_[n].z = palette.GetColor(n).GetRGB().z / 255;
saved_palette_[n].w = 255; // Alpha
}
}
absl::Status status_;
PaletteEditorHistory history_;
ImVec4 saved_palette_[256] = {};
ImVec4 current_color_;
ImGuiColorEditFlags color_popup_flags =
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha;
ImGuiColorEditFlags palette_button_flags =
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoTooltip;
ImGuiColorEditFlags palette_button_flags_2 = ImGuiColorEditFlags_NoAlpha |
ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif

View File

@@ -0,0 +1,298 @@
#include "tile16_editor.h"
#include <imgui/imgui.h>
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
namespace yaze {
namespace app {
namespace editor {
using ImGui::BeginChild;
using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
using ImGui::BeginTable;
using ImGui::Combo;
using ImGui::EndChild;
using ImGui::EndTabBar;
using ImGui::EndTabItem;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
absl::Status Tile16Editor::Update() {
// Create a tab for Tile16 Editing
static bool start_task = false;
if (ImGui::Button("Test")) {
start_task = true;
}
if (start_task && !map_blockset_loaded_) {
LoadTile8();
}
// Create a tab bar for Tile16 Editing and Tile16 Transfer
if (BeginTabBar("Tile16 Editor Tabs")) {
if (BeginTabItem("Tile16 Editing")) {
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
TableSetupColumn("Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableHeadersRow();
TableNextRow();
TableNextColumn();
RETURN_IF_ERROR(UpdateBlockset());
TableNextColumn();
RETURN_IF_ERROR(UpdateTile16Edit());
ImGui::EndTable();
}
ImGui::EndTabItem();
}
// Create a tab for Tile16 Transfer
if (BeginTabItem("Tile16 Transfer")) {
if (BeginTable("#Tile16TransferTable", 2,
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
ImVec2(0, 0))) {
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x / 2);
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
ImGui::GetContentRegionAvail().x / 2);
TableHeadersRow();
TableNextRow();
TableNextColumn();
RETURN_IF_ERROR(UpdateBlockset());
TableNextColumn();
RETURN_IF_ERROR(UpdateTransferTileCanvas());
ImGui::EndTable();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateBlockset() {
gui::BitmapCanvasPipeline(blockset_canvas_, tile16_blockset_bmp_, 0x100,
(8192 * 2), 0x20, map_blockset_loaded_, true, 55);
if (!blockset_canvas_.Points().empty()) {
uint16_t x = blockset_canvas_.Points().front().x / 32;
uint16_t y = blockset_canvas_.Points().front().y / 32;
notify_tile16.mutable_get() = x + (y * 8);
notify_tile16.apply_changes();
if (notify_tile16.modified()) {
current_tile16_bmp_ = tile16_individual_[notify_tile16];
current_tile16_bmp_.ApplyPalette(
rom()->palette_group("ow_main")[current_palette_]);
rom()->RenderBitmap(&current_tile16_bmp_);
}
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateTile16Edit() {
if (ImGui::BeginChild("Tile8 Selector",
ImVec2(ImGui::GetContentRegionAvail().x, 0x100),
true)) {
tile8_source_canvas_.DrawBackground(
ImVec2(core::kTilesheetWidth * 2, core::kTilesheetHeight * 0x10 * 2));
tile8_source_canvas_.DrawContextMenu();
tile8_source_canvas_.DrawTileSelector(16);
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 2.0f);
tile8_source_canvas_.DrawGrid(16.0f);
tile8_source_canvas_.DrawOverlay();
}
ImGui::EndChild();
if (ImGui::BeginChild("Tile16 Editor Options",
ImVec2(ImGui::GetContentRegionAvail().x, 0x50), true)) {
tile16_edit_canvas_.DrawBackground(ImVec2(0x40, 0x40));
tile16_edit_canvas_.DrawContextMenu();
// if (current_tile8_bmp_.modified()) {
// rom()->UpdateBitmap(&current_tile8_bmp_);
// current_tile8_bmp_.set_modified(false);
// }
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
tile16_edit_canvas_.HandleTileEdits(
tile8_source_canvas_, current_gfx_individual_, current_tile8_bmp_,
current_tile8_, 2.0f);
tile16_edit_canvas_.DrawGrid(128.0f);
tile16_edit_canvas_.DrawOverlay();
}
ImGui::EndChild();
DrawTileEditControls();
return absl::OkStatus();
}
void Tile16Editor::DrawTileEditControls() {
ImGui::Separator();
ImGui::Text("Options:");
gui::InputHexByte("Palette", &notify_palette.mutable_get());
notify_palette.apply_changes();
if (notify_palette.modified()) {
current_gfx_bmp_.ApplyPalette(
rom()->palette_group("ow_main")[notify_palette.get()]);
current_tile16_bmp_.ApplyPalette(
rom()->palette_group("ow_main")[notify_palette.get()]);
rom()->UpdateBitmap(&current_gfx_bmp_);
}
ImGui::Checkbox("X Flip", &x_flip);
ImGui::Checkbox("Y Flip", &y_flip);
ImGui::Checkbox("Priority Tile", &priority_tile);
}
absl::Status Tile16Editor::UpdateTransferTileCanvas() {
// Create a button for loading another ROM
if (ImGui::Button("Load ROM")) {
ImGuiFileDialog::Instance()->OpenDialog(
"ChooseTransferFileDlgKey", "Open Transfer ROM", ".sfc,.smc", ".");
}
gui::FileDialogPipeline(
"ChooseTransferFileDlgKey", ".sfc,.smc", std::nullopt, [&]() {
std::string filePathName =
ImGuiFileDialog::Instance()->GetFilePathName();
transfer_status_ = transfer_rom_.LoadFromFile(filePathName);
transfer_started_ = true;
});
if (transfer_started_ && !transfer_blockset_loaded_) {
PRINT_IF_ERROR(transfer_rom_.LoadAllGraphicsData())
graphics_bin_ = transfer_rom_.graphics_bin();
// Load the Link to the Past overworld.
PRINT_IF_ERROR(transfer_overworld_.Load(transfer_rom_))
transfer_overworld_.SetCurrentMap(0);
palette_ = transfer_overworld_.AreaPalette();
// Create the tile16 blockset image
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x80,
transfer_overworld_.Tile16Blockset(),
*rom(), transfer_blockset_bmp_, palette_);
transfer_blockset_loaded_ = true;
}
// Create a canvas for holding the tiles which will be exported
gui::BitmapCanvasPipeline(transfer_canvas_, transfer_blockset_bmp_, 0x100,
(8192 * 2), 0x20, transfer_blockset_loaded_, true,
3);
return absl::OkStatus();
}
using core::TaskManager;
absl::Status Tile16Editor::InitBlockset(
const gfx::Bitmap& tile16_blockset_bmp, gfx::Bitmap current_gfx_bmp,
const std::vector<gfx::Bitmap>& tile16_individual) {
tile16_blockset_bmp_ = tile16_blockset_bmp;
tile16_individual_ = tile16_individual;
current_gfx_bmp_ = current_gfx_bmp;
return absl::OkStatus();
}
absl::Status Tile16Editor::LoadTile8() {
current_gfx_individual_.reserve(128);
// Define the task function
std::function<void(int)> taskFunc = [&](int index) {
auto current_gfx_data = current_gfx_bmp_.mutable_data();
std::vector<uint8_t> tile_data;
tile_data.reserve(0x40);
for (int i = 0; i < 0x40; i++) {
tile_data.emplace_back(0x00);
}
// Copy the pixel data for the current tile into the vector
for (int ty = 0; ty < 8; ty++) {
for (int tx = 0; tx < 8; tx++) {
int position = tx + (ty * 0x08);
uint8_t value =
current_gfx_data[(index % 16 * 32) + (index / 16 * 32 * 0x80) +
(ty * 0x80) + tx];
tile_data[position] = value;
}
}
current_gfx_individual_.emplace_back();
current_gfx_individual_[index].Create(0x08, 0x08, 0x80, tile_data);
current_gfx_individual_[index].ApplyPalette(
rom()->palette_group("ow_main")[current_palette_]);
rom()->RenderBitmap(&current_gfx_individual_[index]);
};
// Create the task manager
static bool started = false;
if (!started) {
task_manager_ = TaskManager<std::function<void(int)>>(127, 1);
started = true;
}
task_manager_.ExecuteTasks(taskFunc);
if (task_manager_.IsTaskComplete()) {
// All tasks are complete
current_tile8_bmp_ = current_gfx_individual_[0];
map_blockset_loaded_ = true;
}
// auto current_gfx_data = current_gfx_bmp_.mutable_data();
// for (int i = 0; i < 128; i++) {
// std::vector<uint8_t> tile_data(0x40, 0x00);
// Copy the pixel data for the current tile into the vector
// for (int ty = 0; ty < 8; ty++) {
// for (int tx = 0; tx < 8; tx++) {
// int position = tx + (ty * 0x10);
// uint8_t value = current_gfx_data[(i % 16 * 32) + (i / 16 * 32 * 0x80)
// +
// (ty * 0x80) + tx];
// tile_data[position] = value;
// }
// }
// current_gfx_individual_data_.emplace_back(tile_data);
// current_gfx_individual_.emplace_back();
// current_gfx_individual_[i].Create(0x08, 0x08, 0x80, tile_data);
// current_gfx_individual_[i].ApplyPalette(
// rom()->palette_group("ow_main")[current_palette_]);
// rom()->RenderBitmap(&current_gfx_individual_[i]);
// }
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,107 @@
#ifndef YAZE_APP_EDITOR_TILE16EDITOR_H
#define YAZE_APP_EDITOR_TILE16EDITOR_H
#include <imgui/imgui.h>
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/gui/pipeline.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
namespace yaze {
namespace app {
namespace editor {
class Tile16Editor : public SharedROM {
public:
absl::Status Update();
absl::Status UpdateBlockset();
absl::Status UpdateTile16Edit();
void DrawTileEditControls();
absl::Status UpdateTransferTileCanvas();
absl::Status InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
gfx::Bitmap current_gfx_bmp,
const std::vector<gfx::Bitmap>& tile16_individual);
absl::Status LoadTile8();
private:
bool map_blockset_loaded_ = false;
bool transfer_started_ = false;
bool transfer_blockset_loaded_ = false;
int current_tile16_ = 0;
int current_tile8_ = 0;
uint8_t current_palette_ = 0;
core::NotifyValue<uint8_t> notify_tile16;
core::NotifyValue<uint8_t> notify_palette;
// Canvas dimensions
int canvas_width;
int canvas_height;
// Texture ID for the canvas
int texture_id;
// Various options for the Tile16 Editor
bool x_flip;
bool y_flip;
bool priority_tile;
int tile_size;
// Tile16 blockset for selecting the tile to edit
gui::Canvas blockset_canvas_;
gfx::Bitmap tile16_blockset_bmp_;
// Canvas for editing the selected tile
gui::Canvas tile16_edit_canvas_;
gfx::Bitmap current_tile16_bmp_;
gfx::Bitmap current_tile8_bmp_;
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
gui::Canvas tile8_source_canvas_;
gfx::Bitmap current_gfx_bmp_;
gui::Canvas transfer_canvas_;
gfx::Bitmap transfer_blockset_bmp_;
gfx::Bitmap transfer_current_bmp_;
std::vector<Bytes> tile16_individual_data_;
std::vector<gfx::Bitmap> tile16_individual_;
std::vector<gfx::Bitmap> current_gfx_individual_;
std::vector<uint8_t> current_tile16_data_;
PaletteEditor palette_editor_;
gfx::SNESPalette palette_;
zelda3::Overworld transfer_overworld_;
gfx::BitmapTable graphics_bin_;
ROM transfer_rom_;
absl::Status transfer_status_;
core::TaskManager<std::function<void(int)>> task_manager_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_TILE16EDITOR_H

View File

@@ -9,143 +9,312 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/editor/palette_editor.h"
#include "app/core/common.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/pipeline.h"
#include "app/gui/style.h"
#include "app/gui/widgets.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "gui/canvas.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
// ----------------------------------------------------------------------------
using ImGui::Button;
using ImGui::Separator;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
using ImGui::Text;
absl::Status OverworldEditor::Update() {
// Initialize overworld graphics, maps, and palettes
if (rom_.isLoaded() && !all_gfx_loaded_) {
RETURN_IF_ERROR(LoadGraphics())
if (rom()->isLoaded() && !all_gfx_loaded_) {
RETURN_IF_ERROR(tile16_editor_.InitBlockset(
tile16_blockset_bmp_, current_gfx_bmp_, tile16_individual_));
gfx_group_editor_.InitBlockset(tile16_blockset_bmp_);
all_gfx_loaded_ = true;
} else if (!rom()->isLoaded() && all_gfx_loaded_) {
// TODO: Destroy the overworld graphics canvas.
// Reset the editor if the ROM is unloaded
Shutdown();
all_gfx_loaded_ = false;
map_blockset_loaded_ = false;
}
// Draws the toolset for editing the Overworld.
RETURN_IF_ERROR(DrawToolset())
ImGui::Separator();
if (ImGui::BeginTable("#owEditTable", 2, ow_edit_flags, ImVec2(0, 0))) {
ImGui::TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
ImGui::TableSetupColumn("Tile Selector");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginTable(kOWEditTable.data(), 2, kOWEditFlags, ImVec2(0, 0))) {
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
TableSetupColumn("Tile Selector", ImGuiTableColumnFlags_WidthFixed, 256);
TableHeadersRow();
TableNextRow();
TableNextColumn();
DrawOverworldCanvas();
ImGui::TableNextColumn();
TableNextColumn();
DrawTileSelector();
ImGui::EndTable();
}
if (!status_.ok()) {
auto temp = status_;
status_ = absl::OkStatus();
return temp;
}
return absl::OkStatus();
}
// ----------------------------------------------------------------------------
absl::Status OverworldEditor::DrawToolset() {
if (ImGui::BeginTable("OWToolset", 17, toolset_table_flags, ImVec2(0, 0))) {
static bool show_gfx_group = false;
static bool show_tile16 = false;
if (ImGui::BeginTable("OWToolset", 19, kToolsetTableFlags, ImVec2(0, 0))) {
for (const auto &name : kToolsetColumnNames)
ImGui::TableSetupColumn(name.data());
BUTTON_COLUMN(ICON_MD_UNDO) // Undo
BUTTON_COLUMN(ICON_MD_REDO) // Redo
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
BUTTON_COLUMN(ICON_MD_ZOOM_OUT) // Zoom Out
BUTTON_COLUMN(ICON_MD_ZOOM_IN) // Zoom In
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
BUTTON_COLUMN(ICON_MD_DRAW) // Draw Tile
BUTTON_COLUMN(ICON_MD_DOOR_FRONT) // Entrances
BUTTON_COLUMN(ICON_MD_DOOR_BACK) // Exits
BUTTON_COLUMN(ICON_MD_GRASS) // Items
BUTTON_COLUMN(ICON_MD_PEST_CONTROL_RODENT) // Sprites
BUTTON_COLUMN(ICON_MD_ADD_LOCATION) // Transports
BUTTON_COLUMN(ICON_MD_MUSIC_NOTE) // Music
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
ImGui::TableNextColumn(); // Palette
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_UNDO)) {
status_ = Undo();
}
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_REDO)) {
status_ = Redo();
}
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_ZOOM_OUT)) {
ow_map_canvas_.ZoomOut();
}
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_ZOOM_IN)) {
ow_map_canvas_.ZoomIn();
}
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_DRAW)) {
current_mode = EditingMode::DRAW_TILE;
}
HOVER_HINT("Draw Tile")
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_DOOR_FRONT)) {
current_mode = EditingMode::ENTRANCES;
}
HOVER_HINT("Entrances")
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_DOOR_BACK)) {
current_mode = EditingMode::EXITS;
}
HOVER_HINT("Exits")
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_GRASS)) {
current_mode = EditingMode::ITEMS;
}
HOVER_HINT("Items")
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_PEST_CONTROL_RODENT)) {
current_mode = EditingMode::SPRITES;
}
HOVER_HINT("Sprites")
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_ADD_LOCATION)) {
current_mode = EditingMode::TRANSPORTS;
}
HOVER_HINT("Transports")
NEXT_COLUMN()
if (ImGui::Button(ICON_MD_MUSIC_NOTE)) {
current_mode = EditingMode::MUSIC;
}
HOVER_HINT("Music")
TableNextColumn();
if (ImGui::Button(ICON_MD_GRID_VIEW)) {
show_tile16 = !show_tile16;
}
TableNextColumn();
if (ImGui::Button(ICON_MD_TABLE_CHART)) {
show_gfx_group = !show_gfx_group;
}
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
TableNextColumn(); // Palette
palette_editor_.DisplayPalette(palette_, overworld_.isLoaded());
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
TableNextColumn(); // Experimental
ImGui::Checkbox("Experimental", &show_experimental);
ImGui::EndTable();
}
if (show_experimental) {
RETURN_IF_ERROR(DrawExperimentalModal())
}
if (show_tile16) {
// Create a table in ImGui for the Tile16 Editor
ImGui::Begin("Tile16 Editor", &show_tile16);
RETURN_IF_ERROR(tile16_editor_.Update())
ImGui::End();
}
if (show_gfx_group) {
ImGui::Begin("Gfx Group Editor", &show_gfx_group);
RETURN_IF_ERROR(gfx_group_editor_.Update())
ImGui::End();
}
return absl::OkStatus();
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldMapSettings() {
if (ImGui::BeginTable("#mapSettings", 8, ow_map_flags, ImVec2(0, 0), -1)) {
for (const auto &name : kOverworldSettingsColumnNames)
ImGui::TableSetupColumn(name.data());
if (ImGui::BeginTable(kOWMapTable.data(), 7, kOWMapFlags, ImVec2(0, 0), -1)) {
for (const auto &name : {"##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
"##sprpalCol", "##msgidCol", "##2ndCol"})
ImGui::TableSetupColumn(name);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(50.f);
ImGui::InputInt("Current Map", &current_map_);
TableNextColumn();
ImGui::SetNextItemWidth(120.f);
ImGui::Combo("##world", &current_world_, kWorldList.data(), 3);
ImGui::TableNextColumn();
TableNextColumn();
gui::InputHexByte(
"Gfx",
overworld_.mutable_overworld_map(current_map_)->mutable_area_graphics(),
kInputFieldSize);
TableNextColumn();
gui::InputHexByte(
"Palette",
overworld_.mutable_overworld_map(current_map_)->mutable_area_palette(),
kInputFieldSize);
TableNextColumn();
gui::InputHexByte("Spr Gfx",
overworld_.mutable_overworld_map(current_map_)
->mutable_sprite_graphics(game_state_),
kInputFieldSize);
TableNextColumn();
gui::InputHexByte("Spr Palette",
overworld_.mutable_overworld_map(current_map_)
->mutable_sprite_palette(game_state_),
kInputFieldSize);
TableNextColumn();
gui::InputHexByte(
"Msg Id",
overworld_.mutable_overworld_map(current_map_)->mutable_message_id(),
kInputFieldSize);
TableNextColumn();
ImGui::SetNextItemWidth(100.f);
ImGui::Combo("##world", &current_world_,
"Light World\0Dark World\0Extra World\0");
ImGui::Combo("##World", &game_state_, kGamePartComboString.data(), 3);
ImGui::TableNextColumn();
ImGui::Text("GFX");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##mapGFX", map_gfx_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Palette");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##mapPal", map_palette_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Spr GFX");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##sprGFX", spr_gfx_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Spr Palette");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##sprPal", spr_palette_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Msg ID");
ImGui::SameLine();
ImGui::SetNextItemWidth(50.f);
ImGui::InputText("##msgid", spr_palette_, kMessageIdSize);
ImGui::TableNextColumn();
ImGui::Checkbox("Show grid", &opt_enable_grid); // TODO
ImGui::EndTable();
}
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldEntrances() {
for (const auto &each : overworld_.Entrances()) {
void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0,
ImVec2 scrolling) {
for (auto &each : overworld_.Entrances()) {
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
each.map_id_ >= (current_world_ * 0x40)) {
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
ImVec4(210, 24, 210, 150));
std::string str = absl::StrFormat("%#x", each.entrance_id_);
ow_map_canvas_.DrawText(str, each.x_ - 4, each.y_ - 2);
// Check if this entrance is being clicked and dragged
if (IsMouseHoveringOverEntrance(each, canvas_p0, scrolling) &&
ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
dragged_entrance_ = &each;
is_dragging_entrance_ = true;
if (ImGui::BeginDragDropSource()) {
ImGui::SetDragDropPayload("ENTRANCE_PAYLOAD", &each,
sizeof(zelda3::OverworldEntrance));
Text("Moving Entrance ID: %s", str.c_str());
ImGui::EndDragDropSource();
}
} else if (is_dragging_entrance_ && dragged_entrance_ == &each &&
ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
// Adjust the X and Y position of the entrance rectangle based on the
// mouse position when it is released after being dragged
const ImGuiIO &io = ImGui::GetIO();
const ImVec2 origin(canvas_p0.x + scrolling.x,
canvas_p0.y + scrolling.y);
dragged_entrance_->x_ = io.MousePos.x - origin.x - 8;
dragged_entrance_->y_ = io.MousePos.y - origin.y - 8;
is_dragging_entrance_ = false;
}
}
}
}
bool OverworldEditor::IsMouseHoveringOverEntrance(
const zelda3::OverworldEntrance &entrance, ImVec2 canvas_p0,
ImVec2 scrolling) {
// Get the mouse position relative to the canvas
const ImGuiIO &io = ImGui::GetIO();
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
// Check if the mouse is hovering over the entrance
if (mouse_pos.x >= entrance.x_ && mouse_pos.x <= entrance.x_ + 16 &&
mouse_pos.y >= entrance.y_ && mouse_pos.y <= entrance.y_ + 16) {
return true;
}
return false;
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldSprites() {
for (const auto &sprite : overworld_.Sprites(game_state_)) {
// Get the sprite's bitmap and real X and Y positions
auto id = sprite.id();
const gfx::Bitmap &sprite_bitmap = sprite_previews_[id];
int realX = sprite.GetRealX();
int realY = sprite.GetRealY();
// Draw the sprite's bitmap onto the canvas at its real X and Y positions
ow_map_canvas_.DrawBitmap(sprite_bitmap, realX, realY);
ow_map_canvas_.DrawRect(realX, realY, sprite.Width(), sprite.Height(),
ImVec4(255, 0, 0, 150));
std::string str = absl::StrFormat("%s", sprite.Name());
ow_map_canvas_.DrawText(str, realX - 4, realY - 2);
}
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldMaps() {
@@ -153,42 +322,169 @@ void OverworldEditor::DrawOverworldMaps() {
int yy = 0;
for (int i = 0; i < 0x40; i++) {
int world_index = i + (current_world_ * 0x40);
int map_x = (xx * 0x200);
int map_y = (yy * 0x200);
ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y);
int map_x = (xx * 0x200 * ow_map_canvas_.global_scale());
int map_y = (yy * 0x200 * ow_map_canvas_.global_scale());
ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y,
ow_map_canvas_.global_scale());
xx++;
if (xx >= 8) {
yy++;
xx = 0;
}
DrawOverworldEntrances();
}
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldEdits() {
auto mouse_position = ow_map_canvas_.drawn_tile_position();
auto canvas_size = ow_map_canvas_.canvas_size();
int x = mouse_position.x / canvas_size.x;
int y = mouse_position.y / canvas_size.y;
// 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_]);
}
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;
}
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<int>(click_position.x) % 512) / tile_size;
int tile_index_y = (static_cast<int>(click_position.y) % 512) / 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 &current_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()->UpdateBitmap(&current_bitmap);
}
void OverworldEditor::SaveOverworldChanges() {
// Store the changes made by the user to the ROM (or project file)
rom()->QueueChanges([&]() {
PRINT_IF_ERROR(overworld_.SaveOverworldMaps());
if (!overworld_.CreateTile32Tilemap()) {
// overworld_.SaveMap16Tiles();
PRINT_IF_ERROR(overworld_.SaveMap32Tiles());
} else {
std::cout << "Failed to create tile32 tilemap" << std::endl;
}
});
}
void OverworldEditor::CheckForOverworldEdits() {
if (!blockset_canvas_.Points().empty()) {
// User has selected a tile they want to draw from the blockset.
int x = blockset_canvas_.Points().front().x / 32;
int y = blockset_canvas_.Points().front().y / 32;
current_tile16_ = x + (y * 8);
if (ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_],
16)) {
// Update the overworld map.
DrawOverworldEdits();
}
}
}
void OverworldEditor::CheckForCurrentMap() {
// DetermineActiveMap(ImGui::GetIO().MousePos);
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
auto current_map_x = current_map_ % 8;
auto current_map_y = current_map_ / 8;
auto large_map_size = 1024;
auto map_size = 512;
auto mouse_position = ImGui::GetIO().MousePos;
// 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;
if (overworld_.overworld_map(current_map_).IsLargeMap()) {
// Draw an outline around the current map
ow_map_canvas_.DrawOutline(current_map_x * large_map_size,
current_map_y * large_map_size, large_map_size,
large_map_size);
} else {
// Draw an outline around the current map
ow_map_canvas_.DrawOutline(current_map_x * map_size,
current_map_y * map_size, map_size, map_size);
}
static int prev_map_;
if (current_map_ != prev_map_) {
// Update the current map's tile16 blockset
// core::BuildAndRenderBitmapPipeline(
// 0x80, 0x2000, 0x80, maps_bmp_[current_map_].mutable_data(), *rom(),
// maps_bmp_[current_map_], palette_);
prev_map_ = current_map_;
}
}
// Overworld Editor canvas
// Allows the user to make changes to the overworld map.
void OverworldEditor::DrawOverworldCanvas() {
DrawOverworldMapSettings();
ImGui::Separator();
if (all_gfx_loaded_) {
DrawOverworldMapSettings();
Separator();
}
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)7);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
ow_map_canvas_.DrawBackground(ImVec2(0x200 * 8, 0x200 * 8));
ImGui::PopStyleVar(2);
ow_map_canvas_.DrawContextMenu();
if (overworld_.isLoaded()) {
DrawOverworldMaps();
// User has selected a tile they want to draw from the blockset.
if (!blockset_canvas_.Points().empty()) {
int x = blockset_canvas_.Points().front().x / 32;
int y = blockset_canvas_.Points().front().y / 32;
std::cout << x << " " << y << std::endl;
current_tile16_ = x + (y * 8);
std::cout << current_tile16_ << std::endl;
ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_], 16);
DrawOverworldEntrances(ow_map_canvas_.zero_point(),
ow_map_canvas_.Scrolling());
if (flags()->kDrawOverworldSprites) {
DrawOverworldSprites();
}
CheckForCurrentMap();
CheckForOverworldEdits();
}
ow_map_canvas_.DrawGrid(64.0f);
ow_map_canvas_.DrawOverlay();
@@ -196,21 +492,6 @@ void OverworldEditor::DrawOverworldCanvas() {
ImGui::EndChild();
}
// ----------------------------------------------------------------------------
// Tile 16 Selector
// Displays all the tiles in the game.
void OverworldEditor::DrawTile16Selector() {
blockset_canvas_.DrawBackground(ImVec2(0x100 + 1, (8192 * 2) + 1));
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 2, map_blockset_loaded_);
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawGrid(32.0f);
blockset_canvas_.DrawOverlay();
}
// ----------------------------------------------------------------------------
// Tile 8 Selector
// Displays all the individual tiles that make up a tile16.
void OverworldEditor::DrawTile8Selector() {
@@ -218,65 +499,46 @@ void OverworldEditor::DrawTile8Selector() {
ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1));
graphics_bin_canvas_.DrawContextMenu();
if (all_gfx_loaded_) {
for (const auto &[key, value] : graphics_bin_) {
// for (const auto &[key, value] : graphics_bin_) {
for (auto &[key, value] : rom()->bitmap_manager()) {
int offset = 0x40 * (key + 1);
int top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 2;
int top_left_y = graphics_bin_canvas_.zero_point().y + 2;
if (key >= 1) {
top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 0x40 * key;
top_left_y = graphics_bin_canvas_.zero_point().y + 0x40 * key;
}
auto texture = value.get()->texture();
graphics_bin_canvas_.GetDrawList()->AddImage(
(void *)value.GetTexture(),
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2, top_left_y),
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 0x100,
graphics_bin_canvas_.GetZeroPoint().y + offset));
(void *)texture,
ImVec2(graphics_bin_canvas_.zero_point().x + 2, top_left_y),
ImVec2(graphics_bin_canvas_.zero_point().x + 0x100,
graphics_bin_canvas_.zero_point().y + offset));
}
}
graphics_bin_canvas_.DrawGrid(16.0f);
graphics_bin_canvas_.DrawOverlay();
}
// ----------------------------------------------------------------------------
// Displays the graphics tilesheets that are available on the current selected
// overworld map.
void OverworldEditor::DrawAreaGraphics() {
current_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
current_gfx_canvas_.DrawContextMenu();
current_gfx_canvas_.DrawTileSelector(32);
current_gfx_canvas_.DrawBitmap(current_gfx_bmp_, 2, overworld_.isLoaded());
current_gfx_canvas_.DrawGrid(32.0f);
current_gfx_canvas_.DrawOverlay();
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawTileSelector() {
if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
if (ImGui::BeginTabBar(kTileSelectorTab.data(),
ImGuiTabBarFlags_FittingPolicyScroll)) {
if (ImGui::BeginTabItem("Tile16")) {
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)2);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawTile16Selector();
}
ImGui::EndChild();
gui::BitmapCanvasPipeline(blockset_canvas_, tile16_blockset_bmp_, 0x100,
(8192 * 2), 0x20, map_blockset_loaded_, true,
1);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Tile8")) {
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)1);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (ImGui::BeginChild("##tile8viewer", ImGui::GetContentRegionAvail(),
true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawTile8Selector();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Area Graphics")) {
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)3);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawAreaGraphics();
}
ImGui::EndChild();
gui::BitmapCanvasPipeline(current_gfx_canvas_, current_gfx_bmp_, 256,
0x10 * 0x40, 0x20, overworld_.isLoaded(), true,
3);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
@@ -287,36 +549,40 @@ void OverworldEditor::DrawTileSelector() {
absl::Status OverworldEditor::LoadGraphics() {
// Load all of the graphics data from the game.
PRINT_IF_ERROR(rom_.LoadAllGraphicsData())
graphics_bin_ = rom_.GetGraphicsBin();
PRINT_IF_ERROR(rom()->LoadAllGraphicsData())
graphics_bin_ = rom()->graphics_bin();
// Load the Link to the Past overworld.
RETURN_IF_ERROR(overworld_.Load(rom_))
RETURN_IF_ERROR(overworld_.Load(*rom()))
palette_ = overworld_.AreaPalette();
current_gfx_bmp_.Create(0x80, 0x200, 0x40, overworld_.AreaGraphics());
current_gfx_bmp_.ApplyPalette(palette_);
rom_.RenderBitmap(&current_gfx_bmp_);
// Create the area graphics image
gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x40,
overworld_.AreaGraphics(), *rom(),
current_gfx_bmp_, palette_);
// Create the tile16 blockset image
tile16_blockset_bmp_.Create(0x80, 8192, 0x80, overworld_.Tile16Blockset());
tile16_blockset_bmp_.ApplyPalette(palette_);
rom_.RenderBitmap(&tile16_blockset_bmp_);
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x80,
overworld_.Tile16Blockset(), *rom(),
tile16_blockset_bmp_, palette_);
map_blockset_loaded_ = true;
// 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 < 32 * 32; 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 < 32; ty++) {
for (int tx = 0; tx < 32; tx++) {
int position = (tx + (ty * 0x20));
uchar value = tile16_data[i + tx + (ty * 0x80)];
for (int ty = 0; ty < 16; ty++) {
for (int tx = 0; tx < 16; tx++) {
int position = tx + (ty * 0x10);
uchar value =
tile16_data[(i % 8 * 16) + (i / 8 * 16 * 0x80) + (ty * 0x80) + tx];
tile_data[position] = value;
}
}
@@ -327,23 +593,87 @@ absl::Status OverworldEditor::LoadGraphics() {
// Render the bitmaps of each tile.
for (int id = 0; id < 4096; id++) {
gfx::Bitmap new_tile16;
tile16_individual_.emplace_back(new_tile16);
tile16_individual_[id].Create(0x10, 0x10, 0x80,
tile16_individual_data_[id]);
tile16_individual_[id].ApplyPalette(palette_);
rom_.RenderBitmap(&tile16_individual_[id]);
tile16_individual_.emplace_back();
gui::BuildAndRenderBitmapPipeline(0x10, 0x10, 0x80,
tile16_individual_data_[id], *rom(),
tile16_individual_[id], palette_);
}
// Render the overworld maps loaded from the ROM.
for (int i = 0; i < core::kNumOverworldMaps; ++i) {
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
overworld_.SetCurrentMap(i);
auto palette = overworld_.AreaPalette();
maps_bmp_[i].Create(0x200, 0x200, 0x200, overworld_.BitmapData());
maps_bmp_[i].ApplyPalette(palette);
rom_.RenderBitmap(&(maps_bmp_[i]));
gui::BuildAndRenderBitmapPipeline(0x200, 0x200, 0x200,
overworld_.BitmapData(), *rom(),
maps_bmp_[i], palette);
}
if (flags()->kDrawOverworldSprites) {
RETURN_IF_ERROR(LoadSpriteGraphics());
}
return absl::OkStatus();
}
absl::Status OverworldEditor::LoadSpriteGraphics() {
// Render the sprites for each Overworld map
for (int i = 0; i < 3; i++)
for (auto const &sprite : overworld_.Sprites(i)) {
int width = sprite.Width();
int height = sprite.Height();
int depth = 0x40;
auto spr_gfx = sprite.PreviewGraphics();
sprite_previews_[sprite.id()].Create(width, height, depth, spr_gfx);
sprite_previews_[sprite.id()].ApplyPalette(palette_);
rom()->RenderBitmap(&(sprite_previews_[sprite.id()]));
}
return absl::OkStatus();
}
absl::Status OverworldEditor::DrawExperimentalModal() {
ImGui::Begin("Experimental", &show_experimental);
gui::TextWithSeparators("PROTOTYPE OVERWORLD TILEMAP LOADER");
Text("Please provide two files:");
Text("One based on MAPn.DAT, which represents the overworld tilemap");
Text("One based on MAPDATn.DAT, which is the tile32 configurations.");
Text("Currently, loading CGX for this component is NOT supported. ");
Text("Please load a US ROM of LTTP (JP ROM support coming soon).");
Text(
"Once you've loaded the files, you can click the button below to load "
"the tilemap into the editor");
ImGui::InputText("##TilemapFile", &ow_tilemap_filename_);
ImGui::SameLine();
gui::FileDialogPipeline(
"ImportTilemapsKey", ".DAT,.dat\0", "Tilemap Hex File", [this]() {
ow_tilemap_filename_ = ImGuiFileDialog::Instance()->GetFilePathName();
});
ImGui::InputText("##Tile32ConfigurationFile",
&tile32_configuration_filename_);
ImGui::SameLine();
gui::FileDialogPipeline("ImportTile32Key", ".DAT,.dat\0", "Tile32 Hex File",
[this]() {
tile32_configuration_filename_ =
ImGuiFileDialog::Instance()->GetFilePathName();
});
if (ImGui::Button("Load Prototype Overworld with ROM graphics")) {
RETURN_IF_ERROR(LoadGraphics())
all_gfx_loaded_ = true;
}
gui::TextWithSeparators("Configuration");
gui::InputHexShort("Tilemap File Offset (High)", &tilemap_file_offset_high_);
gui::InputHexShort("Tilemap File Offset (Low)", &tilemap_file_offset_low_);
gui::InputHexShort("LW Maps to Load", &light_maps_to_load_);
gui::InputHexShort("DW Maps to Load", &dark_maps_to_load_);
gui::InputHexShort("SP Maps to Load", &sp_maps_to_load_);
ImGui::End();
return absl::OkStatus();
}

View File

@@ -10,14 +10,19 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/editor/palette_editor.h"
#include "app/core/common.h"
#include "app/core/editor.h"
#include "app/editor/modules/gfx_group_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/editor/modules/tile16_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/pipeline.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "gui/canvas.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
@@ -31,43 +36,98 @@ static constexpr uint kTile8DisplayHeight = 64;
static constexpr float kInputFieldSize = 30.f;
static constexpr absl::string_view kToolsetColumnNames[] = {
"#undoTool", "#redoTool", "#drawTool", "#separator2",
"#zoomOutTool", "#zoomInTool", "#separator", "#history",
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
"#transportTool", "#musicTool"};
"#undoTool", "#redoTool", "#drawTool", "#separator2",
"#zoomOutTool", "#zoomInTool", "#separator", "#history",
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
"#transportTool", "#musicTool", "#separator3", "#tilemapTool"};
static constexpr absl::string_view kOverworldSettingsColumnNames[] = {
"##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
"##sprpalCol", "##msgidCol", "##2ndCol"};
constexpr ImGuiTableFlags kOWMapFlags = ImGuiTableFlags_Borders;
constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit;
constexpr ImGuiTableFlags kOWEditFlags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV;
class OverworldEditor {
constexpr absl::string_view kWorldList =
"Light World\0Dark World\0Extra World\0";
constexpr absl::string_view kGamePartComboString = "Part 0\0Part 1\0Part 2\0";
constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
constexpr absl::string_view kOWEditTable = "##OWEditTable";
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
class OverworldEditor : public Editor,
public SharedROM,
public core::ExperimentFlags {
public:
absl::Status Update();
absl::Status Undo() const { return absl::UnimplementedError("Undo"); }
absl::Status Redo() const { return absl::UnimplementedError("Redo"); }
absl::Status Cut() const { return absl::UnimplementedError("Cut"); }
absl::Status Copy() const { return absl::UnimplementedError("Copy"); }
absl::Status Paste() const { return absl::UnimplementedError("Paste"); }
void SetupROM(ROM &rom) { rom_ = rom; }
absl::Status Update() final;
absl::Status Undo() { return absl::UnimplementedError("Undo"); }
absl::Status Redo() { return absl::UnimplementedError("Redo"); }
absl::Status Cut() { return absl::UnimplementedError("Cut"); }
absl::Status Copy() { return absl::UnimplementedError("Copy"); }
absl::Status Paste() { return absl::UnimplementedError("Paste"); }
auto overworld() { return &overworld_; }
void Shutdown() {
for (auto &bmp : tile16_individual_) {
bmp.Cleanup();
}
for (auto &[i, bmp] : maps_bmp_) {
bmp.Cleanup();
}
for (auto &[i, bmp] : graphics_bin_) {
bmp.Cleanup();
}
for (auto &[i, bmp] : current_graphics_set_) {
bmp.Cleanup();
}
}
absl::Status LoadGraphics();
private:
absl::Status DrawToolset();
void DrawOverworldMapSettings();
void DrawOverworldEntrances();
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling);
void DrawOverworldMaps();
void DrawOverworldSprites();
void DrawOverworldEdits();
void RenderUpdatedMapBitmap(const ImVec2 &click_position,
const Bytes &tile_data);
void SaveOverworldChanges();
void DetermineActiveMap(const ImVec2 &mouse_position);
void CheckForOverworldEdits();
void CheckForCurrentMap();
void DrawOverworldCanvas();
void DrawTile16Selector();
void DrawTile8Selector();
void DrawAreaGraphics();
void DrawTileSelector();
absl::Status LoadGraphics();
absl::Status LoadSpriteGraphics();
absl::Status DrawExperimentalModal();
enum class EditingMode {
DRAW_TILE,
ENTRANCES,
EXITS,
ITEMS,
SPRITES,
TRANSPORTS,
MUSIC
};
EditingMode current_mode = EditingMode::DRAW_TILE;
int current_world_ = 0;
int current_map_ = 0;
int current_tile16_ = 0;
int selected_tile_ = 0;
int game_state_ = 0;
char map_gfx_[3] = "";
char map_palette_[3] = "";
char spr_gfx_[3] = "";
@@ -75,41 +135,58 @@ class OverworldEditor {
char message_id_[5] = "";
char staticgfx[16];
uint32_t tilemap_file_offset_high_ = 0;
uint32_t tilemap_file_offset_low_ = 0;
uint32_t light_maps_to_load_ = 0x51;
uint32_t dark_maps_to_load_ = 0x2A;
uint32_t sp_maps_to_load_ = 0x07;
bool opt_enable_grid = true;
bool all_gfx_loaded_ = false;
bool map_blockset_loaded_ = false;
bool selected_tile_loaded_ = false;
bool update_selected_tile_ = true;
bool is_dragging_entrance_ = false;
bool show_tile16_editor_ = false;
bool show_gfx_group_editor_ = false;
ImGuiTableFlags toolset_table_flags = ImGuiTableFlags_SizingFixedFit;
ImGuiTableFlags ow_map_flags = ImGuiTableFlags_Borders;
ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
bool IsMouseHoveringOverEntrance(const zelda3::OverworldEntrance &entrance,
ImVec2 canvas_p, ImVec2 scrolling);
zelda3::OverworldEntrance *dragged_entrance_;
bool show_experimental = false;
std::string ow_tilemap_filename_ = "";
std::string tile32_configuration_filename_ = "";
Bytes selected_tile_data_;
std::unordered_map<int, gfx::Bitmap> graphics_bin_;
std::unordered_map<int, gfx::Bitmap> current_graphics_set_;
std::unordered_map<int, gfx::Bitmap> maps_bmp_;
std::unordered_map<int, gfx::Bitmap> sprite_previews_;
std::vector<Bytes> tile16_individual_data_;
std::vector<gfx::Bitmap> tile16_individual_;
ROM rom_;
std::vector<Bytes> tile8_individual_data_;
std::vector<gfx::Bitmap> tile8_individual_;
Tile16Editor tile16_editor_;
GfxGroupEditor gfx_group_editor_;
PaletteEditor palette_editor_;
zelda3::Overworld overworld_;
gui::Canvas ow_map_canvas_;
gui::Canvas current_gfx_canvas_;
gui::Canvas blockset_canvas_;
gui::Canvas graphics_bin_canvas_;
gfx::SNESPalette palette_;
gfx::Bitmap selected_tile_bmp_;
gfx::Bitmap tile16_blockset_bmp_;
gfx::Bitmap current_gfx_bmp_;
gfx::Bitmap all_gfx_bmp;
gui::Canvas ow_map_canvas_;
gui::Canvas current_gfx_canvas_;
gui::Canvas blockset_canvas_;
gui::Canvas graphics_bin_canvas_;
gfx::BitmapTable maps_bmp_;
gfx::BitmapTable graphics_bin_;
gfx::BitmapTable current_graphics_set_;
gfx::BitmapTable sprite_previews_;
absl::Status status_;
};
} // namespace editor
} // namespace app

View File

@@ -1,128 +0,0 @@
#include "palette_editor.h"
#include <imgui/imgui.h>
#include "absl/status/status.h"
#include "app/gfx/snes_palette.h"
#include "gui/canvas.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
absl::Status PaletteEditor::Update() {
for (int i = 0; i < 11; ++i) {
if (ImGui::TreeNode(kPaletteCategoryNames[i].data())) {
auto size = rom_.GetPaletteGroup(kPaletteGroupNames[i].data()).size;
auto palettes = rom_.GetPaletteGroup(kPaletteGroupNames[i].data());
for (int j = 0; j < size; j++) {
ImGui::Text("%d", j);
auto palette = palettes[j];
for (int n = 0; n < size; n++) {
ImGui::PushID(n);
if ((n % 8) != 0)
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
ImGuiColorEditFlags palette_button_flags =
ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker;
if (ImGui::ColorButton("##palette", palette[n].RGB(),
palette_button_flags, ImVec2(20, 20)))
current_color_ =
ImVec4(palette[n].rgb.x, palette[n].rgb.y, palette[n].rgb.z,
current_color_.w); // Preserve alpha!
ImGui::PopID();
}
}
ImGui::TreePop();
}
}
return absl::OkStatus();
}
void PaletteEditor::DisplayPalette(gfx::SNESPalette& palette, bool loaded) {
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
ImGuiColorEditFlags_NoDragDrop |
ImGuiColorEditFlags_NoOptions;
// Generate a default palette. The palette will persist and can be edited.
static bool init = false;
static ImVec4 saved_palette[256] = {};
if (loaded && !init) {
for (int n = 0; n < palette.size_; n++) {
saved_palette[n].x = palette.GetColor(n).rgb.x / 255;
saved_palette[n].y = palette.GetColor(n).rgb.y / 255;
saved_palette[n].z = palette.GetColor(n).rgb.z / 255;
saved_palette[n].w = 255; // Alpha
}
init = true;
}
static ImVec4 backup_color;
bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags);
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
open_popup |= ImGui::Button("Palette");
if (open_popup) {
ImGui::OpenPopup("mypicker");
backup_color = color;
}
if (ImGui::BeginPopup("mypicker")) {
ImGui::Text("Current Overworld Palette");
ImGui::Separator();
ImGui::ColorPicker4("##picker", (float*)&color,
misc_flags | ImGuiColorEditFlags_NoSidePreview |
ImGuiColorEditFlags_NoSmallPreview);
ImGui::SameLine();
ImGui::BeginGroup(); // Lock X position
ImGui::Text("Current ==>");
ImGui::SameLine();
ImGui::Text("Previous");
ImGui::ColorButton(
"##current", color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40));
ImGui::SameLine();
if (ImGui::ColorButton(
"##previous", backup_color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40)))
color = backup_color;
ImGui::Separator();
ImGui::Text("Palette");
for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) {
ImGui::PushID(n);
if ((n % 8) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha |
ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip;
if (ImGui::ColorButton("##palette", saved_palette[n],
palette_button_flags, ImVec2(20, 20)))
color = ImVec4(saved_palette[n].x, saved_palette[n].y,
saved_palette[n].z, color.w); // Preserve alpha!
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
if (const ImGuiPayload* payload =
ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
ImGui::EndDragDropTarget();
}
ImGui::PopID();
}
ImGui::EndGroup();
ImGui::EndPopup();
}
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,42 +0,0 @@
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
#include <imgui/imgui.h>
#include "absl/status/status.h"
#include "app/gfx/snes_palette.h"
#include "app/rom.h"
#include "gui/canvas.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
static constexpr absl::string_view kPaletteCategoryNames[] = {
"Sword", "Shield", "Clothes", "World Colors",
"Area Colors", "Enemies", "Dungeons", "World Map",
"Dungeon Map", "Triforce", "Crystal"};
static constexpr absl::string_view kPaletteGroupNames[] = {
"swords", "shields", "armors", "ow_main",
"ow_aux", "global_sprites", "dungeon_main", "ow_mini_map",
"ow_mini_map", "3d_object", "3d_object"};
class PaletteEditor {
public:
absl::Status Update();
void DisplayPalette(gfx::SNESPalette& palette, bool loaded);
auto SetupROM(ROM& rom) { rom_ = rom; }
private:
ImVec4 current_color_;
ROM rom_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif

View File

@@ -15,9 +15,9 @@
#include "app/core/constants.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
namespace yaze {
namespace app {
@@ -32,38 +32,14 @@ void ScreenEditor::Update() {
DrawNamingScreenEditor();
DrawOverworldMapEditor();
DrawDungeonMapsEditor();
DrawMosaicEditor();
END_TAB_BAR()
}
void ScreenEditor::DrawWorldGrid(int world, int h, int w) {
const float time = (float)ImGui::GetTime();
int i = 0;
if (world == 1) {
i = 64;
} else if (world == 2) {
i = 128;
}
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
if (x > 0) ImGui::SameLine();
ImGui::PushID(y * 4 + x);
std::string label = absl::StrCat(" #", absl::StrFormat("%x", i));
if (ImGui::Selectable(label.c_str(), mosaic_tiles_[i] != 0, 0,
ImVec2(35, 25))) {
mosaic_tiles_[i] ^= 1;
}
ImGui::PopID();
i++;
}
}
void ScreenEditor::DrawInventoryMenuEditor() {
TAB_ITEM("Inventory Menu")
static bool create = false;
if (!create && rom_.isLoaded()) {
if (!create && rom()->isLoaded()) {
inventory_.Create();
palette_ = inventory_.Palette();
create = true;
@@ -117,40 +93,6 @@ void ScreenEditor::DrawDungeonMapsEditor() {
END_TAB_ITEM()
}
void ScreenEditor::DrawMosaicEditor() {
TAB_ITEM("Mosaic Transitions")
if (ImGui::BeginTable("Worlds", 3, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("Light World");
ImGui::TableSetupColumn("Dark World");
ImGui::TableSetupColumn("Special World");
ImGui::TableHeadersRow();
ImGui::TableNextColumn();
DrawWorldGrid(0);
ImGui::TableNextColumn();
DrawWorldGrid(1);
ImGui::TableNextColumn();
DrawWorldGrid(2, 4);
ImGui::EndTable();
}
gui::InputHex("Routine Location", &overworldCustomMosaicASM);
if (ImGui::Button("Generate Mosaic Assembly")) {
auto mosaic =
rom_.PatchOverworldMosaic(mosaic_tiles_, overworldCustomMosaicASM);
if (!mosaic.ok()) {
std::cout << mosaic;
}
}
END_TAB_ITEM()
}
void ScreenEditor::DrawToolset() {
static bool show_bg1 = true;
static bool show_bg2 = true;

View File

@@ -9,30 +9,22 @@
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "app/zelda3/inventory.h"
#include "gui/canvas.h"
#include "gui/color.h"
#include "gui/icons.h"
#include "app/zelda3/screen/inventory.h"
namespace yaze {
namespace app {
namespace editor {
using MosaicArray = std::array<int, core::kNumOverworldMaps>;
static int overworldCustomMosaicASM = 0x1301D0;
class ScreenEditor {
class ScreenEditor : public SharedROM {
public:
ScreenEditor();
void SetupROM(ROM &rom) {
rom_ = rom;
inventory_.SetupROM(rom_);
}
void Update();
private:
void DrawMosaicEditor();
void DrawTitleScreenEditor();
void DrawNamingScreenEditor();
void DrawOverworldMapEditor();
@@ -41,11 +33,7 @@ class ScreenEditor {
void DrawToolset();
void DrawInventoryToolset();
void DrawWorldGrid(int world, int h = 8, int w = 8);
char mosaic_tiles_[core::kNumOverworldMaps];
ROM rom_;
Bytes all_gfx_;
zelda3::Inventory inventory_;
gfx::SNESPalette palette_;

View File

@@ -0,0 +1,11 @@
#include "sprite_editor.h"
namespace yaze {
namespace app {
namespace editor {
absl::Status SpriteEditor::Update() { return absl::OkStatus(); }
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,19 @@
#ifndef YAZE_APP_EDITOR_SPRITE_EDITOR_H
#define YAZE_APP_EDITOR_SPRITE_EDITOR_H
#include "absl/status/status.h"
namespace yaze {
namespace app {
namespace editor {
class SpriteEditor {
public:
absl::Status Update();
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_SPRITE_EDITOR_H