11#include "absl/strings/match.h"
12#include "absl/strings/str_format.h"
13#include "absl/strings/str_join.h"
14#include "absl/strings/str_split.h"
16#include "imgui/imgui.h"
22#include "yaze_config.h"
40 value.begin(), value.end(), value.begin(),
41 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
46std::pair<std::string, std::string> ParseKeyValue(
const std::string& line) {
47 size_t eq_pos = line.find(
'=');
48 if (eq_pos == std::string::npos)
51 std::string key = line.substr(0, eq_pos);
52 std::string value = line.substr(eq_pos + 1);
55 key.erase(0, key.find_first_not_of(
" \t"));
56 key.erase(key.find_last_not_of(
" \t") + 1);
57 value.erase(0, value.find_first_not_of(
" \t"));
58 value.erase(value.find_last_not_of(
" \t") + 1);
64 return value ==
"true" || value ==
"1" || value ==
"yes";
69 return std::stof(value);
76 std::vector<std::string> result;
80 std::vector<std::string> parts = absl::StrSplit(value,
',');
81 for (
const auto& part : parts) {
82 std::string trimmed = std::string(part);
83 trimmed.erase(0, trimmed.find_first_not_of(
" \t"));
84 trimmed.erase(trimmed.find_last_not_of(
" \t") + 1);
85 if (!trimmed.empty()) {
86 result.push_back(trimmed);
93 std::vector<uint16_t> result;
99 result.reserve(parts.size());
100 for (
const auto& part : parts) {
101 std::string token = part;
102 if (token.rfind(
"0x", 0) == 0 || token.rfind(
"0X", 0) == 0) {
103 token = token.substr(2);
105 result.push_back(
static_cast<uint16_t
>(std::stoul(token,
nullptr, 16)));
111 result.push_back(
static_cast<uint16_t
>(std::stoul(token,
nullptr, 10)));
124 std::string token = value;
125 if (token.rfind(
"0x", 0) == 0 || token.rfind(
"0X", 0) == 0) {
126 token = token.substr(2);
128 return static_cast<uint32_t
>(std::stoul(token,
nullptr, 16));
134 return static_cast<uint32_t
>(std::stoul(token,
nullptr, 10));
141 return absl::StrJoin(values,
",", [](std::string* out, uint16_t value) {
142 out->append(absl::StrFormat(
"0x%02X", value));
147 return absl::StrFormat(
"0x%06X", value);
151 std::string key(input);
152 for (
char& c : key) {
153 if (!std::isalnum(
static_cast<unsigned char>(c))) {
179 std::string lower = ToLowerCopy(std::string(value));
180 if (lower ==
"base") {
183 if (lower ==
"patched") {
186 if (lower ==
"release") {
205 std::string lower = ToLowerCopy(std::string(value));
206 if (lower ==
"allow") {
209 if (lower ==
"block") {
217 const std::string& base_path) {
219 filepath = base_path +
"/" + project_name +
".yaze";
222 auto now = std::chrono::system_clock::now();
223 auto time_t = std::chrono::system_clock::to_time_t(now);
224 std::stringstream ss;
225 ss << std::put_time(std::localtime(&time_t),
"%Y-%m-%d %H:%M:%S");
236#ifndef __EMSCRIPTEN__
238 std::filesystem::path project_dir(base_path +
"/" + project_name);
239 std::filesystem::create_directories(project_dir);
240 std::filesystem::create_directories(project_dir /
"code");
241 std::filesystem::create_directories(project_dir /
"assets");
242 std::filesystem::create_directories(project_dir /
"patches");
243 std::filesystem::create_directories(project_dir /
"backups");
244 std::filesystem::create_directories(project_dir /
"output");
277 auto current = std::filesystem::path(path).lexically_normal();
279 for (; !current.empty(); current = current.parent_path()) {
280 if (current.extension() ==
".yazeproj") {
282 if (std::filesystem::is_directory(current, ec) && !ec) {
283 return current.string();
287 if (current == current.parent_path()) {
299 std::string resolved_path = project_path;
301 if (!bundle_root.empty() && bundle_root != project_path) {
303 resolved_path = bundle_root;
311 auto storage_or = platform::WasmStorage::LoadProject(storage_key);
312 if (storage_or.ok()) {
318 absl::Status load_status;
319 if (resolved_path.ends_with(
".yazeproj")) {
322 const std::filesystem::path bundle_path(resolved_path);
324 if (!std::filesystem::exists(bundle_path, ec) || ec ||
325 !std::filesystem::is_directory(bundle_path, ec) || ec) {
326 return absl::InvalidArgumentError(
327 absl::StrFormat(
"Project bundle does not exist: %s", resolved_path));
332 const std::filesystem::path project_file = bundle_path /
"project.yaze";
335 if (!std::filesystem::exists(project_file, ec) || ec) {
338 name = bundle_path.stem().string();
341 auto now = std::chrono::system_clock::now();
342 auto time_t = std::chrono::system_clock::to_time_t(now);
343 std::stringstream ss;
344 ss << std::put_time(std::localtime(&time_t),
"%Y-%m-%d %H:%M:%S");
364 const std::filesystem::path rom_candidate = bundle_path /
"rom";
365 if (std::filesystem::exists(rom_candidate, ec) &&
366 std::filesystem::is_regular_file(rom_candidate, ec) && !ec) {
370 const std::filesystem::path project_dir = bundle_path /
"project";
371 const std::filesystem::path code_dir = bundle_path /
"code";
372 if (std::filesystem::exists(project_dir, ec) &&
373 std::filesystem::is_directory(project_dir, ec) && !ec) {
375 }
else if (std::filesystem::exists(code_dir, ec) &&
376 std::filesystem::is_directory(code_dir, ec) && !ec) {
391 }
else if (resolved_path.ends_with(
".yaze")) {
395 std::ifstream file(resolved_path);
396 if (file.is_open()) {
397 std::stringstream buffer;
398 buffer << file.rdbuf();
399 std::string content = buffer.str();
401#ifdef YAZE_ENABLE_JSON_PROJECT_FORMAT
402 if (!content.empty() && content.front() ==
'{') {
403 LOG_DEBUG(
"Project",
"Detected JSON format project file");
404 load_status = LoadFromJsonFormat(resolved_path);
412 return absl::InvalidArgumentError(
413 absl::StrFormat(
"Cannot open project file: %s", resolved_path));
415 }
else if (resolved_path.ends_with(
".zsproj")) {
419 return absl::InvalidArgumentError(
"Unsupported project file format");
422 if (!load_status.ok()) {
433 return absl::OkStatus();
441 std::string old_filepath =
filepath;
444 auto status =
Save();
456 }
else if (!
name.empty()) {
459 base = std::filesystem::path(
filepath).stem().string();
461 base = SanitizeStorageKey(base);
462 if (suffix.empty()) {
465 return absl::StrFormat(
"%s_%s", base, suffix);
469 std::ostringstream file;
472 file <<
"# yaze Project File\n";
473 file <<
"# Format Version: 2.0\n";
478 file <<
"[project]\n";
479 file <<
"name=" <<
name <<
"\n";
489 file <<
"tags=" << absl::StrJoin(
metadata.
tags,
",") <<
"\n\n";
504 file <<
"additional_roms=" << absl::StrJoin(
additional_roms,
",") <<
"\n\n";
514 file <<
"[feature_flags]\n";
515 file <<
"load_custom_overworld="
518 file <<
"apply_zs_custom_overworld_asm="
522 file <<
"save_dungeon_maps="
524 file <<
"save_overworld_maps="
527 file <<
"save_overworld_entrances="
530 file <<
"save_overworld_exits="
533 file <<
"save_overworld_items="
536 file <<
"save_overworld_properties="
539 file <<
"save_dungeon_objects="
541 file <<
"save_dungeon_sprites="
543 file <<
"save_dungeon_room_headers="
545 file <<
"save_dungeon_torches="
547 file <<
"save_dungeon_pits="
549 file <<
"save_dungeon_blocks="
551 file <<
"save_dungeon_collision="
553 file <<
"save_dungeon_chests="
555 file <<
"save_dungeon_pot_items="
557 file <<
"save_dungeon_palettes="
559 file <<
"save_graphics_sheet="
561 file <<
"save_all_palettes="
563 file <<
"save_gfx_groups="
567 file <<
"enable_custom_objects="
571 file <<
"[workspace]\n";
576 file <<
"autosave_enabled="
580 file <<
"backup_on_save="
584 file <<
"backup_keep_daily="
590 file <<
"show_collision="
592 file <<
"prefer_hmagic_names="
596 file <<
"saved_layouts="
603 if (track_tiles.empty()) {
604 for (uint16_t tile = 0xB0; tile <= 0xBE; ++tile) {
605 track_tiles.push_back(tile);
609 if (track_stop_tiles.empty()) {
610 track_stop_tiles = {0xB7, 0xB8, 0xB9, 0xBA};
613 if (track_switch_tiles.empty()) {
614 track_switch_tiles = {0xD0, 0xD1, 0xD2, 0xD3};
617 if (track_object_ids.empty()) {
618 track_object_ids = {0x31};
621 if (minecart_sprite_ids.empty()) {
622 minecart_sprite_ids = {0xA3};
624 file <<
"[dungeon_overlay]\n";
625 file <<
"track_tiles=" << FormatHexUintList(track_tiles) <<
"\n";
626 file <<
"track_stop_tiles=" << FormatHexUintList(track_stop_tiles) <<
"\n";
627 file <<
"track_switch_tiles=" << FormatHexUintList(track_switch_tiles)
629 file <<
"track_object_ids=" << FormatHexUintList(track_object_ids) <<
"\n";
630 file <<
"minecart_sprite_ids=" << FormatHexUintList(minecart_sprite_ids)
634 file <<
"[rom_addresses]\n";
636 file << key <<
"=" << FormatHexUint32(value) <<
"\n";
642 file <<
"[custom_objects]\n";
644 file << absl::StrFormat(
"object_0x%X", object_id) <<
"="
645 << absl::StrJoin(files,
",") <<
"\n";
651 file <<
"[agent_settings]\n";
656 file <<
"custom_system_prompt="
658 file <<
"use_custom_prompt="
660 file <<
"show_reasoning="
668 file <<
"stream_responses="
670 file <<
"favorite_models="
675 file <<
"enable_tool_resources="
677 file <<
"enable_tool_dungeon="
679 file <<
"enable_tool_overworld="
681 file <<
"enable_tool_messages="
683 file <<
"enable_tool_dialogue="
685 file <<
"enable_tool_gui="
687 file <<
"enable_tool_music="
689 file <<
"enable_tool_sprite="
691 file <<
"enable_tool_emulator="
693 file <<
"enable_tool_memory_inspector="
701 file <<
"[keybindings]\n";
703 file << key <<
"=" << value <<
"\n";
710 file <<
"[editor_visibility]\n";
712 file << key <<
"=" << (value ?
"true" :
"false") <<
"\n";
719 if (!labels.empty()) {
720 file <<
"[labels_" << type <<
"]\n";
721 for (
const auto& [key, value] : labels) {
722 file << key <<
"=" << value <<
"\n";
733 file <<
"track_changes=" << (
track_changes ?
"true" :
"false") <<
"\n";
738 file <<
"asm_sources=" << absl::StrJoin(
asm_sources,
",") <<
"\n";
744 file <<
"persist_custom_music="
751 file <<
"[zscream_compatibility]\n";
754 file << key <<
"=" << value <<
"\n";
759 file <<
"# End of YAZE Project File\n";
764 std::istringstream stream(content);
766 std::string current_section;
768 while (std::getline(stream, line)) {
769 if (line.empty() || line[0] ==
'#')
772 if (line.front() ==
'[' && line.back() ==
']') {
773 current_section = line.substr(1, line.length() - 2);
777 auto [key, value] = ParseKeyValue(line);
781 if (current_section ==
"project") {
784 else if (key ==
"description")
786 else if (key ==
"author")
788 else if (key ==
"license")
790 else if (key ==
"version")
792 else if (key ==
"created_date")
794 else if (key ==
"last_modified")
796 else if (key ==
"yaze_version")
798 else if (key ==
"created_by")
800 else if (key ==
"tags")
802 else if (key ==
"project_id")
804 }
else if (current_section ==
"files") {
805 if (key ==
"rom_filename")
807 else if (key ==
"rom_backup_folder")
809 else if (key ==
"code_folder")
811 else if (key ==
"assets_folder")
813 else if (key ==
"patches_folder")
815 else if (key ==
"labels_filename")
817 else if (key ==
"symbols_filename")
819 else if (key ==
"output_folder")
821 else if (key ==
"custom_objects_folder")
823 else if (key ==
"hack_manifest_file")
825 else if (key ==
"additional_roms")
827 }
else if (current_section ==
"rom") {
830 else if (key ==
"expected_hash")
832 else if (key ==
"write_policy")
834 }
else if (current_section ==
"feature_flags") {
835 if (key ==
"load_custom_overworld")
837 else if (key ==
"apply_zs_custom_overworld_asm")
839 else if (key ==
"save_dungeon_maps")
841 else if (key ==
"save_overworld_maps")
843 else if (key ==
"save_overworld_entrances")
845 else if (key ==
"save_overworld_exits")
847 else if (key ==
"save_overworld_items")
849 else if (key ==
"save_overworld_properties")
851 else if (key ==
"save_dungeon_objects")
853 else if (key ==
"save_dungeon_sprites")
855 else if (key ==
"save_dungeon_room_headers")
857 else if (key ==
"save_dungeon_torches")
859 else if (key ==
"save_dungeon_pits")
861 else if (key ==
"save_dungeon_blocks")
863 else if (key ==
"save_dungeon_collision")
865 else if (key ==
"save_dungeon_chests")
867 else if (key ==
"save_dungeon_pot_items")
869 else if (key ==
"save_dungeon_palettes")
871 else if (key ==
"save_graphics_sheet")
873 else if (key ==
"save_all_palettes")
875 else if (key ==
"save_gfx_groups")
877 else if (key ==
"save_messages")
879 else if (key ==
"enable_custom_objects")
881 }
else if (current_section ==
"workspace") {
882 if (key ==
"font_global_scale")
884 else if (key ==
"dark_mode")
886 else if (key ==
"ui_theme")
888 else if (key ==
"autosave_enabled")
890 else if (key ==
"autosave_interval_secs")
892 else if (key ==
"backup_on_save")
894 else if (key ==
"backup_retention_count")
896 else if (key ==
"backup_keep_daily")
898 else if (key ==
"backup_keep_daily_days")
900 else if (key ==
"show_grid")
902 else if (key ==
"show_collision")
904 else if (key ==
"prefer_hmagic_names")
906 else if (key ==
"last_layout_preset")
908 else if (key ==
"saved_layouts")
910 else if (key ==
"recent_files")
912 }
else if (current_section ==
"dungeon_overlay") {
913 if (key ==
"track_tiles")
915 else if (key ==
"track_stop_tiles")
917 else if (key ==
"track_switch_tiles")
919 else if (key ==
"track_object_ids")
921 else if (key ==
"minecart_sprite_ids")
923 }
else if (current_section ==
"rom_addresses") {
924 auto parsed = ParseHexUint32(value);
925 if (parsed.has_value()) {
928 }
else if (current_section ==
"custom_objects") {
929 std::string id_token = key;
930 if (absl::StartsWith(id_token,
"object_")) {
931 id_token = id_token.substr(7);
933 auto parsed = ParseHexUint32(id_token);
934 if (parsed.has_value()) {
937 }
else if (current_section ==
"agent_settings") {
938 if (key ==
"ai_provider")
940 else if (key ==
"ai_model")
942 else if (key ==
"ollama_host")
944 else if (key ==
"gemini_api_key")
946 else if (key ==
"custom_system_prompt")
948 else if (key ==
"use_custom_prompt")
950 else if (key ==
"show_reasoning")
952 else if (key ==
"verbose")
954 else if (key ==
"max_tool_iterations")
956 else if (key ==
"max_retry_attempts")
958 else if (key ==
"temperature")
960 else if (key ==
"top_p")
962 else if (key ==
"max_output_tokens")
964 else if (key ==
"stream_responses")
966 else if (key ==
"favorite_models")
968 else if (key ==
"model_chain")
970 else if (key ==
"chain_mode")
972 else if (key ==
"enable_tool_resources")
974 else if (key ==
"enable_tool_dungeon")
976 else if (key ==
"enable_tool_overworld")
978 else if (key ==
"enable_tool_messages")
980 else if (key ==
"enable_tool_dialogue")
982 else if (key ==
"enable_tool_gui")
984 else if (key ==
"enable_tool_music")
986 else if (key ==
"enable_tool_sprite")
988 else if (key ==
"enable_tool_emulator")
990 else if (key ==
"enable_tool_memory_inspector")
992 else if (key ==
"builder_blueprint_path")
994 }
else if (current_section ==
"build") {
995 if (key ==
"build_script")
997 else if (key ==
"output_folder")
999 else if (key ==
"git_repository")
1001 else if (key ==
"track_changes")
1003 else if (key ==
"build_configurations")
1005 else if (key ==
"build_target")
1007 else if (key ==
"asm_entry_point")
1009 else if (key ==
"asm_sources")
1011 else if (key ==
"last_build_hash")
1013 else if (key ==
"build_number")
1015 }
else if (current_section.rfind(
"labels_", 0) == 0) {
1016 std::string label_type = current_section.substr(7);
1018 }
else if (current_section ==
"keybindings") {
1020 }
else if (current_section ==
"editor_visibility") {
1022 }
else if (current_section ==
"zscream_compatibility") {
1023 if (key ==
"original_project_file")
1027 }
else if (current_section ==
"music") {
1028 if (key ==
"persist_custom_music")
1030 else if (key ==
"storage_key")
1032 else if (key ==
"last_saved_at")
1047 return absl::OkStatus();
1051#ifdef __EMSCRIPTEN__
1053 auto storage_or = platform::WasmStorage::LoadProject(storage_key);
1054 if (storage_or.ok()) {
1059 std::ifstream file(project_path);
1060 if (!file.is_open()) {
1061 return absl::InvalidArgumentError(
1062 absl::StrFormat(
"Cannot open project file: %s", project_path));
1065 std::stringstream buffer;
1066 buffer << file.rdbuf();
1073 auto now = std::chrono::system_clock::now();
1074 auto time_t = std::chrono::system_clock::to_time_t(now);
1075 std::stringstream ss;
1076 ss << std::put_time(std::localtime(&time_t),
"%Y-%m-%d %H:%M:%S");
1088#ifdef __EMSCRIPTEN__
1089 auto storage_status =
1090 platform::WasmStorage::SaveProject(
MakeStorageKey(
"project"), serialized);
1091 if (!storage_status.ok()) {
1092 return storage_status;
1097 if (!file.is_open()) {
1098 return absl::InvalidArgumentError(
1099 absl::StrFormat(
"Cannot create project file: %s",
filepath));
1106 return absl::OkStatus();
1110 const std::string& zscream_project_path) {
1116 std::filesystem::path zs_path(zscream_project_path);
1117 name = zs_path.stem().string() +
"_imported";
1129 return absl::OkStatus();
1134 std::ofstream file(target_path);
1135 if (!file.is_open()) {
1136 return absl::InvalidArgumentError(
1137 absl::StrFormat(
"Cannot create ZScream project file: %s", target_path));
1141 file <<
"# ZScream Compatible Project File\n";
1143 file <<
"name=" <<
name <<
"\n";
1151 return absl::OkStatus();
1171 std::vector<std::string> errors;
1174 errors.push_back(
"Project name is required");
1176 errors.push_back(
"Project file path is required");
1178 errors.push_back(
"ROM file is required");
1180#ifndef __EMSCRIPTEN__
1184 errors.push_back(
"ROM file does not exist: " +
rom_filename);
1189 errors.push_back(
"Code folder does not exist: " +
code_folder);
1198 if (!errors.empty()) {
1199 return absl::InvalidArgumentError(absl::StrJoin(errors,
"; "));
1202 return absl::OkStatus();
1206 std::vector<std::string> missing;
1208#ifndef __EMSCRIPTEN__
1227#ifdef __EMSCRIPTEN__
1229 return absl::OkStatus();
1236 for (
const auto& folder : folders) {
1237 if (!folder.empty()) {
1239 if (!std::filesystem::exists(abs_path)) {
1240 std::filesystem::create_directories(abs_path);
1248 if (!std::filesystem::exists(abs_labels)) {
1249 std::ofstream labels_file(abs_labels);
1250 labels_file <<
"# yaze Resource Labels\n";
1251 labels_file <<
"# Format: [type] key=value\n\n";
1252 labels_file.close();
1256 return absl::OkStatus();
1264 return name.empty() ?
"Untitled Project" :
name;
1268 const std::string& absolute_path)
const {
1269 if (absolute_path.empty() ||
filepath.empty())
1270 return absolute_path;
1272 std::filesystem::path project_dir =
1273 std::filesystem::path(
filepath).parent_path();
1274 std::filesystem::path abs_path(absolute_path);
1277 std::filesystem::path relative =
1278 std::filesystem::relative(abs_path.lexically_normal(), project_dir);
1280 return relative.generic_string();
1283 return abs_path.lexically_normal().generic_string();
1288 const std::string& relative_path)
const {
1289 if (relative_path.empty() ||
filepath.empty())
1290 return relative_path;
1292 std::filesystem::path project_dir =
1293 std::filesystem::path(
filepath).parent_path();
1294 std::filesystem::path abs_path(relative_path);
1295 if (abs_path.is_absolute()) {
1296 abs_path = abs_path.lexically_normal();
1297 abs_path.make_preferred();
1298 return abs_path.string();
1300 abs_path = (project_dir / abs_path).lexically_normal();
1301 abs_path.make_preferred();
1303 return abs_path.string();
1307#ifdef __EMSCRIPTEN__
1315 auto normalize = [
this](std::string* path) {
1316 if (!path || path->empty()) {
1334 if (!rom_path.empty()) {
1345 const std::string& project_path) {
1349 std::filesystem::path zs_path(project_path);
1350 name = zs_path.stem().string() +
"_imported";
1355 return absl::OkStatus();
1363#ifdef __EMSCRIPTEN__
1373 std::filesystem::path loaded_manifest_path;
1374 auto load_manifest = [&](
const std::filesystem::path& candidate,
1375 bool update_project_setting) ->
bool {
1376 if (candidate.empty() || !std::filesystem::exists(candidate)) {
1381 LOG_WARN(
"Project",
"Failed to load hack manifest %s: %s",
1382 candidate.string().c_str(),
1383 std::string(status.message()).c_str());
1386 loaded_manifest_path = candidate;
1387 if (update_project_setting) {
1390 LOG_DEBUG(
"Project",
"Loaded hack manifest: %s",
1391 candidate.string().c_str());
1404 auto candidate = std::filesystem::path(code_path) /
"hack_manifest.json";
1405 (void)load_manifest(candidate,
true);
1410 const std::filesystem::path project_dir =
1411 std::filesystem::path(
filepath).parent_path();
1412 (void)load_manifest(project_dir /
"hack_manifest.json",
true);
1414 (void)load_manifest(project_dir.parent_path() /
"hack_manifest.json",
1425 auto try_load_registry = [&](
const std::filesystem::path& base) ->
bool {
1429 const auto planning = base /
"Docs" /
"Dev" /
"Planning";
1430 if (!std::filesystem::exists(planning)) {
1435 LOG_WARN(
"Project",
"Failed to load project registry from %s: %s",
1436 base.string().c_str(), std::string(status.message()).c_str());
1442 bool registry_loaded =
false;
1451 if (!registry_loaded && !loaded_manifest_path.empty()) {
1452 registry_loaded = try_load_registry(loaded_manifest_path.parent_path());
1456 if (!registry_loaded && !
filepath.empty()) {
1458 try_load_registry(std::filesystem::path(
filepath).parent_path());
1461 if (!registry_loaded) {
1463 "Hack manifest loaded but project registry was not found "
1464 "(code_folder='%s', manifest='%s')",
1465 code_folder.c_str(), loaded_manifest_path.string().c_str());
1470 size_t injected = 0;
1471 for (
const auto& [type_key, labels] :
1473 for (
const auto& [id_str, label] : labels) {
1478 LOG_DEBUG(
"Project",
"Loaded project registry: %zu resource labels injected",
1519 for (uint16_t tile = 0xB0; tile <= 0xBE; ++tile) {
1551 auto now = std::chrono::system_clock::now().time_since_epoch();
1553 std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
1554 return absl::StrFormat(
"yaze_project_%lld", timestamp);
1558std::vector<ProjectManager::ProjectTemplate>
1560 std::vector<ProjectTemplate> templates;
1569 t.
name =
"Vanilla ROM Hack";
1571 "Standard ROM editing without custom ASM. Limited to vanilla features.";
1580 templates.push_back(t);
1586 t.
name =
"ZSCustomOverworld v2";
1588 "Basic overworld expansion: custom BG colors, main palettes, parent "
1601 templates.push_back(t);
1607 t.
name =
"ZSCustomOverworld v3 (Recommended)";
1609 "Full overworld expansion: wide/tall areas, animated GFX, overlays, "
1626 templates.push_back(t);
1632 t.
name =
"Randomizer Compatible";
1634 "Compatible with ALttP Randomizer. Minimal custom features to avoid "
1643 templates.push_back(t);
1653 t.
name =
"Dungeon Designer";
1654 t.
description =
"Focused on dungeon creation and modification.";
1661 templates.push_back(t);
1667 t.
name =
"Graphics Pack";
1669 "Project focused on graphics, sprites, and visual modifications.";
1678 templates.push_back(t);
1684 t.
name =
"Complete Overhaul";
1685 t.
description =
"Full-scale ROM hack with all features enabled.";
1700 templates.push_back(t);
1707 const std::string& template_name,
const std::string& project_name,
1708 const std::string& base_path) {
1710 auto status = project.
Create(project_name, base_path);
1716 if (template_name ==
"Full Overworld Mod") {
1720 project.
metadata.
tags = {
"overworld",
"maps",
"graphics"};
1721 }
else if (template_name ==
"Dungeon Designer") {
1725 project.
metadata.
tags = {
"dungeons",
"rooms",
"design"};
1726 }
else if (template_name ==
"Graphics Pack") {
1730 project.
metadata.
tags = {
"graphics",
"sprites",
"palettes"};
1731 }
else if (template_name ==
"Complete Overhaul") {
1737 project.
metadata.
tags = {
"complete",
"overhaul",
"full-mod"};
1740 status = project.
Save();
1749 const std::string& directory) {
1750#ifdef __EMSCRIPTEN__
1754 std::vector<std::string> projects;
1757 for (
const auto& entry : std::filesystem::directory_iterator(directory)) {
1758 if (entry.is_regular_file()) {
1759 std::string filename = entry.path().filename().string();
1760 if (filename.ends_with(
".yaze") || filename.ends_with(
".zsproj")) {
1761 projects.push_back(entry.path().string());
1763 }
else if (entry.is_directory()) {
1764 std::string filename = entry.path().filename().string();
1765 if (filename.ends_with(
".yazeproj")) {
1766 projects.push_back(entry.path().string());
1770 }
catch (
const std::filesystem::filesystem_error& e) {
1779#ifdef __EMSCRIPTEN__
1781 return absl::UnimplementedError(
1782 "Project backups are not supported in the web build");
1785 return absl::InvalidArgumentError(
"Project has no file path");
1788 std::filesystem::path project_path(project.
filepath);
1789 std::filesystem::path backup_dir = project_path.parent_path() /
"backups";
1790 std::filesystem::create_directories(backup_dir);
1792 auto now = std::chrono::system_clock::now();
1793 auto time_t = std::chrono::system_clock::to_time_t(now);
1794 std::stringstream ss;
1795 ss << std::put_time(std::localtime(&time_t),
"%Y%m%d_%H%M%S");
1797 std::string backup_filename = project.
name +
"_backup_" + ss.str() +
".yaze";
1798 std::filesystem::path backup_path = backup_dir / backup_filename;
1801 std::filesystem::copy_file(project.
filepath, backup_path);
1802 }
catch (
const std::filesystem::filesystem_error& e) {
1803 return absl::InternalError(
1804 absl::StrFormat(
"Failed to backup project: %s", e.what()));
1807 return absl::OkStatus();
1818 std::vector<std::string> recommendations;
1821 recommendations.push_back(
"Add a ROM file to begin editing");
1825 recommendations.push_back(
"Set up a code folder for assembly patches");
1829 recommendations.push_back(
"Create a labels file for better organization");
1833 recommendations.push_back(
"Add a project description for documentation");
1837 recommendations.push_back(
1838 "Consider setting up version control for your project");
1842 if (!missing_files.empty()) {
1843 recommendations.push_back(
1844 "Some project files are missing - use Project > Repair to fix");
1847 return recommendations;
1853 std::ifstream file(filename);
1854 if (!file.is_open()) {
1861 std::string current_type =
"";
1863 while (std::getline(file, line)) {
1864 if (line.empty() || line[0] ==
'#')
1868 if (line[0] ==
'[' && line.back() ==
']') {
1869 current_type = line.substr(1, line.length() - 2);
1874 size_t eq_pos = line.find(
'=');
1875 if (eq_pos != std::string::npos && !current_type.empty()) {
1876 std::string key = line.substr(0, eq_pos);
1877 std::string value = line.substr(eq_pos + 1);
1878 labels_[current_type][key] = value;
1892 if (!file.is_open())
1895 file <<
"# yaze Resource Labels\n";
1896 file <<
"# Format: [type] followed by key=value pairs\n\n";
1898 for (
const auto& [type, type_labels] :
labels_) {
1899 if (!type_labels.empty()) {
1900 file <<
"[" << type <<
"]\n";
1901 for (
const auto& [key, value] : type_labels) {
1902 file << key <<
"=" << value <<
"\n";
1913 if (!p_open || !*p_open)
1917 if (ImGui::Begin(
"Resource Labels", p_open)) {
1918 ImGui::Text(
"Resource Labels Manager");
1920 ImGui::Text(
"Total types: %zu",
labels_.size());
1922 for (
const auto& [type, type_labels] :
labels_) {
1923 if (ImGui::TreeNode(type.c_str())) {
1924 ImGui::Text(
"Labels: %zu", type_labels.size());
1925 for (
const auto& [key, value] : type_labels) {
1926 ImGui::Text(
"%s = %s", key.c_str(), value.c_str());
1936 const std::string& key,
1937 const std::string& newValue) {
1938 labels_[type][key] = newValue;
1942 bool selected,
const std::string& type,
const std::string& key,
1943 const std::string& defaultValue) {
1945 if (ImGui::Selectable(
1946 absl::StrFormat(
"%s: %s", key.c_str(),
GetLabel(type, key).c_str())
1954 const std::string& key) {
1955 auto type_it =
labels_.find(type);
1959 auto label_it = type_it->second.find(key);
1960 if (label_it == type_it->second.end())
1963 return label_it->second;
1967 const std::string& type,
const std::string& key,
1968 const std::string& defaultValue) {
1969 auto existing =
GetLabel(type, key);
1970 if (!existing.empty())
1973 labels_[type][key] = defaultValue;
1974 return defaultValue;
1982 const std::unordered_map<
1983 std::string, std::unordered_map<std::string, std::string>>& labels) {
2012 LOG_DEBUG(
"Project",
"Initialized embedded labels:");
2014 LOG_DEBUG(
"Project",
" - %d entrance names",
2016 LOG_DEBUG(
"Project",
" - %d sprite names",
2018 LOG_DEBUG(
"Project",
" - %d overlord names",
2021 LOG_DEBUG(
"Project",
" - %d music names",
2023 LOG_DEBUG(
"Project",
" - %d graphics names",
2025 LOG_DEBUG(
"Project",
" - %d room effect names",
2027 LOG_DEBUG(
"Project",
" - %d room tag names",
2029 LOG_DEBUG(
"Project",
" - %d tile type names",
2032 return absl::OkStatus();
2033 }
catch (
const std::exception& e) {
2034 return absl::InternalError(
2035 absl::StrCat(
"Failed to initialize embedded labels: ", e.what()));
2040 const std::string& default_value)
const {
2044 auto label_it = type_it->second.find(std::to_string(
id));
2045 if (label_it != type_it->second.end()) {
2046 return label_it->second;
2050 return default_value.empty() ? resource_type +
"_" + std::to_string(
id)
2055#ifdef __EMSCRIPTEN__
2057 return absl::UnimplementedError(
2058 "File-based label import is not supported in the web build");
2061 if (!file.is_open()) {
2062 return absl::InvalidArgumentError(
2063 absl::StrFormat(
"Cannot open labels file: %s",
filepath));
2066 std::stringstream buffer;
2067 buffer << file.rdbuf();
2075 const std::string& content) {
2082 auto status = provider.ImportFromZScreamFormat(content);
2087 LOG_DEBUG(
"Project",
"Imported ZScream labels:");
2088 LOG_DEBUG(
"Project",
" - %d sprite labels",
2092 LOG_DEBUG(
"Project",
" - %d room tag labels",
2095 return absl::OkStatus();
2104 LOG_DEBUG(
"Project",
"Initialized ResourceLabelProvider with project labels");
2105 LOG_DEBUG(
"Project",
" - prefer_hmagic_names: %s",
2107 LOG_DEBUG(
"Project",
" - hack_manifest: %s",
2115#ifdef YAZE_ENABLE_JSON_PROJECT_FORMAT
2117absl::Status YazeProject::LoadFromJsonFormat(
const std::string& project_path) {
2118#ifdef __EMSCRIPTEN__
2119 return absl::UnimplementedError(
2120 "JSON project format loading is not supported in the web build");
2122 std::ifstream file(project_path);
2123 if (!file.is_open()) {
2124 return absl::InvalidArgumentError(
2125 absl::StrFormat(
"Cannot open JSON project file: %s", project_path));
2133 if (j.contains(
"yaze_project")) {
2134 auto& proj = j[
"yaze_project"];
2136 if (proj.contains(
"name"))
2137 name = proj[
"name"].get<std::string>();
2138 if (proj.contains(
"description"))
2140 if (proj.contains(
"author"))
2142 if (proj.contains(
"version"))
2144 if (proj.contains(
"created"))
2146 if (proj.contains(
"modified"))
2148 if (proj.contains(
"created_by"))
2152 if (proj.contains(
"rom_filename"))
2153 rom_filename = proj[
"rom_filename"].get<std::string>();
2154 if (proj.contains(
"rom_backup_folder"))
2156 if (proj.contains(
"code_folder"))
2157 code_folder = proj[
"code_folder"].get<std::string>();
2158 if (proj.contains(
"assets_folder"))
2160 if (proj.contains(
"patches_folder"))
2162 if (proj.contains(
"labels_filename"))
2164 if (proj.contains(
"symbols_filename"))
2166 if (proj.contains(
"hack_manifest_file"))
2169 if (proj.contains(
"rom") && proj[
"rom"].is_object()) {
2170 auto& rom = proj[
"rom"];
2171 if (rom.contains(
"role"))
2173 if (rom.contains(
"expected_hash"))
2175 if (rom.contains(
"write_policy"))
2181 if (proj.contains(
"use_embedded_labels")) {
2186 if (proj.contains(
"feature_flags")) {
2187 auto& flags = proj[
"feature_flags"];
2190 if (flags.contains(
"kSaveDungeonMaps"))
2192 flags[
"kSaveDungeonMaps"].get<
bool>();
2193 if (flags.contains(
"kSaveOverworldMaps"))
2195 flags[
"kSaveOverworldMaps"].get<
bool>();
2196 if (flags.contains(
"kSaveOverworldEntrances"))
2198 flags[
"kSaveOverworldEntrances"].get<
bool>();
2199 if (flags.contains(
"kSaveOverworldExits"))
2201 flags[
"kSaveOverworldExits"].get<
bool>();
2202 if (flags.contains(
"kSaveOverworldItems"))
2204 flags[
"kSaveOverworldItems"].get<
bool>();
2205 if (flags.contains(
"kSaveOverworldProperties"))
2207 flags[
"kSaveOverworldProperties"].get<
bool>();
2208 if (flags.contains(
"kSaveDungeonObjects"))
2210 flags[
"kSaveDungeonObjects"].get<
bool>();
2211 if (flags.contains(
"kSaveDungeonSprites"))
2213 flags[
"kSaveDungeonSprites"].get<
bool>();
2214 if (flags.contains(
"kSaveDungeonRoomHeaders"))
2216 flags[
"kSaveDungeonRoomHeaders"].get<
bool>();
2217 if (flags.contains(
"kSaveDungeonTorches"))
2219 flags[
"kSaveDungeonTorches"].get<
bool>();
2220 if (flags.contains(
"kSaveDungeonPits"))
2222 flags[
"kSaveDungeonPits"].get<
bool>();
2223 if (flags.contains(
"kSaveDungeonBlocks"))
2225 flags[
"kSaveDungeonBlocks"].get<
bool>();
2226 if (flags.contains(
"kSaveDungeonCollision"))
2228 flags[
"kSaveDungeonCollision"].get<
bool>();
2229 if (flags.contains(
"kSaveDungeonWaterFillZones"))
2231 flags[
"kSaveDungeonWaterFillZones"].get<
bool>();
2232 if (flags.contains(
"kSaveDungeonChests"))
2234 flags[
"kSaveDungeonChests"].get<
bool>();
2235 if (flags.contains(
"kSaveDungeonPotItems"))
2237 flags[
"kSaveDungeonPotItems"].get<
bool>();
2238 if (flags.contains(
"kSaveDungeonPalettes"))
2240 flags[
"kSaveDungeonPalettes"].get<
bool>();
2241 if (flags.contains(
"kSaveGraphicsSheet"))
2243 flags[
"kSaveGraphicsSheet"].get<
bool>();
2244 if (flags.contains(
"kSaveAllPalettes"))
2246 flags[
"kSaveAllPalettes"].get<
bool>();
2247 if (flags.contains(
"kSaveGfxGroups"))
2249 if (flags.contains(
"kSaveMessages"))
2254 if (proj.contains(
"workspace_settings")) {
2255 auto& ws = proj[
"workspace_settings"];
2256 if (ws.contains(
"auto_save_enabled"))
2258 ws[
"auto_save_enabled"].get<
bool>();
2259 if (ws.contains(
"auto_save_interval"))
2261 ws[
"auto_save_interval"].get<
float>();
2262 if (ws.contains(
"backup_on_save"))
2264 if (ws.contains(
"backup_retention_count"))
2266 ws[
"backup_retention_count"].get<
int>();
2267 if (ws.contains(
"backup_keep_daily"))
2269 ws[
"backup_keep_daily"].get<
bool>();
2270 if (ws.contains(
"backup_keep_daily_days"))
2272 ws[
"backup_keep_daily_days"].get<
int>();
2275 if (proj.contains(
"rom_addresses") && proj[
"rom_addresses"].is_object()) {
2277 for (
auto it = proj[
"rom_addresses"].begin();
2278 it != proj[
"rom_addresses"].end(); ++it) {
2279 if (it.value().is_number_unsigned()) {
2281 it.value().get<uint32_t>();
2282 }
else if (it.value().is_string()) {
2284 if (parsed.has_value()) {
2291 if (proj.contains(
"custom_objects") &&
2292 proj[
"custom_objects"].is_object()) {
2294 for (
auto it = proj[
"custom_objects"].begin();
2295 it != proj[
"custom_objects"].end(); ++it) {
2296 if (!it.value().is_array())
2299 if (!parsed.has_value()) {
2302 std::vector<std::string> files;
2303 for (
const auto& entry : it.value()) {
2304 if (entry.is_string()) {
2305 files.push_back(entry.get<std::string>());
2308 if (!files.empty()) {
2314 if (proj.contains(
"agent_settings") &&
2315 proj[
"agent_settings"].is_object()) {
2316 auto& agent = proj[
"agent_settings"];
2343 if (agent.contains(
"favorite_models") &&
2344 agent[
"favorite_models"].is_array()) {
2346 for (
const auto& model : agent[
"favorite_models"]) {
2347 if (model.is_string())
2349 model.get<std::string>());
2352 if (agent.contains(
"model_chain") && agent[
"model_chain"].is_array()) {
2354 for (
const auto& model : agent[
"model_chain"]) {
2355 if (model.is_string())
2380 agent.value(
"enable_tool_memory_inspector",
2387 if (proj.contains(
"build_script"))
2388 build_script = proj[
"build_script"].get<std::string>();
2389 if (proj.contains(
"output_folder"))
2391 if (proj.contains(
"git_repository"))
2393 if (proj.contains(
"track_changes"))
2397 return absl::OkStatus();
2398 }
catch (
const json::exception& e) {
2399 return absl::InvalidArgumentError(
2400 absl::StrFormat(
"JSON parse error: %s", e.what()));
2404absl::Status YazeProject::SaveToJsonFormat() {
2405#ifdef __EMSCRIPTEN__
2406 return absl::UnimplementedError(
2407 "JSON project format saving is not supported in the web build");
2410 auto& proj = j[
"yaze_project"];
2414 proj[
"name"] =
name;
2434 proj[
"rom"][
"write_policy"] =
2443 proj[
"feature_flags"][
"kSaveOverworldMaps"] =
2445 proj[
"feature_flags"][
"kSaveOverworldEntrances"] =
2447 proj[
"feature_flags"][
"kSaveOverworldExits"] =
2449 proj[
"feature_flags"][
"kSaveOverworldItems"] =
2451 proj[
"feature_flags"][
"kSaveOverworldProperties"] =
2453 proj[
"feature_flags"][
"kSaveDungeonObjects"] =
2455 proj[
"feature_flags"][
"kSaveDungeonSprites"] =
2457 proj[
"feature_flags"][
"kSaveDungeonRoomHeaders"] =
2459 proj[
"feature_flags"][
"kSaveDungeonTorches"] =
2462 proj[
"feature_flags"][
"kSaveDungeonBlocks"] =
2464 proj[
"feature_flags"][
"kSaveDungeonCollision"] =
2466 proj[
"feature_flags"][
"kSaveDungeonWaterFillZones"] =
2468 proj[
"feature_flags"][
"kSaveDungeonChests"] =
2470 proj[
"feature_flags"][
"kSaveDungeonPotItems"] =
2472 proj[
"feature_flags"][
"kSaveDungeonPalettes"] =
2474 proj[
"feature_flags"][
"kSaveGraphicsSheet"] =
2481 proj[
"workspace_settings"][
"auto_save_enabled"] =
2483 proj[
"workspace_settings"][
"auto_save_interval"] =
2485 proj[
"workspace_settings"][
"backup_on_save"] =
2487 proj[
"workspace_settings"][
"backup_retention_count"] =
2489 proj[
"workspace_settings"][
"backup_keep_daily"] =
2491 proj[
"workspace_settings"][
"backup_keep_daily_days"] =
2494 auto& agent = proj[
"agent_settings"];
2521 agent[
"enable_tool_memory_inspector"] =
2526 auto& addrs = proj[
"rom_addresses"];
2533 auto& objs = proj[
"custom_objects"];
2535 objs[absl::StrFormat(
"0x%X", object_id)] = files;
2546 if (!file.is_open()) {
2547 return absl::InvalidArgumentError(
2548 absl::StrFormat(
"Cannot write JSON project file: %s",
filepath));
2552 return absl::OkStatus();
2560 if (!config_dir.ok()) {
2567#ifdef __EMSCRIPTEN__
2568 auto status = platform::WasmStorage::SaveProject(
2571 LOG_WARN(
"RecentFilesManager",
"Could not persist recent files: %s",
2572 status.ToString().c_str());
2578 if (!config_dir_status.ok()) {
2579 LOG_ERROR(
"Project",
"Failed to get or create config directory: %s",
2580 config_dir_status.status().ToString().c_str());
2585 std::ofstream file(filepath);
2586 if (!file.is_open()) {
2587 LOG_WARN(
"RecentFilesManager",
"Could not save recent files to %s",
2593 file << file_path << std::endl;
2598#ifdef __EMSCRIPTEN__
2600 if (!storage_or.ok()) {
2604 std::istringstream stream(storage_or.value());
2606 while (std::getline(stream, line)) {
2607 if (!line.empty()) {
2613 std::ifstream file(filepath);
2614 if (!file.is_open()) {
2621 while (std::getline(file, line)) {
2622 if (!line.empty()) {
void Clear()
Clear any loaded manifest state.
const ProjectRegistry & project_registry() const
bool HasProjectRegistry() const
absl::Status LoadProjectRegistry(const std::string &code_folder)
Load project registry data from the code folder.
absl::Status LoadFromFile(const std::string &filepath)
Load manifest from a JSON file path.
bool loaded() const
Check if the manifest has been loaded.
static std::vector< std::string > FindProjectsInDirectory(const std::string &directory)
static absl::Status ValidateProjectStructure(const YazeProject &project)
static absl::StatusOr< YazeProject > CreateFromTemplate(const std::string &template_name, const std::string &project_name, const std::string &base_path)
static std::vector< std::string > GetRecommendedFixesForProject(const YazeProject &project)
static std::vector< ProjectTemplate > GetProjectTemplates()
static absl::Status BackupProject(const YazeProject &project)
std::string GetFilePath() const
std::vector< std::string > recent_files_
void SetHackManifest(const core::HackManifest *manifest)
Set the hack manifest reference for ASM-defined labels.
#define YAZE_VERSION_STRING
#define ICON_MD_VIDEOGAME_ASSET
#define LOG_DEBUG(category, format,...)
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
float ParseFloat(const std::string &value)
std::vector< uint16_t > ParseHexUintList(const std::string &value)
std::string ToLowerCopy(std::string value)
bool ParseBool(const std::string &value)
std::optional< uint32_t > ParseHexUint32(const std::string &value)
std::string FormatHexUint32(uint32_t value)
std::string SanitizeStorageKey(absl::string_view input)
std::string FormatHexUintList(const std::vector< uint16_t > &values)
std::vector< std::string > ParseStringList(const std::string &value)
std::string RomRoleToString(RomRole role)
RomRole ParseRomRole(absl::string_view value)
const std::string kRecentFilesFilename
RomWritePolicy ParseRomWritePolicy(absl::string_view value)
std::string RomWritePolicyToString(RomWritePolicy policy)
ResourceLabelProvider & GetResourceLabels()
Get the global ResourceLabelProvider instance.
bool kSaveOverworldProperties
bool kApplyZSCustomOverworldASM
bool kLoadCustomOverworld
bool kSaveOverworldEntrances
bool kEnableCustomObjects
struct yaze::core::FeatureFlags::Flags::Dungeon dungeon
struct yaze::core::FeatureFlags::Flags::Overworld overworld
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > all_resource_labels
std::unordered_map< std::string, uint32_t > addresses
std::vector< uint16_t > track_object_ids
std::vector< uint16_t > minecart_sprite_ids
std::vector< uint16_t > track_stop_tiles
std::vector< uint16_t > track_tiles
std::vector< uint16_t > track_switch_tiles
YazeProject template_project
std::string CreateOrGetLabel(const std::string &type, const std::string &key, const std::string &defaultValue)
void DisplayLabels(bool *p_open)
std::string GetLabel(const std::string &type, const std::string &key)
void EditLabel(const std::string &type, const std::string &key, const std::string &newValue)
bool LoadLabels(const std::string &filename)
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
int backup_keep_daily_days
std::string last_layout_preset
std::map< std::string, std::string > custom_keybindings
int backup_retention_count
std::vector< std::string > saved_layouts
std::map< std::string, bool > editor_visibility
float autosave_interval_secs
std::vector< std::string > recent_files
std::string custom_system_prompt
std::string gemini_api_key
bool enable_tool_emulator
bool enable_tool_memory_inspector
std::vector< std::string > favorite_models
bool enable_tool_resources
bool enable_tool_messages
std::vector< std::string > model_chain
std::string builder_blueprint_path
bool enable_tool_dialogue
bool enable_tool_overworld
std::string last_saved_at
bool persist_custom_music
Modern project structure with comprehensive settings consolidation.
std::string rom_backup_folder
std::unordered_map< int, std::vector< std::string > > custom_object_files
absl::Status ResetToDefaults()
std::string custom_objects_folder
absl::Status RepairProject()
std::string MakeStorageKey(absl::string_view suffix) const
static std::string ResolveBundleRoot(const std::string &path)
struct yaze::project::YazeProject::MusicPersistence music_persistence
absl::StatusOr< std::string > SerializeToString() const
std::string zscream_project_file
absl::Status ExportForZScream(const std::string &target_path)
absl::Status ImportZScreamProject(const std::string &zscream_project_path)
absl::Status SaveAllSettings()
void NormalizePathsToAbsolute()
absl::Status ImportLabelsFromZScreamContent(const std::string &content)
Import labels from ZScream format content directly.
std::string git_repository
core::HackManifest hack_manifest
void InitializeResourceLabelProvider()
Initialize the global ResourceLabelProvider with this project's labels.
absl::Status ParseFromString(const std::string &content)
std::vector< std::string > additional_roms
std::string patches_folder
absl::Status LoadFromYazeFormat(const std::string &project_path)
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > resource_labels
std::string GenerateProjectId() const
absl::Status Create(const std::string &project_name, const std::string &base_path)
std::string assets_folder
void ReloadHackManifest()
absl::Status SaveToYazeFormat()
absl::Status LoadAllSettings()
std::string labels_filename
std::vector< std::string > asm_sources
std::string hack_manifest_file
std::string GetDisplayName() const
std::vector< std::string > GetMissingFiles() const
WorkspaceSettings workspace_settings
std::string output_folder
std::string asm_entry_point
std::string GetRelativePath(const std::string &absolute_path) const
absl::Status InitializeEmbeddedLabels(const std::unordered_map< std::string, std::unordered_map< std::string, std::string > > &labels)
absl::Status SaveAs(const std::string &new_path)
struct yaze::project::YazeProject::AgentSettings agent_settings
DungeonOverlaySettings dungeon_overlay
absl::Status ImportFromZScreamFormat(const std::string &project_path)
void InitializeDefaults()
std::string GetAbsolutePath(const std::string &relative_path) const
std::string GetLabel(const std::string &resource_type, int id, const std::string &default_value="") const
absl::Status Open(const std::string &project_path)
absl::Status ImportLabelsFromZScream(const std::string &filepath)
Import labels from a ZScream DefaultNames.txt file.
std::string last_build_hash
void TryLoadHackManifest()
std::map< std::string, std::string > zscream_mappings
absl::Status Validate() const
core::FeatureFlags::Flags feature_flags
std::vector< std::string > build_configurations
core::RomAddressOverrides rom_address_overrides
std::string symbols_filename