backend-infra-engineer: Pre-0.2.2 2024 Q4 snapshot

This commit is contained in:
scawful
2024-11-28 11:50:47 -05:00
parent 75bf38fa71
commit 18b7fb9abf
238 changed files with 22057 additions and 8538 deletions

View File

@@ -1,16 +1,17 @@
#include "assembly_editor.h"
#include "ImGuiColorTextEdit/TextEditor.h"
#include "ImGuiFileDialog/ImGuiFileDialog.h"
#include "absl/strings/str_cat.h"
#include "app/core/platform/file_dialog.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "core/constants.h"
namespace yaze {
namespace app {
namespace editor {
using core::FileDialogWrapper;
namespace {
std::vector<std::string> RemoveIgnoredFiles(
@@ -57,11 +58,11 @@ core::FolderItem LoadFolder(const std::string& folder) {
auto root_files = FileDialogWrapper::GetFilesInFolder(current_folder.name);
current_folder.files = RemoveIgnoredFiles(root_files, ignored_files);
for (const auto& folder :
for (const auto& subfolder :
FileDialogWrapper::GetSubdirectoriesInFolder(current_folder.name)) {
core::FolderItem folder_item;
folder_item.name = folder;
std::string full_folder = current_folder.name + "/" + folder;
folder_item.name = subfolder;
std::string full_folder = current_folder.name + "/" + subfolder;
auto folder_files = FileDialogWrapper::GetFilesInFolder(full_folder);
for (const auto& files : folder_files) {
// Remove subdirectory files

View File

@@ -1,15 +1,11 @@
#ifndef YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
#define YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
#include "ImGuiColorTextEdit/TextEditor.h"
#include "ImGuiFileDialog/ImGuiFileDialog.h"
#include <fstream>
#include <istream>
#include <string>
#include "ImGuiColorTextEdit/TextEditor.h"
#include "app/core/common.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gui/style.h"
namespace yaze {

View File

@@ -1,27 +1,20 @@
#ifndef YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
#define YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
#include "imgui_memory_editor.h"
#include "absl/status/status.h"
#include "app/core/common.h"
#include "app/core/constants.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/project.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/code/memory_editor.h"
#include "app/editor/dungeon/dungeon_editor.h"
#include "app/editor/editor.h"
#include "app/editor/graphics/graphics_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/graphics/screen_editor.h"
#include "app/editor/music/music_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/overworld/overworld_editor.h"
#include "app/editor/sprite/sprite_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/utils/gfx_context.h"
#include "app/editor/utils/recent_files.h"
#include "app/emu/emulator.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
@@ -30,6 +23,9 @@
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/rom.h"
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
#include "imgui_memory_editor.h"
namespace yaze {
namespace app {
@@ -46,7 +42,7 @@ struct MemoryEditorWithDiffChecker : public SharedRom {
static Rom comparison_rom;
ImGui::Begin("Hex Editor", &show_memory_editor);
if (ImGui::Button("Compare Rom")) {
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
auto file_name = core::FileDialogWrapper::ShowOpenFileDialog();
PRINT_IF_ERROR(comparison_rom.LoadFromFile(file_name));
show_compare_rom = true;
}

View File

@@ -1,9 +1,7 @@
#include "dungeon_editor.h"
#include "imgui/imgui.h"
#include "app/core/common.h"
#include "app/core/labeling.h"
#include "absl/container/flat_hash_map.h"
#include "app/core/platform/renderer.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
@@ -11,13 +9,15 @@
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/object_names.h"
#include "app/zelda3/dungeon/room_names.h"
#include "imgui/imgui.h"
#include "zelda3/dungeon/room.h"
namespace yaze {
namespace app {
namespace editor {
using core::Renderer;
using ImGui::BeginChild;
using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
@@ -50,21 +50,22 @@ absl::Status DungeonEditor::Update() {
refresh_graphics_ = false;
}
TAB_BAR("##DungeonEditorTabBar")
TAB_ITEM("Room Editor")
status_ = UpdateDungeonRoomView();
END_TAB_ITEM()
TAB_ITEM("Usage Statistics")
if (is_loaded_) {
static bool calc_stats = false;
if (!calc_stats) {
CalculateUsageStats();
calc_stats = true;
if (ImGui::BeginTabBar("##DungeonEditorTabBar")) {
TAB_ITEM("Room Editor")
status_ = UpdateDungeonRoomView();
END_TAB_ITEM()
TAB_ITEM("Usage Statistics")
if (is_loaded_) {
static bool calc_stats = false;
if (!calc_stats) {
CalculateUsageStats();
calc_stats = true;
}
DrawUsageStats();
}
DrawUsageStats();
END_TAB_ITEM()
ImGui::EndTabBar();
}
END_TAB_ITEM()
END_TAB_BAR()
return absl::OkStatus();
}
@@ -72,7 +73,7 @@ absl::Status DungeonEditor::Update() {
absl::Status DungeonEditor::Initialize() {
auto dungeon_man_pal_group = rom()->palette_group().dungeon_main;
for (int i = 0; i < 0x100 + 40; i++) {
rooms_.emplace_back(zelda3::dungeon::Room(i));
rooms_.emplace_back(zelda3::dungeon::Room(/*room_id=*/i));
rooms_[i].LoadHeader();
rooms_[i].LoadRoomFromROM();
if (flags()->kDrawDungeonRoomGraphics) {
@@ -107,7 +108,7 @@ absl::Status DungeonEditor::Initialize() {
ASSIGN_OR_RETURN(current_palette_group_,
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
graphics_bin_ = *rom()->mutable_bitmap_manager();
graphics_bin_ = rom()->gfx_sheets();
// 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]);
@@ -120,14 +121,14 @@ absl::Status DungeonEditor::RefreshGraphics() {
int block = rooms_[current_room_id_].blocks()[i];
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
current_palette_group_[current_palette_id_], 0));
rom()->UpdateBitmap(&graphics_bin_[block], true);
Renderer::GetInstance().UpdateBitmap(&graphics_bin_[block]);
}
auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
for (int i = 9; i < 16; i++) {
int block = rooms_[current_room_id_].blocks()[i];
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
sprites_aux1_pal_group[current_palette_id_], 0));
rom()->UpdateBitmap(&graphics_bin_[block], true);
Renderer::GetInstance().UpdateBitmap(&graphics_bin_[block]);
}
return absl::OkStatus();
}
@@ -195,14 +196,15 @@ absl::Status DungeonEditor::UpdateDungeonRoomView() {
TableNextRow();
TableNextColumn();
TAB_BAR("##DungeonRoomTabBar");
TAB_ITEM("Rooms");
DrawRoomSelector();
END_TAB_ITEM();
TAB_ITEM("Entrances");
DrawEntranceSelector();
END_TAB_ITEM();
END_TAB_BAR();
if (ImGui::BeginTabBar("##DungeonRoomTabBar")) {
TAB_ITEM("Rooms");
DrawRoomSelector();
END_TAB_ITEM();
TAB_ITEM("Entrances");
DrawEntranceSelector();
END_TAB_ITEM();
ImGui::EndTabBar();
}
TableNextColumn();
DrawDungeonTabView();
@@ -320,7 +322,7 @@ void DungeonEditor::DrawRoomSelector() {
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
rom()->resource_label()->SelectableLabelWithNameEdit(
current_room_id_ == i, "Dungeon Room Names",
core::UppercaseHexByte(i), zelda3::dungeon::kRoomNames[i].data());
core::UppercaseHexByte(i), each_room_name.data());
if (ImGui::IsItemClicked()) {
// TODO: Jump to tab if room is already open
current_room_id_ = i;
@@ -498,7 +500,7 @@ void DungeonEditor::DrawRoomGraphics() {
top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
}
room_gfx_canvas_.draw_list()->AddImage(
(void*)graphics_bin_[block].texture(),
(ImTextureID)(intptr_t)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));
@@ -547,7 +549,7 @@ void DungeonEditor::DrawObjectRenderer() {
current_object_ = i;
object_renderer_.LoadObject(i,
rooms_[current_room_id_].mutable_blocks());
rom()->RenderBitmap(object_renderer_.bitmap());
Renderer::GetInstance().RenderBitmap(object_renderer_.bitmap());
object_loaded_ = true;
}
i += 1;
@@ -827,4 +829,4 @@ void DungeonEditor::DrawUsageGrid() {
} // namespace editor
} // namespace app
} // namespace yaze
} // namespace yaze

View File

@@ -1,16 +1,14 @@
#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H
#define YAZE_APP_EDITOR_DUNGEONEDITOR_H
#include "imgui/imgui.h"
#include "app/core/common.h"
#include "app/core/labeling.h"
#include "absl/container/flat_hash_map.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "imgui/imgui.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_entrance.h"
#include "zelda3/dungeon/room_object.h"
@@ -100,7 +98,6 @@ class DungeonEditor : public Editor,
bool object_loaded_ = false;
bool palette_showing_ = false;
bool refresh_graphics_ = false;
bool show_object_render_ = false;
uint16_t current_entrance_id_ = 0;
uint16_t current_room_id_ = 0;
@@ -120,12 +117,11 @@ class DungeonEditor : public Editor,
gui::Canvas object_canvas_;
gfx::Bitmap room_gfx_bmp_;
gfx::BitmapManager graphics_bin_;
std::array<gfx::Bitmap, kNumGfxSheets> graphics_bin_;
std::vector<gfx::Bitmap*> room_gfx_sheets_;
std::vector<zelda3::dungeon::Room> rooms_;
std::vector<zelda3::dungeon::RoomEntrance> entrances_;
std::vector<gfx::BitmapManager> room_graphics_;
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
absl::flat_hash_map<uint16_t, int> spriteset_usage_;

52
src/app/editor/editor.cc Normal file
View File

@@ -0,0 +1,52 @@
#include "editor.h"
#include "app/core/constants.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
absl::Status DrawEditor(EditorLayoutParams *params) {
if (params->editor == nullptr) {
return absl::InternalError("Editor is not initialized");
}
// Draw the editors based on h_split and v_split in a recursive manner
if (params->v_split) {
ImGui::BeginTable("##VerticalSplitTable", 2);
ImGui::TableNextColumn();
if (params->left)
RETURN_IF_ERROR(DrawEditor(params->left));
ImGui::TableNextColumn();
if (params->right)
RETURN_IF_ERROR(DrawEditor(params->right));
ImGui::EndTable();
} else if (params->h_split) {
ImGui::BeginTable("##HorizontalSplitTable", 1);
ImGui::TableNextColumn();
if (params->top)
RETURN_IF_ERROR(DrawEditor(params->top));
ImGui::TableNextColumn();
if (params->bottom)
RETURN_IF_ERROR(DrawEditor(params->bottom));
ImGui::EndTable();
} else {
// No split, just draw the single editor
ImGui::Text("%s Editor",
kEditorNames[static_cast<int>(params->editor->type())]);
RETURN_IF_ERROR(params->editor->Update());
}
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,21 +1,21 @@
set(
YAZE_APP_EDITOR_SRC
app/editor/editor.cc
app/editor/editor_manager.cc
app/editor/dungeon/dungeon_editor.cc
app/editor/master_editor.cc
app/editor/master_editor_test.cc
app/editor/settings_editor.cc
app/editor/overworld_editor.cc
app/editor/overworld/overworld_editor.cc
app/editor/sprite/sprite_editor.cc
app/editor/music/music_editor.cc
app/editor/message/message_editor.cc
app/editor/message/message_editor_test.cc
app/editor/message/message_data.cc
app/editor/code/assembly_editor.cc
app/editor/graphics/screen_editor.cc
app/editor/graphics/graphics_editor.cc
app/editor/graphics/palette_editor.cc
app/editor/graphics/tile16_editor.cc
app/editor/graphics/gfx_group_editor.cc
app/editor/utils/gfx_context.cc
app/editor/overworld/refresh.cc
app/editor/overworld/entity.cc
)
app/editor/system/settings_editor.cc
app/editor/system/command_manager.cc
app/editor/system/extension_manager.cc
)

View File

@@ -1,7 +1,14 @@
#ifndef YAZE_APP_CORE_EDITOR_H
#define YAZE_APP_CORE_EDITOR_H
#include <array>
#include "absl/status/status.h"
#include "app/editor/system/command_manager.h"
#include "app/editor/system/constant_manager.h"
#include "app/editor/system/extension_manager.h"
#include "app/editor/system/history_manager.h"
#include "app/editor/system/resource_manager.h"
namespace yaze {
namespace app {
@@ -12,6 +19,14 @@ namespace app {
*/
namespace editor {
struct EditorContext {
ConstantManager constant_manager;
CommandManager command_manager;
ExtensionManager extension_manager;
HistoryManager history_manager;
ResourceManager resource_manager;
};
enum class EditorType {
kAssembly,
kDungeon,
@@ -25,7 +40,7 @@ enum class EditorType {
kSettings,
};
constexpr std::array<const char*, 10> kEditorNames = {
constexpr std::array<const char *, 10> kEditorNames = {
"Assembly", "Dungeon", "Graphics", "Music", "Overworld",
"Palette", "Screen", "Sprite", "Message", "Settings",
};
@@ -56,10 +71,35 @@ class Editor {
protected:
EditorType type_;
EditorContext context_;
};
/**
* @brief Dynamic Editor Layout Parameters
*/
typedef struct EditorLayoutParams {
bool v_split;
bool h_split;
int v_split_pos;
int h_split_pos;
Editor *editor = nullptr;
EditorLayoutParams *left = nullptr;
EditorLayoutParams *right = nullptr;
EditorLayoutParams *top = nullptr;
EditorLayoutParams *bottom = nullptr;
EditorLayoutParams() {
v_split = false;
h_split = false;
v_split_pos = 0;
h_split_pos = 0;
}
} EditorLayoutParams;
absl::Status DrawEditor(EditorLayoutParams *params);
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_CORE_EDITOR_H
#endif // YAZE_APP_CORE_EDITOR_H

View File

@@ -1,48 +1,39 @@
#include "master_editor.h"
#include "ImGuiColorTextEdit/TextEditor.h"
#include "ImGuiFileDialog/ImGuiFileDialog.h"
#include "abseil-cpp/absl/strings/match.h"
#include "imgui/backends/imgui_impl_sdl2.h"
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
#include "imgui_internal.h"
#include "imgui_memory_editor.h"
#include "editor_manager.h"
#include "absl/status/status.h"
#include "app/core/common.h"
#include "absl/strings/match.h"
#include "app/core/constants.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/project.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/dungeon/dungeon_editor.h"
#include "app/editor/graphics/graphics_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/graphics/screen_editor.h"
#include "app/editor/music/music_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/overworld/overworld_editor.h"
#include "app/editor/sprite/sprite_editor.h"
#include "app/editor/utils/flags.h"
#include "app/editor/utils/recent_files.h"
#include "app/editor/system/flags.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/style.h"
#include "app/rom.h"
#include "editor/editor.h"
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
namespace yaze {
namespace app {
namespace editor {
using namespace ImGui;
using core::FileDialogWrapper;
namespace {
bool BeginCentered(const char *name) {
ImGuiIO const &io = GetIO();
bool BeginCentered(const char* name) {
ImGuiIO const& io = GetIO();
ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGuiWindowFlags flags =
@@ -58,21 +49,18 @@ bool IsEditorActive(Editor* editor, std::vector<Editor*>& active_editors) {
} // namespace
void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer,
std::string filename) {
sdl_renderer_ = renderer;
rom()->SetupRenderer(renderer);
void EditorManager::SetupScreen(std::string filename) {
if (!filename.empty()) {
PRINT_IF_ERROR(rom()->LoadFromFile(filename));
}
overworld_editor_.InitializeZeml();
InitializeCommands();
}
absl::Status MasterEditor::Update() {
absl::Status EditorManager::Update() {
ManageKeyboardShortcuts();
DrawYazeMenu();
DrawFileDialog();
DrawStatusPopup();
DrawAboutPopup();
DrawInfoPopup();
@@ -85,12 +73,15 @@ absl::Status MasterEditor::Update() {
rom_assets_loaded_ = true;
}
ManageActiveEditors();
if (dynamic_layout_)
RETURN_IF_ERROR(DrawDynamicLayout())
else
ManageActiveEditors();
return absl::OkStatus();
}
void MasterEditor::ManageActiveEditors() {
void EditorManager::ManageActiveEditors() {
// Show popup pane to select an editor to add
static bool show_add_editor = false;
if (show_add_editor) OpenPopup("AddEditor");
@@ -253,16 +244,23 @@ void MasterEditor::ManageActiveEditors() {
}
}
void MasterEditor::ManageKeyboardShortcuts() {
absl::Status EditorManager::DrawDynamicLayout() {
// Dynamic layout for multiple editors to be open at once
// Allows for tiling and resizing of editors using ImGui
return DrawEditor(&root_layout_);
}
void EditorManager::ManageKeyboardShortcuts() {
bool ctrl_or_super = (GetIO().KeyCtrl || GetIO().KeySuper);
editor_context_.command_manager.ShowWhichKey();
// If CMD + R is pressed, reload the top result of recent files
if (IsKeyDown(ImGuiKey_R) && ctrl_or_super) {
static RecentFilesManager manager("recent_files.txt");
manager.Load();
if (!manager.GetRecentFiles().empty()) {
auto front = manager.GetRecentFiles().front();
std::cout << "Reloading: " << front << std::endl;
OpenRomOrProject(front);
}
}
@@ -311,55 +309,113 @@ void MasterEditor::ManageKeyboardShortcuts() {
}
}
void MasterEditor::DrawFileDialog() {
gui::FileDialogPipeline("ChooseFileDlgKey", ".sfc,.smc", std::nullopt, [&]() {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
status_ = rom()->LoadFromFile(filePathName);
static RecentFilesManager manager("recent_files.txt");
void EditorManager::InitializeCommands() {
if (root_layout_.editor == nullptr) {
root_layout_.editor = &overworld_editor_;
}
// Load existing recent files
manager.Load();
// New editor popup for window management commands
static EditorLayoutParams new_layout;
if (ImGui::BeginPopup("NewEditor")) {
ImGui::Text("New Editor");
ImGui::Separator();
if (ImGui::Button("Overworld")) {
new_layout.editor = &overworld_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Dungeon")) {
new_layout.editor = &dungeon_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Graphics")) {
new_layout.editor = &graphics_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Music")) {
new_layout.editor = &music_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Palette")) {
new_layout.editor = &palette_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Screen")) {
new_layout.editor = &screen_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Sprite")) {
new_layout.editor = &sprite_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Code")) {
new_layout.editor = &assembly_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Settings")) {
new_layout.editor = &settings_editor_;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Message")) {
new_layout.editor = &message_editor_;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
// Add a new file
manager.AddFile(filePathName);
// Save the updated list
manager.Save();
});
editor_context_.command_manager.RegisterPrefix("window", 'w',
"window management", "");
editor_context_.command_manager.RegisterSubcommand(
"window", "vsplit", '/', "vertical split",
"split windows vertically and place editor in new window", [this]() {
ImGui::OpenPopup("NewEditor");
root_layout_.v_split = true;
});
editor_context_.command_manager.RegisterSubcommand(
"window", "hsplit", '-', "horizontal split",
"split windows horizontally and place editor in new window", [this]() {
ImGui::OpenPopup("NewEditor");
root_layout_.h_split = true;
});
editor_context_.command_manager.RegisterSubcommand(
"window", "close", 'd', "close", "close the current editor", [this]() {
if (root_layout_.editor != nullptr) {
root_layout_.editor = nullptr;
}
});
}
void MasterEditor::DrawStatusPopup() {
void EditorManager::DrawStatusPopup() {
static absl::Status prev_status;
if (!status_.ok()) {
show_status_ = true;
prev_status_ = status_;
prev_status = status_;
}
if (show_status_ && (BeginCentered("StatusWindow"))) {
Text("%s", ICON_MD_ERROR);
Text("%s", prev_status_.ToString().c_str());
Text("%s", prev_status.ToString().c_str());
Spacing();
NextColumn();
Columns(1);
Separator();
NewLine();
SameLine(128);
if (Button("OK", gui::kDefaultModalSize) ||
IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
if (Button("OK", gui::kDefaultModalSize) || IsKeyPressed(ImGuiKey_Space)) {
show_status_ = false;
status_ = absl::OkStatus();
}
SameLine();
if (Button(ICON_MD_CONTENT_COPY, ImVec2(50, 0))) {
SetClipboardText(prev_status_.ToString().c_str());
SetClipboardText(prev_status.ToString().c_str());
}
End();
}
}
void MasterEditor::DrawAboutPopup() {
void EditorManager::DrawAboutPopup() {
if (about_) OpenPopup("About");
if (BeginPopupModal("About", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
Text("Yet Another Zelda3 Editor - v%.2f", core::kYazeVersion);
Text("Yet Another Zelda3 Editor - v%s", core::kYazeVersion.data());
Text("Written by: scawful");
Spacing();
Text("Special Thanks: Zarby89, JaredBrian");
@@ -373,15 +429,15 @@ void MasterEditor::DrawAboutPopup() {
}
}
void MasterEditor::DrawInfoPopup() {
void EditorManager::DrawInfoPopup() {
if (rom_info_) OpenPopup("ROM Information");
if (BeginPopupModal("ROM Information", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
Text("Title: %s", rom()->title());
Text("ROM Size: %ld", rom()->size());
Text("Title: %s", rom()->title().c_str());
Text("ROM Size: %s", core::UppercaseHexLongLong(rom()->size()).c_str());
if (Button("Close", gui::kDefaultModalSize) ||
IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
IsKeyPressed(ImGuiKey_Escape)) {
rom_info_ = false;
CloseCurrentPopup();
}
@@ -389,33 +445,19 @@ void MasterEditor::DrawInfoPopup() {
}
}
void MasterEditor::DrawYazeMenu() {
void EditorManager::DrawYazeMenu() {
static bool show_display_settings = false;
static bool show_command_line_interface = false;
if (BeginMenuBar()) {
DrawFileMenu();
DrawEditMenu();
DrawViewMenu();
DrawTestMenu();
DrawProjectMenu();
DrawHelpMenu();
DrawYazeMenuBar();
SameLine(GetWindowWidth() - GetStyle().ItemSpacing.x -
CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 150);
// Modify the style of the button to have no background color
CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 110);
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
if (Button(ICON_MD_DISPLAY_SETTINGS)) {
show_display_settings = !show_display_settings;
}
if (Button(ICON_MD_TERMINAL)) {
show_command_line_interface = !show_command_line_interface;
}
PopStyleColor();
Text("%s", absl::StrCat("yaze v", core::kYazeVersion).c_str());
Text("yaze v%s", core::kYazeVersion.data());
EndMenuBar();
}
@@ -424,27 +466,9 @@ void MasterEditor::DrawYazeMenu() {
gui::DrawDisplaySettings();
End();
}
if (show_command_line_interface) {
Begin("Command Line Interface", &show_command_line_interface,
ImGuiWindowFlags_None);
Text("Enter a command:");
End();
}
}
void MasterEditor::OpenRomOrProject(const std::string& filename) {
if (absl::StrContains(filename, ".yaze")) {
status_ = current_project_.Open(filename);
if (status_.ok()) {
status_ = OpenProject();
}
} else {
status_ = rom()->LoadFromFile(filename);
}
}
void MasterEditor::DrawFileMenu() {
void EditorManager::DrawYazeMenuBar() {
static bool save_as_menu = false;
static bool new_project_menu = false;
@@ -476,7 +500,6 @@ void MasterEditor::DrawFileMenu() {
MENU_ITEM("Save As..") { save_as_menu = true; }
if (rom()->is_loaded()) {
MENU_ITEM("Reload") { status_ = rom()->Reload(); }
MENU_ITEM("Close") {
status_ = rom()->Close();
rom_assets_loaded_ = false;
@@ -492,8 +515,8 @@ void MasterEditor::DrawFileMenu() {
}
if (MenuItem("Open Project")) {
// Open an existing project
status_ =
current_project_.Open(FileDialogWrapper::ShowOpenFileDialog());
status_ = current_project_.Open(
core::FileDialogWrapper::ShowOpenFileDialog());
if (status_.ok()) {
status_ = OpenProject();
}
@@ -582,9 +605,7 @@ void MasterEditor::DrawFileMenu() {
}
End();
}
}
void MasterEditor::DrawEditMenu() {
if (BeginMenu("Edit")) {
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = current_editor_->Undo(); }
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = current_editor_->Redo(); }
@@ -593,12 +614,10 @@ void MasterEditor::DrawEditMenu() {
MENU_ITEM2("Copy", "Ctrl+C") { status_ = current_editor_->Copy(); }
MENU_ITEM2("Paste", "Ctrl+V") { status_ = current_editor_->Paste(); }
Separator();
MENU_ITEM2("Find", "Ctrl+F") {}
MENU_ITEM2("Find", "Ctrl+F") { status_ = current_editor_->Find(); }
EndMenu();
}
}
void MasterEditor::DrawViewMenu() {
static bool show_imgui_metrics = false;
static bool show_memory_editor = false;
static bool show_asm_editor = false;
@@ -606,28 +625,17 @@ void MasterEditor::DrawViewMenu() {
static bool show_palette_editor = false;
static bool show_emulator = false;
if (show_imgui_demo) ShowDemoWindow();
if (show_imgui_metrics) ShowMetricsWindow(&show_imgui_metrics);
if (show_memory_editor) memory_editor_.Update(show_memory_editor);
if (show_asm_editor) assembly_editor_.Update(show_asm_editor);
if (show_emulator) {
Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
emulator_.Run();
End();
}
if (show_imgui_metrics) {
ShowMetricsWindow(&show_imgui_metrics);
}
if (show_memory_editor) {
memory_editor_.Update(show_memory_editor);
}
if (show_imgui_demo) {
ShowDemoWindow();
}
if (show_asm_editor) {
assembly_editor_.Update(show_asm_editor);
}
if (show_palette_editor) {
Begin("Palette Editor", &show_palette_editor);
status_ = palette_editor_.Update();
@@ -635,6 +643,7 @@ void MasterEditor::DrawViewMenu() {
}
if (BeginMenu("View")) {
MenuItem("Dynamic Layout", nullptr, &dynamic_layout_);
MenuItem("Emulator", nullptr, &show_emulator);
Separator();
MenuItem("Memory Editor", nullptr, &show_memory_editor);
@@ -647,23 +656,9 @@ void MasterEditor::DrawViewMenu() {
MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
EndMenu();
}
}
void MasterEditor::DrawTestMenu() {
static bool show_tests_ = false;
if (BeginMenu("Tests")) {
MenuItem("Run Tests", nullptr, &show_tests_);
EndMenu();
}
}
void MasterEditor::DrawProjectMenu() {
static bool show_resource_label_manager = false;
if (current_project_.project_opened_) {
// Project Menu
if (BeginMenu("Project")) {
Text("Name: %s", current_project_.name.c_str());
Text("ROM: %s", current_project_.rom_filename_.c_str());
@@ -674,16 +669,7 @@ void MasterEditor::DrawProjectMenu() {
EndMenu();
}
}
if (show_resource_label_manager) {
rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
if (current_project_.project_opened_ &&
!current_project_.labels_filename_.empty()) {
current_project_.labels_filename_ = rom()->resource_label()->filename_;
}
}
}
void MasterEditor::DrawHelpMenu() {
static bool open_rom_help = false;
static bool open_supported_features = false;
static bool open_manage_project = false;
@@ -752,11 +738,10 @@ void MasterEditor::DrawHelpMenu() {
Text("Project Menu");
Text("Create a new project or open an existing one.");
Text("Save the project to save the current state of the project.");
Text(
TextWrapped(
"To save a project, you need to first open a ROM and initialize your "
"code path and labels file. Label resource manager can be found in "
"the "
"View menu. Code path is set in the Code editor after opening a "
"the View menu. Code path is set in the Code editor after opening a "
"folder.");
if (Button("Close", gui::kDefaultModalSize)) {
@@ -765,58 +750,51 @@ void MasterEditor::DrawHelpMenu() {
}
EndPopup();
}
if (show_resource_label_manager) {
rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
if (current_project_.project_opened_ &&
!current_project_.labels_filename_.empty()) {
current_project_.labels_filename_ = rom()->resource_label()->filename_;
}
}
}
void MasterEditor::LoadRom() {
if (flags()->kNewFileDialogWrapper) {
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
PRINT_IF_ERROR(rom()->LoadFromFile(file_name));
void EditorManager::LoadRom() {
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
auto load_rom = rom()->LoadFromFile(file_name);
if (load_rom.ok()) {
static RecentFilesManager manager("recent_files.txt");
manager.Load();
manager.AddFile(file_name);
manager.Save();
} else {
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
".sfc,.smc", ".");
}
}
void MasterEditor::SaveRom() {
void EditorManager::SaveRom() {
if (flags()->kSaveDungeonMaps) {
status_ = screen_editor_.SaveDungeonMaps();
RETURN_VOID_IF_ERROR(status_);
}
if (flags()->overworld.kSaveOverworldMaps) {
RETURN_VOID_IF_ERROR(
status_ = overworld_editor_.overworld()->CreateTile32Tilemap());
status_ = overworld_editor_.overworld()->SaveMap32Tiles();
RETURN_VOID_IF_ERROR(status_);
status_ = overworld_editor_.overworld()->SaveMap16Tiles();
RETURN_VOID_IF_ERROR(status_);
status_ = overworld_editor_.overworld()->SaveOverworldMaps();
RETURN_VOID_IF_ERROR(status_);
}
if (flags()->overworld.kSaveOverworldEntrances) {
status_ = overworld_editor_.overworld()->SaveEntrances();
RETURN_VOID_IF_ERROR(status_);
}
if (flags()->overworld.kSaveOverworldExits) {
status_ = overworld_editor_.overworld()->SaveExits();
RETURN_VOID_IF_ERROR(status_);
}
if (flags()->overworld.kSaveOverworldItems) {
status_ = overworld_editor_.overworld()->SaveItems();
RETURN_VOID_IF_ERROR(status_);
}
if (flags()->overworld.kSaveOverworldProperties) {
status_ = overworld_editor_.overworld()->SaveMapProperties();
RETURN_VOID_IF_ERROR(status_);
}
status_ = overworld_editor_.Save();
RETURN_VOID_IF_ERROR(status_);
status_ = rom()->SaveToFile(backup_rom_, save_new_auto_);
}
absl::Status MasterEditor::OpenProject() {
void EditorManager::OpenRomOrProject(const std::string& filename) {
if (absl::StrContains(filename, ".yaze")) {
status_ = current_project_.Open(filename);
if (status_.ok()) {
status_ = OpenProject();
}
} else {
status_ = rom()->LoadFromFile(filename);
}
}
absl::Status EditorManager::OpenProject() {
RETURN_IF_ERROR(rom()->LoadFromFile(current_project_.rom_filename_));
if (!rom()->resource_label()->LoadLabels(current_project_.labels_filename_)) {

View File

@@ -1,17 +1,9 @@
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
#ifndef YAZE_APP_EDITOR_EDITOR_MANAGER_H
#define YAZE_APP_EDITOR_EDITOR_MANAGER_H
#define IMGUI_DEFINE_MATH_OPERATORS
#include "ImGuiColorTextEdit/TextEditor.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 "app/core/common.h"
#include "app/core/constants.h"
#include "app/core/project.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/code/memory_editor.h"
@@ -21,15 +13,10 @@
#include "app/editor/graphics/screen_editor.h"
#include "app/editor/message/message_editor.h"
#include "app/editor/music/music_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/settings_editor.h"
#include "app/editor/overworld/overworld_editor.h"
#include "app/editor/sprite/sprite_editor.h"
#include "app/editor/utils/gfx_context.h"
#include "app/editor/system/settings_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"
@@ -38,28 +25,19 @@ namespace app {
namespace editor {
/**
* @class MasterEditor
* @brief The MasterEditor class represents the main editor for a Rom in the
* Yaze application.
* @class EditorManager
* @brief The EditorManager controls the main editor window and manages the
* various editor classes.
*
* This class inherits from SharedRom, GfxContext, and ExperimentFlags, and
* provides functionality for setting up the screen, updating the editor, and
* shutting down the editor. It also includes methods for drawing various menus
* and popups, saving the Rom, and managing editor-specific flags.
*
* The MasterEditor class contains instances of various editor classes such as
* The EditorManager class contains instances of various editor classes such as
* AssemblyEditor, DungeonEditor, GraphicsEditor, MusicEditor, OverworldEditor,
* PaletteEditor, ScreenEditor, and SpriteEditor. The current_editor_ member
* variable points to the currently active editor in the tab view.
*
* @note This class assumes the presence of an SDL_Renderer object for rendering
* graphics.
*/
class MasterEditor : public SharedRom,
public context::GfxContext,
public core::ExperimentFlags {
class EditorManager : public SharedRom, public core::ExperimentFlags {
public:
MasterEditor() {
EditorManager() {
current_editor_ = &overworld_editor_;
active_editors_.push_back(&overworld_editor_);
active_editors_.push_back(&dungeon_editor_);
@@ -67,37 +45,33 @@ class MasterEditor : public SharedRom,
active_editors_.push_back(&palette_editor_);
active_editors_.push_back(&sprite_editor_);
active_editors_.push_back(&message_editor_);
active_editors_.push_back(&screen_editor_);
}
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer,
std::string filename = "");
void SetupScreen(std::string filename = "");
absl::Status Update();
auto emulator() -> emu::Emulator& { return emulator_; }
auto emulator() -> emu::Emulator & { return emulator_; }
auto quit() { return quit_; }
auto overworld_editor() -> OverworldEditor& { return overworld_editor_; }
private:
void ManageActiveEditors();
void ManageKeyboardShortcuts();
void OpenRomOrProject(const std::string& filename);
absl::Status DrawDynamicLayout();
void ManageKeyboardShortcuts();
void InitializeCommands();
void DrawFileDialog();
void DrawStatusPopup();
void DrawAboutPopup();
void DrawInfoPopup();
void DrawYazeMenu();
void DrawFileMenu();
void DrawEditMenu();
void DrawViewMenu();
void DrawTestMenu();
void DrawProjectMenu();
void DrawHelpMenu();
void DrawYazeMenuBar();
void LoadRom();
void SaveRom();
void OpenRomOrProject(const std::string &filename);
absl::Status OpenProject();
bool quit_ = false;
@@ -107,16 +81,21 @@ class MasterEditor : public SharedRom,
bool save_new_auto_ = true;
bool show_status_ = false;
bool rom_assets_loaded_ = false;
bool dynamic_layout_ = false;
absl::Status status_;
absl::Status prev_status_;
std::shared_ptr<SDL_Renderer> sdl_renderer_;
emu::Emulator emulator_;
Project current_project_;
std::vector<Editor *> active_editors_;
std::vector<EditorLayoutParams> active_layouts_;
EditorLayoutParams root_layout_;
Project current_project_;
EditorContext editor_context_;
Editor *current_editor_ = nullptr;
AssemblyEditor assembly_editor_;
DungeonEditor dungeon_editor_;
GraphicsEditor graphics_editor_;
@@ -128,14 +107,10 @@ class MasterEditor : public SharedRom,
SettingsEditor settings_editor_;
MessageEditor message_editor_;
MemoryEditorWithDiffChecker memory_editor_;
ImVector<int> active_tabs_;
std::vector<Editor*> active_editors_;
Editor* current_editor_ = nullptr;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H
#endif // YAZE_APP_EDITOR_EDITOR_MANAGER_H

View File

@@ -1,22 +1,14 @@
#include "gfx_group_editor.h"
#include "imgui/imgui.h"
#include <cmath>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/utils/editor.h"
#include "absl/strings/str_cat.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/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -27,7 +19,6 @@ using ImGui::BeginGroup;
using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
using ImGui::BeginTable;
using ImGui::ColorButton;
using ImGui::EndChild;
using ImGui::EndGroup;
using ImGui::EndTabBar;
@@ -39,7 +30,6 @@ using ImGui::IsItemClicked;
using ImGui::PopID;
using ImGui::PushID;
using ImGui::SameLine;
using ImGui::Selectable;
using ImGui::Separator;
using ImGui::SetNextItemWidth;
using ImGui::TableHeadersRow;
@@ -123,7 +113,7 @@ void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
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];
auto sheet = rom()->gfx_sheets().at(sheet_id);
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 22);
}
@@ -176,7 +166,7 @@ void GfxGroupEditor::DrawRoomsetViewer() {
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];
auto sheet = rom()->gfx_sheets().at(sheet_id);
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 23);
}
@@ -214,7 +204,7 @@ void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
auto sheet = rom()->bitmap_manager()[115 + sheet_id];
auto sheet = rom()->gfx_sheets().at(115 + sheet_id);
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 24);
}
@@ -229,7 +219,7 @@ void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
if (palette.empty()) {
return;
}
for (int n = 0; n < palette.size(); n++) {
for (size_t n = 0; n < palette.size(); n++) {
PushID(n);
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);

View File

@@ -1,21 +1,10 @@
#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/editor/utils/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/style.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -40,13 +29,7 @@ class GfxGroupEditor : public SharedRom {
selected_spriteset_ = spriteset;
}
void InitBlockset(gfx::Bitmap* tile16_blockset) {
tile16_blockset_bmp_ = 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;
@@ -57,17 +40,9 @@ class GfxGroupEditor : public SharedRom {
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 overworld_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H

View File

@@ -1,30 +1,33 @@
#include "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/core/platform/renderer.h"
#include "app/editor/graphics/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/asset_browser.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/modules/asset_browser.h"
#include "app/gui/style.h"
#include "app/rom.h"
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
#include "imgui_memory_editor.h"
namespace yaze {
namespace app {
namespace editor {
using core::Renderer;
using gfx::kPaletteGroupAddressesKeys;
using ImGui::Button;
using ImGui::InputInt;
@@ -32,70 +35,56 @@ using ImGui::InputText;
using ImGui::SameLine;
using ImGui::TableNextColumn;
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
"#memoryEditor",
"##separator_gfx1",
};
constexpr ImGuiTableFlags kGfxEditTableFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
ImGuiTableFlags_SizingFixedFit;
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
ImGuiTabBarFlags_FittingPolicyResizeDown |
ImGuiTabBarFlags_TabListPopupButton;
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
absl::Status GraphicsEditor::Update() {
TAB_BAR("##TabBar")
status_ = UpdateGfxEdit();
TAB_ITEM("Sheet Browser")
if (asset_browser_.Initialized == false) {
asset_browser_.Initialize(rom()->mutable_bitmap_manager());
if (ImGui::BeginTabBar("##TabBar")) {
status_ = UpdateGfxEdit();
TAB_ITEM("Sheet Browser")
if (asset_browser_.Initialized == false) {
asset_browser_.Initialize(rom()->gfx_sheets());
}
asset_browser_.Draw(rom()->gfx_sheets());
END_TAB_ITEM()
status_ = UpdateScadView();
status_ = UpdateLinkGfxView();
ImGui::EndTabBar();
}
asset_browser_.Draw(rom()->mutable_bitmap_manager());
END_TAB_ITEM()
status_ = UpdateScadView();
status_ = UpdateLinkGfxView();
END_TAB_BAR()
CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdateGfxEdit() {
TAB_ITEM("Sheet Editor")
if (ImGui::BeginTabItem("Sheet Editor")) {
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
ImVec2(0, 0))) {
for (const auto& name :
{"Tilesheets", "Current Graphics", "Palette Controls"})
ImGui::TableSetupColumn(name);
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
ImVec2(0, 0))) {
for (const auto& name :
{"Tilesheets", "Current Graphics", "Palette Controls"})
ImGui::TableSetupColumn(name);
ImGui::TableHeadersRow();
ImGui::TableHeadersRow();
NEXT_COLUMN();
status_ = UpdateGfxSheetList();
NEXT_COLUMN();
status_ = UpdateGfxSheetList();
NEXT_COLUMN();
if (rom()->is_loaded()) {
DrawGfxEditToolset();
status_ = UpdateGfxTabView();
}
NEXT_COLUMN();
if (rom()->is_loaded()) {
DrawGfxEditToolset();
status_ = UpdateGfxTabView();
NEXT_COLUMN();
if (rom()->is_loaded()) {
status_ = UpdatePaletteColumn();
}
}
ImGui::EndTable();
NEXT_COLUMN();
if (rom()->is_loaded()) {
status_ = UpdatePaletteColumn();
}
ImGui::EndTabItem();
}
ImGui::EndTable();
END_TAB_ITEM()
return absl::OkStatus();
}
@@ -127,8 +116,8 @@ void GraphicsEditor::DrawGfxEditToolset() {
TableNextColumn();
if (Button(ICON_MD_CONTENT_COPY)) {
std::vector<uint8_t> png_data =
rom()->bitmap_manager().shared_bitmap(current_sheet_).GetPngData();
CopyImageToClipboard(png_data);
rom()->gfx_sheets().at(current_sheet_).GetPngData();
core::CopyImageToClipboard(png_data);
}
HOVER_HINT("Copy to Clipboard");
@@ -136,14 +125,14 @@ void GraphicsEditor::DrawGfxEditToolset() {
if (Button(ICON_MD_CONTENT_PASTE)) {
std::vector<uint8_t> png_data;
int width, height;
GetImageFromClipboard(png_data, width, height);
core::GetImageFromClipboard(png_data, width, height);
if (png_data.size() > 0) {
rom()
->mutable_bitmap_manager()
->mutable_bitmap(current_sheet_)
->Create(width, height, 8, png_data);
rom()->UpdateBitmap(
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_));
->mutable_gfx_sheets()
->at(current_sheet_)
.Create(width, height, 8, png_data);
Renderer::GetInstance().UpdateBitmap(
&rom()->mutable_gfx_sheets()->at(current_sheet_));
}
}
HOVER_HINT("Paste from Clipboard");
@@ -163,7 +152,7 @@ void GraphicsEditor::DrawGfxEditToolset() {
}
TableNextColumn();
auto bitmap = rom()->bitmap_manager()[current_sheet_];
auto bitmap = rom()->gfx_sheets()[current_sheet_];
auto palette = bitmap.palette();
for (int i = 0; i < 8; i++) {
ImGui::SameLine();
@@ -192,28 +181,28 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
static ImGuiSelectionBasicStorage selection;
ImGuiMultiSelectFlags flags =
ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(
flags, selection.Size, rom()->bitmap_manager().size());
ImGuiMultiSelectIO* ms_io =
ImGui::BeginMultiSelect(flags, selection.Size, kNumGfxSheets);
selection.ApplyRequests(ms_io);
ImGuiListClipper clipper;
clipper.Begin(rom()->bitmap_manager().size());
clipper.Begin(kNumGfxSheets);
if (ms_io->RangeSrcItem != -1)
clipper.IncludeItemByIndex(
(int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
for (auto& [key, value] : rom()->bitmap_manager()) {
int key = 0;
for (auto& value : rom()->gfx_sheets()) {
ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
ImVec2(0x100 + 1, 0x40 + 1), true,
ImGuiWindowFlags_NoDecoration);
ImGui::PopStyleVar();
gui::Canvas graphics_bin_canvas_;
graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
graphics_bin_canvas_.DrawContextMenu();
if (value.is_active()) {
auto texture = value.texture();
graphics_bin_canvas_.draw_list()->AddImage(
(void*)texture,
(ImTextureID)(intptr_t)texture,
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
graphics_bin_canvas_.zero_point().y + 2),
ImVec2(graphics_bin_canvas_.zero_point().x +
@@ -240,6 +229,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
graphics_bin_canvas_.draw_list()->AddText(
text_pos, IM_COL32(125, 255, 125, 255),
absl::StrFormat("%02X", key).c_str());
key++;
}
graphics_bin_canvas_.DrawGrid(16.0f);
graphics_bin_canvas_.DrawOverlay();
@@ -256,6 +247,10 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
absl::Status GraphicsEditor::UpdateGfxTabView() {
static int next_tab_id = 0;
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
ImGuiTabBarFlags_FittingPolicyResizeDown |
ImGuiTabBarFlags_TabListPopupButton;
if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
@@ -285,18 +280,17 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
gfx::Bitmap& current_bitmap =
*rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id);
gfx::Bitmap& current_bitmap = rom()->mutable_gfx_sheets()->at(sheet_id);
auto draw_tile_event = [&]() {
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, &current_bitmap,
current_color_);
rom()->UpdateBitmap(&current_bitmap, true);
Renderer::GetInstance().UpdateBitmap(&current_bitmap);
};
current_sheet_canvas_.UpdateColorPainter(
rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
tile_size_, current_scale_);
rom()->mutable_gfx_sheets()->at(sheet_id), current_color_,
draw_tile_event, tile_size_, current_scale_);
ImGui::EndChild();
ImGui::EndTabItem();
@@ -328,7 +322,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
current_sheet_ = id;
// ImVec2(0x100, 0x40),
current_sheet_canvas_.UpdateColorPainter(
rom()->bitmap_manager()[id], current_color_,
rom()->mutable_gfx_sheets()->at(id), current_color_,
[&]() {
},
@@ -365,11 +359,12 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
if (refresh_graphics_ && !open_sheets_.empty()) {
RETURN_IF_ERROR(
rom()->bitmap_manager()[current_sheet_].ApplyPaletteWithTransparent(
palette, edit_palette_sub_index_));
rom()->UpdateBitmap(
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_),
true);
rom()
->mutable_gfx_sheets()
->data()[current_sheet_]
.ApplyPaletteWithTransparent(palette, edit_palette_sub_index_));
Renderer::GetInstance().UpdateBitmap(
&rom()->mutable_gfx_sheets()->data()[current_sheet_]);
refresh_graphics_ = false;
}
}
@@ -391,9 +386,9 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
link_canvas_.DrawGrid(16.0f);
int i = 0;
for (auto [key, link_sheet] : *rom()->mutable_link_graphics()) {
for (auto link_sheet : *rom()->mutable_link_graphics()) {
int x_offset = 0;
int y_offset = core::kTilesheetHeight * i * 4;
int y_offset = gfx::kTilesheetHeight * i * 4;
link_canvas_.DrawContextMenu(&link_sheet);
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
i++;
@@ -435,6 +430,10 @@ absl::Status GraphicsEditor::UpdateScadView() {
ImGui::End();
}
constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
SETUP_COLUMN("File Import (BIN, CGX, ROM)")
SETUP_COLUMN("Palette (COL)")
@@ -458,17 +457,18 @@ absl::Status GraphicsEditor::UpdateScadView() {
NEXT_COLUMN()
if (super_donkey_) {
if (refresh_graphics_) {
for (int i = 0; i < graphics_bin_.size(); i++) {
status_ = graphics_bin_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
rom()->UpdateBitmap(&graphics_bin_[i]);
}
refresh_graphics_ = false;
}
// TODO: Implement the Super Donkey 1 graphics decompression
// if (refresh_graphics_) {
// for (int i = 0; i < kNumGfxSheets; i++) {
// status_ = graphics_bin_[i].ApplyPalette(
// col_file_palette_group_[current_palette_index_]);
// Renderer::GetInstance().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_);
// 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,
@@ -485,6 +485,11 @@ absl::Status GraphicsEditor::UpdateScadView() {
}
absl::Status GraphicsEditor::DrawToolset() {
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
"#memoryEditor",
"##separator_gfx1",
};
if (ImGui::BeginTable("GraphicsToolset", 2, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
for (const auto& name : kGfxToolsetColumnNames)
@@ -535,7 +540,7 @@ absl::Status GraphicsEditor::DrawCgxImport() {
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
if (col_file_) {
cgx_bitmap_.ApplyPalette(decoded_col_);
rom()->RenderBitmap(&cgx_bitmap_);
Renderer::GetInstance().RenderBitmap(&cgx_bitmap_);
}
}
@@ -570,7 +575,7 @@ absl::Status GraphicsEditor::DrawScrImport() {
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
if (scr_loaded_) {
scr_bitmap_.ApplyPalette(decoded_col_);
rom()->RenderBitmap(&scr_bitmap_);
Renderer::GetInstance().RenderBitmap(&scr_bitmap_);
}
}
@@ -594,7 +599,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
/*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_.clear();
}
auto col_file_palette_group_status =
gfx::CreatePaletteGroupFromColFile(col_data_);
@@ -707,7 +712,8 @@ absl::Status GraphicsEditor::DrawClipboardImport() {
if (Button("Paste From Clipboard")) {
const char* text = ImGui::GetClipboardText();
if (text) {
const auto clipboard_data = Bytes(text, text + strlen(text));
const auto clipboard_data =
std::vector<uint8_t>(text, text + strlen(text));
ImGui::MemFree((void*)text);
status_ = temp_rom_.LoadFromBytes(clipboard_data);
is_open_ = true;
@@ -762,7 +768,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
temp_rom_.data(), current_offset_, size))
auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
bin_bitmap_.Create(core::kTilesheetWidth, 0x2000, core::kTilesheetDepth,
bin_bitmap_.Create(gfx::kTilesheetWidth, 0x2000, gfx::kTilesheetDepth,
converted_sheet);
if (rom()->is_loaded()) {
@@ -775,7 +781,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
}
}
rom()->RenderBitmap(&bin_bitmap_);
Renderer::GetInstance().RenderBitmap(&bin_bitmap_);
gfx_loaded_ = true;
return absl::OkStatus();
@@ -790,11 +796,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
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);
gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
gfx::kTilesheetDepth, converted_sheet);
if (col_file_) {
status_ = graphics_bin_[i].ApplyPalette(
status_ = gfx_sheets_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
@@ -802,10 +807,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
auto palette_group = rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
}
rom()->RenderBitmap(&graphics_bin_[i]);
Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
i++;
}
@@ -816,21 +821,20 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
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);
gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
gfx::kTilesheetDepth, converted_sheet);
if (col_file_) {
status_ = graphics_bin_[i].ApplyPalette(
status_ = gfx_sheets_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group = rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
}
rom()->RenderBitmap(&graphics_bin_[i]);
Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
i++;
}
super_donkey_ = true;

View File

@@ -1,22 +1,19 @@
#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 <stack>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/asset_browser.h"
#include "app/gui/modules/asset_browser.h"
#include "app/gui/canvas.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
#include "imgui_memory_editor.h"
namespace yaze {
namespace app {
@@ -167,8 +164,8 @@ class GraphicsEditor : public SharedRom, public Editor {
MemoryEditor cgx_memory_editor_;
MemoryEditor col_memory_editor_;
PaletteEditor palette_editor_;
Bytes import_data_;
Bytes graphics_buffer_;
std::vector<uint8_t> import_data_;
std::vector<uint8_t> graphics_buffer_;
std::vector<uint8_t> decoded_cgx_;
std::vector<uint8_t> cgx_data_;
std::vector<uint8_t> extra_cgx_data_;
@@ -179,9 +176,8 @@ class GraphicsEditor : public SharedRom, public Editor {
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_;
std::array<gfx::Bitmap, kNumGfxSheets> gfx_sheets_;
gfx::PaletteGroup col_file_palette_group_;
gfx::SnesPalette z3_rom_palette_;
gfx::SnesPalette col_file_palette_;
@@ -189,11 +185,12 @@ class GraphicsEditor : public SharedRom, public Editor {
gui::Canvas import_canvas_;
gui::Canvas scr_canvas_;
gui::Canvas super_donkey_canvas_;
gui::Canvas graphics_bin_canvas_;
gui::Canvas current_sheet_canvas_{"CurrentSheetCanvas", ImVec2(0x80, 0x20),
gui::CanvasGridSize::k8x8};
gui::Canvas link_canvas_{
"LinkCanvas",
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
ImVec2(gfx::kTilesheetWidth * 4, gfx::kTilesheetHeight * 0x10 * 4),
gui::CanvasGridSize::k16x16};
absl::Status status_;
};

View File

@@ -1,13 +1,11 @@
#include "palette_editor.h"
#include "imgui/imgui.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/style.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -86,6 +84,93 @@ static inline float color_saturate(float f) {
0.5f)) // Saturated, always output 0..255
} // namespace
absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
static ImVec4 current_palette[256] = {};
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) {
for (int n = 0; n < palette.size(); n++) {
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
current_palette[n].x = color.rgb().x / 255;
current_palette[n].y = color.rgb().y / 255;
current_palette[n].z = color.rgb().z / 255;
current_palette[n].w = 255; // Alpha
}
init = true;
}
static ImVec4 backup_color;
bool open_popup = ColorButton("MyColor##3b", color, misc_flags);
SameLine(0, GetStyle().ItemInnerSpacing.x);
open_popup |= Button("Palette");
if (open_popup) {
OpenPopup("mypicker");
backup_color = color;
}
if (BeginPopup("mypicker")) {
TEXT_WITH_SEPARATOR("Current Overworld Palette");
ColorPicker4("##picker", (float*)&color,
misc_flags | ImGuiColorEditFlags_NoSidePreview |
ImGuiColorEditFlags_NoSmallPreview);
SameLine();
BeginGroup(); // Lock X position
Text("Current ==>");
SameLine();
Text("Previous");
if (Button("Update Map Palette")) {
}
ColorButton(
"##current", color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40));
SameLine();
if (ColorButton(
"##previous", backup_color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40)))
color = backup_color;
// List of Colors in Overworld Palette
Separator();
Text("Palette");
for (int n = 0; n < IM_ARRAYSIZE(current_palette); n++) {
PushID(n);
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
if (ColorButton("##palette", current_palette[n], kPalButtonFlags2,
ImVec2(20, 20)))
color = ImVec4(current_palette[n].x, current_palette[n].y,
current_palette[n].z, color.w); // Preserve alpha!
if (BeginDragDropTarget()) {
if (const ImGuiPayload* payload =
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
memcpy((float*)&current_palette[n], payload->Data, sizeof(float) * 3);
if (const ImGuiPayload* payload =
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
memcpy((float*)&current_palette[n], payload->Data, sizeof(float) * 4);
EndDragDropTarget();
}
PopID();
}
EndGroup();
EndPopup();
}
return absl::OkStatus();
}
absl::Status PaletteEditor::Update() {
if (rom()->is_loaded()) {
// Initialize the labels
@@ -348,7 +433,7 @@ absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
// SNES Format
CustomFormatString(buf, IM_ARRAYSIZE(buf), "$%04X",
ConvertRGBtoSNES(ImVec4(col[0], col[1], col[2], 1.0f)));
ConvertRgbToSnes(ImVec4(col[0], col[1], col[2], 1.0f)));
if (Selectable(buf)) SetClipboardText(buf);
EndPopup();
@@ -358,84 +443,6 @@ absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
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) {
status_ = InitializeSavedPalette(palette);
init = true;
}
static ImVec4 backup_color;
bool open_popup = ColorButton("MyColor##3b", color, misc_flags);
SameLine(0, GetStyle().ItemInnerSpacing.x);
open_popup |= Button("Palette");
if (open_popup) {
OpenPopup("mypicker");
backup_color = color;
}
if (BeginPopup("mypicker")) {
TEXT_WITH_SEPARATOR("Current Overworld Palette");
ColorPicker4("##picker", (float*)&color,
misc_flags | ImGuiColorEditFlags_NoSidePreview |
ImGuiColorEditFlags_NoSmallPreview);
SameLine();
BeginGroup(); // Lock X position
Text("Current ==>");
SameLine();
Text("Previous");
if (Button("Update Map Palette")) {
}
ColorButton(
"##current", color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40));
SameLine();
if (ColorButton(
"##previous", backup_color,
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
ImVec2(60, 40)))
color = backup_color;
// List of Colors in Overworld Palette
Separator();
Text("Palette");
for (int n = 0; n < IM_ARRAYSIZE(saved_palette_); n++) {
PushID(n);
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
if (ColorButton("##palette", saved_palette_[n], kPalButtonFlags2,
ImVec2(20, 20)))
color = ImVec4(saved_palette_[n].x, saved_palette_[n].y,
saved_palette_[n].z, color.w); // Preserve alpha!
if (BeginDragDropTarget()) {
if (const ImGuiPayload* payload =
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 3);
if (const ImGuiPayload* payload =
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
memcpy((float*)&saved_palette_[n], payload->Data, sizeof(float) * 4);
EndDragDropTarget();
}
PopID();
}
EndGroup();
EndPopup();
}
}
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
int index) {
if (index >= palette.size()) {

View File

@@ -1,15 +1,17 @@
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
#include "imgui/imgui.h"
#include <deque>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gfx/snes_color.h"
#include "app/rom.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -26,56 +28,51 @@ struct PaletteChange {
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();
void RecordChange(const std::string& group_name, size_t palette_index,
size_t color_index, const gfx::SnesColor& original_color,
const gfx::SnesColor& new_color) {
if (recent_changes_.size() >= kMaxHistorySize) {
recent_changes_.pop_front();
}
// Push the new change
recentChanges.push_back(
{groupName, paletteIndex, colorIndex, originalColor, newColor});
recent_changes_.push_back(
{group_name, palette_index, color_index, original_color, new_color});
}
// Get recent changes for display in the palette editor
const std::deque<PaletteChange>& GetRecentChanges() const {
return recentChanges;
}
// Restore the original color
gfx::SnesColor RestoreOriginalColor(const std::string& groupName,
size_t paletteIndex,
size_t colorIndex) const {
for (const auto& change : recentChanges) {
if (change.group_name == groupName &&
change.palette_index == paletteIndex &&
change.color_index == colorIndex) {
gfx::SnesColor RestoreOriginalColor(const std::string& group_name,
size_t palette_index,
size_t color_index) const {
for (const auto& change : recent_changes_) {
if (change.group_name == group_name &&
change.palette_index == palette_index &&
change.color_index == color_index) {
return change.original_color;
}
}
// Handle error or return default (this is just an example,
// handle as appropriate for your application)
return gfx::SnesColor();
}
auto size() const { return recentChanges.size(); }
auto size() const { return recent_changes_.size(); }
gfx::SnesColor& GetModifiedColor(size_t index) {
return recentChanges[index].new_color;
return recent_changes_[index].new_color;
}
gfx::SnesColor& GetOriginalColor(size_t index) {
return recentChanges[index].original_color;
return recent_changes_[index].original_color;
}
const std::deque<PaletteChange>& GetRecentChanges() const {
return recent_changes_;
}
private:
std::deque<PaletteChange> recentChanges;
static const size_t maxHistorySize = 50; // or any other number you deem fit
std::deque<PaletteChange> recent_changes_;
static const size_t kMaxHistorySize = 50;
};
} // namespace palette_internal
absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded);
/**
* @class PaletteEditor
* @brief Allows the user to view and edit in game palettes.
@@ -101,7 +98,6 @@ class PaletteEditor : public SharedRom, public Editor {
absl::Status EditColorInPalette(gfx::SnesPalette& palette, int index);
absl::Status ResetColorToOriginal(gfx::SnesPalette& palette, int index,
const gfx::SnesPalette& originalPalette);
void DisplayPalette(gfx::SnesPalette& palette, bool loaded);
absl::Status DrawPaletteGroup(int category, bool right_side = false);
void DrawCustomPalette();
@@ -110,16 +106,6 @@ class PaletteEditor : public SharedRom, public Editor {
private:
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
absl::Status InitializeSavedPalette(const gfx::SnesPalette& palette) {
for (int n = 0; n < palette.size(); n++) {
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
saved_palette_[n].x = color.rgb().x / 255;
saved_palette_[n].y = color.rgb().y / 255;
saved_palette_[n].z = color.rgb().z / 255;
saved_palette_[n].w = 255; // Alpha
}
return absl::OkStatus();
}
absl::Status status_;
gfx::SnesColor current_color_;
@@ -137,4 +123,4 @@ class PaletteEditor : public SharedRom, public Editor {
} // namespace app
} // namespace yaze
#endif
#endif

View File

@@ -1,44 +1,45 @@
#include "screen_editor.h"
#include "imgui/imgui.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "app/core/common.h"
#include "app/core/constants.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/platform/renderer.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/tilesheet.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/zelda3/dungeon/room.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
absl::Status ScreenEditor::Update() {
TAB_BAR("##TabBar")
TAB_ITEM("Dungeon Maps")
if (rom()->is_loaded()) {
DrawDungeonMapsEditor();
}
END_TAB_ITEM()
DrawInventoryMenuEditor();
DrawOverworldMapEditor();
DrawTitleScreenEditor();
DrawNamingScreenEditor();
END_TAB_BAR()
using core::Renderer;
return absl::OkStatus();
constexpr uint32_t kRedPen = 0xFF0000FF;
absl::Status ScreenEditor::Update() {
if (ImGui::BeginTabBar("##ScreenEditorTabBar")) {
if (ImGui::BeginTabItem("Dungeon Maps")) {
if (rom()->is_loaded()) {
DrawDungeonMapsEditor();
}
ImGui::EndTabItem();
}
DrawInventoryMenuEditor();
DrawOverworldMapEditor();
DrawTitleScreenEditor();
DrawNamingScreenEditor();
ImGui::EndTabBar();
}
return status_;
}
void ScreenEditor::DrawInventoryMenuEditor() {
@@ -121,16 +122,16 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
int ptr,
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
ASSIGN_OR_RETURN(
int ptrGFX,
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
ptr |= 0x0A0000; // Add bank to the short ptr
ptrGFX |= 0x0A0000; // Add bank to the short ptr
int pcPtr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
int pcPtrGFX =
core::SnesToPc(ptrGFX); // Contains data for the next 25 rooms
int ptr_gfx,
rom()->ReadWord(zelda3::screen::kDungeonMapGfxPtr + (d * 2)));
ptr |= 0x0A0000; // Add bank to the short ptr
ptr_gfx |= 0x0A0000; // Add bank to the short ptr
int pc_ptr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
int pc_ptr_gfx =
core::SnesToPc(ptr_gfx); // Contains data for the next 25 rooms
ASSIGN_OR_RETURN(
ushort bossRoomD,
ushort boss_room_d,
rom()->ReadWord(zelda3::screen::kDungeonMapBossRooms + (d * 2)));
ASSIGN_OR_RETURN(
@@ -157,14 +158,13 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
// for each room on the floor
for (int j = 0; j < 25; j++) {
// rdata[j] = 0x0F;
gdata[j] = 0xFF;
rdata[j] = rom()->data()[pcPtr + j + (i * 25)]; // Set the rooms
rdata[j] = rom()->data()[pc_ptr + j + (i * 25)]; // Set the rooms
if (rdata[j] == 0x0F) {
gdata[j] = 0xFF;
} else {
gdata[j] = rom()->data()[pcPtrGFX++];
gdata[j] = rom()->data()[pc_ptr_gfx++];
}
std::string label = core::UppercaseHexByte(rdata[j]);
@@ -175,7 +175,7 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
current_floor_rooms_d.push_back(rdata); // Add new floor data
}
dungeon_maps_.emplace_back(bossRoomD, nbr_floor_d, nbr_basement_d,
dungeon_maps_.emplace_back(boss_room_d, nbr_floor_d, nbr_basement_d,
current_floor_rooms_d, current_floor_gfx_d);
}
@@ -185,23 +185,19 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
absl::Status ScreenEditor::SaveDungeonMaps() {
for (int d = 0; d < 14; d++) {
int ptr = zelda3::screen::kDungeonMapRoomsPtr + (d * 2);
int ptrGFX = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
int pcPtr = core::SnesToPc(ptr);
int pcPtrGFX = core::SnesToPc(ptrGFX);
int ptr_gfx = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
int pc_ptr = core::SnesToPc(ptr);
int pc_ptr_gfx = core::SnesToPc(ptr_gfx);
const int nbr_floors = dungeon_maps_[d].nbr_of_floor;
const int nbr_basements = dungeon_maps_[d].nbr_of_basement;
for (int i = 0; i < nbr_floors + nbr_basements; i++) {
for (int j = 0; j < 25; j++) {
// rom()->data()[pcPtr + j + (i * 25)] =
// dungeon_maps_[d].floor_rooms[i][j];
// rom()->data()[pcPtrGFX++] = dungeon_maps_[d].floor_gfx[i][j];
RETURN_IF_ERROR(rom()->WriteByte(ptr + j + (i * 25),
RETURN_IF_ERROR(rom()->WriteByte(pc_ptr + j + (i * 25),
dungeon_maps_[d].floor_rooms[i][j]));
RETURN_IF_ERROR(rom()->WriteByte(ptrGFX + j + (i * 25),
RETURN_IF_ERROR(rom()->WriteByte(pc_ptr_gfx + j + (i * 25),
dungeon_maps_[d].floor_gfx[i][j]));
pcPtrGFX++;
pc_ptr_gfx++;
}
}
}
@@ -209,7 +205,8 @@ absl::Status ScreenEditor::SaveDungeonMaps() {
return absl::OkStatus();
}
absl::Status ScreenEditor::LoadDungeonMapTile16() {
absl::Status ScreenEditor::LoadDungeonMapTile16(
const std::vector<uint8_t>& gfx_data, bool bin_mode) {
tile16_sheet_.Init(256, 192, gfx::TileType::Tile16);
for (int i = 0; i < 186; i++) {
@@ -230,69 +227,99 @@ absl::Status ScreenEditor::LoadDungeonMapTile16() {
ASSIGN_OR_RETURN(auto br, rom()->ReadWord(addr + 6 + (i * 8)));
gfx::TileInfo t4 = gfx::WordToTileInfo(br); // Bottom right
tile16_sheet_.ComposeTile16(rom()->graphics_buffer(), t1, t2, t3, t4);
int sheet_offset = 212;
if (bin_mode) {
sheet_offset = 0;
}
tile16_sheet_.ComposeTile16(gfx_data, t1, t2, t3, t4, sheet_offset);
}
RETURN_IF_ERROR(tile16_sheet_.mutable_bitmap()->ApplyPalette(
*rom()->mutable_dungeon_palette(3)));
rom()->RenderBitmap(&*tile16_sheet_.mutable_bitmap().get());
Renderer::GetInstance().RenderBitmap(&*tile16_sheet_.mutable_bitmap().get());
for (int i = 0; i < tile16_sheet_.num_tiles(); ++i) {
if (tile16_individual_.count(i) == 0) {
auto tile = tile16_sheet_.GetTile16(i);
tile16_individual_[i] = tile;
rom()->RenderBitmap(&tile16_individual_[i]);
}
auto tile = tile16_sheet_.GetTile16(i);
tile16_individual_[i] = tile;
RETURN_IF_ERROR(
tile16_individual_[i].ApplyPalette(*rom()->mutable_dungeon_palette(3)));
Renderer::GetInstance().RenderBitmap(&tile16_individual_[i]);
}
return absl::OkStatus();
}
absl::Status ScreenEditor::SaveDungeonMapTile16() {
for (int i = 0; i < 186; i++) {
int addr = zelda3::screen::kDungeonMapTile16;
if (rom()->data()[zelda3::screen::kDungeonMapExpCheck] != 0xB9) {
addr = zelda3::screen::kDungeonMapTile16Expanded;
}
gfx::TileInfo t1 = tile16_sheet_.tile_info()[i].tiles[0];
gfx::TileInfo t2 = tile16_sheet_.tile_info()[i].tiles[1];
gfx::TileInfo t3 = tile16_sheet_.tile_info()[i].tiles[2];
gfx::TileInfo t4 = tile16_sheet_.tile_info()[i].tiles[3];
auto tl = gfx::TileInfoToWord(t1);
RETURN_IF_ERROR(rom()->WriteWord(addr + (i * 8), tl));
auto tr = gfx::TileInfoToWord(t2);
RETURN_IF_ERROR(rom()->WriteWord(addr + 2 + (i * 8), tr));
auto bl = gfx::TileInfoToWord(t3);
RETURN_IF_ERROR(rom()->WriteWord(addr + 4 + (i * 8), bl));
auto br = gfx::TileInfoToWord(t4);
RETURN_IF_ERROR(rom()->WriteWord(addr + 6 + (i * 8), br));
}
return absl::OkStatus();
}
void ScreenEditor::DrawDungeonMapsTabs() {
auto current_dungeon = dungeon_maps_[selected_dungeon];
auto& current_dungeon = dungeon_maps_[selected_dungeon];
if (ImGui::BeginTabBar("##DungeonMapTabs")) {
auto nbr_floors =
current_dungeon.nbr_of_floor + current_dungeon.nbr_of_basement;
for (int i = 0; i < nbr_floors; i++) {
std::string tab_name = absl::StrFormat("Floor %d", i + 1);
if (i >= current_dungeon.nbr_of_floor) {
tab_name = absl::StrFormat("Basement %d",
i - current_dungeon.nbr_of_floor + 1);
int basement_num = current_dungeon.nbr_of_basement - i;
std::string tab_name = absl::StrFormat("Basement %d", basement_num);
if (i >= current_dungeon.nbr_of_basement) {
tab_name = absl::StrFormat("Floor %d",
i - current_dungeon.nbr_of_basement + 1);
}
if (ImGui::BeginTabItem(tab_name.c_str())) {
floor_number = i;
// screen_canvas_.LoadCustomLabels(dungeon_map_labels_[selected_dungeon]);
// screen_canvas_.set_current_labels(floor_number);
screen_canvas_.DrawBackground(ImVec2(325, 325));
screen_canvas_.DrawTileSelector(64.f);
auto boss_room = current_dungeon.boss_room;
for (int j = 0; j < 25; j++) {
if (current_dungeon.floor_rooms[floor_number][j] != 0x0F) {
int tile16_id = current_dungeon.floor_rooms[floor_number][j];
int tile_x = (tile16_id % 16) * 16;
int tile_y = (tile16_id / 16) * 16;
int tile16_id = current_dungeon.floor_gfx[floor_number][j];
int posX = ((j % 5) * 32);
int posY = ((j / 5) * 32);
if (tile16_individual_.count(tile16_id) == 0) {
auto tile = tile16_sheet_.GetTile16(tile16_id);
std::cout << "Tile16: " << tile16_id << std::endl;
rom()->RenderBitmap(&tile);
tile16_individual_[tile16_id] = tile;
tile16_individual_[tile16_id] =
tile16_sheet_.GetTile16(tile16_id);
Renderer::GetInstance().RenderBitmap(
&tile16_individual_[tile16_id]);
}
screen_canvas_.DrawBitmap(tile16_individual_[tile16_id], (posX * 2),
(posY * 2), 4.0f);
if (current_dungeon.floor_rooms[floor_number][j] == boss_room) {
screen_canvas_.DrawOutlineWithColor((posX * 2), (posY * 2), 64,
64, core::kRedPen);
64, kRedPen);
}
std::string label =
dungeon_map_labels_[selected_dungeon][floor_number][j];
screen_canvas_.DrawText(label, (posX * 2), (posY * 2));
std::string gfx_id = core::UppercaseHexByte(tile16_id);
screen_canvas_.DrawText(gfx_id, (posX * 2), (posY * 2) + 16);
}
}
@@ -316,31 +343,76 @@ void ScreenEditor::DrawDungeonMapsTabs() {
gui::InputHexWord("Boss Room", &current_dungeon.boss_room);
if (ImGui::Button("Copy Floor", ImVec2(100, 0))) {
const ImVec2 button_size = ImVec2(130, 0);
// Add Floor Button
if (ImGui::Button("Add Floor", button_size) &&
current_dungeon.nbr_of_floor < 8) {
current_dungeon.nbr_of_floor++;
dungeon_map_labels_[selected_dungeon].emplace_back();
}
ImGui::SameLine();
if (ImGui::Button("Remove Floor", button_size) &&
current_dungeon.nbr_of_floor > 0) {
current_dungeon.nbr_of_floor--;
dungeon_map_labels_[selected_dungeon].pop_back();
}
// Add Basement Button
if (ImGui::Button("Add Basement", button_size) &&
current_dungeon.nbr_of_basement < 8) {
current_dungeon.nbr_of_basement++;
dungeon_map_labels_[selected_dungeon].emplace_back();
}
ImGui::SameLine();
if (ImGui::Button("Remove Basement", button_size) &&
current_dungeon.nbr_of_basement > 0) {
current_dungeon.nbr_of_basement--;
dungeon_map_labels_[selected_dungeon].pop_back();
}
if (ImGui::Button("Copy Floor", button_size)) {
copy_button_pressed = true;
}
ImGui::SameLine();
if (ImGui::Button("Paste Floor", ImVec2(100, 0))) {
if (ImGui::Button("Paste Floor", button_size)) {
paste_button_pressed = true;
}
}
void ScreenEditor::DrawDungeonMapsEditor() {
if (!dungeon_maps_loaded_) {
if (LoadDungeonMaps().ok()) {
if (LoadDungeonMapTile16().ok()) {
auto bitmap_manager = rom()->mutable_bitmap_manager();
sheets_.emplace(0, *bitmap_manager->mutable_bitmap(212));
sheets_.emplace(1, *bitmap_manager->mutable_bitmap(213));
sheets_.emplace(2, *bitmap_manager->mutable_bitmap(214));
sheets_.emplace(3, *bitmap_manager->mutable_bitmap(215));
dungeon_maps_loaded_ = true;
} else {
ImGui::Text("Failed to load dungeon map tile16");
}
} else {
if (!LoadDungeonMaps().ok()) {
ImGui::Text("Failed to load dungeon maps");
}
if (LoadDungeonMapTile16(rom()->graphics_buffer()).ok()) {
// TODO: Load roomset gfx based on dungeon ID
sheets_.emplace(0, rom()->gfx_sheets()[212]);
sheets_.emplace(1, rom()->gfx_sheets()[213]);
sheets_.emplace(2, rom()->gfx_sheets()[214]);
sheets_.emplace(3, rom()->gfx_sheets()[215]);
dungeon_maps_loaded_ = true;
} else {
ImGui::Text("Failed to load dungeon map tile16");
}
}
if (ImGui::BeginTable("##DungeonMapToolset", 2, ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupColumn("Draw Mode");
ImGui::TableSetupColumn("Edit Mode");
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_DRAW)) {
current_mode_ = EditingMode::DRAW;
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_EDIT)) {
current_mode_ = EditingMode::EDIT;
}
ImGui::EndTable();
}
static std::vector<std::string> dungeon_names = {
@@ -350,7 +422,10 @@ void ScreenEditor::DrawDungeonMapsEditor() {
"Thieves' Town", "Ice Palace", "Misery Mire",
"Turtle Rock", "Ganon's Tower"};
if (ImGui::BeginTable("DungeonMapsTable", 4, ImGuiTableFlags_Resizable)) {
if (ImGui::BeginTable("DungeonMapsTable", 4,
ImGuiTableFlags_Resizable |
ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable)) {
ImGui::TableSetupColumn("Dungeon");
ImGui::TableSetupColumn("Map");
ImGui::TableSetupColumn("Rooms Gfx");
@@ -384,32 +459,113 @@ void ScreenEditor::DrawDungeonMapsEditor() {
if (!tilesheet_canvas_.points().empty()) {
selected_tile16_ = tilesheet_canvas_.points().front().x / 32 +
(tilesheet_canvas_.points().front().y / 32) * 16;
current_tile16_info = tile16_sheet_.tile_info().at(selected_tile16_);
// Draw the selected tile
if (!screen_canvas_.points().empty()) {
dungeon_maps_[selected_dungeon]
.floor_gfx[floor_number][selected_room] = selected_tile16_;
tilesheet_canvas_.mutable_points()->clear();
}
}
ImGui::Separator();
current_tile_canvas_.DrawBackground(ImVec2(64 * 2 + 2, 64 * 2 + 4));
current_tile_canvas_.DrawContextMenu();
current_tile_canvas_.DrawBitmap(tile16_individual_[selected_tile16_], 2,
4.0f);
current_tile_canvas_.DrawGrid(16.f);
current_tile_canvas_.DrawOverlay();
gui::InputTileInfo("TL", &current_tile16_info.tiles[0]);
ImGui::SameLine();
gui::InputTileInfo("TR", &current_tile16_info.tiles[1]);
gui::InputTileInfo("BL", &current_tile16_info.tiles[2]);
ImGui::SameLine();
gui::InputTileInfo("BR", &current_tile16_info.tiles[3]);
if (ImGui::Button("Modify Tile16")) {
tile16_sheet_.ModifyTile16(
rom()->graphics_buffer(), current_tile16_info.tiles[0],
current_tile16_info.tiles[1], current_tile16_info.tiles[2],
current_tile16_info.tiles[3], selected_tile16_, 212);
tile16_individual_[selected_tile16_] =
tile16_sheet_.GetTile16(selected_tile16_);
RETURN_VOID_IF_ERROR(tile16_individual_[selected_tile16_].ApplyPalette(
*rom()->mutable_dungeon_palette(3)));
Renderer::GetInstance().RenderBitmap(
&tile16_individual_[selected_tile16_]);
}
}
ImGui::EndChild();
ImGui::TableNextColumn();
tilemap_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
tilemap_canvas_.DrawBackground();
tilemap_canvas_.DrawContextMenu();
if (tilemap_canvas_.DrawTileSelector(16.f)) {
// Get the tile8 ID to use for the tile16 drawing above
selected_tile8_ = tilemap_canvas_.GetTileIdFromMousePos();
}
tilemap_canvas_.DrawBitmapTable(sheets_);
tilemap_canvas_.DrawGrid();
tilemap_canvas_.DrawOverlay();
ImGui::Text("Selected tile8: %d", selected_tile8_);
ImGui::Separator();
ImGui::Text("For use with custom inserted graphics assembly patches.");
if (ImGui::Button("Load GFX from BIN file")) LoadBinaryGfx();
ImGui::EndTable();
}
}
void ScreenEditor::LoadBinaryGfx() {
std::string bin_file = core::FileDialogWrapper::ShowOpenFileDialog();
if (!bin_file.empty()) {
std::ifstream file(bin_file, std::ios::binary);
if (file.is_open()) {
// Read the gfx data into a buffer
std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
gfx_bin_data_ = converted_bin;
tile16_sheet_.clear();
if (LoadDungeonMapTile16(converted_bin, true).ok()) {
sheets_.clear();
std::vector<std::vector<uint8_t>> gfx_sheets;
for (int i = 0; i < 4; i++) {
gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
converted_bin.begin() + ((i + 1) * 0x1000));
sheets_.emplace(i, gfx::Bitmap(128, 32, 8, gfx_sheets[i]));
sheets_[i].ApplyPalette(*rom()->mutable_dungeon_palette(3));
Renderer::GetInstance().RenderBitmap(&sheets_[i]);
}
binary_gfx_loaded_ = true;
} else {
status_ = absl::InternalError("Failed to load dungeon map tile16");
}
file.close();
}
}
}
void ScreenEditor::DrawTitleScreenEditor() {
TAB_ITEM("Title Screen")
END_TAB_ITEM()
if (ImGui::BeginTabItem("Title Screen")) {
ImGui::EndTabItem();
}
}
void ScreenEditor::DrawNamingScreenEditor() {
TAB_ITEM("Naming Screen")
END_TAB_ITEM()
if (ImGui::BeginTabItem("Naming Screen")) {
ImGui::EndTabItem();
}
}
void ScreenEditor::DrawOverworldMapEditor() {
TAB_ITEM("Overworld Map")
END_TAB_ITEM()
if (ImGui::BeginTabItem("Overworld Map")) {
ImGui::EndTabItem();
}
}
void ScreenEditor::DrawToolset() {

View File

@@ -1,23 +1,18 @@
#ifndef YAZE_APP_EDITOR_SCREEN_EDITOR_H
#define YAZE_APP_EDITOR_SCREEN_EDITOR_H
#include "imgui/imgui.h"
#include <array>
#include "absl/status/status.h"
#include "app/core/constants.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/tilesheet.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "app/zelda3/screen/dungeon_map.h"
#include "app/zelda3/screen/inventory.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -38,7 +33,7 @@ namespace editor {
* The class inherits from the SharedRom class.
*/
class ScreenEditor : public SharedRom, public Editor {
public:
public:
ScreenEditor() {
screen_canvas_.SetCanvasSize(ImVec2(512, 512));
type_ = EditorType::kScreen;
@@ -55,7 +50,7 @@ class ScreenEditor : public SharedRom, public Editor {
absl::Status SaveDungeonMaps();
private:
private:
void DrawTitleScreenEditor();
void DrawNamingScreenEditor();
void DrawOverworldMapEditor();
@@ -65,42 +60,58 @@ class ScreenEditor : public SharedRom, public Editor {
void DrawInventoryToolset();
absl::Status LoadDungeonMaps();
absl::Status LoadDungeonMapTile16();
absl::Status LoadDungeonMapTile16(const std::vector<uint8_t> &gfx_data,
bool bin_mode = false);
absl::Status SaveDungeonMapTile16();
void DrawDungeonMapsTabs();
void DrawDungeonMapsEditor();
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
void LoadBinaryGfx();
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
enum class EditingMode { DRAW, EDIT };
EditingMode current_mode_ = EditingMode::DRAW;
bool dungeon_maps_loaded_ = false;
bool binary_gfx_loaded_ = false;
int selected_tile16_ = 0;
int selected_dungeon = 0;
uint8_t selected_room = 0;
uint8_t boss_room = 0;
int selected_tile16_ = 0;
int selected_tile8_ = 0;
int selected_dungeon = 0;
int floor_number = 1;
bool copy_button_pressed = false;
bool paste_button_pressed = false;
Bytes all_gfx_;
zelda3::screen::Inventory inventory_;
gfx::SnesPalette palette_;
gui::Canvas screen_canvas_;
gui::Canvas tilesheet_canvas_;
gui::Canvas tilemap_canvas_;
gfx::BitmapTable sheets_;
gfx::Tilesheet tile16_sheet_;
std::vector<uint8_t> all_gfx_;
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
std::array<uint16_t, 4> current_tile16_data_;
std::vector<uint8_t> gfx_bin_data_;
absl::Status status_;
gfx::SnesPalette palette_;
gfx::BitmapTable sheets_;
gfx::Tilesheet tile16_sheet_;
gfx::InternalTile16 current_tile16_info;
gui::Canvas current_tile_canvas_{"##CurrentTileCanvas"};
gui::Canvas screen_canvas_;
gui::Canvas tilesheet_canvas_;
gui::Canvas tilemap_canvas_{"##TilemapCanvas",
ImVec2(128 + 2, (192) + 4),
gui::CanvasGridSize::k8x8, 2.f};
zelda3::screen::Inventory inventory_;
};
} // namespace editor
} // namespace app
} // namespace yaze
} // namespace editor
} // namespace app
} // namespace yaze
#endif

View File

@@ -1,14 +1,13 @@
#include "tile16_editor.h"
#include "ImGuiFileDialog/ImGuiFileDialog.h"
#include "imgui/imgui.h"
#include <cmath>
#include "ImGuiFileDialog/ImGuiFileDialog.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/platform/renderer.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
@@ -19,11 +18,14 @@
#include "app/gui/style.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
using core::Renderer;
using ImGui::BeginChild;
using ImGui::BeginMenu;
using ImGui::BeginMenuBar;
@@ -48,14 +50,13 @@ using ImGui::TableSetupColumn;
using ImGui::Text;
absl::Status Tile16Editor::InitBlockset(
gfx::Bitmap* tile16_blockset_bmp, gfx::Bitmap current_gfx_bmp,
const gfx::Bitmap& tile16_blockset_bmp, const gfx::Bitmap& current_gfx_bmp,
const std::vector<gfx::Bitmap>& tile16_individual,
uint8_t all_tiles_types[0x200]) {
all_tiles_types_ = all_tiles_types;
tile16_blockset_bmp_ = tile16_blockset_bmp;
tile16_individual_ = tile16_individual;
current_gfx_bmp_ = current_gfx_bmp;
tile8_gfx_data_ = current_gfx_bmp_.vector();
RETURN_IF_ERROR(LoadTile8());
ImVector<std::string> tile16_names;
for (int i = 0; i < 0x200; ++i) {
@@ -75,7 +76,7 @@ absl::Status Tile16Editor::Update() {
RETURN_IF_ERROR(DrawMenu());
if (BeginTabBar("Tile16 Editor Tabs")) {
RETURN_IF_ERROR(DrawTile16Editor());
DrawTile16Editor();
RETURN_IF_ERROR(UpdateTile16Transfer());
EndTabBar();
}
@@ -97,7 +98,7 @@ absl::Status Tile16Editor::DrawMenu() {
return absl::OkStatus();
}
absl::Status Tile16Editor::DrawTile16Editor() {
void Tile16Editor::DrawTile16Editor() {
if (BeginTabItem("Tile16 Editing")) {
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
ImVec2(0, 0))) {
@@ -108,18 +109,23 @@ absl::Status Tile16Editor::DrawTile16Editor() {
TableHeadersRow();
TableNextRow();
TableNextColumn();
RETURN_IF_ERROR(UpdateBlockset());
status_ = UpdateBlockset();
if (!status_.ok()) {
EndTable();
}
TableNextColumn();
RETURN_IF_ERROR(UpdateTile16Edit());
RETURN_IF_ERROR(DrawTileEditControls());
status_ = UpdateTile16Edit();
if (status_ != absl::OkStatus()) {
EndTable();
}
status_ = DrawTileEditControls();
EndTable();
}
EndTabItem();
}
return absl::OkStatus();
}
absl::Status Tile16Editor::UpdateBlockset() {
@@ -127,14 +133,12 @@ absl::Status Tile16Editor::UpdateBlockset() {
gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
blockset_canvas_.DrawBackground();
gui::EndPadding();
{
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawBitmap(*tile16_blockset_bmp_, 0, map_blockset_loaded_);
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
EndChild();
}
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
blockset_canvas_.DrawGrid();
blockset_canvas_.DrawOverlay();
EndChild();
if (!blockset_canvas_.points().empty()) {
notify_tile16.mutable_get() = blockset_canvas_.GetTileIdFromMousePos();
@@ -142,11 +146,11 @@ absl::Status Tile16Editor::UpdateBlockset() {
if (notify_tile16.modified()) {
current_tile16_ = notify_tile16.get();
current_tile16_bmp_ = &tile16_individual_[notify_tile16];
current_tile16_bmp_ = tile16_individual_[notify_tile16];
auto ow_main_pal_group = rom()->palette_group().overworld_main;
RETURN_IF_ERROR(current_tile16_bmp_->ApplyPalette(
RETURN_IF_ERROR(current_tile16_bmp_.ApplyPalette(
ow_main_pal_group[current_palette_]));
rom()->RenderBitmap(current_tile16_bmp_);
Renderer::GetInstance().RenderBitmap(&current_tile16_bmp_);
}
}
@@ -166,8 +170,8 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
// Calculate the pixel start position within the Tile16
ImVec2 start_position;
start_position.x = ((tile_index_x) / 4) * 0x40;
start_position.y = ((tile_index_y) / 4) * 0x40;
start_position.x = tile_index_x * 0x40;
start_position.y = tile_index_y * 0x40;
std::cout << "Start Position X: " << start_position.x << std::endl;
std::cout << "Start Position Y: " << start_position.y << std::endl;
@@ -177,7 +181,7 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
int pixel_index =
(start_position.y + y) * tile16_size + ((start_position.x) + x);
int gfx_pixel_index = y * tile8_size + x;
current_tile16_bmp_->WriteToPixel(
current_tile16_bmp_.WriteToPixel(
pixel_index,
current_gfx_individual_[current_tile8_].data()[gfx_pixel_index]);
}
@@ -197,7 +201,8 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
RETURN_IF_ERROR(
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
rom()->UpdateBitmap(&current_gfx_individual_[current_tile8_]);
Renderer::GetInstance().UpdateBitmap(
&current_gfx_individual_[current_tile8_]);
}
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 4.0f);
tile8_source_canvas_.DrawGrid();
@@ -214,20 +219,21 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
RETURN_IF_ERROR(
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
rom()->UpdateBitmap(&current_gfx_individual_[current_tile8_]);
Renderer::GetInstance().UpdateBitmap(
&current_gfx_individual_[current_tile8_]);
}
if (BeginChild("Tile16 Editor Options",
ImVec2(GetContentRegionAvail().x, 0x50), true)) {
tile16_edit_canvas_.DrawBackground();
tile16_edit_canvas_.DrawContextMenu(current_tile16_bmp_);
tile16_edit_canvas_.DrawBitmap(*current_tile16_bmp_, 0, 0, 4.0f);
tile16_edit_canvas_.DrawContextMenu(&current_tile16_bmp_);
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
if (!tile8_source_canvas_.points().empty()) {
if (tile16_edit_canvas_.DrawTilePainter(
current_gfx_individual_[current_tile8_], 16, 2.0f)) {
RETURN_IF_ERROR(
DrawToCurrentTile16(tile16_edit_canvas_.drawn_tile_position()));
rom()->UpdateBitmap(current_tile16_bmp_);
Renderer::GetInstance().UpdateBitmap(&current_tile16_bmp_);
}
}
tile16_edit_canvas_.DrawGrid();
@@ -258,10 +264,11 @@ absl::Status Tile16Editor::DrawTileEditControls() {
if (value > 0x00) {
RETURN_IF_ERROR(
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value));
Renderer::GetInstance().UpdateBitmap(&current_gfx_bmp_);
RETURN_IF_ERROR(
current_tile16_bmp_->ApplyPaletteWithTransparent(palette, value));
rom()->UpdateBitmap(&current_gfx_bmp_);
rom()->UpdateBitmap(current_tile16_bmp_);
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value));
Renderer::GetInstance().UpdateBitmap(&current_tile16_bmp_);
}
}
@@ -290,7 +297,6 @@ absl::Status Tile16Editor::LoadTile8() {
// Calculate the position in the current gfx data
int num_columns = current_gfx_bmp_.width() / 8;
int num_rows = current_gfx_bmp_.height() / 8;
int x = (index % num_columns) * 8 + tx;
int y = (index / num_columns) * 8 + ty;
int gfx_position = x + (y * 0x100);
@@ -310,7 +316,7 @@ absl::Status Tile16Editor::LoadTile8() {
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
RETURN_IF_ERROR(current_gfx_individual_[index].ApplyPaletteWithTransparent(
ow_main_pal_group[0], current_palette_));
rom()->RenderBitmap(&current_gfx_individual_[index]);
Renderer::GetInstance().RenderBitmap(&current_gfx_individual_[index]);
}
map_blockset_loaded_ = true;
@@ -318,8 +324,17 @@ absl::Status Tile16Editor::LoadTile8() {
return absl::OkStatus();
}
// ============================================================================
// Tile16 Transfer
absl::Status Tile16Editor::SetCurrentTile(int id) {
current_tile16_ = id;
current_tile16_bmp_ = tile16_individual_[id];
auto ow_main_pal_group = rom()->palette_group().overworld_main;
RETURN_IF_ERROR(
current_tile16_bmp_.ApplyPalette(ow_main_pal_group[current_palette_]));
Renderer::GetInstance().RenderBitmap(&current_tile16_bmp_);
return absl::OkStatus();
}
#pragma mark - Tile16Transfer
absl::Status Tile16Editor::UpdateTile16Transfer() {
if (BeginTabItem("Tile16 Transfer")) {
@@ -363,16 +378,15 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
// TODO: Implement tile16 transfer
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_.set_current_map(0);
palette_ = transfer_overworld_.AreaPalette();
palette_ = transfer_overworld_.current_area_palette();
// Create the tile16 blockset image
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
0x80, 0x2000, 0x80, transfer_overworld_.Tile16Blockset(),
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
0x80, 0x2000, 0x80, transfer_overworld_.tile16_blockset_data(),
transfer_blockset_bmp_, palette_));
transfer_blockset_loaded_ = true;
}
@@ -385,16 +399,6 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
return absl::OkStatus();
}
absl::Status Tile16Editor::SetCurrentTile(int id) {
current_tile16_ = id;
current_tile16_bmp_ = &tile16_individual_[id];
auto ow_main_pal_group = rom()->palette_group().overworld_main;
RETURN_IF_ERROR(
current_tile16_bmp_->ApplyPalette(ow_main_pal_group[current_palette_]));
rom()->RenderBitmap(current_tile16_bmp_);
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,23 +1,16 @@
#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/common.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/utils/gfx_context.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/tilesheet.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -26,17 +19,17 @@ namespace editor {
/**
* @brief Popup window to edit Tile16 data
*/
class Tile16Editor : public context::GfxContext, public SharedRom {
class Tile16Editor : public GfxContext, public SharedRom {
public:
absl::Status InitBlockset(gfx::Bitmap* tile16_blockset_bmp,
gfx::Bitmap current_gfx_bmp,
absl::Status InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
const gfx::Bitmap& current_gfx_bmp,
const std::vector<gfx::Bitmap>& tile16_individual,
uint8_t all_tiles_types[0x200]);
absl::Status Update();
absl::Status DrawMenu();
absl::Status DrawTile16Editor();
void DrawTile16Editor();
absl::Status UpdateTile16Transfer();
absl::Status UpdateBlockset();
@@ -75,38 +68,32 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
// Tile16 blockset for selecting the tile to edit
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100, 0x4000),
gui::CanvasGridSize::k32x32};
gfx::Bitmap* tile16_blockset_bmp_;
gfx::Bitmap tile16_blockset_bmp_;
// Canvas for editing the selected tile
gui::Canvas tile16_edit_canvas_{"Tile16EditCanvas", ImVec2(0x40, 0x40),
gui::CanvasGridSize::k64x64};
gfx::Bitmap* current_tile16_bmp_;
gfx::Bitmap current_tile16_bmp_;
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
gui::Canvas tile8_source_canvas_{
"Tile8SourceCanvas",
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
ImVec2(gfx::kTilesheetWidth * 4, gfx::kTilesheetHeight * 0x10 * 4),
gui::CanvasGridSize::k32x32};
gfx::Bitmap current_gfx_bmp_;
gui::Canvas transfer_canvas_;
gfx::Bitmap transfer_blockset_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_;
std::vector<uint8_t> tile8_gfx_data_;
PaletteEditor palette_editor_;
gfx::SnesPalette palette_;
zelda3::overworld::Overworld transfer_overworld_;
gfx::BitmapTable graphics_bin_;
absl::Status status_;
Rom transfer_rom_;
absl::Status transfer_status_;
@@ -115,4 +102,4 @@ class Tile16Editor : public context::GfxContext, public SharedRom {
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_TILE16EDITOR_H
#endif // YAZE_APP_EDITOR_TILE16EDITOR_H

View File

@@ -1,10 +0,0 @@
#include "master_editor.h"
namespace yaze {
namespace app {
namespace editor {
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,182 @@
#include "message_data.h"
#include "app/core/common.h"
namespace yaze {
namespace app {
namespace editor {
uint8_t FindMatchingCharacter(char value) {
for (const auto [key, char_value] : CharEncoder) {
if (value == char_value) {
return key;
}
}
return 0xFF;
}
uint8_t FindDictionaryEntry(uint8_t value) {
if (value < DICTOFF || value == 0xFF) {
return -1;
}
return value - DICTOFF;
}
TextElement FindMatchingCommand(uint8_t b) {
TextElement empty_element;
for (const auto& text_element : TextCommands) {
if (text_element.ID == b) {
return text_element;
}
}
return empty_element;
}
TextElement FindMatchingSpecial(uint8_t value) {
auto it = std::find_if(SpecialChars.begin(), SpecialChars.end(),
[value](const TextElement& text_element) {
return text_element.ID == value;
});
if (it != SpecialChars.end()) {
return *it;
}
return TextElement();
}
ParsedElement FindMatchingElement(const std::string& str) {
std::smatch match;
for (auto& textElement : TextCommands) {
match = textElement.MatchMe(str);
if (match.size() > 0) {
if (textElement.HasArgument) {
return ParsedElement(textElement,
std::stoi(match[1].str(), nullptr, 16));
} else {
return ParsedElement(textElement, 0);
}
}
}
const auto dictionary_element =
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
match = dictionary_element.MatchMe(str);
if (match.size() > 0) {
return ParsedElement(dictionary_element,
DICTOFF + std::stoi(match[1].str(), nullptr, 16));
}
return ParsedElement();
}
std::string ParseTextDataByte(uint8_t value) {
if (CharEncoder.contains(value)) {
char c = CharEncoder.at(value);
std::string str = "";
str.push_back(c);
return str;
}
// Check for command.
TextElement textElement = FindMatchingCommand(value);
if (!textElement.Empty()) {
return textElement.GenericToken;
}
// Check for special characters.
textElement = FindMatchingSpecial(value);
if (!textElement.Empty()) {
return textElement.GenericToken;
}
// Check for dictionary.
int dictionary = FindDictionaryEntry(value);
if (dictionary >= 0) {
return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary);
}
return "";
}
std::vector<uint8_t> ParseMessageToData(std::string str) {
std::vector<uint8_t> bytes;
std::string temp_string = str;
int pos = 0;
while (pos < temp_string.size()) {
// Get next text fragment.
if (temp_string[pos] == '[') {
int next = temp_string.find(']', pos);
if (next == -1) {
break;
}
ParsedElement parsedElement =
FindMatchingElement(temp_string.substr(pos, next - pos + 1));
const auto dictionary_element =
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
if (!parsedElement.Active) {
core::logf("Error parsing message: %s", temp_string);
break;
} else if (parsedElement.Parent == dictionary_element) {
bytes.push_back(parsedElement.Value);
} else {
bytes.push_back(parsedElement.Parent.ID);
if (parsedElement.Parent.HasArgument) {
bytes.push_back(parsedElement.Value);
}
}
pos = next + 1;
continue;
} else {
uint8_t bb = FindMatchingCharacter(temp_string[pos++]);
if (bb != 0xFF) {
core::logf("Error parsing message: %s", temp_string);
bytes.push_back(bb);
}
}
}
return bytes;
}
std::vector<DictionaryEntry> BuildDictionaryEntries(app::Rom* rom) {
std::vector<DictionaryEntry> AllDictionaries;
for (int i = 0; i < kNumDictionaryEntries; i++) {
std::vector<uint8_t> bytes;
std::stringstream stringBuilder;
int address = core::SnesToPc(
kTextData + (rom->data()[kPointersDictionaries + (i * 2) + 1] << 8) +
rom->data()[kPointersDictionaries + (i * 2)]);
int temppush_backress = core::SnesToPc(
kTextData +
(rom->data()[kPointersDictionaries + ((i + 1) * 2) + 1] << 8) +
rom->data()[kPointersDictionaries + ((i + 1) * 2)]);
while (address < temppush_backress) {
uint8_t uint8_tDictionary = rom->data()[address++];
bytes.push_back(uint8_tDictionary);
stringBuilder << ParseTextDataByte(uint8_tDictionary);
}
AllDictionaries.push_back(DictionaryEntry{(uint8_t)i, stringBuilder.str()});
}
std::sort(AllDictionaries.begin(), AllDictionaries.end(),
[](const DictionaryEntry& a, const DictionaryEntry& b) {
return a.Contents.size() > b.Contents.size();
});
return AllDictionaries;
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,23 +1,90 @@
#ifndef YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
#define YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
#include <regex>
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_replace.h"
#include "app/rom.h"
namespace yaze {
namespace app {
namespace editor {
const uint8_t MESSAGETERMINATOR = 0x7F;
const uint8_t kMessageTerminator = 0x7F;
const std::string BANKToken = "BANK";
const std::string DICTIONARYTOKEN = "D";
constexpr uint8_t DICTOFF = 0x88;
static std::string AddNewLinesToCommands(std::string str);
static std::string ReplaceAllDictionaryWords(std::string str);
static std::vector<uint8_t> ParseMessageToData(std::string str);
static const std::unordered_map<uint8_t, wchar_t> CharEncoder = {
{0x00, 'A'}, {0x01, 'B'}, {0x02, 'C'}, {0x03, 'D'}, {0x04, 'E'},
{0x05, 'F'}, {0x06, 'G'}, {0x07, 'H'}, {0x08, 'I'}, {0x09, 'J'},
{0x0A, 'K'}, {0x0B, 'L'}, {0x0C, 'M'}, {0x0D, 'N'}, {0x0E, 'O'},
{0x0F, 'P'}, {0x10, 'Q'}, {0x11, 'R'}, {0x12, 'S'}, {0x13, 'T'},
{0x14, 'U'}, {0x15, 'V'}, {0x16, 'W'}, {0x17, 'X'}, {0x18, 'Y'},
{0x19, 'Z'}, {0x1A, 'a'}, {0x1B, 'b'}, {0x1C, 'c'}, {0x1D, 'd'},
{0x1E, 'e'}, {0x1F, 'f'}, {0x20, 'g'}, {0x21, 'h'}, {0x22, 'i'},
{0x23, 'j'}, {0x24, 'k'}, {0x25, 'l'}, {0x26, 'm'}, {0x27, 'n'},
{0x28, 'o'}, {0x29, 'p'}, {0x2A, 'q'}, {0x2B, 'r'}, {0x2C, 's'},
{0x2D, 't'}, {0x2E, 'u'}, {0x2F, 'v'}, {0x30, 'w'}, {0x31, 'x'},
{0x32, 'y'}, {0x33, 'z'}, {0x34, '0'}, {0x35, '1'}, {0x36, '2'},
{0x37, '3'}, {0x38, '4'}, {0x39, '5'}, {0x3A, '6'}, {0x3B, '7'},
{0x3C, '8'}, {0x3D, '9'}, {0x3E, '!'}, {0x3F, '?'}, {0x40, '-'},
{0x41, '.'}, {0x42, ','}, {0x44, '>'}, {0x45, '('}, {0x46, ')'},
{0x4C, '"'}, {0x51, '\''}, {0x59, ' '}, {0x5A, '<'}, {0x5F, L'¡'},
{0x60, L'¡'}, {0x61, L'¡'}, {0x62, L' '}, {0x63, L' '}, {0x64, L' '},
{0x65, ' '}, {0x66, '_'},
};
const std::string CHEESE = "\uBEBE"; // Inserted into commands to protect
// them from dictionary replacements.
uint8_t FindMatchingCharacter(char value);
uint8_t FindDictionaryEntry(uint8_t value);
std::vector<uint8_t> ParseMessageToData(std::string str);
struct DictionaryEntry {
uint8_t ID;
std::string Contents;
std::vector<uint8_t> Data;
int Length;
std::string Token;
DictionaryEntry() = default;
DictionaryEntry(uint8_t i, std::string s)
: Contents(s), ID(i), Length(s.length()) {
Token = absl::StrFormat("[%s:%00X]", DICTIONARYTOKEN, ID);
Data = ParseMessageToData(Contents);
}
bool ContainedInString(std::string s) {
return s.find(Contents) != std::string::npos;
}
std::string ReplaceInstancesOfIn(std::string s) {
std::string replacedString = s;
size_t pos = replacedString.find(Contents);
while (pos != std::string::npos) {
replacedString.replace(pos, Contents.length(), Token);
pos = replacedString.find(Contents, pos + Token.length());
}
return replacedString;
}
};
constexpr int kTextData = 0xE0000;
constexpr int kTextDataEnd = 0xE7FFF;
constexpr int kNumDictionaryEntries = 97;
constexpr int kPointersDictionaries = 0x74703;
std::vector<DictionaryEntry> BuildDictionaryEntries(app::Rom* rom);
std::string ReplaceAllDictionaryWords(std::string str,
std::vector<DictionaryEntry> dictionary);
// Inserted into commands to protect them from dictionary replacements.
const std::string CHEESE = "\uBEBE";
struct MessageData {
int ID;
@@ -49,35 +116,13 @@ struct MessageData {
ContentsParsed = other.ContentsParsed;
}
void SetMessage(std::string messageString) {
ContentsParsed = messageString;
RawString = OptimizeMessageForDictionary(messageString);
RecalculateData();
}
std::string ToString() {
return absl::StrFormat("%0X - %s", ID, ContentsParsed);
}
std::string GetReadableDumpedContents() {
std::stringstream stringBuilder;
for (const auto& b : Data) {
stringBuilder << absl::StrFormat("%0X ", b);
}
stringBuilder << absl::StrFormat("%00X", MESSAGETERMINATOR);
return absl::StrFormat(
"[[[[\r\nMessage "
"%000X]]]]\r\n[Contents]\r\n%s\r\n\r\n[Data]\r\n%s"
"\r\n\r\n\r\n\r\n",
ID, AddNewLinesToCommands(ContentsParsed), stringBuilder.str());
}
std::string GetDumpedContents() {
return absl::StrFormat("%000X : %s\r\n\r\n", ID, ContentsParsed);
}
std::string OptimizeMessageForDictionary(std::string messageString) {
std::string OptimizeMessageForDictionary(
std::string messageString,
const std::vector<DictionaryEntry>& dictionary) {
std::stringstream protons;
bool command = false;
for (const auto& c : messageString) {
@@ -94,16 +139,18 @@ struct MessageData {
}
std::string protonsString = protons.str();
std::string replacedString = ReplaceAllDictionaryWords(protonsString);
std::string replacedString =
ReplaceAllDictionaryWords(protonsString, dictionary);
std::string finalString =
absl::StrReplaceAll(replacedString, {{CHEESE, ""}});
return finalString;
}
void RecalculateData() {
Data = ParseMessageToData(RawString);
DataParsed = ParseMessageToData(ContentsParsed);
void SetMessage(const std::string& message,
const std::vector<DictionaryEntry>& dictionary) {
RawString = message;
ContentsParsed = OptimizeMessageForDictionary(message, dictionary);
}
};
@@ -155,8 +202,64 @@ struct TextElement {
}
bool Empty() { return ID == 0; }
// Comparison operator
bool operator==(const TextElement& other) const { return ID == other.ID; }
};
static const std::vector<TextElement> TextCommands = {
TextElement(0x6B, "W", true, "Window border"),
TextElement(0x6D, "P", true, "Window position"),
TextElement(0x6E, "SPD", true, "Scroll speed"),
TextElement(0x7A, "S", true, "Text draw speed"),
TextElement(0x77, "C", true, "Text color"),
TextElement(0x6A, "L", false, "Player name"),
TextElement(0x74, "1", false, "Line 1"),
TextElement(0x75, "2", false, "Line 2"),
TextElement(0x76, "3", false, "Line 3"),
TextElement(0x7E, "K", false, "Wait for key"),
TextElement(0x73, "V", false, "Scroll text"),
TextElement(0x78, "WT", true, "Delay X"),
TextElement(0x6C, "N", true, "BCD number"),
TextElement(0x79, "SFX", true, "Sound effect"),
TextElement(0x71, "CH3", false, "Choose 3"),
TextElement(0x72, "CH2", false, "Choose 2 high"),
TextElement(0x6F, "CH2L", false, "Choose 2 low"),
TextElement(0x68, "CH2I", false, "Choose 2 indented"),
TextElement(0x69, "CHI", false, "Choose item"),
TextElement(0x67, "IMG", false, "Next attract image"),
TextElement(0x80, BANKToken, false, "Bank marker (automatic)"),
TextElement(0x70, "NONO", false, "Crash"),
};
TextElement FindMatchingCommand(uint8_t b);
static const std::vector<TextElement> SpecialChars = {
TextElement(0x43, "...", false, "Ellipsis …"),
TextElement(0x4D, "UP", false, "Arrow ↑"),
TextElement(0x4E, "DOWN", false, "Arrow ↓"),
TextElement(0x4F, "LEFT", false, "Arrow ←"),
TextElement(0x50, "RIGHT", false, "Arrow →"),
TextElement(0x5B, "A", false, "Button Ⓐ"),
TextElement(0x5C, "B", false, "Button Ⓑ"),
TextElement(0x5D, "X", false, "Button ⓧ"),
TextElement(0x5E, "Y", false, "Button ⓨ"),
TextElement(0x52, "HP1L", false, "1 HP left"),
TextElement(0x53, "HP1R", false, "1 HP right"),
TextElement(0x54, "HP2L", false, "2 HP left"),
TextElement(0x55, "HP3L", false, "3 HP left"),
TextElement(0x56, "HP3R", false, "3 HP right"),
TextElement(0x57, "HP4L", false, "4 HP left"),
TextElement(0x58, "HP4R", false, "4 HP right"),
TextElement(0x47, "HY0", false, "Hieroglyph ☥"),
TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"),
TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"),
TextElement(0x4A, "LFL", false, "Link face left"),
TextElement(0x4B, "LFR", false, "Link face right"),
};
TextElement FindMatchingSpecial(uint8_t b);
struct ParsedElement {
TextElement Parent;
uint8_t Value;
@@ -170,8 +273,12 @@ struct ParsedElement {
}
};
ParsedElement FindMatchingElement(const std::string& str);
std::string ParseTextDataByte(uint8_t value);
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H
#endif // YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H

View File

@@ -1,7 +1,5 @@
#include "message_editor.h"
#include <regex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -9,26 +7,24 @@
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "app/core/common.h"
#include "app/editor/utils/editor.h"
#include "app/core/platform/renderer.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/style.h"
#include "app/rom.h"
#include "imgui.h"
namespace yaze {
namespace app {
namespace editor {
using ImGui::Begin;
using core::Renderer;
using ImGui::BeginChild;
using ImGui::BeginTable;
using ImGui::Button;
using ImGui::End;
using ImGui::EndChild;
using ImGui::EndTable;
using ImGui::InputText;
@@ -37,216 +33,24 @@ using ImGui::SameLine;
using ImGui::Separator;
using ImGui::TableHeadersRow;
using ImGui::TableNextColumn;
using ImGui::TableNextRow;
using ImGui::TableSetupColumn;
using ImGui::Text;
using ImGui::TextWrapped;
using ImGui::TreeNode;
static ParsedElement FindMatchingElement(string str) {
std::smatch match;
for (auto& textElement : TextCommands) {
match = textElement.MatchMe(str);
if (match.size() > 0) {
if (textElement.HasArgument) {
return ParsedElement(textElement,
std::stoi(match[1].str(), nullptr, 16));
} else {
return ParsedElement(textElement, 0);
}
}
}
constexpr ImGuiTableFlags kMessageTableFlags = ImGuiTableFlags_Hideable |
ImGuiTableFlags_Borders |
ImGuiTableFlags_Resizable;
match = DictionaryElement.MatchMe(str);
if (match.size() > 0) {
return ParsedElement(DictionaryElement,
DICTOFF + std::stoi(match[1].str(), nullptr, 16));
}
return ParsedElement();
}
static string ReplaceAllDictionaryWords(string str) {
string temp = str;
for (const auto& entry : AllDictionaries) {
if (absl::StrContains(temp, entry.Contents)) {
temp = absl::StrReplaceAll(temp, {{entry.Contents, entry.Contents}});
}
}
return temp;
}
static std::vector<uint8_t> ParseMessageToData(string str) {
std::vector<uint8_t> bytes;
string tempString = str;
int pos = 0;
while (pos < tempString.size()) {
// Get next text fragment.
if (tempString[pos] == '[') {
int next = tempString.find(']', pos);
if (next == -1) {
break;
}
ParsedElement parsedElement =
FindMatchingElement(tempString.substr(pos, next - pos + 1));
if (!parsedElement.Active) {
break; // TODO: handle badness.
// } else if (parsedElement.Parent == DictionaryElement) {
// bytes.push_back(parsedElement.Value);
} else {
bytes.push_back(parsedElement.Parent.ID);
if (parsedElement.Parent.HasArgument) {
bytes.push_back(parsedElement.Value);
}
}
pos = next + 1;
continue;
} else {
uint8_t bb = MessageEditor::FindMatchingCharacter(tempString[pos++]);
if (bb != 0xFF) {
// TODO: handle badness.
bytes.push_back(bb);
}
}
}
return bytes;
}
absl::Status MessageEditor::Update() {
if (rom()->is_loaded() && !data_loaded_) {
RETURN_IF_ERROR(Initialize());
CurrentMessage = ListOfTexts[1];
data_loaded_ = true;
}
if (BeginTable("##MessageEditor", 3,
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable)) {
TableSetupColumn("List");
TableSetupColumn("Contents");
TableSetupColumn("Commands");
TableHeadersRow();
TableNextColumn();
DrawMessageList();
TableNextColumn();
DrawCurrentMessage();
TableNextColumn();
DrawTextCommands();
EndTable();
}
return absl::OkStatus();
}
void MessageEditor::DrawMessageList() {
if (InputText("Search", &search_text_)) {
DisplayedMessages.clear();
for (const auto& message : ListOfTexts) {
if (absl::StrContains(message.ContentsParsed, search_text_)) {
DisplayedMessages.push_back(message);
}
}
}
if (BeginChild("##MessagesList", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (BeginTable("##MessagesTable", 3,
ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders |
ImGuiTableFlags_Resizable)) {
TableSetupColumn("ID");
TableSetupColumn("Contents");
TableSetupColumn("Data");
TableHeadersRow();
for (const auto& message : ListOfTexts) {
TableNextColumn();
if (Button(core::UppercaseHexWord(message.ID).c_str())) {
CurrentMessage = message;
DrawMessagePreview();
}
TableNextColumn();
TextWrapped("%s", ParsedMessages[message.ID].c_str());
TableNextColumn();
TextWrapped(
"%s",
core::UppercaseHexLong(ListOfTexts[message.ID].Address).c_str());
}
EndTable();
}
EndChild();
}
}
void MessageEditor::DrawCurrentMessage() {
Button(absl::StrCat("Message ", CurrentMessage.ID).c_str());
if (InputTextMultiline("##MessageEditor", &ParsedMessages[CurrentMessage.ID],
ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
CurrentMessage.Data = ParseMessageToData(message_text_box_.text);
DrawMessagePreview();
}
Separator();
Text("Font Graphics");
gui::BeginPadding(1);
BeginChild("MessageEditorCanvas", ImVec2(0, 130));
font_gfx_canvas_.DrawBackground();
font_gfx_canvas_.DrawContextMenu();
font_gfx_canvas_.DrawBitmap(font_gfx_bitmap_, 0, 0);
font_gfx_canvas_.DrawGrid();
font_gfx_canvas_.DrawOverlay();
EndChild();
gui::EndPadding();
Separator();
Text("Message Preview");
if (Button("Refresh Bitmap")) {
rom()->UpdateBitmap(&current_font_gfx16_bitmap_);
}
gui::BeginPadding(1);
BeginChild("CurrentGfxFont", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
current_font_gfx16_canvas_.DrawBackground();
gui::EndPadding();
current_font_gfx16_canvas_.DrawContextMenu();
current_font_gfx16_canvas_.DrawBitmap(current_font_gfx16_bitmap_, 0, 0);
current_font_gfx16_canvas_.DrawGrid();
current_font_gfx16_canvas_.DrawOverlay();
EndChild();
}
void MessageEditor::DrawTextCommands() {
if (BeginChild("##TextCommands", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
for (const auto& text_element : TextCommands) {
if (Button(text_element.GenericToken.c_str())) {
}
SameLine();
TextWrapped("%s", text_element.Description.c_str());
Separator();
}
EndChild();
}
}
constexpr ImGuiTableFlags kDictTableFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable;
absl::Status MessageEditor::Initialize() {
for (int i = 0; i < 100; i++) {
for (int i = 0; i < kWidthArraySize; i++) {
width_array[i] = rom()->data()[kCharactersWidth + i];
}
BuildDictionaryEntries();
ReadAllTextData();
all_dictionaries_ = BuildDictionaryEntries(rom());
ReadAllTextDataV2();
font_preview_colors_.AddColor(0x7FFF); // White
font_preview_colors_.AddColor(0x7C00); // Red
@@ -257,21 +61,23 @@ absl::Status MessageEditor::Initialize() {
for (int i = 0; i < 0x4000; i++) {
data[i] = rom()->data()[kGfxFont + i];
}
font_gfx16_data = gfx::SnesTo8bppSheet(data, /*bpp=*/2);
font_gfx16_data_ = gfx::SnesTo8bppSheet(data, /*bpp=*/2, /*num_sheets=*/2);
// 4bpp
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
128, 128, 8, font_gfx16_data, font_gfx_bitmap_, font_preview_colors_))
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
kFontGfxMessageSize, kFontGfxMessageSize, kFontGfxMessageDepth,
font_gfx16_data_, font_gfx_bitmap_, font_preview_colors_))
current_font_gfx16_data_.reserve(172 * 4096);
for (int i = 0; i < 172 * 4096; i++) {
current_font_gfx16_data_.reserve(kCurrentMessageWidth *
kCurrentMessageHeight);
for (int i = 0; i < kCurrentMessageWidth * kCurrentMessageHeight; i++) {
current_font_gfx16_data_.push_back(0);
}
// 8bpp
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
172, 4096, 64, current_font_gfx16_data_, current_font_gfx16_bitmap_,
font_preview_colors_))
RETURN_IF_ERROR(Renderer::GetInstance().CreateAndRenderBitmap(
kCurrentMessageWidth, kCurrentMessageHeight, 64, current_font_gfx16_data_,
current_font_gfx16_bitmap_, font_preview_colors_))
gfx::SnesPalette color_palette = font_gfx_bitmap_.palette();
for (int i = 0; i < font_preview_colors_.size(); i++) {
@@ -280,11 +86,11 @@ absl::Status MessageEditor::Initialize() {
*font_gfx_bitmap_.mutable_palette() = color_palette;
for (const auto& message : ListOfTexts) {
DisplayedMessages.push_back(message);
}
for (const auto& each_message : list_of_texts_) {
std::cout << "Message #" << each_message.ID << " at address "
<< core::UppercaseHexLong(each_message.Address) << std::endl;
std::cout << " " << each_message.RawString << std::endl;
for (const auto& each_message : ListOfTexts) {
// Each string has a [:XX] char encoded
// The corresponding character is found in CharEncoder unordered_map
std::string parsed_message = "";
@@ -313,7 +119,8 @@ absl::Status MessageEditor::Initialize() {
}
}
}
ParsedMessages.push_back(parsed_message);
std::cout << " > " << parsed_message << std::endl;
parsed_messages_.push_back(parsed_message);
}
DrawMessagePreview();
@@ -321,38 +128,245 @@ absl::Status MessageEditor::Initialize() {
return absl::OkStatus();
}
void MessageEditor::BuildDictionaryEntries() {
for (int i = 0; i < 97; i++) {
std::vector<uint8_t> bytes;
std::stringstream stringBuilder;
int address = core::SnesToPc(
0x0E0000 + (rom()->data()[kPointersDictionaries + (i * 2) + 1] << 8) +
rom()->data()[kPointersDictionaries + (i * 2)]);
int temppush_backress = core::SnesToPc(
0x0E0000 +
(rom()->data()[kPointersDictionaries + ((i + 1) * 2) + 1] << 8) +
rom()->data()[kPointersDictionaries + ((i + 1) * 2)]);
while (address < temppush_backress) {
uint8_t uint8_tDictionary = rom()->data()[address++];
bytes.push_back(uint8_tDictionary);
stringBuilder << ParseTextDataByte(uint8_tDictionary);
}
// AllDictionaries[i] = DictionaryEntry{(uint8_t)i, stringBuilder.str()};
AllDictionaries.push_back(DictionaryEntry{(uint8_t)i, stringBuilder.str()});
absl::Status MessageEditor::Update() {
if (rom()->is_loaded() && !data_loaded_) {
RETURN_IF_ERROR(Initialize());
current_message_ = list_of_texts_[1];
data_loaded_ = true;
}
// AllDictionaries.OrderByDescending(dictionary = > dictionary.Length);
AllDictionaries[0].Length = 0;
if (BeginTable("##MessageEditor", 4, kDictTableFlags)) {
TableSetupColumn("List");
TableSetupColumn("Contents");
TableSetupColumn("Commands");
TableSetupColumn("Dictionary");
TableHeadersRow();
TableNextColumn();
DrawMessageList();
TableNextColumn();
DrawCurrentMessage();
TableNextColumn();
DrawTextCommands();
TableNextColumn();
DrawDictionary();
EndTable();
}
return absl::OkStatus();
}
void MessageEditor::DrawMessageList() {
if (InputText("Search", &search_text_)) {
// TODO: ImGui style text filtering
}
if (BeginChild("##MessagesList", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (BeginTable("##MessagesTable", 3, kMessageTableFlags)) {
TableSetupColumn("ID");
TableSetupColumn("Contents");
TableSetupColumn("Data");
TableHeadersRow();
for (const auto& message : list_of_texts_) {
TableNextColumn();
if (Button(core::UppercaseHexWord(message.ID).c_str())) {
current_message_ = message;
DrawMessagePreview();
}
TableNextColumn();
TextWrapped("%s", parsed_messages_[message.ID].c_str());
TableNextColumn();
TextWrapped(
"%s",
core::UppercaseHexLong(list_of_texts_[message.ID].Address).c_str());
}
EndTable();
}
EndChild();
}
}
void MessageEditor::DrawCurrentMessage() {
Button(absl::StrCat("Message ", current_message_.ID).c_str());
if (InputTextMultiline("##MessageEditor",
&parsed_messages_[current_message_.ID],
ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
current_message_.Data = ParseMessageToData(message_text_box_.text);
DrawMessagePreview();
}
Separator();
Text("Font Graphics");
gui::BeginPadding(1);
BeginChild("MessageEditorCanvas", ImVec2(0, 130));
font_gfx_canvas_.DrawBackground();
font_gfx_canvas_.DrawContextMenu();
font_gfx_canvas_.DrawBitmap(font_gfx_bitmap_, 0, 0);
font_gfx_canvas_.DrawGrid();
font_gfx_canvas_.DrawOverlay();
EndChild();
gui::EndPadding();
Separator();
Text("Message Preview");
if (Button("Refresh Bitmap")) {
Renderer::GetInstance().UpdateBitmap(&current_font_gfx16_bitmap_);
}
gui::BeginPadding(1);
BeginChild("CurrentGfxFont", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
current_font_gfx16_canvas_.DrawBackground();
gui::EndPadding();
current_font_gfx16_canvas_.DrawContextMenu();
current_font_gfx16_canvas_.DrawBitmap(current_font_gfx16_bitmap_, 0, 0);
current_font_gfx16_canvas_.DrawGrid();
current_font_gfx16_canvas_.DrawOverlay();
EndChild();
}
void MessageEditor::DrawTextCommands() {
if (BeginChild("##TextCommands", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
for (const auto& text_element : TextCommands) {
if (Button(text_element.GenericToken.c_str())) {
}
SameLine();
TextWrapped("%s", text_element.Description.c_str());
Separator();
}
EndChild();
}
}
void MessageEditor::DrawDictionary() {
if (ImGui::BeginChild("##DictionaryChild", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (BeginTable("##Dictionary", 2, kDictTableFlags)) {
TableSetupColumn("ID");
TableSetupColumn("Contents");
for (const auto& dictionary : all_dictionaries_) {
TableNextColumn();
Text("%s", core::UppercaseHexWord(dictionary.ID).c_str());
TableNextColumn();
Text("%s", dictionary.Contents.c_str());
}
EndTable();
}
EndChild();
}
}
// TODO: Fix the command parsing.
void MessageEditor::ReadAllTextDataV2() {
// Read all text data from the ROM.
int pos = kTextData;
int message_id = 0;
std::vector<uint8_t> raw_message;
std::vector<uint8_t> parsed_message;
std::string current_raw_message;
std::string current_parsed_message;
uint8_t current_byte = 0;
while (current_byte != 0xFF) {
current_byte = rom()->data()[pos++];
if (current_byte == kMessageTerminator) {
auto message =
MessageData(message_id++, pos, current_raw_message, raw_message,
current_parsed_message, parsed_message);
list_of_texts_.push_back(message);
raw_message.clear();
parsed_message.clear();
current_raw_message.clear();
current_parsed_message.clear();
continue;
}
raw_message.push_back(current_byte);
// Check for command.
TextElement text_element = FindMatchingCommand(current_byte);
if (!text_element.Empty()) {
parsed_message.push_back(current_byte);
if (text_element.HasArgument) {
current_byte = rom()->data()[pos++];
raw_message.push_back(current_byte);
parsed_message.push_back(current_byte);
}
current_raw_message.append(
text_element.GetParameterizedToken(current_byte));
current_parsed_message.append(
text_element.GetParameterizedToken(current_byte));
if (text_element.Token == BANKToken) {
pos = kTextData2;
}
continue;
}
// Check for special characters.
text_element = FindMatchingSpecial(current_byte);
if (!text_element.Empty()) {
current_raw_message.append(text_element.GetParameterizedToken());
current_parsed_message.append(text_element.GetParameterizedToken());
parsed_message.push_back(current_byte);
continue;
}
// Check for dictionary.
int dictionary = FindDictionaryEntry(current_byte);
if (dictionary >= 0) {
current_raw_message.append("[");
current_raw_message.append(DICTIONARYTOKEN);
current_raw_message.append(":");
current_raw_message.append(core::UppercaseHexWord(dictionary));
current_raw_message.append("]");
uint32_t address = core::Get24LocalFromPC(
rom()->data(), kPointersDictionaries + (dictionary * 2));
uint32_t address_end = core::Get24LocalFromPC(
rom()->data(), kPointersDictionaries + ((dictionary + 1) * 2));
for (uint32_t i = address; i < address_end; i++) {
parsed_message.push_back(rom()->data()[i]);
current_parsed_message.append(ParseTextDataByte(rom()->data()[i]));
}
continue;
}
// Everything else.
if (CharEncoder.contains(current_byte)) {
std::string str = "";
str.push_back(CharEncoder.at(current_byte));
current_raw_message.append(str);
current_parsed_message.append(str);
parsed_message.push_back(current_byte);
}
}
}
void MessageEditor::ReadAllTextData() {
int messageID = 0;
uint8_t current_byte;
int pos = kTextData;
int message_id = 0;
uint8_t current_byte;
std::vector<uint8_t> temp_bytes_raw;
std::vector<uint8_t> temp_bytes_parsed;
@@ -363,12 +377,12 @@ void MessageEditor::ReadAllTextData() {
while (true) {
current_byte = rom()->data()[pos++];
if (current_byte == MESSAGETERMINATOR) {
if (current_byte == kMessageTerminator) {
auto message =
MessageData(messageID++, pos, current_message_raw, temp_bytes_raw,
MessageData(message_id++, pos, current_message_raw, temp_bytes_raw,
current_message_parsed, temp_bytes_parsed);
ListOfTexts.push_back(message);
list_of_texts_.push_back(message);
temp_bytes_raw.clear();
temp_bytes_parsed.clear();
@@ -407,7 +421,6 @@ void MessageEditor::ReadAllTextData() {
// Check for special characters.
text_element = FindMatchingSpecial(current_byte);
if (!text_element.Empty()) {
current_message_raw.append(text_element.GetParameterizedToken());
current_message_parsed.append(text_element.GetParameterizedToken());
@@ -449,83 +462,29 @@ void MessageEditor::ReadAllTextData() {
}
}
TextElement MessageEditor::FindMatchingCommand(uint8_t b) {
TextElement empty_element;
for (const auto text_element : TextCommands) {
if (text_element.ID == b) {
return text_element;
std::string ReplaceAllDictionaryWords(std::string str,
std::vector<DictionaryEntry> dictionary) {
std::string temp = str;
for (const auto& entry : dictionary) {
if (absl::StrContains(temp, entry.Contents)) {
temp = absl::StrReplaceAll(temp, {{entry.Contents, entry.Contents}});
}
}
return empty_element;
return temp;
}
TextElement MessageEditor::FindMatchingSpecial(uint8_t value) {
TextElement empty_element;
for (const auto text_element : SpecialChars) {
if (text_element.ID == value) {
return text_element;
}
}
return empty_element;
}
MessageEditor::DictionaryEntry MessageEditor::GetDictionaryFromID(
uint8_t value) {
if (value < 0 || value >= AllDictionaries.size()) {
DictionaryEntry MessageEditor::GetDictionaryFromID(uint8_t value) {
if (value < 0 || value >= all_dictionaries_.size()) {
return DictionaryEntry();
}
return AllDictionaries[value];
}
uint8_t MessageEditor::FindDictionaryEntry(uint8_t value) {
if (value < DICTOFF || value == 0xFF) {
return -1;
}
return value - DICTOFF;
}
uint8_t MessageEditor::FindMatchingCharacter(char value) {
for (const auto [key, char_value] : CharEncoder) {
if (value == char_value) {
return key;
}
}
return 0xFF;
}
string MessageEditor::ParseTextDataByte(uint8_t value) {
if (CharEncoder.contains(value)) {
char c = CharEncoder.at(value);
string str = "";
str.push_back(c);
return str;
}
// Check for command.
TextElement textElement = FindMatchingCommand(value);
if (!textElement.Empty()) {
return textElement.GenericToken;
}
// Check for special characters.
textElement = FindMatchingSpecial(value);
if (!textElement.Empty()) {
return textElement.GenericToken;
}
// Check for dictionary.
int dictionary = FindDictionaryEntry(value);
if (dictionary >= 0) {
return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary);
}
return "";
return all_dictionaries_[value];
}
void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
int sizex, int sizey) {
int drawid = srcx + (srcy * 32);
const int num_x_tiles = 16;
const int img_width = 512; // (imgwidth/2)
int draw_id = srcx + (srcy * 32);
for (int yl = 0; yl < sizey * 8; yl++) {
for (int xl = 0; xl < 4; xl++) {
int mx = xl;
@@ -533,8 +492,9 @@ void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
// Formula information to get tile index position in the array.
// ((ID / nbrofXtiles) * (imgwidth/2) + (ID - ((ID/16)*16) ))
int tx = ((drawid / 16) * 512) + ((drawid - ((drawid / 16) * 16)) * 4);
uint8_t pixel = font_gfx16_data[tx + (yl * 64) + xl];
int tx = ((draw_id / num_x_tiles) * img_width) +
((draw_id - ((draw_id / 16) * 16)) * 4);
uint8_t pixel = font_gfx16_data_[tx + (yl * 64) + xl];
// nx,ny = object position, xx,yy = tile position, xl,yl = pixel
// position
@@ -552,7 +512,7 @@ void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
}
}
void MessageEditor::DrawStringToPreview(string str) {
void MessageEditor::DrawStringToPreview(std::string str) {
for (const auto c : str) {
DrawCharacterToPreview(c);
}
@@ -573,25 +533,25 @@ void MessageEditor::DrawCharacterToPreview(const std::vector<uint8_t>& text) {
int srcy = value / 16;
int srcx = value - (value & (~0xF));
if (text_pos >= 170) {
text_pos = 0;
text_line++;
if (text_position_ >= 170) {
text_position_ = 0;
text_line_++;
}
DrawTileToPreview(text_pos, text_line * 16, srcx, srcy, 0, 1, 2);
text_pos += width_array[value];
DrawTileToPreview(text_position_, text_line_ * 16, srcx, srcy, 0, 1, 2);
text_position_ += width_array[value];
} else if (value == kLine1) {
text_pos = 0;
text_line = 0;
text_position_ = 0;
text_line_ = 0;
} else if (value == kScrollVertical) {
text_pos = 0;
text_line += 1;
text_position_ = 0;
text_line_ += 1;
} else if (value == kLine2) {
text_pos = 0;
text_line = 1;
text_position_ = 0;
text_line_ = 1;
} else if (value == kLine3) {
text_pos = 0;
text_line = 2;
text_position_ = 0;
text_line_ = 2;
} else if (value == 0x6B || value == 0x6D || value == 0x6E ||
value == 0x77 || value == 0x78 || value == 0x79 ||
value == 0x7A) {
@@ -615,15 +575,15 @@ void MessageEditor::DrawCharacterToPreview(const std::vector<uint8_t>& text) {
}
}
void MessageEditor::DrawMessagePreview() // From Parsing.
{
text_line = 0;
void MessageEditor::DrawMessagePreview() {
// From Parsing.
text_line_ = 0;
for (int i = 0; i < (172 * 4096); i++) {
current_font_gfx16_data_[i] = 0;
}
text_pos = 0;
DrawCharacterToPreview(CurrentMessage.Data);
shown_lines = 0;
text_position_ = 0;
DrawCharacterToPreview(current_message_.Data);
shown_lines_ = 0;
}
absl::Status MessageEditor::Cut() {
@@ -675,7 +635,7 @@ absl::Status MessageEditor::Save() {
int pos = kTextData;
bool in_second_bank = false;
for (const auto& message : ListOfTexts) {
for (const auto& message : list_of_texts_) {
for (const auto value : message.Data) {
RETURN_IF_ERROR(rom()->Write(pos, value));
@@ -695,7 +655,7 @@ absl::Status MessageEditor::Save() {
}
RETURN_IF_ERROR(
rom()->Write(pos++, MESSAGETERMINATOR)); // , true, "Terminator text"
rom()->Write(pos++, kMessageTerminator)); // , true, "Terminator text"
}
// Verify that we didn't go over the space available for the second block.
@@ -712,9 +672,10 @@ absl::Status MessageEditor::Save() {
std::string MessageEditor::DisplayTextOverflowError(int pos, bool bank) {
int space = bank ? kTextDataEnd - kTextData : kTextData2End - kTextData2;
string bankSTR = bank ? "1st" : "2nd";
string posSTR = bank ? absl::StrFormat("%X4", pos & 0xFFFF)
: absl::StrFormat("%X4", (pos - kTextData2) & 0xFFFF);
std::string bankSTR = bank ? "1st" : "2nd";
std::string posSTR =
bank ? absl::StrFormat("%X4", pos & 0xFFFF)
: absl::StrFormat("%X4", (pos - kTextData2) & 0xFFFF);
std::string message = absl::StrFormat(
"There is too much text data in the %s block to save.\n"
"Available: %X4 | Used: %s",
@@ -722,30 +683,6 @@ std::string MessageEditor::DisplayTextOverflowError(int pos, bool bank) {
return message;
}
// push_backs a command to the text field when the push_back command button is
// pressed or the command is double clicked in the list.
void MessageEditor::InsertCommandButton_Click_1() {
// InsertSelectedText(
// TextCommands[TextCommandList.SelectedIndex].GetParameterizedToken(
// (uint8_t)ParamsBox.HexValue));
}
// push_backs a special character to the text field when the push_back command
// button is pressed or the character is double clicked in the list.
void MessageEditor::InsertSpecialButton_Click() {
// InsertSelectedText(
// SpecialChars[SpecialsList.SelectedIndex].GetParameterizedToken());
}
void MessageEditor::InsertSelectedText(string str) {
int textboxPos = message_text_box_.selection_start;
from_form = true;
// message_text_box_.Text = message_text_box_.Text.Insert(textboxPos, str);
from_form = false;
message_text_box_.selection_start = textboxPos + str.size();
message_text_box_.Focus();
}
void MessageEditor::Delete() {
// Determine if any text is selected in the TextBox control.
if (message_text_box_.selection_length == 0) {

View File

@@ -1,229 +1,54 @@
#ifndef YAZE_APP_EDITOR_MESSAGE_EDITOR_H
#define YAZE_APP_EDITOR_MESSAGE_EDITOR_H
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "app/editor/message/message_data.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/rom.h"
namespace yaze {
namespace app {
namespace editor {
using std::string;
// TEXT EDITOR RELATED CONSTANTS
const int kGfxFont = 0x70000; // 2bpp format
const int kTextData = 0xE0000;
const int kTextDataEnd = 0xE7FFF;
const int kTextData2 = 0x75F40;
const int kTextData2End = 0x773FF;
const int kPointersDictionaries = 0x74703;
const int kCharactersWidth = 0x74ADF;
const string DICTIONARYTOKEN = "D";
const uint8_t DICTOFF = 0x88;
const string BANKToken = "BANK";
const uint8_t BANKID = 0x80;
constexpr int kGfxFont = 0x70000; // 2bpp format
constexpr int kTextData2 = 0x75F40;
constexpr int kTextData2End = 0x773FF;
constexpr int kCharactersWidth = 0x74ADF;
constexpr int kNumMessages = 396;
constexpr int kCurrentMessageWidth = 172;
constexpr int kCurrentMessageHeight = 4096;
constexpr int kFontGfxMessageSize = 128;
constexpr int kFontGfxMessageDepth = 8;
constexpr uint8_t kWidthArraySize = 100;
constexpr uint8_t kBlockTerminator = 0x80;
static std::vector<uint8_t> ParseMessageToData(string str);
static ParsedElement FindMatchingElement(string str);
constexpr uint8_t kMessageBankChangeId = 0x80;
constexpr uint8_t kScrollVertical = 0x73;
constexpr uint8_t kLine1 = 0x74;
constexpr uint8_t kLine2 = 0x75;
constexpr uint8_t kLine3 = 0x76;
static const TextElement TextCommands[] = {
TextElement(0x6B, "W", true, "Window border"),
TextElement(0x6D, "P", true, "Window position"),
TextElement(0x6E, "SPD", true, "Scroll speed"),
TextElement(0x7A, "S", true, "Text draw speed"),
TextElement(0x77, "C", true, "Text color"),
TextElement(0x6A, "L", false, "Player name"),
TextElement(0x74, "1", false, "Line 1"),
TextElement(0x75, "2", false, "Line 2"),
TextElement(0x76, "3", false, "Line 3"),
TextElement(0x7E, "K", false, "Wait for key"),
TextElement(0x73, "V", false, "Scroll text"),
TextElement(0x78, "WT", true, "Delay X"),
TextElement(0x6C, "N", true, "BCD number"),
TextElement(0x79, "SFX", true, "Sound effect"),
TextElement(0x71, "CH3", false, "Choose 3"),
TextElement(0x72, "CH2", false, "Choose 2 high"),
TextElement(0x6F, "CH2L", false, "Choose 2 low"),
TextElement(0x68, "CH2I", false, "Choose 2 indented"),
TextElement(0x69, "CHI", false, "Choose item"),
TextElement(0x67, "IMG", false, "Next attract image"),
TextElement(0x80, BANKToken, false, "Bank marker (automatic)"),
TextElement(0x70, "NONO", false, "Crash"),
};
static std::vector<TextElement> SpecialChars = {
TextElement(0x43, "...", false, "Ellipsis …"),
TextElement(0x4D, "UP", false, "Arrow ↑"),
TextElement(0x4E, "DOWN", false, "Arrow ↓"),
TextElement(0x4F, "LEFT", false, "Arrow ←"),
TextElement(0x50, "RIGHT", false, "Arrow →"),
TextElement(0x5B, "A", false, "Button Ⓐ"),
TextElement(0x5C, "B", false, "Button Ⓑ"),
TextElement(0x5D, "X", false, "Button ⓧ"),
TextElement(0x5E, "Y", false, "Button ⓨ"),
TextElement(0x52, "HP1L", false, "1 HP left"),
TextElement(0x53, "HP1R", false, "1 HP right"),
TextElement(0x54, "HP2L", false, "2 HP left"),
TextElement(0x55, "HP3L", false, "3 HP left"),
TextElement(0x56, "HP3R", false, "3 HP right"),
TextElement(0x57, "HP4L", false, "4 HP left"),
TextElement(0x58, "HP4R", false, "4 HP right"),
TextElement(0x47, "HY0", false, "Hieroglyph ☥"),
TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"),
TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"),
TextElement(0x4A, "LFL", false, "Link face left"),
TextElement(0x4B, "LFR", false, "Link face right"),
};
static const std::unordered_map<uint8_t, wchar_t> CharEncoder = {
{0x00, 'A'},
{0x01, 'B'},
{0x02, 'C'},
{0x03, 'D'},
{0x04, 'E'},
{0x05, 'F'},
{0x06, 'G'},
{0x07, 'H'},
{0x08, 'I'},
{0x09, 'J'},
{0x0A, 'K'},
{0x0B, 'L'},
{0x0C, 'M'},
{0x0D, 'N'},
{0x0E, 'O'},
{0x0F, 'P'},
{0x10, 'Q'},
{0x11, 'R'},
{0x12, 'S'},
{0x13, 'T'},
{0x14, 'U'},
{0x15, 'V'},
{0x16, 'W'},
{0x17, 'X'},
{0x18, 'Y'},
{0x19, 'Z'},
{0x1A, 'a'},
{0x1B, 'b'},
{0x1C, 'c'},
{0x1D, 'd'},
{0x1E, 'e'},
{0x1F, 'f'},
{0x20, 'g'},
{0x21, 'h'},
{0x22, 'i'},
{0x23, 'j'},
{0x24, 'k'},
{0x25, 'l'},
{0x26, 'm'},
{0x27, 'n'},
{0x28, 'o'},
{0x29, 'p'},
{0x2A, 'q'},
{0x2B, 'r'},
{0x2C, 's'},
{0x2D, 't'},
{0x2E, 'u'},
{0x2F, 'v'},
{0x30, 'w'},
{0x31, 'x'},
{0x32, 'y'},
{0x33, 'z'},
{0x34, '0'},
{0x35, '1'},
{0x36, '2'},
{0x37, '3'},
{0x38, '4'},
{0x39, '5'},
{0x3A, '6'},
{0x3B, '7'},
{0x3C, '8'},
{0x3D, '9'},
{0x3E, '!'},
{0x3F, '?'},
{0x40, '-'},
{0x41, '.'},
{0x42, ','},
{0x44, '>'},
{0x45, '('},
{0x46, ')'},
{0x4C, '"'},
{0x51, '\''},
{0x59, ' '},
{0x5A, '<'},
// {0x5F, '¡'}, {0x60, '¡'}, {0x61, '¡'}, {0x62, ' '}, {0x63, ' '}, {0x64,
// ' '},
{0x65, ' '},
{0x66, '_'},
};
static TextElement DictionaryElement =
TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
class MessageEditor : public Editor, public SharedRom {
public:
struct DictionaryEntry {
uint8_t ID;
std::string Contents;
std::vector<uint8_t> Data;
int Length;
std::string Token;
DictionaryEntry() = default;
DictionaryEntry(uint8_t i, std::string s)
: Contents(s), ID(i), Length(s.length()) {
Token = absl::StrFormat("[%s:%00X]", DICTIONARYTOKEN, ID);
Data = ParseMessageToData(Contents);
}
bool ContainedInString(std::string s) {
return s.find(Contents) != std::string::npos;
}
std::string ReplaceInstancesOfIn(std::string s) {
std::string replacedString = s;
size_t pos = replacedString.find(Contents);
while (pos != std::string::npos) {
replacedString.replace(pos, Contents.length(), Token);
pos = replacedString.find(Contents, pos + Token.length());
}
return replacedString;
}
};
MessageEditor() { type_ = EditorType::kMessage; }
absl::Status Initialize();
absl::Status Update() override;
void DrawMessageList();
void DrawCurrentMessage();
void DrawTextCommands();
void DrawDictionary();
absl::Status Initialize();
void ReadAllTextData();
void BuildDictionaryEntries();
void ReadAllTextDataV2();
[[deprecated]] void ReadAllTextData();
absl::Status Cut() override;
absl::Status Copy() override;
@@ -238,67 +63,47 @@ class MessageEditor : public Editor, public SharedRom {
absl::Status Save();
void Delete();
void SelectAll();
// void RegisterTests(ImGuiTestEngine* e) override;
TextElement FindMatchingCommand(uint8_t byte);
TextElement FindMatchingSpecial(uint8_t value);
string ParseTextDataByte(uint8_t value);
DictionaryEntry GetDictionaryFromID(uint8_t value);
static uint8_t FindDictionaryEntry(uint8_t value);
static uint8_t FindMatchingCharacter(char value);
void DrawTileToPreview(int x, int y, int srcx, int srcy, int pal,
int sizex = 1, int sizey = 1);
void DrawCharacterToPreview(char c);
void DrawCharacterToPreview(const std::vector<uint8_t>& text);
void DrawStringToPreview(string str);
void DrawStringToPreview(std::string str);
void DrawMessagePreview();
std::string DisplayTextOverflowError(int pos, bool bank);
void InsertCommandButton_Click_1();
void InsertSpecialButton_Click();
void InsertSelectedText(string str);
static const std::vector<DictionaryEntry> AllDicts;
uint8_t width_array[100];
string romname = "";
int text_line = 0;
int text_pos = 0;
int shown_lines = 0;
int selected_tile = 0;
bool skip_next = false;
bool from_form = false;
std::vector<MessageData> ListOfTexts;
std::vector<MessageData> DisplayedMessages;
std::vector<std::string> ParsedMessages;
MessageData CurrentMessage;
private:
static const TextElement DictionaryElement;
bool skip_next = false;
bool data_loaded_ = false;
int current_message_id_ = 0;
int text_line_ = 0;
int text_position_ = 0;
int shown_lines_ = 0;
uint8_t width_array[kWidthArraySize];
std::string search_text_ = "";
std::vector<uint8_t> font_gfx16_data_;
std::vector<uint8_t> current_font_gfx16_data_;
std::vector<std::string> parsed_messages_;
std::vector<MessageData> list_of_texts_;
std::vector<DictionaryEntry> all_dictionaries_;
MessageData current_message_;
gfx::Bitmap font_gfx_bitmap_;
gfx::Bitmap current_font_gfx16_bitmap_;
gfx::SnesPalette font_preview_colors_;
gui::Canvas font_gfx_canvas_{"##FontGfxCanvas", ImVec2(128, 128)};
gui::Canvas current_font_gfx16_canvas_{"##CurrentMessageGfx",
ImVec2(172, 4096)};
gfx::Bitmap font_gfx_bitmap_;
gfx::Bitmap current_font_gfx16_bitmap_;
Bytes font_gfx16_data;
Bytes current_font_gfx16_data_;
gfx::SnesPalette font_preview_colors_;
struct TextBox {
std::string text;
std::string buffer;
@@ -357,8 +162,6 @@ class MessageEditor : public Editor, public SharedRom {
TextBox message_text_box_;
};
static std::vector<MessageEditor::DictionaryEntry> AllDictionaries;
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,7 +0,0 @@
#include "message_editor.h"
namespace yaze {
namespace app {
namespace editor {} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,19 +1,15 @@
#ifndef YAZE_APP_EDITOR_MUSIC_EDITOR_H
#define YAZE_APP_EDITOR_MUSIC_EDITOR_H
#include "imgui/imgui.h"
#include "absl/strings/str_format.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/utils/editor.h"
#include "app/editor/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"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -81,20 +77,11 @@ class MusicEditor : public SharedRom, public Editor {
zelda3::music::Tracker music_tracker_;
// Mix_Music* current_song_ = NULL;
AssemblyEditor assembly_editor_;
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
ImGuiTableFlags music_editor_flags_ = ImGuiTableFlags_SizingFixedFit |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_Reorderable;
ImGuiTableFlags channel_table_flags_ =
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable |
ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg |
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY;
};
} // namespace editor

View File

@@ -1,5 +1,6 @@
#include "app/editor/overworld/entity.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/style.h"
@@ -8,7 +9,6 @@ namespace app {
namespace editor {
using ImGui::BeginChild;
using ImGui::BeginGroup;
using ImGui::Button;
using ImGui::Checkbox;
using ImGui::EndChild;
@@ -16,7 +16,9 @@ using ImGui::SameLine;
using ImGui::Selectable;
using ImGui::Text;
bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
constexpr float kInputFieldSize = 30.f;
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity,
ImVec2 canvas_p0, ImVec2 scrolling) {
// Get the mouse position relative to the canvas
const ImGuiIO &io = ImGui::GetIO();
@@ -31,7 +33,7 @@ bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
return false;
}
void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0,
ImVec2 scrolling, bool free_movement) {
// Get the mouse position relative to the canvas
const ImGuiIO &io = ImGui::GetIO();
@@ -51,19 +53,20 @@ void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
entity->set_y(new_y);
}
void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
void HandleEntityDragging(zelda3::GameEntity *entity, ImVec2 canvas_p0,
ImVec2 scrolling, bool &is_dragging_entity,
zelda3::OverworldEntity *&dragged_entity,
zelda3::OverworldEntity *&current_entity,
zelda3::GameEntity *&dragged_entity,
zelda3::GameEntity *&current_entity,
bool free_movement) {
std::string entity_type = "Entity";
if (entity->type_ == zelda3::OverworldEntity::EntityType::kExit) {
if (entity->entity_type_ == zelda3::GameEntity::EntityType::kExit) {
entity_type = "Exit";
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kEntrance) {
} else if (entity->entity_type_ ==
zelda3::GameEntity::EntityType::kEntrance) {
entity_type = "Entrance";
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kSprite) {
} else if (entity->entity_type_ == zelda3::GameEntity::EntityType::kSprite) {
entity_type = "Sprite";
} else if (entity->type_ == zelda3::OverworldEntity::EntityType::kItem) {
} else if (entity->entity_type_ == zelda3::GameEntity::EntityType::kItem) {
entity_type = "Item";
}
const auto is_hovering =
@@ -87,7 +90,7 @@ void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
} else if (is_dragging_entity && dragged_entity == entity) {
if (ImGui::BeginDragDropSource()) {
ImGui::SetDragDropPayload("ENTITY_PAYLOAD", &entity,
sizeof(zelda3::OverworldEntity));
sizeof(zelda3::GameEntity));
Text("Moving %s ID: %s", entity_type.c_str(),
core::UppercaseHexByte(entity->entity_id_).c_str());
ImGui::EndDragDropSource();
@@ -132,7 +135,7 @@ bool DrawOverworldEntrancePopup(
}
if (ImGui::BeginPopupModal("Entrance editor", NULL,
ImGuiWindowFlags_AlwaysAutoResize)) {
gui::InputHex("Map ID", &entrance.map_id_);
gui::InputHexWord("Map ID", &entrance.map_id_);
gui::InputHexByte("Entrance ID", &entrance.entrance_id_,
kInputFieldSize + 20);
gui::InputHex("X", &entrance.x_);
@@ -209,7 +212,7 @@ bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
gui::InputHexWord("Room", &exit.room_id_);
SameLine();
gui::InputHex("Entity ID", &exit.entity_id_, 4);
gui::InputHex("Map", &exit.map_id_);
gui::InputHexWord("Map", &exit.map_id_);
SameLine();
Checkbox("Automatic", &exit.is_automatic_);
@@ -310,11 +313,11 @@ bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
void DrawItemInsertPopup() {
// Contents of the Context Menu
if (ImGui::BeginPopup("Item Inserter")) {
static int new_item_id = 0;
static size_t new_item_id = 0;
Text("Add Item");
BeginChild("ScrollRegion", ImVec2(150, 150), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
for (size_t i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
if (Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
i == new_item_id)) {
new_item_id = i;
@@ -348,10 +351,10 @@ bool DrawItemEditorPopup(zelda3::overworld::OverworldItem &item) {
BeginChild("ScrollRegion", ImVec2(150, 150), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
ImGui::BeginGroup();
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
for (size_t i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
if (Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
item.id == i)) {
item.id = i;
item.id_ == i)) {
item.id_ = i;
}
}
ImGui::EndGroup();
@@ -384,7 +387,7 @@ void DrawSpriteTable(std::function<void(int)> onSpriteSelect) {
// Initialize items if empty
if (items.empty()) {
for (int i = 0; i < 256; ++i) {
items.push_back(SpriteItem{i, core::kSpriteDefaultNames[i].data()});
items.push_back(SpriteItem{i, zelda3::kSpriteDefaultNames[i].data()});
}
}

View File

@@ -3,7 +3,6 @@
#include "imgui/imgui.h"
#include "app/editor/overworld_editor.h"
#include "app/zelda3/common.h"
#include "app/zelda3/overworld/overworld.h"
@@ -11,16 +10,16 @@ namespace yaze {
namespace app {
namespace editor {
bool IsMouseHoveringOverEntity(const zelda3::OverworldEntity &entity,
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity,
ImVec2 canvas_p0, ImVec2 scrolling);
void MoveEntityOnGrid(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0,
ImVec2 scrolling, bool free_movement = false);
void HandleEntityDragging(zelda3::OverworldEntity *entity, ImVec2 canvas_p0,
void HandleEntityDragging(zelda3::GameEntity *entity, ImVec2 canvas_p0,
ImVec2 scrolling, bool &is_dragging_entity,
zelda3::OverworldEntity *&dragged_entity,
zelda3::OverworldEntity *&current_entity,
zelda3::GameEntity *&dragged_entity,
zelda3::GameEntity *&current_entity,
bool free_movement = false);
bool DrawEntranceInserterPopup();
@@ -85,4 +84,4 @@ bool DrawSpriteEditorPopup(zelda3::Sprite &sprite);
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
#endif // YAZE_APP_EDITOR_OVERWORLD_ENTITY_H

View File

@@ -1,8 +1,6 @@
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#include "imgui/imgui.h"
#include <cmath>
#include <unordered_map>
@@ -10,13 +8,11 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/core/common.h"
#include "app/editor/editor.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/graphics/tile16_editor.h"
#include "app/editor/overworld/entity.h"
#include "app/editor/utils/editor.h"
#include "app/editor/utils/gfx_context.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
@@ -25,24 +21,24 @@
#include "app/gui/zeml.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
static constexpr uint k4BPP = 4;
static constexpr uint kByteSize = 3;
static constexpr uint kMessageIdSize = 5;
static constexpr uint kNumSheetsToLoad = 223;
static constexpr uint kTile8DisplayHeight = 64;
static constexpr float kInputFieldSize = 30.f;
static constexpr absl::string_view kToolsetColumnNames[] = {
"#undoTool", "#redoTool", "#separator2", "#zoomOutTool",
"#zoomInTool", "#separator", "#drawTool", "#history",
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
"#transportTool", "#musicTool", "#separator3", "#tilemapTool",
"propertiesTool"};
constexpr uint k4BPP = 4;
constexpr uint kByteSize = 3;
constexpr uint kMessageIdSize = 5;
constexpr uint kNumSheetsToLoad = 223;
constexpr uint kTile8DisplayHeight = 64;
constexpr uint kOverworldMapSize = 0x200;
constexpr float kInputFieldSize = 30.f;
constexpr ImVec2 kOverworldCanvasSize(kOverworldMapSize * 8,
kOverworldMapSize * 8);
constexpr ImVec2 kCurrentGfxCanvasSize(0x100 + 1, 0x10 * 0x40 + 1);
constexpr ImVec2 kBlocksetCanvasSize(0x100 + 1, 0x4000 + 1);
constexpr ImVec2 kGraphicsBinCanvasSize(0x100 + 1, kNumSheetsToLoad * 0x40 + 1);
constexpr ImGuiTableFlags kOWMapFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable;
@@ -52,6 +48,14 @@ constexpr ImGuiTableFlags kOWEditFlags =
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV;
static constexpr absl::string_view kToolsetColumnNames[] = {
"#undoTool", "#redoTool", "#separator2", "#zoomOutTool",
"#zoomInTool", "#separator", "#drawTool", "#history",
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
"#transportTool", "#musicTool", "#separator3", "#tilemapTool",
"propertiesTool", "#separator4", "#experimentalTool", "#properties",
"#separator5"};
constexpr absl::string_view kWorldList =
"Light World\0Dark World\0Extra World\0";
@@ -61,13 +65,17 @@ constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
constexpr absl::string_view kOWEditTable = "##OWEditTable";
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
constexpr int kEntranceTileTypePtrLow = 0xDB8BF;
constexpr int kEntranceTileTypePtrHigh = 0xDB917;
constexpr int kNumEntranceTileTypes = 0x2C;
class EntranceContext {
public:
absl::Status LoadEntranceTileTypes(Rom& rom) {
int offset_low = 0xDB8BF;
int offset_high = 0xDB917;
int offset_low = kEntranceTileTypePtrLow;
int offset_high = kEntranceTileTypePtrHigh;
for (int i = 0; i < 0x2C; i++) {
for (int i = 0; i < kNumEntranceTileTypes; i++) {
// Load entrance tile types
ASSIGN_OR_RETURN(auto value_low, rom.ReadWord(offset_low + i));
entrance_tile_types_low_.push_back(value_low);
@@ -101,8 +109,8 @@ class EntranceContext {
*/
class OverworldEditor : public Editor,
public SharedRom,
public context::GfxContext,
public EntranceContext,
public GfxContext,
public core::ExperimentFlags {
public:
OverworldEditor() { type_ = EditorType::kOverworld; }
@@ -110,12 +118,15 @@ class OverworldEditor : public Editor,
void InitializeZeml();
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"); }
absl::Status Find() { return absl::UnimplementedError("Find Unused Tiles"); }
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
absl::Status Find() override {
return absl::UnimplementedError("Find Unused Tiles");
}
absl::Status Save();
auto overworld() { return &overworld_; }
@@ -135,11 +146,26 @@ class OverworldEditor : public Editor,
absl::Status LoadGraphics();
private:
absl::Status UpdateFullscreenCanvas();
/**
* @brief Draws the canvas, tile16 selector, and toolset in fullscreen
*/
void DrawFullscreenCanvas();
absl::Status DrawToolset();
/**
* @brief Toolset for entrances, exits, items, sprites, and transports.
*/
void DrawToolset();
/**
* @brief Draws the overworld map settings. Graphics, palettes, etc.
*/
void DrawOverworldMapSettings();
/**
* @brief Draw the overworld settings for ZSCustomOverworld.
*/
void DrawCustomOverworldMapSettings();
void RefreshChildMap(int i);
void RefreshOverworldMap();
absl::Status RefreshMapPalette();
@@ -155,9 +181,28 @@ class OverworldEditor : public Editor,
void DrawOverworldMaps();
void DrawOverworldEdits();
void RenderUpdatedMapBitmap(const ImVec2& click_position,
const Bytes& tile_data);
const std::vector<uint8_t>& tile_data);
/**
* @brief Check for changes to the overworld map.
*
* This function either draws the tile painter with the current tile16 or
* group of tile16 data with ow_map_canvas_ and DrawOverworldEdits or it
* checks for left mouse button click/drag to select a tile16 or group of
* tile16 data from the overworld map canvas. Similar to ZScream selection.
*/
void CheckForOverworldEdits();
/**
* @brief Draw and create the tile16 IDs that are currently selected.
*/
void CheckForSelectRectangle();
/**
* @brief Check for changes to the overworld map. Calls RefreshOverworldMap
* and RefreshTile16Blockset on the current map if it is modified and is
* actively being edited.
*/
absl::Status CheckForCurrentMap();
void CheckForMousePan();
@@ -175,17 +220,10 @@ class OverworldEditor : public Editor,
void DrawOverworldProperties();
absl::Status DrawExperimentalModal();
absl::Status UpdateUsageStats();
void DrawUsageGrid();
void CalculateUsageStats();
absl::Status LoadAnimatedMaps();
void DrawDebugWindow();
auto gfx_group_editor() const { return gfx_group_editor_; }
enum class EditingMode {
DRAW_TILE,
ENTRANCES,
@@ -200,84 +238,55 @@ class OverworldEditor : public Editor,
EditingMode current_mode = EditingMode::DRAW_TILE;
EditingMode previous_mode = EditingMode::DRAW_TILE;
enum OverworldProperty {
LW_AREA_GFX,
DW_AREA_GFX,
LW_AREA_PAL,
DW_AREA_PAL,
LW_SPR_GFX_PART1,
LW_SPR_GFX_PART2,
DW_SPR_GFX_PART1,
DW_SPR_GFX_PART2,
LW_SPR_PAL_PART1,
LW_SPR_PAL_PART2,
DW_SPR_PAL_PART1,
DW_SPR_PAL_PART2,
};
int current_world_ = 0;
int current_map_ = 0;
int current_parent_ = 0;
int current_entrance_id_ = 0;
int current_exit_id_ = 0;
int current_item_id_ = 0;
int current_sprite_id_ = 0;
int current_blockset_ = 0;
int game_state_ = 1;
int current_tile16_ = 0;
int selected_tile_ = 0;
int current_blockset_ = 0;
int selected_entrance_ = 0;
int selected_usage_map_ = 0xFFFF;
char map_gfx_[3] = "";
char map_palette_[3] = "";
char spr_gfx_[3] = "";
char spr_palette_[3] = "";
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;
bool overworld_canvas_fullscreen_ = false;
bool middle_mouse_dragging_ = false;
bool is_dragging_entity_ = false;
zelda3::OverworldEntity* dragged_entity_;
zelda3::OverworldEntity* current_entity_;
int current_entrance_id_ = 0;
zelda3::overworld::OverworldEntrance current_entrance_;
int current_exit_id_ = 0;
zelda3::overworld::OverworldExit current_exit_;
int current_item_id_ = 0;
zelda3::overworld::OverworldItem current_item_;
int current_sprite_id_ = 0;
zelda3::Sprite current_sprite_;
bool show_experimental = false;
std::string ow_tilemap_filename_ = "";
std::string tile32_configuration_filename_ = "";
Bytes selected_tile_data_;
std::vector<Bytes> tile16_individual_data_;
std::vector<uint8_t> selected_tile_data_;
std::vector<std::vector<uint8_t>> tile16_individual_data_;
std::vector<gfx::Bitmap> tile16_individual_;
std::vector<Bytes> tile8_individual_data_;
std::vector<std::vector<uint8_t>> tile8_individual_data_;
std::vector<gfx::Bitmap> tile8_individual_;
Tile16Editor tile16_editor_;
GfxGroupEditor gfx_group_editor_;
PaletteEditor palette_editor_;
zelda3::overworld::Overworld overworld_;
gui::Canvas ow_map_canvas_{"owMapCanvas", ImVec2(0x200 * 8, 0x200 * 8),
gui::CanvasGridSize::k64x64};
gui::Canvas current_gfx_canvas_{"customGfxCanvas",
ImVec2(0x100 + 1, 0x10 * 0x40 + 1),
gui::CanvasGridSize::k32x32};
gui::Canvas blockset_canvas_{"blocksetCanvas", ImVec2(0x100 + 1, 0x2000 + 1),
gui::CanvasGridSize::k32x32};
gui::Canvas graphics_bin_canvas_{
"graphicsBinCanvas", ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1),
gui::CanvasGridSize::k16x16};
gui::Canvas properties_canvas_;
gfx::SnesPalette palette_;
gfx::Bitmap selected_tile_bmp_;
gfx::Bitmap tile16_blockset_bmp_;
gfx::Bitmap current_gfx_bmp_;
@@ -286,10 +295,28 @@ class OverworldEditor : public Editor,
gfx::BitmapTable maps_bmp_;
gfx::BitmapTable current_graphics_set_;
gfx::BitmapTable sprite_previews_;
gfx::BitmapTable animated_maps_;
OWBlockset refresh_blockset_;
zelda3::overworld::Overworld overworld_;
zelda3::OWBlockset refresh_blockset_;
zelda3::Sprite current_sprite_;
zelda3::overworld::OverworldEntrance current_entrance_;
zelda3::overworld::OverworldExit current_exit_;
zelda3::overworld::OverworldItem current_item_;
zelda3::GameEntity* current_entity_;
zelda3::GameEntity* dragged_entity_;
gui::Canvas ow_map_canvas_{"OwMap", kOverworldCanvasSize,
gui::CanvasGridSize::k64x64};
gui::Canvas current_gfx_canvas_{"CurrentGfx", kCurrentGfxCanvasSize,
gui::CanvasGridSize::k32x32};
gui::Canvas blockset_canvas_{"OwBlockset", kBlocksetCanvasSize,
gui::CanvasGridSize::k32x32};
gui::Canvas graphics_bin_canvas_{"GraphicsBin", kGraphicsBinCanvasSize,
gui::CanvasGridSize::k16x16};
gui::Canvas properties_canvas_;
gui::zeml::Node layout_node_;
absl::Status status_;
};
@@ -297,4 +324,4 @@ class OverworldEditor : public Editor,
} // namespace app
} // namespace yaze
#endif
#endif

View File

@@ -1,157 +0,0 @@
#include "app/editor/overworld_editor.h"
namespace yaze {
namespace app {
namespace editor {
void OverworldEditor::RefreshChildMap(int map_index) {
overworld_.mutable_overworld_map(map_index)->LoadAreaGraphics();
status_ = overworld_.mutable_overworld_map(map_index)->BuildTileset();
PRINT_IF_ERROR(status_);
status_ = overworld_.mutable_overworld_map(map_index)->BuildTiles16Gfx(
overworld_.tiles16().size());
PRINT_IF_ERROR(status_);
status_ = overworld_.mutable_overworld_map(map_index)->BuildBitmap(
overworld_.GetMapTiles(current_world_));
maps_bmp_[map_index].set_data(
overworld_.mutable_overworld_map(map_index)->bitmap_data());
maps_bmp_[map_index].set_modified(true);
PRINT_IF_ERROR(status_);
}
void OverworldEditor::RefreshOverworldMap() {
std::vector<std::future<void>> futures;
int indices[4];
auto refresh_map_async = [this](int map_index) {
RefreshChildMap(map_index);
};
int source_map_id = current_map_;
bool is_large = overworld_.overworld_map(current_map_)->is_large_map();
if (is_large) {
source_map_id = current_parent_;
// We need to update the map and its siblings if it's a large map
for (int i = 1; i < 4; i++) {
int sibling_index = overworld_.overworld_map(source_map_id)->parent() + i;
if (i >= 2) sibling_index += 6;
futures.push_back(
std::async(std::launch::async, refresh_map_async, sibling_index));
indices[i] = sibling_index;
}
}
indices[0] = source_map_id;
futures.push_back(
std::async(std::launch::async, refresh_map_async, source_map_id));
for (auto &each : futures) {
each.get();
}
int n = is_large ? 4 : 1;
// We do texture updating on the main thread
for (int i = 0; i < n; ++i) {
rom()->UpdateBitmap(&maps_bmp_[indices[i]]);
}
}
absl::Status OverworldEditor::RefreshMapPalette() {
RETURN_IF_ERROR(
overworld_.mutable_overworld_map(current_map_)->LoadPalette());
const auto current_map_palette = overworld_.AreaPalette();
if (overworld_.overworld_map(current_map_)->is_large_map()) {
// We need to update the map and its siblings if it's a large map
for (int i = 1; i < 4; i++) {
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
if (i >= 2) sibling_index += 6;
RETURN_IF_ERROR(
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
RETURN_IF_ERROR(
maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
}
}
RETURN_IF_ERROR(maps_bmp_[current_map_].ApplyPalette(current_map_palette));
return absl::OkStatus();
}
void OverworldEditor::RefreshMapProperties() {
auto &current_ow_map = *overworld_.mutable_overworld_map(current_map_);
if (current_ow_map.is_large_map()) {
// We need to copy the properties from the parent map to the children
for (int i = 1; i < 4; i++) {
int sibling_index = current_ow_map.parent() + i;
if (i >= 2) {
sibling_index += 6;
}
auto &map = *overworld_.mutable_overworld_map(sibling_index);
map.set_area_graphics(current_ow_map.area_graphics());
map.set_area_palette(current_ow_map.area_palette());
map.set_sprite_graphics(game_state_,
current_ow_map.sprite_graphics(game_state_));
map.set_sprite_palette(game_state_,
current_ow_map.sprite_palette(game_state_));
map.set_message_id(current_ow_map.message_id());
}
}
}
absl::Status OverworldEditor::RefreshTile16Blockset() {
if (current_blockset_ ==
overworld_.overworld_map(current_map_)->area_graphics()) {
return absl::OkStatus();
}
current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
overworld_.set_current_map(current_map_);
palette_ = overworld_.AreaPalette();
// Create the tile16 blockset image
rom()->UpdateBitmap(&tile16_blockset_bmp_);
RETURN_IF_ERROR(tile16_blockset_bmp_.ApplyPalette(palette_));
// Copy the tile16 data into individual tiles.
auto tile16_data = overworld_.Tile16Blockset();
std::vector<std::future<void>> futures;
// Loop through the tiles and copy their pixel data into separate vectors
for (int i = 0; i < 4096; i++) {
futures.push_back(std::async(
std::launch::async,
[&](int index) {
// Create a new vector for the pixel data of the current tile
Bytes tile_data(16 * 16, 0x00); // More efficient initialization
// Copy the pixel data for the current tile into the vector
for (int ty = 0; ty < 16; ty++) {
for (int tx = 0; tx < 16; tx++) {
int position = tx + (ty * 0x10);
uint8_t value =
tile16_data[(index % 8 * 16) + (index / 8 * 16 * 0x80) +
(ty * 0x80) + tx];
tile_data[position] = value;
}
}
// Add the vector for the current tile to the vector of tile pixel
// data
tile16_individual_[index].set_data(tile_data);
},
i));
}
for (auto &future : futures) {
future.get();
}
// Render the bitmaps of each tile.
for (int id = 0; id < 4096; id++) {
RETURN_IF_ERROR(tile16_individual_[id].ApplyPalette(palette_));
rom()->UpdateBitmap(&tile16_individual_[id]);
}
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -4,6 +4,7 @@
#include "app/editor/sprite/zsprite.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/zelda3/sprite/sprite.h"
namespace yaze {
namespace app {
@@ -71,13 +72,13 @@ void SpriteEditor::DrawVanillaSpriteEditor() {
for (int n = 0; n < active_sprites_.Size;) {
bool open = true;
if (active_sprites_[n] > sizeof(core::kSpriteDefaultNames) / 4) {
if (active_sprites_[n] > sizeof(zelda3::kSpriteDefaultNames) / 4) {
active_sprites_.erase(active_sprites_.Data + n);
continue;
}
if (ImGui::BeginTabItem(
core::kSpriteDefaultNames[active_sprites_[n]].data(), &open,
zelda3::kSpriteDefaultNames[active_sprites_[n]].data(), &open,
ImGuiTabItemFlags_None)) {
DrawSpriteCanvas();
ImGui::EndTabItem();
@@ -175,7 +176,7 @@ void SpriteEditor::DrawCurrentSheets() {
graphics_sheet_canvas_.DrawTileSelector(32);
for (int i = 0; i < 8; i++) {
graphics_sheet_canvas_.DrawBitmap(
rom()->bitmap_manager()[current_sheets_[i]], 1, (i * 0x40) + 1, 2);
rom()->gfx_sheets().at(current_sheets_[i]), 1, (i * 0x40) + 1, 2);
}
graphics_sheet_canvas_.DrawGrid();
graphics_sheet_canvas_.DrawOverlay();
@@ -188,10 +189,10 @@ void SpriteEditor::DrawSpritesList() {
ImVec2(ImGui::GetContentRegionAvail().x, 0), true,
ImGuiWindowFlags_NoDecoration)) {
int i = 0;
for (const auto each_sprite_name : core::kSpriteDefaultNames) {
for (const auto each_sprite_name : zelda3::kSpriteDefaultNames) {
rom()->resource_label()->SelectableLabelWithNameEdit(
current_sprite_id_ == i, "Sprite Names", core::UppercaseHexByte(i),
core::kSpriteDefaultNames[i].data());
zelda3::kSpriteDefaultNames[i].data());
if (ImGui::IsItemClicked()) {
current_sprite_id_ = i;
if (!active_sprites_.contains(i)) {
@@ -243,7 +244,7 @@ void SpriteEditor::DrawCustomSpritesMetadata() {
// ZSprite Maker format open file dialog
if (ImGui::Button("Open ZSprite")) {
// Open ZSprite file
std::string file_path = FileDialogWrapper::ShowOpenFileDialog();
std::string file_path = core::FileDialogWrapper::ShowOpenFileDialog();
if (!file_path.empty()) {
zsprite::ZSprite zsprite;
status_ = zsprite.Load(file_path);

View File

@@ -3,7 +3,7 @@
#include "absl/status/status.h"
#include "app/editor/sprite/zsprite.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
#include "app/gui/canvas.h"
#include "app/rom.h"

View File

@@ -1,16 +1,16 @@
#ifndef YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
#define YAZE_APP_EDITOR_SPRITE_ZSPRITE_H
#include "imgui/imgui.h"
#include <algorithm>
#include <cstdint>
#include <fstream>
#include <string>
#include <vector>
#include "app/core/constants.h"
#include "absl/status/status.h"
#include "app/gfx/snes_tile.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -46,7 +46,7 @@ struct OamTile {
struct AnimationGroup {
AnimationGroup() = default;
AnimationGroup(uint8_t fs, uint8_t fe, uint8_t fsp, std::string fn)
: frame_start(fs), frame_end(fe), frame_speed(fsp), frame_name(fn) {}
: frame_name(fn), frame_start(fs), frame_end(fe), frame_speed(fsp) {}
std::string frame_name;
uint8_t frame_start;

View File

@@ -0,0 +1,143 @@
#include "command_manager.h"
#include <fstream>
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
ImGuiKey MapKeyToImGuiKey(char key) {
switch (key) {
case 'A':
return ImGuiKey_A;
case 'B':
return ImGuiKey_B;
case 'C':
return ImGuiKey_C;
case 'D':
return ImGuiKey_D;
case 'E':
return ImGuiKey_E;
case 'F':
return ImGuiKey_F;
case 'G':
return ImGuiKey_G;
case 'H':
return ImGuiKey_H;
case 'I':
return ImGuiKey_I;
case 'J':
return ImGuiKey_J;
case 'K':
return ImGuiKey_K;
case 'L':
return ImGuiKey_L;
case 'M':
return ImGuiKey_M;
case 'N':
return ImGuiKey_N;
case 'O':
return ImGuiKey_O;
case 'P':
return ImGuiKey_P;
case 'Q':
return ImGuiKey_Q;
case 'R':
return ImGuiKey_R;
case 'S':
return ImGuiKey_S;
case 'T':
return ImGuiKey_T;
case 'U':
return ImGuiKey_U;
case 'V':
return ImGuiKey_V;
case 'W':
return ImGuiKey_W;
case 'X':
return ImGuiKey_X;
case 'Y':
return ImGuiKey_Y;
case 'Z':
return ImGuiKey_Z;
case '/':
return ImGuiKey_Slash;
case '-':
return ImGuiKey_Minus;
default:
return ImGuiKey_COUNT;
}
}
// When the player presses Space, a popup will appear fixed to the bottom of the
// ImGui window with a list of the available key commands which can be used.
void CommandManager::ShowWhichKey() {
if (ImGui::IsKeyPressed(ImGuiKey_Space)) {
ImGui::OpenPopup("WhichKey");
}
ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetIO().DisplaySize.y - 100),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 100),
ImGuiCond_Always);
if (ImGui::BeginPopup("WhichKey")) {
// ESC to close the popup
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
const ImVec4 colors[] = {
ImVec4(0.8f, 0.2f, 0.2f, 1.0f), // Soft Red
ImVec4(0.2f, 0.8f, 0.2f, 1.0f), // Soft Green
ImVec4(0.2f, 0.2f, 0.8f, 1.0f), // Soft Blue
ImVec4(0.8f, 0.8f, 0.2f, 1.0f), // Soft Yellow
ImVec4(0.8f, 0.2f, 0.8f, 1.0f), // Soft Magenta
ImVec4(0.2f, 0.8f, 0.8f, 1.0f) // Soft Cyan
};
const int numColors = sizeof(colors) / sizeof(colors[0]);
int colorIndex = 0;
if (ImGui::BeginTable("CommandsTable", commands_.size(),
ImGuiTableFlags_SizingStretchProp)) {
for (const auto &[shortcut, info] : commands_) {
ImGui::TableNextColumn();
ImGui::TextColored(colors[colorIndex], "%c: %s",
info.command_info.mnemonic,
info.command_info.name.c_str());
colorIndex = (colorIndex + 1) % numColors;
}
ImGui::EndTable();
}
ImGui::EndPopup();
}
}
void CommandManager::SaveKeybindings(const std::string &filepath) {
std::ofstream out(filepath);
if (out.is_open()) {
for (const auto &[shortcut, info] : commands_) {
out << shortcut << " " << info.command_info.mnemonic << " "
<< info.command_info.name << " " << info.command_info.desc << "\n";
}
out.close();
}
}
void CommandManager::LoadKeybindings(const std::string &filepath) {
std::ifstream in(filepath);
if (in.is_open()) {
commands_.clear();
std::string shortcut, name, desc;
char mnemonic;
while (in >> shortcut >> mnemonic >> name >> desc) {
commands_[shortcut].command_info = {nullptr, mnemonic, name, desc};
}
in.close();
}
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,83 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_COMMAND_MANAGER_H
#define YAZE_APP_EDITOR_SYSTEM_COMMAND_MANAGER_H
#include <functional>
#include <string>
#include <unordered_map>
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
ImGuiKey MapKeyToImGuiKey(char key);
class CommandManager {
public:
CommandManager() = default;
~CommandManager() = default;
using Command = std::function<void()>;
struct CommandInfo {
Command command;
char mnemonic;
std::string name;
std::string desc;
CommandInfo(Command command, char mnemonic, const std::string &name,
const std::string &desc)
: command(std::move(command)), mnemonic(mnemonic), name(name),
desc(desc) {}
CommandInfo() = default;
};
// New command info which supports subsections of commands
struct CommandInfoOrPrefix {
CommandInfo command_info;
std::unordered_map<std::string, CommandInfoOrPrefix> subcommands;
CommandInfoOrPrefix(CommandInfo command_info)
: command_info(std::move(command_info)) {}
CommandInfoOrPrefix() = default;
};
void RegisterPrefix(const std::string &group_name, const char prefix,
const std::string &name, const std::string &desc) {
commands_[group_name].command_info = {nullptr, prefix, name, desc};
}
void RegisterSubcommand(const std::string &group_name,
const std::string &shortcut, const char mnemonic,
const std::string &name, const std::string &desc,
Command command) {
commands_[group_name].subcommands[shortcut].command_info = {
command, mnemonic, name, desc};
}
void RegisterCommand(const std::string &shortcut, Command command,
char mnemonic, const std::string &name,
const std::string &desc) {
commands_[shortcut].command_info = {std::move(command), mnemonic, name,
desc};
}
void ExecuteCommand(const std::string &shortcut) {
if (commands_.find(shortcut) != commands_.end()) {
commands_[shortcut].command_info.command();
}
}
void ShowWhichKey();
void SaveKeybindings(const std::string &filepath);
void LoadKeybindings(const std::string &filepath);
private:
std::unordered_map<std::string, CommandInfoOrPrefix> commands_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_COMMAND_MANAGER_H

View File

@@ -0,0 +1,77 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_CONSTANT_MANAGER_H
#define YAZE_APP_EDITOR_SYSTEM_CONSTANT_MANAGER_H
#include <cstddef>
#include "app/zelda3/overworld/overworld_map.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
class ConstantManager {
public:
void ShowConstantManager() {
ImGui::Begin("Constant Manager");
// Tab bar for the different types of constants
// Overworld, dungeon, graphics, expanded
ImGui::TextWrapped(
"This is the constant manager. It allows you to view and edit "
"constants in the ROM. You should only edit these if you know what "
"you're doing.");
if (ImGui::BeginTabBar("Constant Manager Tabs")) {
if (ImGui::BeginTabItem("Overworld")) {
ImGui::Text("Overworld constants");
ImGui::Separator();
ImGui::Text("OverworldCustomASMHasBeenApplied: %d",
zelda3::overworld::OverworldCustomASMHasBeenApplied);
ImGui::Text("OverworldCustomAreaSpecificBGPalette: %d",
zelda3::overworld::OverworldCustomAreaSpecificBGPalette);
ImGui::Text("OverworldCustomAreaSpecificBGEnabled: %d",
zelda3::overworld::OverworldCustomAreaSpecificBGEnabled);
ImGui::Text("OverworldCustomMainPaletteArray: %d",
zelda3::overworld::OverworldCustomMainPaletteArray);
ImGui::Text("OverworldCustomMainPaletteEnabled: %d",
zelda3::overworld::OverworldCustomMainPaletteEnabled);
ImGui::Text("OverworldCustomMosaicArray: %d",
zelda3::overworld::OverworldCustomMosaicArray);
ImGui::Text("OverworldCustomMosaicEnabled: %d",
zelda3::overworld::OverworldCustomMosaicEnabled);
ImGui::Text("OverworldCustomAnimatedGFXArray: %d",
zelda3::overworld::OverworldCustomAnimatedGFXArray);
ImGui::Text("OverworldCustomAnimatedGFXEnabled: %d",
zelda3::overworld::OverworldCustomAnimatedGFXEnabled);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Dungeon")) {
ImGui::Text("Dungeon constants");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Graphics")) {
ImGui::Text("Graphics constants");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Expanded")) {
ImGui::Text("Expanded constants");
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::End();
}
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_CONSTANT_MANAGER_H

View File

@@ -0,0 +1,85 @@
#include "extension_manager.h"
#if defined(__unix__) || defined(__unix) || defined(unix) || \
defined(__APPLE__) && defined(__MACH__)
#include <dlfcn.h>
#endif
#include <system/extension.h>
#include <iostream>
#include <vector>
namespace yaze {
namespace app {
namespace editor {
void ExtensionManager::LoadExtension(const std::string& filename,
yaze_editor_context* context) {
#if defined(__unix__) || defined(__unix) || defined(unix) || \
defined(__APPLE__) && defined(__MACH__)
auto extension_path = filename.c_str();
void* handle = dlopen(extension_path, RTLD_LAZY);
if (!handle) {
std::cerr << "Cannot open extension: " << dlerror() << std::endl;
return;
}
dlerror(); // Clear any existing error
// Load the symbol to retrieve the extension
auto get_extension = reinterpret_cast<yaze_extension* (*)()>(
dlsym(handle, "get_yaze_extension"));
const char* dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "Cannot load symbol 'get_yaze_extension': " << dlsym_error
<< std::endl;
dlclose(handle);
return;
}
yaze_extension* extension = get_extension();
if (extension && extension->initialize) {
extension->initialize(context);
} else {
std::cerr << "Failed to initialize the extension." << std::endl;
dlclose(handle);
return;
}
extensions_.push_back(extension);
#endif
}
void ExtensionManager::RegisterExtension(yaze_extension* extension) {
extensions_.push_back(extension);
}
void ExtensionManager::InitializeExtensions(yaze_editor_context* context) {
for (auto& extension : extensions_) {
extension->initialize(context);
}
}
void ExtensionManager::ShutdownExtensions() {
for (auto& extension : extensions_) {
extension->cleanup();
}
// if (handle) {
// dlclose(handle);
// handle = nullptr;
// extension = nullptr;
// }
}
void ExtensionManager::ExecuteExtensionUI(yaze_editor_context* context) {
for (auto& extension : extensions_) {
if (extension->extend_ui) {
extension->extend_ui(context);
}
}
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,29 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_EXTENSION_MANAGER_H
#define YAZE_APP_EDITOR_SYSTEM_EXTENSION_MANAGER_H
#include <system/extension.h>
#include <string>
#include <vector>
namespace yaze {
namespace app {
namespace editor {
class ExtensionManager {
public:
void LoadExtension(const std::string& filename, yaze_editor_context* context);
void RegisterExtension(yaze_extension* extension);
void InitializeExtensions(yaze_editor_context* context);
void ShutdownExtensions();
void ExecuteExtensionUI(yaze_editor_context* context);
private:
std::vector<yaze_extension*> extensions_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_EXTENSION_MANAGER_H

View File

@@ -1,9 +1,8 @@
#ifndef YAZE_APP_EDITOR_UTILS_FLAGS_H
#define YAZE_APP_EDITOR_UTILS_FLAGS_H
#include "imgui/imgui.h"
#include "core/common.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -31,6 +30,8 @@ struct FlagsMenu : public core::ExperimentFlags {
&mutable_flags()->overworld.kSaveOverworldItems);
Checkbox("Save Overworld Properties",
&mutable_flags()->overworld.kSaveOverworldProperties);
Checkbox("Load Custom Overworld",
&mutable_flags()->overworld.kLoadCustomOverworld);
ImGui::EndMenu();
}
@@ -42,21 +43,16 @@ struct FlagsMenu : public core::ExperimentFlags {
ImGui::EndMenu();
}
if (BeginMenu("Emulator Flags")) {
Checkbox("Load Audio Device", &mutable_flags()->kLoadAudioDevice);
ImGui::EndMenu();
}
Checkbox("Use built-in file dialog",
&mutable_flags()->kNewFileDialogWrapper);
Checkbox("Enable Console Logging", &mutable_flags()->kLogToConsole);
Checkbox("Enable Texture Streaming",
&mutable_flags()->kLoadTexturesAsStreaming);
Checkbox("Use Bitmap Manager", &mutable_flags()->kUseBitmapManager);
Checkbox("Log Instructions to Debugger",
&mutable_flags()->kLogInstructions);
Checkbox("Save All Palettes", &mutable_flags()->kSaveAllPalettes);
Checkbox("Save Gfx Groups", &mutable_flags()->kSaveGfxGroups);
Checkbox("Save Graphics Sheets", &mutable_flags()->kSaveGraphicsSheet);
Checkbox("Use New ImGui Input", &mutable_flags()->kUseNewImGuiInput);
}
};

View File

@@ -0,0 +1,32 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_HISTORY_MANAGER_H
#define YAZE_APP_EDITOR_SYSTEM_HISTORY_MANAGER_H
#include <cstddef>
#include <stack>
#include <vector>
namespace yaze {
namespace app {
namespace editor {
// System history manager, undo and redo.
class HistoryManager {
public:
HistoryManager() = default;
~HistoryManager() = default;
void Add(const char* data);
void Undo();
void Redo();
private:
std::vector<const char*> history_;
std::stack<const char*> undo_;
std::stack<const char*> redo_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_HISTORY_MANAGER_H

View File

@@ -0,0 +1,19 @@
#include "popup_manager.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
PopupManager::PopupManager() {
}
PopupManager::~PopupManager() {
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,21 @@
#ifndef YAZE_APP_EDITOR_POPUP_MANAGER_H
#define YAZE_APP_EDITOR_POPUP_MANAGER_H
namespace yaze {
namespace app {
namespace editor {
// ImGui popup manager.
class PopupManager {
public:
PopupManager();
~PopupManager();
void Show(const char* name);
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_POPUP_MANAGER_H

View File

@@ -0,0 +1,30 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_RESOURCE_MANAGER_H
#define YAZE_APP_EDITOR_SYSTEM_RESOURCE_MANAGER_H
#include <cstddef>
namespace yaze {
namespace app {
namespace editor {
// System resource manager.
class ResourceManager {
public:
ResourceManager() : count_(0) {}
~ResourceManager() = default;
void Load(const char* path);
void Unload(const char* path);
void UnloadAll();
size_t Count() const;
private:
size_t count_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_RESOURCE_MANAGER_H

View File

@@ -1,11 +1,9 @@
#include "imgui/imgui.h"
#include "app/editor/system/settings_editor.h"
#include "absl/status/status.h"
#include "app/editor/utils/flags.h"
#include "app/editor/utils/keyboard_shortcuts.h"
#include "app/editor/settings_editor.h"
#include "app/editor/utils/flags.h"
#include "app/editor/system/flags.h"
#include "imgui/imgui.h"
namespace yaze {
namespace app {

View File

@@ -4,7 +4,7 @@
#include "imgui/imgui.h"
#include "absl/status/status.h"
#include "app/editor/utils/editor.h"
#include "app/editor/editor.h"
namespace yaze {
namespace app {

View File

@@ -1,26 +0,0 @@
#include "app/editor/utils/gfx_context.h"
#include "imgui/imgui.h"
#include <cmath>
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/utils/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"
namespace yaze {
namespace app {
namespace editor {
namespace context {
std::unordered_map<uint8_t, gfx::Paletteset> GfxContext::palettesets_;
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -1,36 +0,0 @@
#ifndef YAZE_APP_EDITOR_VRAM_CONTEXT_H
#define YAZE_APP_EDITOR_VRAM_CONTEXT_H
#include "imgui/imgui.h"
#include <cmath>
#include <vector>
#include "app/editor/utils/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"
namespace yaze {
namespace app {
namespace editor {
namespace context {
/**
* @brief Shared graphical context across editors.
*/
class GfxContext {
protected:
// Palettesets for the tile16 individual tiles
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
};
} // namespace context
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_VRAM_CONTEXT_H

View File

@@ -1,25 +0,0 @@
#ifndef YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H
#define YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H
#include "imgui/imgui.h"
namespace yaze {
namespace app {
namespace editor {
struct KeyboardShortcuts {
enum class ShortcutType {
kCut,
kCopy,
kPaste,
kUndo,
kRedo,
kFind,
};
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_UTILS_KEYBOARD_SHORTCUTS_H_

View File

@@ -1,64 +0,0 @@
#ifndef YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
#define YAZE_APP_EDITOR_UTILS_RECENT_FILES_H
#include <algorithm>
#include <fstream>
#include <string>
#include <vector>
namespace yaze {
namespace app {
namespace editor {
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);
}
}
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 editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_UTILS_RECENT_FILES_H