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

This commit is contained in:
scawful
2023-01-01 17:52:09 -06:00
parent c86a241ee2
commit e7470bdfac
101 changed files with 13685 additions and 3 deletions

View File

@@ -0,0 +1,115 @@
#include "assembly_editor.h"
#include "core/constants.h"
#include "gui/widgets.h"
namespace yaze {
namespace app {
namespace editor {
AssemblyEditor::AssemblyEditor() {
text_editor_.SetLanguageDefinition(gui::widgets::GetAssemblyLanguageDef());
text_editor_.SetPalette(TextEditor::GetDarkPalette());
}
void AssemblyEditor::Update() {
ImGui::Begin("Assembly Editor", &file_is_loaded_);
MENU_BAR()
DrawFileMenu();
DrawEditMenu();
END_MENU_BAR()
auto cpos = text_editor_.GetCursorPosition();
SetEditorText();
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
cpos.mColumn + 1, text_editor_.GetTotalLines(),
text_editor_.IsOverwrite() ? "Ovr" : "Ins",
text_editor_.CanUndo() ? "*" : " ",
text_editor_.GetLanguageDefinition().mName.c_str(),
current_file_.c_str());
text_editor_.Render("##asm_editor");
ImGui::End();
}
void AssemblyEditor::InlineUpdate() {
ChangeActiveFile("assets/asm/template_song.asm");
auto cpos = text_editor_.GetCursorPosition();
SetEditorText();
ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
cpos.mColumn + 1, text_editor_.GetTotalLines(),
text_editor_.IsOverwrite() ? "Ovr" : "Ins",
text_editor_.CanUndo() ? "*" : " ",
text_editor_.GetLanguageDefinition().mName.c_str(),
current_file_.c_str());
text_editor_.Render("##asm_editor", ImVec2(0, 0));
}
void AssemblyEditor::ChangeActiveFile(const std::string& filename) {
current_file_ = filename;
}
void AssemblyEditor::DrawFileMenu() {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open", "Ctrl+O")) {
ImGuiFileDialog::Instance()->OpenDialog(
"ChooseASMFileDlg", "Open ASM file", ".asm,.txt", ".");
}
if (ImGui::MenuItem("Save", "Ctrl+S")) {
// TODO: Implement this
}
ImGui::EndMenu();
}
if (ImGuiFileDialog::Instance()->Display("ChooseASMFileDlg")) {
if (ImGuiFileDialog::Instance()->IsOk()) {
ChangeActiveFile(ImGuiFileDialog::Instance()->GetFilePathName());
}
ImGuiFileDialog::Instance()->Close();
}
}
void AssemblyEditor::DrawEditMenu() {
if (ImGui::BeginMenu("Edit")) {
if (ImGui::MenuItem("Undo", "Ctrl+Z")) {
text_editor_.Undo();
}
if (ImGui::MenuItem("Redo", "Ctrl+Y")) {
text_editor_.Redo();
}
ImGui::Separator();
if (ImGui::MenuItem("Cut", "Ctrl+X")) {
text_editor_.Cut();
}
if (ImGui::MenuItem("Copy", "Ctrl+C")) {
text_editor_.Copy();
}
if (ImGui::MenuItem("Paste", "Ctrl+V")) {
text_editor_.Paste();
}
ImGui::Separator();
if (ImGui::MenuItem("Find", "Ctrl+F")) {
// TODO: Implement this.
}
ImGui::EndMenu();
}
}
void AssemblyEditor::SetEditorText() {
if (!file_is_loaded_) {
std::ifstream t(current_file_);
if (t.good()) {
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
text_editor_.SetText(str);
file_is_loaded_ = true;
}
}
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,38 @@
#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>
namespace yaze {
namespace app {
namespace editor {
class AssemblyEditor {
public:
AssemblyEditor();
void Update();
void InlineUpdate();
void ChangeActiveFile(const std::string &);
private:
void DrawFileMenu();
void DrawEditMenu();
void SetEditorText();
bool file_is_loaded_ = false;
std::string current_file_;
TextEditor text_editor_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif

View File

@@ -0,0 +1,61 @@
#include "dungeon_editor.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
void DungeonEditor::Update() {
DrawToolset();
canvas_.DrawBackground();
canvas_.DrawContextMenu();
canvas_.DrawGrid();
canvas_.DrawOverlay();
}
void DungeonEditor::DrawToolset() {
if (ImGui::BeginTable("DWToolset", 9, toolset_table_flags_, ImVec2(0, 0))) {
ImGui::TableSetupColumn("#undoTool");
ImGui::TableSetupColumn("#redoTool");
ImGui::TableSetupColumn("#history");
ImGui::TableSetupColumn("#separator");
ImGui::TableSetupColumn("#bg1Tool");
ImGui::TableSetupColumn("#bg2Tool");
ImGui::TableSetupColumn("#bg3Tool");
ImGui::TableSetupColumn("#itemTool");
ImGui::TableSetupColumn("#spriteTool");
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_UNDO);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_REDO);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_MANAGE_HISTORY);
ImGui::TableNextColumn();
ImGui::Text(ICON_MD_MORE_VERT);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_FILTER_1);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_FILTER_2);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_FILTER_3);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_GRASS);
ImGui::TableNextColumn();
ImGui::Button(ICON_MD_PEST_CONTROL_RODENT);
ImGui::EndTable();
}
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,26 @@
#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H
#define YAZE_APP_EDITOR_DUNGEONEDITOR_H
#include <imgui/imgui.h>
#include "gui/canvas.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
class DungeonEditor {
public:
void Update();
private:
void DrawToolset();
gui::Canvas canvas_;
ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif

View File

@@ -0,0 +1,322 @@
#include "master_editor.h"
#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/constants.h"
#include "app/editor/assembly_editor.h"
#include "app/editor/dungeon_editor.h"
#include "app/editor/music_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "gui/widgets.h"
namespace yaze {
namespace app {
namespace editor {
namespace {
constexpr ImGuiWindowFlags kMainEditorFlags =
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar |
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar;
void NewMasterFrame() {
const ImGuiIO &io = ImGui::GetIO();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImVec2 dimensions(io.DisplaySize.x, io.DisplaySize.y);
ImGui::SetNextWindowSize(dimensions, ImGuiCond_Always);
if (!ImGui::Begin("##YazeMain", nullptr, kMainEditorFlags)) {
ImGui::End();
return;
}
}
bool BeginCentered(const char *name) {
ImGuiIO const &io = ImGui::GetIO();
ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
ImGui::SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGuiWindowFlags flags =
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
return ImGui::Begin(name, nullptr, flags);
}
void DisplayStatus(absl::Status &status) {
if (BeginCentered("StatusWindow")) {
ImGui::Text("%s", status.ToString().c_str());
ImGui::Spacing();
ImGui::NextColumn();
ImGui::Columns(1);
ImGui::Separator();
ImGui::NewLine();
ImGui::SameLine(270);
if (ImGui::Button("OK", ImVec2(200, 0))) {
status = absl::OkStatus();
}
ImGui::End();
}
}
} // namespace
void MasterEditor::SetupScreen(std::shared_ptr<SDL_Renderer> renderer) {
sdl_renderer_ = renderer;
rom_.SetupRenderer(renderer);
}
void MasterEditor::UpdateScreen() {
NewMasterFrame();
DrawYazeMenu();
DrawFileDialog();
DrawStatusPopup();
DrawAboutPopup();
DrawInfoPopup();
TAB_BAR("##TabBar")
DrawOverworldEditor();
DrawDungeonEditor();
DrawMusicEditor();
DrawSpriteEditor();
DrawScreenEditor();
DrawPaletteEditor();
END_TAB_BAR()
ImGui::End();
}
void MasterEditor::DrawFileDialog() {
if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) {
if (ImGuiFileDialog::Instance()->IsOk()) {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
status_ = rom_.LoadFromFile(filePathName);
overworld_editor_.SetupROM(rom_);
screen_editor_.SetupROM(rom_);
palette_editor_.SetupROM(rom_);
}
ImGuiFileDialog::Instance()->Close();
}
}
void MasterEditor::DrawStatusPopup() {
if (!status_.ok()) {
DisplayStatus(status_);
}
}
void MasterEditor::DrawAboutPopup() {
if (about_) ImGui::OpenPopup("About");
if (ImGui::BeginPopupModal("About", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Yet Another Zelda3 Editor - v0.02");
ImGui::Text("Written by: scawful");
ImGui::Spacing();
ImGui::Text("Special Thanks: Zarby89, JaredBrian");
ImGui::Separator();
if (ImGui::Button("Close", ImVec2(200, 0))) {
about_ = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void MasterEditor::DrawInfoPopup() {
if (rom_info_) ImGui::OpenPopup("ROM Information");
if (ImGui::BeginPopupModal("ROM Information", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Title: %s", rom_.GetTitle());
ImGui::Text("ROM Size: %ld", rom_.size());
if (ImGui::Button("Close", ImVec2(200, 0))) {
rom_info_ = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void MasterEditor::DrawYazeMenu() {
MENU_BAR()
DrawFileMenu();
DrawEditMenu();
DrawViewMenu();
DrawHelpMenu();
END_MENU_BAR()
}
void MasterEditor::DrawFileMenu() const {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open", "Ctrl+O")) {
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
".sfc,.smc", ".");
}
MENU_ITEM2("Save", "Ctrl+S") {}
MENU_ITEM("Save As..") {}
ImGui::Separator();
// TODO: Make these options matter
if (ImGui::BeginMenu("Options")) {
static bool enabled = true;
ImGui::MenuItem("Enabled", "", &enabled);
ImGui::BeginChild("child", ImVec2(0, 60), true);
for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i);
ImGui::EndChild();
static float f = 0.5f;
static int n = 0;
ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
ImGui::InputFloat("Input", &f, 0.1f);
ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
ImGui::EndMenu();
}
ImGui::EndMenu();
}
}
void MasterEditor::DrawEditMenu() {
if (ImGui::BeginMenu("Edit")) {
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = overworld_editor_.Undo(); }
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = overworld_editor_.Redo(); }
ImGui::Separator();
MENU_ITEM2("Cut", "Ctrl+X") { status_ = overworld_editor_.Cut(); }
MENU_ITEM2("Copy", "Ctrl+C") { status_ = overworld_editor_.Copy(); }
MENU_ITEM2("Paste", "Ctrl+V") { status_ = overworld_editor_.Paste(); }
ImGui::Separator();
MENU_ITEM2("Find", "Ctrl+F") {}
ImGui::Separator();
MENU_ITEM("ROM Information") rom_info_ = true;
ImGui::EndMenu();
}
}
void MasterEditor::DrawViewMenu() {
static bool show_imgui_metrics = false;
static bool show_imgui_style_editor = false;
static bool show_memory_editor = false;
static bool show_asm_editor = false;
static bool show_imgui_demo = false;
static bool show_memory_viewer = false;
if (show_imgui_metrics) {
ImGui::ShowMetricsWindow(&show_imgui_metrics);
}
if (show_memory_editor) {
static MemoryEditor mem_edit;
mem_edit.DrawWindow("Memory Editor", (void *)&rom_, rom_.size());
}
if (show_imgui_demo) {
ImGui::ShowDemoWindow();
}
if (show_asm_editor) {
assembly_editor_.Update();
}
if (show_imgui_style_editor) {
ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor);
ImGui::ShowStyleEditor();
ImGui::End();
}
if (show_memory_viewer) {
ImGui::Begin("Memory Viewer (ImGui)", &show_memory_viewer);
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
static ImGuiTableFlags flags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
if (auto outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
ImGui::BeginTable("table1", 3, flags, outer_size)) {
for (int row = 0; row < 10; row++) {
ImGui::TableNextRow();
for (int column = 0; column < 3; column++) {
ImGui::TableNextColumn();
ImGui::Text("Cell %d,%d", column, row);
}
}
ImGui::EndTable();
}
ImGui::End();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("HEX Editor", nullptr, &show_memory_editor);
ImGui::MenuItem("ASM Editor", nullptr, &show_asm_editor);
ImGui::MenuItem("Memory Viewer", nullptr, &show_memory_viewer);
ImGui::MenuItem("ImGui Demo", nullptr, &show_imgui_demo);
ImGui::Separator();
if (ImGui::BeginMenu("GUI Tools")) {
ImGui::MenuItem("Metrics (ImGui)", nullptr, &show_imgui_metrics);
ImGui::MenuItem("Style Editor (ImGui)", nullptr,
&show_imgui_style_editor);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
}
void MasterEditor::DrawHelpMenu() {
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About")) about_ = true;
ImGui::EndMenu();
}
}
void MasterEditor::DrawOverworldEditor() {
TAB_ITEM("Overworld")
status_ = overworld_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawDungeonEditor() {
TAB_ITEM("Dungeon")
dungeon_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawPaletteEditor() {
TAB_ITEM("Palettes")
status_ = palette_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawScreenEditor() {
TAB_ITEM("Screens")
screen_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawMusicEditor() {
TAB_ITEM("Music")
music_editor_.Update();
END_TAB_ITEM()
}
void MasterEditor::DrawSpriteEditor() {
TAB_ITEM("Sprites")
END_TAB_ITEM()
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,72 @@
#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
#define YAZE_APP_EDITOR_MASTER_EDITOR_H
#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/constants.h"
#include "app/editor/assembly_editor.h"
#include "app/editor/dungeon_editor.h"
#include "app/editor/music_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/palette_editor.h"
#include "app/editor/screen_editor.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
namespace yaze {
namespace app {
namespace editor {
class MasterEditor {
public:
void SetupScreen(std::shared_ptr<SDL_Renderer> renderer);
void UpdateScreen();
private:
void DrawFileDialog();
void DrawStatusPopup();
void DrawAboutPopup();
void DrawInfoPopup();
void DrawYazeMenu();
void DrawFileMenu() const;
void DrawEditMenu();
void DrawViewMenu();
void DrawHelpMenu();
void DrawOverworldEditor();
void DrawDungeonEditor();
void DrawPaletteEditor();
void DrawMusicEditor();
void DrawScreenEditor();
void DrawSpriteEditor();
bool about_ = false;
bool rom_info_ = false;
std::shared_ptr<SDL_Renderer> sdl_renderer_;
absl::Status status_;
AssemblyEditor assembly_editor_;
DungeonEditor dungeon_editor_;
OverworldEditor overworld_editor_;
PaletteEditor palette_editor_;
ScreenEditor screen_editor_;
MusicEditor music_editor_;
ROM rom_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H

View File

@@ -0,0 +1,292 @@
#include "music_editor.h"
#include <SDL_mixer.h>
#include <imgui/imgui.h>
#include "absl/strings/str_format.h"
#include "app/editor/assembly_editor.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "snes_spc/demo/demo_util.h"
#include "snes_spc/demo/wave_writer.h"
#include "snes_spc/snes_spc/spc.h"
namespace yaze {
namespace app {
namespace editor {
namespace {
#define BUF_SIZE 2048
void PlaySPC() {
/* Create emulator and filter */
SNES_SPC* snes_spc = spc_new();
SPC_Filter* filter = spc_filter_new();
if (!snes_spc || !filter) error("Out of memory");
/* Load SPC */
{
/* Load file into memory */
long spc_size;
void* spc = load_file("assets/music/hyrule_field.spc", &spc_size);
/* Load SPC data into emulator */
error(spc_load_spc(snes_spc, spc, spc_size));
free(spc); /* emulator makes copy of data */
/* Most SPC files have garbage data in the echo buffer, so clear that */
spc_clear_echo(snes_spc);
/* Clear filter before playing */
spc_filter_clear(filter);
}
/* Record 20 seconds to wave file */
wave_open(spc_sample_rate, "out.wav");
wave_enable_stereo();
while (wave_sample_count() < 30 * spc_sample_rate * 2) {
/* Play into buffer */
short buf[BUF_SIZE];
error(spc_play(snes_spc, BUF_SIZE, buf));
/* Filter samples */
spc_filter_run(filter, buf, BUF_SIZE);
wave_write(buf, BUF_SIZE);
}
/* Cleanup */
spc_filter_delete(filter);
spc_delete(snes_spc);
wave_close();
}
} // namespace
void MusicEditor::Update() {
if (ImGui::BeginTable("MusicEditorColumns", 2, music_editor_flags_,
ImVec2(0, 0))) {
ImGui::TableSetupColumn("Assembly");
ImGui::TableSetupColumn("Composition");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
assembly_editor_.InlineUpdate();
ImGui::TableNextColumn();
DrawToolset();
DrawChannels();
DrawPianoRoll();
ImGui::EndTable();
}
}
void MusicEditor::DrawChannels() {
if (ImGui::BeginTabBar("MyTabBar", ImGuiTabBarFlags_None)) {
for (int i = 1; i <= 8; ++i) {
if (ImGui::BeginTabItem(absl::StrFormat("%d", i).data())) {
DrawPianoStaff();
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
}
}
static const int NUM_KEYS = 25;
static bool keys[NUM_KEYS];
void MusicEditor::DrawPianoStaff() {
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
ImGui::BeginChild(child_id, ImVec2(0, 170), false)) {
const int NUM_LINES = 5;
const int LINE_THICKNESS = 2;
const int LINE_SPACING = 40;
// Get the draw list for the current window
ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Draw the staff lines
ImVec2 canvas_p0 =
ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + ImGui::GetContentRegionAvail().x,
canvas_p0.y + ImGui::GetContentRegionAvail().y);
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(32, 32, 32, 255));
for (int i = 0; i < NUM_LINES; i++) {
auto line_start = ImVec2(canvas_p0.x, canvas_p0.y + i * LINE_SPACING);
auto line_end = ImVec2(canvas_p1.x + ImGui::GetContentRegionAvail().x,
canvas_p0.y + i * LINE_SPACING);
draw_list->AddLine(line_start, line_end, IM_COL32(200, 200, 200, 255),
LINE_THICKNESS);
}
// Draw the ledger lines
const int NUM_LEDGER_LINES = 3;
for (int i = -NUM_LEDGER_LINES; i <= NUM_LINES + NUM_LEDGER_LINES; i++) {
if (i % 2 == 0) continue; // skip every other line
auto line_start = ImVec2(canvas_p0.x, canvas_p0.y + i * LINE_SPACING / 2);
auto line_end = ImVec2(canvas_p1.x + ImGui::GetContentRegionAvail().x,
canvas_p0.y + i * LINE_SPACING / 2);
draw_list->AddLine(line_start, line_end, IM_COL32(150, 150, 150, 255),
LINE_THICKNESS);
}
}
ImGui::EndChild();
}
void MusicEditor::DrawPianoRoll() {
// Render the piano roll
float key_width = ImGui::GetContentRegionAvail().x / NUM_KEYS;
float white_key_height = ImGui::GetContentRegionAvail().y * 0.8f;
float black_key_height = ImGui::GetContentRegionAvail().y * 0.5f;
ImGui::Text("Piano Roll");
ImGui::Separator();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Draw the staff lines
ImVec2 canvas_p0 =
ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y);
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + ImGui::GetContentRegionAvail().x,
canvas_p0.y + ImGui::GetContentRegionAvail().y);
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(200, 200, 200, 255));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.f, 0.f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.f);
for (int i = 0; i < NUM_KEYS; i++) {
// Calculate the position and size of the key
ImVec2 key_pos = ImVec2(i * key_width, 0.0f);
ImVec2 key_size;
ImVec4 key_color;
ImVec4 text_color;
if (i % 12 == 1 || i % 12 == 3 || i % 12 == 6 || i % 12 == 8 ||
i % 12 == 10) {
// This is a black key
key_size = ImVec2(key_width * 0.6f, black_key_height);
key_color = ImVec4(0, 0, 0, 255);
text_color = ImVec4(255, 255, 255, 255);
} else {
// This is a white key
key_size = ImVec2(key_width, white_key_height);
key_color = ImVec4(255, 255, 255, 255);
text_color = ImVec4(0, 0, 0, 255);
}
ImGui::PushID(i);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.f, 0.f));
ImGui::PushStyleColor(ImGuiCol_Button, key_color);
ImGui::PushStyleColor(ImGuiCol_Text, text_color);
if (ImGui::Button(kSongNotes[i].data(), key_size)) {
keys[i] ^= 1;
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleVar();
ImVec2 button_pos = ImGui::GetItemRectMin();
ImVec2 button_size = ImGui::GetItemRectSize();
if (keys[i]) {
ImVec2 dest;
dest.x = button_pos.x + button_size.x;
dest.y = button_pos.y + button_size.y;
ImGui::GetWindowDrawList()->AddRectFilled(button_pos, dest,
IM_COL32(200, 200, 255, 200));
}
ImGui::PopID();
ImGui::SameLine();
}
ImGui::PopStyleVar();
ImGui::PopStyleVar();
}
void MusicEditor::DrawSongToolset() {
if (ImGui::BeginTable("DWToolset", 8, toolset_table_flags_, ImVec2(0, 0))) {
ImGui::TableSetupColumn("#1");
ImGui::TableSetupColumn("#play");
ImGui::TableSetupColumn("#rewind");
ImGui::TableSetupColumn("#fastforward");
ImGui::TableSetupColumn("volumeController");
ImGui::EndTable();
}
}
void MusicEditor::DrawToolset() {
static bool is_playing = false;
static int selected_option = 0;
static int current_volume = 0;
static bool has_loaded_song = false;
const int MAX_VOLUME = 100;
if (is_playing) {
if (!has_loaded_song) {
PlaySPC();
current_song_ = Mix_LoadMUS("out.wav");
Mix_PlayMusic(current_song_, -1);
has_loaded_song = true;
}
// // If there is no music playing
// if (Mix_PlayingMusic() == 0) {
// Mix_PlayMusic(current_song_, -1);
// }
// // If music is being played
// else {
// // If the music is paused
// if (Mix_PausedMusic() == 1) {
// // Resume the music
// Mix_ResumeMusic();
// }
// // If the music is playing
// else {
// // Pause the music
// Mix_PauseMusic();
// }
// }
}
gui::ItemLabel("Select a song to edit: ", gui::ItemLabelFlags::Left);
ImGui::Combo("#songs_in_game", &selected_option, kGameSongs, 30);
gui::ItemLabel("Controls: ", gui::ItemLabelFlags::Left);
if (ImGui::BeginTable("SongToolset", 5, toolset_table_flags_, ImVec2(0, 0))) {
ImGui::TableSetupColumn("#play");
ImGui::TableSetupColumn("#rewind");
ImGui::TableSetupColumn("#fastforward");
ImGui::TableSetupColumn("#volume");
ImGui::TableSetupColumn("#slider");
ImGui::TableNextColumn();
if (ImGui::Button(is_playing ? ICON_MD_STOP : ICON_MD_PLAY_ARROW)) {
if (is_playing) {
Mix_HaltMusic();
has_loaded_song = false;
}
is_playing = !is_playing;
}
BUTTON_COLUMN(ICON_MD_FAST_REWIND)
BUTTON_COLUMN(ICON_MD_FAST_FORWARD)
BUTTON_COLUMN(ICON_MD_VOLUME_UP)
ImGui::TableNextColumn();
ImGui::SliderInt("Volume", &current_volume, 0, 100);
ImGui::EndTable();
}
const int SONG_DURATION = 120; // duration of the song in seconds
static int current_time = 0; // current time in the song in seconds
// Display the current time in the song
gui::ItemLabel("Current Time: ", gui::ItemLabelFlags::Left);
ImGui::Text("%d:%02d", current_time / 60, current_time % 60);
ImGui::SameLine();
// Display the song duration/progress using a progress bar
ImGui::ProgressBar((float)current_time / SONG_DURATION);
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,86 @@
#ifndef YAZE_APP_EDITOR_MUSIC_EDITOR_H
#define YAZE_APP_EDITOR_MUSIC_EDITOR_H
#include <SDL_mixer.h>
#include <imgui/imgui.h>
#include "absl/strings/str_format.h"
#include "app/editor/assembly_editor.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
#include "snes_spc/demo/demo_util.h"
#include "snes_spc/demo/wave_writer.h"
#include "snes_spc/snes_spc/spc.h"
namespace yaze {
namespace app {
namespace editor {
static const char* kGameSongs[] = {"Title",
"Light World",
"Beginning",
"Rabbit",
"Forest",
"Intro",
"Town",
"Warp",
"Dark world",
"Master sword",
"File select",
"Soldier",
"Mountain",
"Shop",
"Fanfare",
"Castle",
"Palace (Pendant)",
"Cave (Same as Secret Way)",
"Clear (Dungeon end)",
"Church",
"Boss",
"Dungeon (Crystal)",
"Psychic",
"Secret Way (Same as Cave)",
"Rescue",
"Crystal",
"Fountain",
"Pyramid",
"Kill Agahnim",
"Ganon Room",
"Last Boss"};
static constexpr absl::string_view kSongNotes[] = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
class MusicEditor {
public:
void Update();
private:
void DrawChannels();
void DrawPianoStaff();
void DrawPianoRoll();
void DrawSongToolset();
void DrawToolset();
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
} // namespace app
} // namespace yaze
#endif

View File

@@ -0,0 +1,352 @@
#include "overworld_editor.h"
#include <imgui/imgui.h>
#include <cmath>
#include <unordered_map>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/editor/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "gui/canvas.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
// ----------------------------------------------------------------------------
absl::Status OverworldEditor::Update() {
// Initialize overworld graphics, maps, and palettes
if (rom_.isLoaded() && !all_gfx_loaded_) {
RETURN_IF_ERROR(LoadGraphics())
all_gfx_loaded_ = true;
}
// Draws the toolset for editing the Overworld.
RETURN_IF_ERROR(DrawToolset())
ImGui::Separator();
if (ImGui::BeginTable("#owEditTable", 2, ow_edit_flags, ImVec2(0, 0))) {
ImGui::TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch,
ImGui::GetContentRegionAvail().x);
ImGui::TableSetupColumn("Tile Selector");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
DrawOverworldCanvas();
ImGui::TableNextColumn();
DrawTileSelector();
ImGui::EndTable();
}
return absl::OkStatus();
}
// ----------------------------------------------------------------------------
absl::Status OverworldEditor::DrawToolset() {
if (ImGui::BeginTable("OWToolset", 17, toolset_table_flags, ImVec2(0, 0))) {
for (const auto &name : kToolsetColumnNames)
ImGui::TableSetupColumn(name.data());
BUTTON_COLUMN(ICON_MD_UNDO) // Undo
BUTTON_COLUMN(ICON_MD_REDO) // Redo
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
BUTTON_COLUMN(ICON_MD_ZOOM_OUT) // Zoom Out
BUTTON_COLUMN(ICON_MD_ZOOM_IN) // Zoom In
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
BUTTON_COLUMN(ICON_MD_DRAW) // Draw Tile
BUTTON_COLUMN(ICON_MD_DOOR_FRONT) // Entrances
BUTTON_COLUMN(ICON_MD_DOOR_BACK) // Exits
BUTTON_COLUMN(ICON_MD_GRASS) // Items
BUTTON_COLUMN(ICON_MD_PEST_CONTROL_RODENT) // Sprites
BUTTON_COLUMN(ICON_MD_ADD_LOCATION) // Transports
BUTTON_COLUMN(ICON_MD_MUSIC_NOTE) // Music
TEXT_COLUMN(ICON_MD_MORE_VERT) // Separator
ImGui::TableNextColumn(); // Palette
palette_editor_.DisplayPalette(palette_, overworld_.isLoaded());
ImGui::EndTable();
}
return absl::OkStatus();
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldMapSettings() {
if (ImGui::BeginTable("#mapSettings", 8, ow_map_flags, ImVec2(0, 0), -1)) {
for (const auto &name : kOverworldSettingsColumnNames)
ImGui::TableSetupColumn(name.data());
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(50.f);
ImGui::InputInt("Current Map", &current_map_);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(100.f);
ImGui::Combo("##world", &current_world_,
"Light World\0Dark World\0Extra World\0");
ImGui::TableNextColumn();
ImGui::Text("GFX");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##mapGFX", map_gfx_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Palette");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##mapPal", map_palette_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Spr GFX");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##sprGFX", spr_gfx_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Spr Palette");
ImGui::SameLine();
ImGui::SetNextItemWidth(kInputFieldSize);
ImGui::InputText("##sprPal", spr_palette_, kByteSize);
ImGui::TableNextColumn();
ImGui::Text("Msg ID");
ImGui::SameLine();
ImGui::SetNextItemWidth(50.f);
ImGui::InputText("##msgid", spr_palette_, kMessageIdSize);
ImGui::TableNextColumn();
ImGui::Checkbox("Show grid", &opt_enable_grid); // TODO
ImGui::EndTable();
}
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldEntrances() {
for (const auto &each : overworld_.Entrances()) {
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
each.map_id_ >= (current_world_ * 0x40)) {
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
ImVec4(210, 24, 210, 150));
std::string str = absl::StrFormat("%#x", each.entrance_id_);
ow_map_canvas_.DrawText(str, each.x_ - 4, each.y_ - 2);
}
}
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawOverworldMaps() {
int xx = 0;
int yy = 0;
for (int i = 0; i < 0x40; i++) {
int world_index = i + (current_world_ * 0x40);
int map_x = (xx * 0x200);
int map_y = (yy * 0x200);
ow_map_canvas_.DrawBitmap(maps_bmp_[world_index], map_x, map_y);
xx++;
if (xx >= 8) {
yy++;
xx = 0;
}
DrawOverworldEntrances();
}
}
// ----------------------------------------------------------------------------
// Overworld Editor canvas
// Allows the user to make changes to the overworld map.
void OverworldEditor::DrawOverworldCanvas() {
DrawOverworldMapSettings();
ImGui::Separator();
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)7);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar)) {
ow_map_canvas_.DrawBackground(ImVec2(0x200 * 8, 0x200 * 8));
ow_map_canvas_.DrawContextMenu();
if (overworld_.isLoaded()) {
DrawOverworldMaps();
// User has selected a tile they want to draw from the blockset.
if (!blockset_canvas_.Points().empty()) {
int x = blockset_canvas_.Points().front().x / 32;
int y = blockset_canvas_.Points().front().y / 32;
std::cout << x << " " << y << std::endl;
current_tile16_ = x + (y * 8);
std::cout << current_tile16_ << std::endl;
ow_map_canvas_.DrawTilePainter(tile16_individual_[current_tile16_], 16);
}
}
ow_map_canvas_.DrawGrid(64.0f);
ow_map_canvas_.DrawOverlay();
}
ImGui::EndChild();
}
// ----------------------------------------------------------------------------
// Tile 16 Selector
// Displays all the tiles in the game.
void OverworldEditor::DrawTile16Selector() {
blockset_canvas_.DrawBackground(ImVec2(0x100 + 1, (8192 * 2) + 1));
blockset_canvas_.DrawContextMenu();
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 2, map_blockset_loaded_);
blockset_canvas_.DrawTileSelector(32);
blockset_canvas_.DrawGrid(32.0f);
blockset_canvas_.DrawOverlay();
}
// ----------------------------------------------------------------------------
// Tile 8 Selector
// Displays all the individual tiles that make up a tile16.
void OverworldEditor::DrawTile8Selector() {
graphics_bin_canvas_.DrawBackground(
ImVec2(0x100 + 1, kNumSheetsToLoad * 0x40 + 1));
graphics_bin_canvas_.DrawContextMenu();
if (all_gfx_loaded_) {
for (const auto &[key, value] : graphics_bin_) {
int offset = 0x40 * (key + 1);
int top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 2;
if (key >= 1) {
top_left_y = graphics_bin_canvas_.GetZeroPoint().y + 0x40 * key;
}
graphics_bin_canvas_.GetDrawList()->AddImage(
(void *)value.GetTexture(),
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 2, top_left_y),
ImVec2(graphics_bin_canvas_.GetZeroPoint().x + 0x100,
graphics_bin_canvas_.GetZeroPoint().y + offset));
}
}
graphics_bin_canvas_.DrawGrid(16.0f);
graphics_bin_canvas_.DrawOverlay();
}
// ----------------------------------------------------------------------------
// Displays the graphics tilesheets that are available on the current selected
// overworld map.
void OverworldEditor::DrawAreaGraphics() {
current_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
current_gfx_canvas_.DrawContextMenu();
current_gfx_canvas_.DrawTileSelector(32);
current_gfx_canvas_.DrawBitmap(current_gfx_bmp_, 2, overworld_.isLoaded());
current_gfx_canvas_.DrawGrid(32.0f);
current_gfx_canvas_.DrawOverlay();
}
// ----------------------------------------------------------------------------
void OverworldEditor::DrawTileSelector() {
if (ImGui::BeginTabBar("##TabBar", ImGuiTabBarFlags_FittingPolicyScroll)) {
if (ImGui::BeginTabItem("Tile16")) {
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)2);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawTile16Selector();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Tile8")) {
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)1);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawTile8Selector();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Area Graphics")) {
if (ImGuiID child_id = ImGui::GetID((void *)(intptr_t)3);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawAreaGraphics();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
// ----------------------------------------------------------------------------
absl::Status OverworldEditor::LoadGraphics() {
// Load all of the graphics data from the game.
PRINT_IF_ERROR(rom_.LoadAllGraphicsData())
graphics_bin_ = rom_.GetGraphicsBin();
// Load the Link to the Past overworld.
RETURN_IF_ERROR(overworld_.Load(rom_))
palette_ = overworld_.AreaPalette();
current_gfx_bmp_.Create(0x80, 0x200, 0x40, overworld_.AreaGraphics());
current_gfx_bmp_.ApplyPalette(palette_);
rom_.RenderBitmap(&current_gfx_bmp_);
// Create the tile16 blockset image
tile16_blockset_bmp_.Create(0x80, 8192, 0x80, overworld_.Tile16Blockset());
tile16_blockset_bmp_.ApplyPalette(palette_);
rom_.RenderBitmap(&tile16_blockset_bmp_);
map_blockset_loaded_ = true;
// Copy the tile16 data into individual tiles.
auto tile16_data = overworld_.Tile16Blockset();
// Loop through the tiles and copy their pixel data into separate vectors
for (int i = 0; i < 4096; i++) {
// Create a new vector for the pixel data of the current tile
Bytes tile_data;
for (int j = 0; j < 32 * 32; j++) tile_data.push_back(0x00);
// Copy the pixel data for the current tile into the vector
for (int ty = 0; ty < 32; ty++) {
for (int tx = 0; tx < 32; tx++) {
int position = (tx + (ty * 0x20));
uchar value = tile16_data[i + tx + (ty * 0x80)];
tile_data[position] = value;
}
}
// Add the vector for the current tile to the vector of tile pixel data
tile16_individual_data_.push_back(tile_data);
}
// Render the bitmaps of each tile.
for (int id = 0; id < 4096; id++) {
gfx::Bitmap new_tile16;
tile16_individual_.emplace_back(new_tile16);
tile16_individual_[id].Create(0x10, 0x10, 0x80,
tile16_individual_data_[id]);
tile16_individual_[id].ApplyPalette(palette_);
rom_.RenderBitmap(&tile16_individual_[id]);
}
// Render the overworld maps loaded from the ROM.
for (int i = 0; i < core::kNumOverworldMaps; ++i) {
overworld_.SetCurrentMap(i);
auto palette = overworld_.AreaPalette();
maps_bmp_[i].Create(0x200, 0x200, 0x200, overworld_.BitmapData());
maps_bmp_[i].ApplyPalette(palette);
rom_.RenderBitmap(&(maps_bmp_[i]));
}
return absl::OkStatus();
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,118 @@
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#include <imgui/imgui.h>
#include <cmath>
#include <unordered_map>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/editor/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "app/zelda3/overworld.h"
#include "gui/canvas.h"
#include "gui/icons.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", "#drawTool", "#separator2",
"#zoomOutTool", "#zoomInTool", "#separator", "#history",
"#entranceTool", "#exitTool", "#itemTool", "#spriteTool",
"#transportTool", "#musicTool"};
static constexpr absl::string_view kOverworldSettingsColumnNames[] = {
"##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
"##sprpalCol", "##msgidCol", "##2ndCol"};
class OverworldEditor {
public:
absl::Status Update();
absl::Status Undo() const { return absl::UnimplementedError("Undo"); }
absl::Status Redo() const { return absl::UnimplementedError("Redo"); }
absl::Status Cut() const { return absl::UnimplementedError("Cut"); }
absl::Status Copy() const { return absl::UnimplementedError("Copy"); }
absl::Status Paste() const { return absl::UnimplementedError("Paste"); }
void SetupROM(ROM &rom) { rom_ = rom; }
private:
absl::Status DrawToolset();
void DrawOverworldMapSettings();
void DrawOverworldEntrances();
void DrawOverworldMaps();
void DrawOverworldCanvas();
void DrawTile16Selector();
void DrawTile8Selector();
void DrawAreaGraphics();
void DrawTileSelector();
absl::Status LoadGraphics();
int current_world_ = 0;
int current_map_ = 0;
int current_tile16_ = 0;
int selected_tile_ = 0;
char map_gfx_[3] = "";
char map_palette_[3] = "";
char spr_gfx_[3] = "";
char spr_palette_[3] = "";
char message_id_[5] = "";
char staticgfx[16];
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;
ImGuiTableFlags toolset_table_flags = ImGuiTableFlags_SizingFixedFit;
ImGuiTableFlags ow_map_flags = ImGuiTableFlags_Borders;
ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchSame;
Bytes selected_tile_data_;
std::unordered_map<int, gfx::Bitmap> graphics_bin_;
std::unordered_map<int, gfx::Bitmap> current_graphics_set_;
std::unordered_map<int, gfx::Bitmap> maps_bmp_;
std::unordered_map<int, gfx::Bitmap> sprite_previews_;
std::vector<Bytes> tile16_individual_data_;
std::vector<gfx::Bitmap> tile16_individual_;
ROM rom_;
PaletteEditor palette_editor_;
zelda3::Overworld overworld_;
gfx::SNESPalette palette_;
gfx::Bitmap selected_tile_bmp_;
gfx::Bitmap tile16_blockset_bmp_;
gfx::Bitmap current_gfx_bmp_;
gfx::Bitmap all_gfx_bmp;
gui::Canvas ow_map_canvas_;
gui::Canvas current_gfx_canvas_;
gui::Canvas blockset_canvas_;
gui::Canvas graphics_bin_canvas_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif

View File

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

View File

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

View File

@@ -0,0 +1,201 @@
#include "app/editor/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/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "gui/canvas.h"
#include "gui/icons.h"
#include "gui/input.h"
namespace yaze {
namespace app {
namespace editor {
ScreenEditor::ScreenEditor() { screen_canvas_.SetCanvasSize(ImVec2(512, 512)); }
void ScreenEditor::Update() {
TAB_BAR("##TabBar")
DrawInventoryMenuEditor();
DrawTitleScreenEditor();
DrawNamingScreenEditor();
DrawOverworldMapEditor();
DrawDungeonMapsEditor();
DrawMosaicEditor();
END_TAB_BAR()
}
void ScreenEditor::DrawWorldGrid(int world, int h, int w) {
const float time = (float)ImGui::GetTime();
int i = 0;
if (world == 1) {
i = 64;
} else if (world == 2) {
i = 128;
}
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
if (x > 0) ImGui::SameLine();
ImGui::PushID(y * 4 + x);
std::string label = absl::StrCat(" #", absl::StrFormat("%x", i));
if (ImGui::Selectable(label.c_str(), mosaic_tiles_[i] != 0, 0,
ImVec2(35, 25))) {
mosaic_tiles_[i] ^= 1;
}
ImGui::PopID();
i++;
}
}
void ScreenEditor::DrawInventoryMenuEditor() {
TAB_ITEM("Inventory Menu")
static bool create = false;
if (!create && rom_.isLoaded()) {
inventory_.Create();
palette_ = inventory_.Palette();
create = true;
}
DrawInventoryToolset();
if (ImGui::BeginTable("InventoryScreen", 3, ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn("Canvas");
ImGui::TableSetupColumn("Tiles");
ImGui::TableSetupColumn("Palette");
ImGui::TableHeadersRow();
ImGui::TableNextColumn();
screen_canvas_.DrawBackground();
screen_canvas_.DrawContextMenu();
screen_canvas_.DrawBitmap(inventory_.Bitmap(), 2, create);
screen_canvas_.DrawGrid(32.0f);
screen_canvas_.DrawOverlay();
ImGui::TableNextColumn();
tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
tilesheet_canvas_.DrawContextMenu();
tilesheet_canvas_.DrawBitmap(inventory_.Tilesheet(), 2, create);
tilesheet_canvas_.DrawGrid(16.0f);
tilesheet_canvas_.DrawOverlay();
ImGui::TableNextColumn();
gui::DisplayPalette(palette_, create);
ImGui::EndTable();
}
ImGui::Separator();
END_TAB_ITEM()
}
void ScreenEditor::DrawTitleScreenEditor() {
TAB_ITEM("Title Screen")
END_TAB_ITEM()
}
void ScreenEditor::DrawNamingScreenEditor() {
TAB_ITEM("Naming Screen")
END_TAB_ITEM()
}
void ScreenEditor::DrawOverworldMapEditor() {
TAB_ITEM("Overworld Map")
END_TAB_ITEM()
}
void ScreenEditor::DrawDungeonMapsEditor() {
TAB_ITEM("Dungeon Maps")
END_TAB_ITEM()
}
void ScreenEditor::DrawMosaicEditor() {
TAB_ITEM("Mosaic Transitions")
if (ImGui::BeginTable("Worlds", 3, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("Light World");
ImGui::TableSetupColumn("Dark World");
ImGui::TableSetupColumn("Special World");
ImGui::TableHeadersRow();
ImGui::TableNextColumn();
DrawWorldGrid(0);
ImGui::TableNextColumn();
DrawWorldGrid(1);
ImGui::TableNextColumn();
DrawWorldGrid(2, 4);
ImGui::EndTable();
}
gui::InputHex("Routine Location", &overworldCustomMosaicASM);
if (ImGui::Button("Generate Mosaic Assembly")) {
auto mosaic =
rom_.PatchOverworldMosaic(mosaic_tiles_, overworldCustomMosaicASM);
if (!mosaic.ok()) {
std::cout << mosaic;
}
}
END_TAB_ITEM()
}
void ScreenEditor::DrawToolset() {
static bool show_bg1 = true;
static bool show_bg2 = true;
static bool show_bg3 = true;
static bool drawing_bg1 = true;
static bool drawing_bg2 = false;
static bool drawing_bg3 = false;
ImGui::Checkbox("Show BG1", &show_bg1);
ImGui::SameLine();
ImGui::Checkbox("Show BG2", &show_bg2);
ImGui::Checkbox("Draw BG1", &drawing_bg1);
ImGui::SameLine();
ImGui::Checkbox("Draw BG2", &drawing_bg2);
ImGui::SameLine();
ImGui::Checkbox("Draw BG3", &drawing_bg3);
}
void ScreenEditor::DrawInventoryToolset() {
if (ImGui::BeginTable("InventoryToolset", 8, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
ImGui::TableSetupColumn("#drawTool");
ImGui::TableSetupColumn("#sep1");
ImGui::TableSetupColumn("#zoomOut");
ImGui::TableSetupColumn("#zoomIN");
ImGui::TableSetupColumn("#sep2");
ImGui::TableSetupColumn("#bg2Tool");
ImGui::TableSetupColumn("#bg3Tool");
ImGui::TableSetupColumn("#itemTool");
BUTTON_COLUMN(ICON_MD_UNDO)
BUTTON_COLUMN(ICON_MD_REDO)
TEXT_COLUMN(ICON_MD_MORE_VERT)
BUTTON_COLUMN(ICON_MD_ZOOM_OUT)
BUTTON_COLUMN(ICON_MD_ZOOM_IN)
TEXT_COLUMN(ICON_MD_MORE_VERT)
BUTTON_COLUMN(ICON_MD_DRAW)
BUTTON_COLUMN(ICON_MD_BUILD)
ImGui::EndTable();
}
}
} // namespace editor
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,60 @@
#ifndef YAZE_APP_EDITOR_SCREEN_EDITOR_H
#define YAZE_APP_EDITOR_SCREEN_EDITOR_H
#include <imgui/imgui.h>
#include <array>
#include "app/core/constants.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "app/zelda3/inventory.h"
#include "gui/canvas.h"
#include "gui/color.h"
#include "gui/icons.h"
namespace yaze {
namespace app {
namespace editor {
using MosaicArray = std::array<int, core::kNumOverworldMaps>;
static int overworldCustomMosaicASM = 0x1301D0;
class ScreenEditor {
public:
ScreenEditor();
void SetupROM(ROM &rom) {
rom_ = rom;
inventory_.SetupROM(rom_);
}
void Update();
private:
void DrawMosaicEditor();
void DrawTitleScreenEditor();
void DrawNamingScreenEditor();
void DrawOverworldMapEditor();
void DrawDungeonMapsEditor();
void DrawInventoryMenuEditor();
void DrawToolset();
void DrawInventoryToolset();
void DrawWorldGrid(int world, int h = 8, int w = 8);
char mosaic_tiles_[core::kNumOverworldMaps];
ROM rom_;
Bytes all_gfx_;
zelda3::Inventory inventory_;
gfx::SNESPalette palette_;
gui::Canvas screen_canvas_;
gui::Canvas tilesheet_canvas_;
};
} // namespace editor
} // namespace app
} // namespace yaze
#endif