7#include "absl/strings/match.h"
8#include "absl/strings/str_format.h"
9#include "absl/strings/str_split.h"
14#include "imgui/imgui.h"
16#include "yaze_config.h"
34 ImGui::SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
35 if (!ImGui::Begin(absl::StrFormat(
"%s Project Editor###ProjectFileEditor",
44 if (ImGui::BeginTable(
"ProjectEditorToolbar", 10,
45 ImGuiTableFlags_SizingFixedFit)) {
46 ImGui::TableNextColumn();
51 ImGui::TableNextColumn();
59 std::string(status.message().data(), status.message().size()),
65 ImGui::TableNextColumn();
68 ImGui::BeginDisabled();
69 if (ImGui::Button(absl::StrFormat(
"%s Save",
ICON_MD_SAVE).c_str())) {
75 std::string(status.message().data(), status.message().size()),
82 ImGui::TableNextColumn();
83 if (ImGui::Button(absl::StrFormat(
"%s Save As",
ICON_MD_SAVE_AS).c_str())) {
92 std::string(status.message().data(), status.message().size()),
98 ImGui::TableNextColumn();
101 ImGui::TableNextColumn();
104 absl::StrFormat(
"%s Import Labels",
ICON_MD_LABEL).c_str())) {
111 std::string(status.message().data(), status.message().size()),
115 if (ImGui::IsItemHovered()) {
116 ImGui::SetTooltip(
"Import labels from ZScream DefaultNames.txt");
119 ImGui::TableNextColumn();
126 ImGui::TableNextColumn();
129 ImGui::TableNextColumn();
131 ImGui::TextDisabled(
"%s",
filepath_.c_str());
133 ImGui::TextDisabled(
"No file loaded");
144 "ValidationErrors", ImVec2(0, 100),
145 {.bg = ImVec4(0.3f, 0.2f, 0.2f, 0.5f)},
true);
147 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
150 ImGui::BulletText(
"%s", error.c_str());
156 ImVec2 editor_size = ImGui::GetContentRegionAvail();
164 std::string key = std::filesystem::path(
filepath).stem().string();
168 auto storage_or = platform::WasmStorage::LoadProject(key);
169 if (storage_or.ok()) {
174 return absl::OkStatus();
179 if (!file.is_open()) {
180 return absl::InvalidArgumentError(
181 absl::StrFormat(
"Cannot open file: %s",
filepath));
184 std::stringstream buffer;
185 buffer << file.rdbuf();
194 return absl::OkStatus();
199 return absl::InvalidArgumentError(
"No file path specified");
208 if (!absl::EndsWith(final_path,
".yaze")) {
209 final_path +=
".yaze";
213 std::string key = std::filesystem::path(final_path).stem().string();
217 auto storage_status =
219 if (!storage_status.ok()) {
220 return storage_status;
227 return absl::OkStatus();
229 std::ofstream file(final_path);
230 if (!file.is_open()) {
231 return absl::InvalidArgumentError(
232 absl::StrFormat(
"Cannot create file: %s", final_path));
246 return absl::OkStatus();
252 const std::string template_content = absl::StrFormat(
253 R
"(# yaze Project File
272ollama_host=http://localhost:11434
273use_custom_prompt=false
277rom_backup_folder=backups
280patches_folder=patches
281labels_filename=labels.txt
282symbols_filename=symbols.txt
284custom_objects_folder=
285# Optional: ASM integration (e.g. Oracle-of-Secrets hack_manifest.json)
295# REMOVED: kLogInstructions - DisassemblyViewer is always active
296kSaveOverworldMaps=true
297kSaveOverworldEntrances=true
298kSaveOverworldExits=true
299kSaveOverworldItems=true
300kSaveOverworldProperties=true
302kSaveDungeonObjects=true
303kSaveDungeonSprites=true
304kSaveDungeonRoomHeaders=true
305kSaveDungeonTorches=true
307kSaveDungeonBlocks=true
308kSaveDungeonCollision=true
309kSaveDungeonChests=true
310kSaveDungeonPotItems=true
311kSaveDungeonPalettes=true
312kSaveGraphicsSheet=true
316kLoadCustomOverworld=false
321autosave_interval_secs=300
323backup_retention_count=20
324backup_keep_daily=true
325backup_keep_daily_days=14
329# expanded_message_start=0x178000
330# expanded_message_end=0x17FFFF
331# expanded_music_hook=0x008919
332# expanded_music_main=0x1A9EF5
333# expanded_music_aux=0x1ACCA7
334# overworld_messages_expanded=0x1417F8
335# overworld_map16_expanded=0x1E8000
336# overworld_map32_tr_expanded=0x020000
337# overworld_map32_bl_expanded=0x1F0000
338# overworld_map32_br_expanded=0x1F8000
339# overworld_entrance_map_expanded=0x0DB55F
340# overworld_entrance_pos_expanded=0x0DB35F
341# overworld_entrance_id_expanded=0x0DB75F
342# overworld_entrance_flag_expanded=0x0DB895
343# overworld_ptr_marker_expanded=0x1423FF
344# overworld_ptr_magic_expanded=0xEA
345# overworld_gfx_ptr1=0x004F80
346# overworld_gfx_ptr2=0x00505F
347# overworld_gfx_ptr3=0x00513E
350# object_0x31=track_LR.bin,track_UD.bin,track_corner_TL.bin
351# object_0x32=furnace.bin,firewood.bin,ice_chair.bin
357asm_entry_point=asm/main.asm
363persist_custom_music=true
384 std::vector<std::string> lines = absl::StrSplit(content,
'\n');
386 std::string current_section;
389 for (
const auto& line : lines) {
391 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
394 if (trimmed.empty() || trimmed[0] ==
'#')
398 if (trimmed[0] ==
'[' && trimmed[trimmed.size() - 1] ==
']') {
399 current_section = trimmed.substr(1, trimmed.size() - 2);
402 if (current_section !=
"project" && current_section !=
"files" &&
403 current_section !=
"feature_flags" &&
404 current_section !=
"workspace" &&
405 current_section !=
"workspace_settings" && current_section !=
"rom" &&
406 current_section !=
"rom_addresses" &&
407 current_section !=
"custom_objects" &&
408 current_section !=
"dungeon_overlay" && current_section !=
"build" &&
409 current_section !=
"agent_settings" && current_section !=
"music" &&
410 current_section !=
"keybindings" &&
411 current_section !=
"editor_visibility") {
413 "Line %d: Unknown section [%s]", line_num, current_section));
419 size_t equals_pos = trimmed.find(
'=');
420 if (equals_pos == std::string::npos) {
422 "Line %d: Invalid format, expected key=value", line_num));
436 ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
"Validation Errors:");
438 ImGui::BulletText(
"%s", error.c_str());
444 return absl::UnimplementedError(
445 "File-based label import is not supported in the web build");
448 return absl::FailedPreconditionError(
449 "No project loaded. Open a project first.");
455 return absl::CancelledError(
"No file selected");
459 std::ifstream input_file(file);
460 if (!input_file.is_open()) {
461 return absl::InvalidArgumentError(
462 absl::StrFormat(
"Cannot open file: %s", file));
465 std::stringstream buffer;
466 buffer << input_file.rdbuf();
void SetShowWhitespaces(bool aValue)
std::string GetText() const
void Render(const char *aTitle, const ImVec2 &aSize=ImVec2(), bool aBorder=false)
void SetText(const std::string &aText)
void SetTabSize(int aValue)
void SetLanguageDefinition(const LanguageDefinition &aLanguageDef)
void ApplySyntaxHighlighting()
ToastManager * toast_manager_
absl::Status SaveFileAs(const std::string &filepath)
Save to a new file path.
absl::Status LoadFile(const std::string &filepath)
Load a project file into the editor.
void NewFile()
Create a new empty project file.
bool IsModified() const
Get whether the file has unsaved changes.
std::vector< std::string > validation_errors_
absl::Status SaveFile()
Save the current editor contents to disk.
absl::Status ImportLabelsFromZScream()
Import labels from a ZScream DefaultNames.txt file.
project::YazeProject * project_
void ShowValidationErrors()
const std::string & filepath() const
Get the current filepath.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
RAII guard for ImGui child windows with optional styling.
static RecentFilesManager & GetInstance()
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define YAZE_VERSION_STRING
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_DESCRIPTION
static const LanguageDefinition & C()
absl::Status ImportLabelsFromZScreamContent(const std::string &content)
Import labels from ZScream format content directly.