7#include "absl/strings/match.h"
8#include "absl/strings/str_cat.h"
21using util::FileDialogWrapper;
25static const char*
const kKeywords[] = {
26 "ADC",
"AND",
"ASL",
"BCC",
"BCS",
"BEQ",
"BIT",
"BMI",
"BNE",
"BPL",
27 "BRA",
"BRL",
"BVC",
"BVS",
"CLC",
"CLD",
"CLI",
"CLV",
"CMP",
"CPX",
28 "CPY",
"DEC",
"DEX",
"DEY",
"EOR",
"INC",
"INX",
"INY",
"JMP",
"JSR",
29 "JSL",
"LDA",
"LDX",
"LDY",
"LSR",
"MVN",
"NOP",
"ORA",
"PEA",
"PER",
30 "PHA",
"PHB",
"PHD",
"PHP",
"PHX",
"PHY",
"PLA",
"PLB",
"PLD",
"PLP",
31 "PLX",
"PLY",
"REP",
"ROL",
"ROR",
"RTI",
"RTL",
"RTS",
"SBC",
"SEC",
32 "SEI",
"SEP",
"STA",
"STP",
"STX",
"STY",
"STZ",
"TAX",
"TAY",
"TCD",
33 "TCS",
"TDC",
"TRB",
"TSB",
"TSC",
"TSX",
"TXA",
"TXS",
"TXY",
"TYA",
34 "TYX",
"WAI",
"WDM",
"XBA",
"XCE",
"ORG",
"LOROM",
"HIROM"};
36static const char*
const kIdentifiers[] = {
37 "abort",
"abs",
"acos",
"asin",
"atan",
"atexit",
38 "atof",
"atoi",
"atol",
"ceil",
"clock",
"cosh",
39 "ctime",
"div",
"exit",
"fabs",
"floor",
"fmod",
40 "getchar",
"getenv",
"isalnum",
"isalpha",
"isdigit",
"isgraph",
41 "ispunct",
"isspace",
"isupper",
"kbhit",
"log10",
"log2",
42 "log",
"memcmp",
"modf",
"pow",
"putchar",
"putenv",
43 "puts",
"rand",
"remove",
"rename",
"sinh",
"sqrt",
44 "srand",
"strcat",
"strcmp",
"strerror",
"time",
"tolower",
49 for (
auto& k : kKeywords)
52 for (
auto& k : kIdentifiers) {
55 language_65816.
mIdentifiers.insert(std::make_pair(std::string(k),
id));
59 std::make_pair<std::string, TextEditor::PaletteIndex>(
62 std::make_pair<std::string, TextEditor::PaletteIndex>(
65 std::make_pair<std::string, TextEditor::PaletteIndex>(
68 std::make_pair<std::string, TextEditor::PaletteIndex>(
69 "[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?",
72 std::make_pair<std::string, TextEditor::PaletteIndex>(
75 std::make_pair<std::string, TextEditor::PaletteIndex>(
78 std::make_pair<std::string, TextEditor::PaletteIndex>(
79 "0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?",
82 std::make_pair<std::string, TextEditor::PaletteIndex>(
85 std::make_pair<std::string, TextEditor::PaletteIndex>(
86 "[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/"
97 language_65816.
mName =
"65816";
99 return language_65816;
103 const std::vector<std::string>& files,
104 const std::vector<std::string>& ignored_files) {
105 std::vector<std::string> filtered_files;
106 for (
const auto& file : files) {
108 if (absl::StrContains(file,
'/')) {
112 if (!absl::StrContains(file,
'.')) {
115 if (std::ranges::find(ignored_files, file) == ignored_files.end()) {
116 filtered_files.push_back(file);
119 return filtered_files;
124 std::ifstream gitignore(folder +
"/.gitignore");
125 std::vector<std::string> ignored_files;
126 if (gitignore.good()) {
128 while (std::getline(gitignore, line)) {
129 if (line[0] ==
'#') {
132 if (line[0] ==
'!') {
136 ignored_files.push_back(line);
141 current_folder.
name = folder;
145 for (
const auto& subfolder :
148 folder_item.
name = subfolder;
149 std::string full_folder = current_folder.
name +
"/" + subfolder;
151 for (
const auto& files : folder_files) {
153 if (absl::StrContains(files,
'/')) {
157 if (!absl::StrContains(files,
'.')) {
160 if (std::ranges::find(ignored_files, files) != ignored_files.end()) {
163 folder_item.
files.push_back(files);
166 for (
const auto& subdir :
169 subfolder_item.
name = subdir;
171 folder_item.
subfolders.push_back(subfolder_item);
173 current_folder.
subfolders.push_back(folder_item);
176 return current_folder;
191 std::make_unique<AssemblyCodeEditorPanel>([
this]() {
DrawCodeEditor(); }));
194 panel_manager->RegisterEditorPanel(
195 std::make_unique<AssemblyFileBrowserPanel>([
this]() {
DrawFileBrowser(); }));
198 panel_manager->RegisterEditorPanel(
202 panel_manager->RegisterEditorPanel(
203 std::make_unique<AssemblyBuildOutputPanel>([
this]() {
DrawBuildOutput(); }));
206 panel_manager->RegisterEditorPanel(
212 return absl::OkStatus();
225 if (ImGui::BeginMenuBar()) {
234 ImGui::Text(
"%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
259 ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
263 ImGui::TextDisabled(
"No folder opened");
268 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
"%s",
278 ImGui::TextDisabled(
"No symbols loaded.");
280 ImGui::TextWrapped(
"Apply a patch or load external symbols to populate this list.");
285 static char filter[256] =
"";
286 ImGui::SetNextItemWidth(-1);
287 ImGui::InputTextWithHint(
"##symbol_filter",
ICON_MD_SEARCH " Filter symbols...",
288 filter,
sizeof(filter));
292 if (ImGui::BeginChild(
"##symbol_list", ImVec2(0, 0),
false)) {
293 for (
const auto& [name, symbol] :
symbols_) {
295 if (filter[0] !=
'\0' && name.find(filter) == std::string::npos) {
299 ImGui::PushID(name.c_str());
300 if (ImGui::Selectable(name.c_str())) {
303 ImGui::SameLine(ImGui::GetContentRegionAvail().x - 60);
304 ImGui::TextDisabled(
"$%06X", symbol.address);
313 ImGui::Text(
"Errors: %zu Warnings: %zu",
last_errors_.size(),
318 bool has_active_file =
323 if (has_active_file) {
331 ImGui::BeginDisabled(!has_rom || !has_active_file);
332 if (ImGui::Button(
ICON_MD_BUILD " Apply to ROM", ImVec2(140, 0))) {
338 ImGui::EndDisabled();
343 if (ImGui::BeginChild(
"##build_log", ImVec2(0, 0),
true)) {
346 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f));
348 ImGui::PopStyleColor();
352 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
354 ImGui::PopStyleColor();
357 ImGui::TextDisabled(
"No build output");
364 float button_size = 32.0f;
369 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"Open Folder");
374 if (!filename.empty()) {
378 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"Open File");
382 ImGui::BeginDisabled(!can_save);
383 if (ImGui::Button(
ICON_MD_SAVE, ImVec2(button_size, button_size))) {
386 ImGui::EndDisabled();
387 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"Save File");
394 ImGui::BeginDisabled(!can_save);
398 ImGui::EndDisabled();
399 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"Validate (Ctrl+B)");
403 ImGui::BeginDisabled(!can_apply);
404 if (ImGui::Button(
ICON_MD_BUILD, ImVec2(button_size, button_size))) {
407 ImGui::EndDisabled();
408 if (ImGui::IsItemHovered()) ImGui::SetTooltip(
"Apply to ROM (Ctrl+Shift+B)");
416 if (ImGui::BeginTabBar(
"##OpenFileTabs", ImGuiTabBarFlags_Reorderable |
417 ImGuiTabBarFlags_AutoSelectNewTabs |
418 ImGuiTabBarFlags_FittingPolicyScroll)) {
421 if (file_id >=
files_.size())
continue;
424 std::string filename =
files_[file_id];
425 size_t pos = filename.find_last_of(
"/\\");
426 if (pos != std::string::npos) {
427 filename = filename.substr(pos + 1);
431 ImGuiTabItemFlags flags = is_active ? ImGuiTabItemFlags_SetSelected : 0;
432 bool tab_open =
true;
434 if (ImGui::BeginTabItem(filename.c_str(), &tab_open, flags)) {
466 ImGui::Begin(
"Assembly Editor", &is_loaded, ImGuiWindowFlags_MenuBar);
476 ImGui::Text(
"%6d/%-6d %6d lines | %s | %s | %s | %s", cpos.mLine + 1,
498 return absl::OkStatus();
500 return absl::FailedPreconditionError(
"No active file to save.");
523 if (ImGui::BeginChild(
"##current_folder", ImVec2(0, 0),
true,
524 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
525 if (ImGui::BeginTable(
"##file_table", 2,
526 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
527 ImGuiTableFlags_Resizable |
528 ImGuiTableFlags_Sortable)) {
529 ImGui::TableSetupColumn(
"Name", ImGuiTableColumnFlags_WidthFixed, 256.0f);
530 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthStretch);
532 ImGui::TableHeadersRow();
535 ImGui::TableNextRow();
536 ImGui::TableNextColumn();
537 if (ImGui::Selectable(file.c_str())) {
540 ImGui::TableNextColumn();
545 ImGui::TableNextRow();
546 ImGui::TableNextColumn();
547 if (ImGui::TreeNode(subfolder.name.c_str())) {
548 for (
const auto& file : subfolder.files) {
549 ImGui::TableNextRow();
550 ImGui::TableNextColumn();
551 if (ImGui::Selectable(file.c_str())) {
553 subfolder.name,
"/", file));
555 ImGui::TableNextColumn();
560 ImGui::TableNextColumn();
561 ImGui::Text(
"Folder");
572 if (ImGui::BeginMenu(
"File")) {
585 if (ImGui::BeginMenu(
"Edit")) {
614 if (
files_[file_id] == filename) {
621 int new_file_id =
files_.size();
622 files_.push_back(std::string(filename));
632 if (!content.empty()) {
637 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error opening file: %s\n",
638 std::string(filename).c_str());
644 return absl::OkStatus();
649 return absl::OkStatus();
654 return absl::OkStatus();
659 return absl::OkStatus();
664 return absl::OkStatus();
668 return absl::OkStatus();
677 return absl::FailedPreconditionError(
"No file is currently active");
705 size_t line_pos = error.find(
':');
706 if (line_pos != std::string::npos) {
707 size_t num_start = line_pos + 1;
708 size_t num_end = error.find(
':', num_start);
709 if (num_end != std::string::npos) {
710 std::string line_str = error.substr(num_start, num_end - num_start);
712 int line = std::stoi(line_str);
713 markers[line] = error;
726 return absl::OkStatus();
731 return absl::FailedPreconditionError(
"No ROM is loaded");
735 return absl::FailedPreconditionError(
"No file is currently active");
751 std::vector<uint8_t> rom_data =
rom_->
vector();
758 return result.status();
761 if (result->success) {
773 return absl::OkStatus();
776 return absl::InternalError(
"Patch application failed");
792 for (
const auto& error : result.
errors) {
795 size_t first_colon = error.find(
':');
796 if (first_colon != std::string::npos) {
797 size_t second_colon = error.find(
':', first_colon + 1);
798 if (second_colon != std::string::npos) {
799 std::string line_str = error.substr(first_colon + 1, second_colon - (first_colon + 1));
800 int line = std::stoi(line_str);
804 markers[line] = error;
827 if (ImGui::BeginMenu(
"Assemble")) {
828 bool has_active_file =
839 if (ImGui::MenuItem(
ICON_MD_BUILD " Apply to ROM",
"Ctrl+Shift+B",
false,
840 has_active_file && has_rom)) {
850 if (!sym_file.empty()) {
881 ImGui::TextDisabled(
"Errors: %zu, Warnings: %zu",
last_errors_.size(),
887 if (ImGui::BeginMenu(
"Version")) {
889 if (ImGui::MenuItem(
ICON_MD_CAMERA_ALT " Create Snapshot",
nullptr,
false, has_version_manager)) {
890 if (has_version_manager) {
891 ImGui::OpenPopup(
"Create Snapshot");
896 if (ImGui::BeginPopupModal(
"Create Snapshot",
nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
897 static char message[256] =
"";
898 ImGui::InputText(
"Message", message,
sizeof(message));
900 if (ImGui::Button(
"Create", ImVec2(120, 0))) {
902 if (result.ok() && result->success) {
908 std::string err = result.ok() ? result->message : std::string(result.status().message());
912 ImGui::CloseCurrentPopup();
916 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
917 ImGui::CloseCurrentPopup();
931 ImGui::SetNextWindowSize(ImVec2(350, 400), ImGuiCond_FirstUseEver);
934 ImGui::TextDisabled(
"No symbols loaded.");
935 ImGui::TextDisabled(
"Apply a patch to load symbols.");
938 static char filter[256] =
"";
939 ImGui::InputTextWithHint(
"##symbol_filter",
"Filter symbols...", filter,
944 if (ImGui::BeginChild(
"##symbol_list", ImVec2(0, 0),
true)) {
945 for (
const auto& [name, symbol] :
symbols_) {
947 if (filter[0] !=
'\0' &&
948 name.find(filter) == std::string::npos) {
952 ImGui::PushID(name.c_str());
953 if (ImGui::Selectable(name.c_str())) {
957 ImGui::SameLine(200);
958 ImGui::TextDisabled(
"$%06X", symbol.address);
static const Palette & GetDarkPalette()
Coordinates GetCursorPosition() const
int GetTotalLines() const
void Render(const char *aTitle, const ImVec2 &aSize=ImVec2(), bool aBorder=false)
std::map< int, std::string > ErrorMarkers
const LanguageDefinition & GetLanguageDefinition() const
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
const auto & vector() const
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
absl::StatusOr< AsarPatchResult > ApplyPatch(const std::string &patch_path, std::vector< uint8_t > &rom_data, const std::vector< std::string > &include_paths={})
absl::Status ValidateAssembly(const std::string &asm_path)
absl::Status Initialize()
absl::Status LoadSymbolsFromFile(const std::string &symbol_path)
std::map< std::string, AsarSymbol > GetSymbolTable() const
absl::StatusOr< SnapshotResult > CreateSnapshot(const std::string &message)
std::string current_file_
absl::Status ValidateCurrentFile()
std::vector< std::string > last_warnings_
std::map< std::string, core::AsarSymbol > symbols_
absl::Status Save() override
std::vector< TextEditor > open_files_
absl::Status Load() override
void Initialize() override
void ChangeActiveFile(const std::string_view &filename)
absl::Status Copy() override
void UpdateErrorMarkers(const core::AsarPatchResult &result)
void OpenFolder(const std::string &folder_path)
absl::Status Paste() override
FolderItem current_folder_
absl::Status Update() override
std::vector< std::string > files_
void DrawToolbarContent()
absl::Status Redo() override
ImVector< int > active_files_
absl::Status Undo() override
void DrawSymbolsContent()
absl::Status ApplyPatchToRom()
absl::Status Cut() override
std::vector< std::string > last_errors_
EditorDependencies dependencies_
void RegisterEditorPanel(std::unique_ptr< EditorPanel > panel)
Register an EditorPanel instance for central drawing.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static std::string ShowOpenFolderDialog()
ShowOpenFolderDialog opens a file dialog and returns the selected folder path. Uses global feature fl...
static std::vector< std::string > GetFilesInFolder(const std::string &folder_path)
static std::vector< std::string > GetSubdirectoriesInFolder(const std::string &folder_path)
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_CONTENT_CUT
#define ICON_MD_FILE_OPEN
#define ICON_MD_CAMERA_ALT
#define ICON_MD_FILE_UPLOAD
#define ICON_MD_CONTENT_PASTE
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_CONTENT_COPY
FolderItem LoadFolder(const std::string &folder)
std::vector< std::string > RemoveIgnoredFiles(const std::vector< std::string > &files, const std::vector< std::string > &ignored_files)
Editors are the view controllers for the application.
TextEditor::LanguageDefinition GetAssemblyLanguageDef()
void SaveFile(const std::string &filename, const std::string &contents)
std::string LoadFile(const std::string &filename)
Loads the entire contents of a file into a string.
TokenRegexStrings mTokenRegexStrings
std::string mSingleLineComment
std::string mCommentStart
Asar patch result information.
std::vector< std::string > errors
std::vector< std::string > warnings
project::YazeProject * project
ToastManager * toast_manager
core::VersionManager * version_manager
PanelManager * panel_manager
std::vector< FolderItem > subfolders
std::vector< std::string > files
std::string GetAbsolutePath(const std::string &relative_path) const
std::string symbols_filename