12#include "absl/flags/declare.h"
13#include "absl/flags/flag.h"
14#include "absl/strings/ascii.h"
15#include "absl/strings/match.h"
16#include "absl/strings/str_cat.h"
17#include "absl/strings/str_format.h"
18#include "absl/strings/str_join.h"
35 return absl::AsciiStrToLower(range);
39 return range ==
"all" || range ==
"vanilla";
43 return range ==
"all" || range ==
"expanded";
52 if (start < 0 ||
static_cast<size_t>(start) >= rom.
size()) {
56 static_cast<int>(rom.
size()) - 1);
63 if (start < 0 || end < start ||
static_cast<size_t>(start) >= rom.
size()) {
66 end = std::min(end,
static_cast<int>(rom.
size()) - 1);
79 int message_limit = -1;
87 : provider_(provider), previous_(provider.hack_manifest()) {}
102 const std::string& path, absl::string_view label) {
104 return absl::FailedPreconditionError(
105 absl::StrFormat(
"%s path is empty", label));
109 auto canonical_path = std::filesystem::canonical(path, ec);
111 return absl::FailedPreconditionError(absl::StrFormat(
112 "Cannot resolve %s path '%s': %s", label, path, ec.message()));
114 return canonical_path;
120 return absl::OkStatus();
123 const std::string manifest_path =
125 const absl::Status manifest_status =
127 if (manifest_status.ok()) {
128 return absl::OkStatus();
131 manifest_status.code(),
132 absl::StrFormat(
"Cannot load project '%s': hack manifest '%s': %s",
133 project_path, manifest_path, manifest_status.message()));
138 absl::StrFormat(
"address 0x%06X is %s", conflict.
address,
140 if (!conflict.module.empty()) {
141 absl::StrAppend(&result,
" (Module: ", conflict.module,
")");
146bool IsCanonicalMappedLoRomAddress(uint32_t address) {
147 const uint8_t bank =
static_cast<uint8_t
>((address >> 16) & 0xFFu);
148 if (bank == 0x7E || bank == 0x7F || (address & 0xFFFFu) < 0x8000u) {
156 absl::string_view mutation_scope) {
157 auto project_path = parser.
GetString(
"project");
158 if (!project_path.has_value() || project_path->empty()) {
159 return absl::FailedPreconditionError(absl::StrFormat(
160 "%s requires explicit --project so Yaze can verify ROM ownership and "
165 return absl::FailedPreconditionError(
"ROM is not loaded");
170 absl::Status open_status;
179 }
catch (
const std::exception& error) {
180 return absl::InvalidArgumentError(absl::StrFormat(
181 "Cannot load project '%s': %s", *project_path, error.what()));
183 return absl::InvalidArgumentError(absl::StrFormat(
184 "Cannot load project '%s': unknown project parse error",
188 if (!open_status.ok()) {
189 return absl::Status(open_status.code(),
190 absl::StrFormat(
"Cannot load project '%s': %s",
191 *project_path, open_status.message()));
195 return absl::PermissionDeniedError(
196 "Message mutation is disabled by the project's read-only policy");
198 if (absl::Status manifest_status =
200 !manifest_status.ok()) {
201 return manifest_status;
205 if (!active_rom_path_or.ok()) {
206 return active_rom_path_or.status();
211 if (!project_rom_path_or.ok()) {
212 return project_rom_path_or.status();
214 if (*active_rom_path_or != *project_rom_path_or) {
215 return absl::FailedPreconditionError(absl::StrFormat(
216 "Project ROM mismatch: active ROM is '%s' but --project binds to '%s'",
217 active_rom_path_or->string(), project_rom_path_or->string()));
219 std::error_code file_size_ec;
220 const uintmax_t raw_file_size =
221 std::filesystem::file_size(*active_rom_path_or, file_size_ec);
223 return absl::FailedPreconditionError(
224 absl::StrFormat(
"Cannot inspect active ROM size '%s': %s",
225 active_rom_path_or->string(), file_size_ec.message()));
227 constexpr uintmax_t kSnesBankSize = 0x8000;
231 return absl::FailedPreconditionError(
232 "Message mutation requires a headerless ROM file; remove the "
233 "512-byte copier header and reopen the ROM");
235 if (raw_file_size != rom.
size()) {
236 return absl::FailedPreconditionError(absl::StrFormat(
237 "Message mutation requires a headerless ROM file: disk size is 0x%zX "
238 "but the loaded ROM is 0x%zX bytes",
239 static_cast<size_t>(raw_file_size), rom.
size()));
244 if (!manifest.loaded()) {
245 return absl::FailedPreconditionError(absl::StrFormat(
246 "Project '%s' has no loaded hack manifest; configure a valid "
247 "hack_manifest_file before mutating messages",
259 if (layout.data_start == 0 || layout.data_end == 0 ||
260 layout.data_end < layout.data_start ||
261 !IsCanonicalMappedLoRomAddress(layout.data_start) ||
262 !IsCanonicalMappedLoRomAddress(layout.data_end)) {
263 return absl::FailedPreconditionError(
264 "Hack manifest does not define a valid LoROM expanded message region");
267 const uint32_t start =
SnesToPc(layout.data_start);
268 const uint32_t end =
SnesToPc(layout.data_end);
270 end >
static_cast<uint32_t
>(std::numeric_limits<int>::max()) ||
272 return absl::OutOfRangeError(absl::StrFormat(
273 "Configured expanded message region [0x%06X, 0x%06X] is outside the "
274 "active ROM (size=0x%zX)",
275 start, end, rom.
size()));
277 context.
start =
static_cast<int>(start);
278 context.
end =
static_cast<int>(end);
284 const int region_capacity = context.
end - context.
start + 1;
285 const int capacity_message_limit = std::max(0, region_capacity - 1);
287 if (layout.expanded_count > 0) {
289 std::min(layout.expanded_count, capacity_message_limit);
291 if (layout.last_expanded_id >= layout.first_expanded_id &&
292 (layout.first_expanded_id != 0 || layout.last_expanded_id != 0)) {
293 const int declared_limit =
294 static_cast<int>(layout.last_expanded_id - layout.first_expanded_id) +
299 const std::string lowered_hack_name =
300 absl::AsciiStrToLower(manifest.hack_name());
303 absl::StrContains(lowered_hack_name,
"oracle of secrets")) {
307 const auto conflicts =
308 manifest.AnalyzePcWriteRanges({{start,
static_cast<uint32_t
>(end + 1u)}});
309 if (!conflicts.empty()) {
313 return absl::PermissionDeniedError(absl::StrFormat(
314 "Expanded message mutation blocked by hack manifest: %s", detail));
319 "Expanded message region conflicts with hack manifest: %s", detail);
327 std::vector<uint8_t>* baseline) {
329 return absl::FailedPreconditionError(
330 "Message bundle apply requires a clean ROM; save or discard existing "
331 "in-memory changes before retrying");
337 const absl::Status load_status =
339 if (!load_status.ok()) {
343 return absl::FailedPreconditionError(
344 "Active ROM no longer matches the file on disk; reopen it before "
345 "applying a message bundle");
347 if (baseline !=
nullptr) {
348 *baseline = disk_rom.
vector();
350 return absl::OkStatus();
354 const std::filesystem::path& rom_path,
355 const std::vector<uint8_t>& expected_bytes) {
356 std::error_code file_size_ec;
357 const uintmax_t raw_file_size =
358 std::filesystem::file_size(rom_path, file_size_ec);
360 raw_file_size !=
static_cast<uintmax_t
>(expected_bytes.size())) {
361 return absl::AbortedError(
362 "ROM size changed on disk after message preflight; no bytes were "
363 "saved. Reopen the ROM and retry");
368 const absl::Status load_status =
370 if (!load_status.ok()) {
373 if (disk_rom.
vector() != expected_bytes) {
374 return absl::AbortedError(
375 "ROM changed on disk after message preflight; no bytes were saved. "
376 "Reopen the ROM and retry");
378 return absl::OkStatus();
383 const std::vector<std::pair<uint32_t, uint32_t>>& changed_ranges,
384 std::string* policy_warning) {
385 const auto conflicts =
387 if (conflicts.empty()) {
388 return absl::OkStatus();
394 return absl::PermissionDeniedError(absl::StrFormat(
395 "Vanilla message mutation blocked by hack manifest: %s; no message "
396 "bytes were written",
401 policy_warning !=
nullptr) {
402 *policy_warning = absl::StrFormat(
403 "Vanilla message write conflicts with hack manifest: %s", detail);
405 return absl::OkStatus();
424 size_t previous_length = 0;
425 size_t encoded_length = 0;
426 bool unchanged =
false;
433 size_t expected_count = 0;
434 bool secondary_length_pinned =
false;
435 std::vector<EditedVanillaMessage>
edited;
452 const std::vector<std::pair<
int, std::vector<uint8_t>>>& edits,
453 std::string* policy_warning) {
455 if (layout.vanilla_count <= 0) {
456 return absl::FailedPreconditionError(
457 "Hack manifest must declare a positive messages.vanilla_count "
458 "before applying vanilla messages");
460 if (rom.
size() >
static_cast<size_t>(std::numeric_limits<int>::max())) {
461 return absl::OutOfRangeError(
462 "ROM is too large for bounded vanilla-message parsing");
464 const size_t expected_count =
static_cast<size_t>(layout.vanilla_count);
470 if (!current_or.ok()) {
471 return current_or.status();
474 const auto dictionary =
476 if (dictionary.empty()) {
477 return absl::FailedPreconditionError(absl::StrFormat(
478 "Word dictionary table at PC 0x%06X is outside the ROM or malformed; "
479 "no message bytes were written",
482 std::vector<EditedVanillaMessage> edited;
483 for (
const auto& [
id, bytes] : edits) {
484 if (id < 0 || id >=
static_cast<int>(messages.size())) {
485 return absl::InvalidArgumentError(
486 absl::StrFormat(
"Vanilla message ID 0x%03X is outside 0x000-0x%03X",
487 id,
static_cast<int>(messages.size()) - 1));
490 std::vector<uint8_t> encoded = bytes;
494 if (!message.
Data.empty() &&
499 const size_t previous_length = message.
Data.size();
506 edited.push_back({id, previous_length, previous_length,
true});
511 edited.push_back({id, previous_length, message.
Data.size(),
false});
516 return plan_or.status();
519 std::move(current_or.value()),
522 layout.hook_address != 0,
526 const auto& new_writes = edit.plan.writes();
527 const auto& old_writes = edit.current.writes();
528 if (edit.secondary_length_pinned && new_writes.size() > 1 &&
529 old_writes.size() > 1 && new_writes[1].end() != old_writes[1].end()) {
530 return absl::FailedPreconditionError(absl::StrFormat(
531 "Vanilla text region 2 (PC 0x%06X) must stay %zu bytes because the "
532 "manifest chains expanded messages from its end (the byte at PC "
533 "0x%06X becomes the switch into the expanded bank); this edit makes "
534 "it %zu bytes. Keep messages in region 2 the same total length. No "
535 "message bytes were written",
537 old_writes[1].end() - 1, new_writes[1].bytes().size() - 1));
555 const uint64_t used =
556 index < new_writes.size() ? new_writes[index].bytes().size() : 0;
557 const uint64_t current_used =
558 index < old_writes.size() ? old_writes[index].bytes().size() : 0;
563 formatter.
AddField(
"capacity", capacity);
564 formatter.
AddField(
"current_used", current_used);
569 "free", pinned || used > capacity ? uint64_t{0} : capacity - used);
571 formatter.
AddField(
"length_pinned",
true);
577 uint64_t changed_bytes = 0;
580 changed_bytes += end - start;
581 formatter.
AddArrayItem(absl::StrFormat(
"0x%06X-0x%06X", start, end));
584 formatter.
AddField(
"changed_bytes", changed_bytes);
589 formatter.
AddHexField(
"id",
static_cast<uint64_t
>(message.id), 3);
590 formatter.
AddField(
"previous_length",
591 static_cast<uint64_t
>(message.previous_length));
592 formatter.
AddField(
"encoded_length",
593 static_cast<uint64_t
>(message.encoded_length));
594 if (message.unchanged) {
595 formatter.
AddField(
"unchanged",
true);
606 const std::vector<std::pair<
int, std::vector<uint8_t>>>& edits,
609 Rom* active_rom = rom;
610 if (active_rom ==
nullptr || !active_rom->
is_loaded()) {
612 if (!rom_path.has_value() || rom_path->empty()) {
613 const std::string global_rom_path = absl::GetFlag(FLAGS_rom);
614 if (!global_rom_path.empty()) {
615 rom_path = global_rom_path;
618 if (!rom_path.has_value() || rom_path->empty()) {
619 formatter.
AddField(
"apply_preflight",
"skipped: no --rom");
624 const absl::Status load_status =
626 if (!load_status.ok()) {
627 formatter.
AddField(
"apply_preflight",
628 absl::StrCat(
"error: ", load_status.message()));
631 active_rom = &owned_rom;
634 parser, *active_rom,
"Message bundle preflight");
635 if (!project_context_or.ok()) {
638 absl::StrCat(
"skipped: ", project_context_or.status().message()));
641 std::string policy_warning;
643 edits, &policy_warning);
645 formatter.
AddField(
"apply_preflight",
646 absl::StrCat(
"blocked: ", edit_or.status().message()));
649 formatter.
AddField(
"apply_preflight",
"ok");
650 if (!policy_warning.empty()) {
651 formatter.
AddField(
"write_policy_warning", policy_warning);
657 const Rom& active_rom,
const std::filesystem::path& saved_rom_path,
658 const std::optional<editor::VanillaMessageSavePlan>& vanilla_plan,
659 std::optional<size_t> expected_vanilla_count) {
663 const absl::Status reopen_status =
664 reopened_rom.
LoadFromFile(saved_rom_path.string(), load_options);
665 if (!reopen_status.ok()) {
666 return absl::DataLossError(absl::StrFormat(
667 "ROM was saved but could not be reopened for verification: %s. "
668 "Restore the required backup before retrying",
669 reopen_status.message()));
672 return absl::DataLossError(
673 "ROM was saved but external readback differs from the planned bytes. "
674 "Restore the required backup before retrying");
677 if (vanilla_plan.has_value()) {
678 if (reopened_rom.
size() >
679 static_cast<size_t>(std::numeric_limits<int>::max())) {
680 return absl::DataLossError(
681 "ROM was saved but is too large for bounded vanilla-message "
682 "readback. Restore the required backup before retrying");
684 const auto reopened_messages =
686 static_cast<int>(reopened_rom.
size()));
688 reopened_messages, expected_vanilla_count);
689 if (!readback_plan_or.ok() ||
690 readback_plan_or.value() != vanilla_plan.value()) {
691 const std::string detail =
692 readback_plan_or.ok()
693 ?
"serialized message bytes differ from the save plan"
694 : std::string(readback_plan_or.status().message());
695 return absl::DataLossError(absl::StrFormat(
696 "ROM was saved but vanilla-message readback failed: %s. Restore the "
697 "required backup before retrying",
702 return absl::OkStatus();
708 return absl::InvalidArgumentError(
709 absl::StrFormat(
"Invalid expanded message ID: %d",
id));
712 return absl::OutOfRangeError(absl::StrFormat(
713 "Expanded message ID %d is outside the manifest range (count=%d)",
id,
716 return absl::OkStatus();
722 message_count >
static_cast<size_t>(context.
message_limit)) {
723 return absl::FailedPreconditionError(absl::StrFormat(
724 "Expanded message bank contains %zu messages, exceeding the manifest "
725 "limit of %d; no changes were applied",
728 return absl::OkStatus();
739 auto limit = parser.
GetInt(
"limit").value_or(50);
746 if (limit >
static_cast<int>(messages.size())) {
747 limit =
static_cast<int>(messages.size());
752 formatter.
AddField(
"total_messages",
static_cast<int>(messages.size()));
753 formatter.
AddField(
"status",
"success");
756 for (
int i = 0; i < limit; ++i) {
757 const auto& msg = messages[i];
761 formatter.
AddField(
"text", msg.ContentsParsed);
762 formatter.
AddField(
"length",
static_cast<int>(msg.Data.size()));
768 return absl::OkStatus();
774 auto message_id_or = parser.
GetInt(
"id");
775 if (!message_id_or.ok()) {
776 return message_id_or.status();
778 int message_id = message_id_or.value();
782 if (message_id < 0 || message_id >=
static_cast<int>(messages.size())) {
783 return absl::NotFoundError(
784 absl::StrFormat(
"Message ID %d not found (max: %d)", message_id,
785 static_cast<int>(messages.size()) - 1));
788 const auto& msg = messages[message_id];
793 formatter.
AddField(
"text", msg.ContentsParsed);
794 formatter.
AddField(
"length",
static_cast<int>(msg.Data.size()));
797 return absl::OkStatus();
803 auto query = parser.
GetString(
"query").value();
804 auto limit = parser.
GetInt(
"limit").value_or(10);
815 formatter.
AddField(
"status",
"success");
817 std::string lowered_query = absl::AsciiStrToLower(query);
818 std::vector<const editor::MessageData*> matches;
819 for (
const auto& msg : messages) {
820 std::string lowered_text = absl::AsciiStrToLower(msg.ContentsParsed);
821 if (lowered_text.find(lowered_query) == std::string::npos) {
824 matches.push_back(&msg);
827 formatter.
AddField(
"matches_found",
static_cast<int>(matches.size()));
830 for (
const auto* msg : matches) {
831 if (limit > 0 && match_count >= limit) {
837 formatter.
AddField(
"text", msg->ContentsParsed);
844 return absl::OkStatus();
854 auto text = parser.
GetString(
"text").value();
857 auto bytes = parse_result.bytes;
861 for (
size_t i = 0; i < bytes.size(); ++i) {
864 hex_str += absl::StrFormat(
"%02X", bytes[i]);
870 formatter.
AddField(
"length",
static_cast<int>(bytes.size()));
874 for (uint8_t
byte : bytes) {
875 formatter.
AddArrayItem(absl::StrFormat(
"0x%02X",
byte));
881 if (!warnings.empty()) {
883 for (
const auto& warning : warnings) {
888 if (!parse_result.warnings.empty()) {
890 for (
const auto& warning : parse_result.warnings) {
895 if (!parse_result.errors.empty()) {
897 for (
const auto& error : parse_result.errors) {
905 return absl::OkStatus();
915 auto hex_input = parser.
GetString(
"hex").value();
918 std::vector<uint8_t> bytes;
919 std::istringstream hex_stream(hex_input);
920 std::string hex_byte;
921 while (hex_stream >> hex_byte) {
923 bytes.push_back(
static_cast<uint8_t
>(std::stoi(hex_byte,
nullptr, 16)));
924 }
catch (
const std::exception&) {
925 return absl::InvalidArgumentError(
926 absl::StrFormat(
"Invalid hex byte: '%s'", hex_byte));
932 for (
size_t i = 0; i < bytes.size(); ++i) {
933 uint8_t
byte = bytes[i];
937 if (cmd.has_value()) {
938 if (cmd->HasArgument && i + 1 < bytes.size()) {
939 decoded += cmd->GetParamToken(bytes[++i]);
941 decoded += cmd->GetParamToken();
951 formatter.
AddField(
"hex", hex_input);
952 formatter.
AddField(
"text", decoded);
953 formatter.
AddField(
"length",
static_cast<int>(bytes.size()));
956 return absl::OkStatus();
966 auto file_path = parser.
GetString(
"file").value();
969 std::ifstream file(file_path);
970 if (!file.is_open()) {
971 return absl::NotFoundError(
972 absl::StrFormat(
"Cannot open file: %s", file_path));
975 std::string content((std::istreambuf_iterator<char>(file)),
976 std::istreambuf_iterator<char>());
982 formatter.
AddField(
"file", file_path);
983 formatter.
AddField(
"messages_parsed",
static_cast<int>(messages.size()));
986 for (
const auto& [msg_id, body] : messages) {
989 auto bytes = parse_result.bytes;
993 for (
size_t i = 0; i < bytes.size(); ++i) {
996 hex_str += absl::StrFormat(
"%02X", bytes[i]);
1002 formatter.
AddField(
"hex", hex_str);
1003 formatter.
AddField(
"encoded_length",
static_cast<int>(bytes.size()));
1004 if (!warnings.empty()) {
1006 for (
const auto& warning : warnings) {
1011 if (!parse_result.warnings.empty()) {
1013 for (
const auto& warning : parse_result.warnings) {
1018 if (!parse_result.errors.empty()) {
1020 for (
const auto& error : parse_result.errors) {
1030 return absl::OkStatus();
1040 auto output_path = parser.
GetString(
"output").value();
1046 std::vector<std::pair<int, std::string>> msg_pairs;
1047 std::vector<std::string> labels;
1048 for (
const auto& msg : messages) {
1049 msg_pairs.push_back({msg.ID, msg.RawString});
1050 labels.push_back(absl::StrFormat(
"Message %02X", msg.ID));
1056 std::ofstream file(output_path);
1057 if (!file.is_open()) {
1058 return absl::InternalError(
1059 absl::StrFormat(
"Cannot write to file: %s", output_path));
1061 file << org_content;
1065 formatter.
AddField(
"output", output_path);
1066 formatter.
AddField(
"messages_exported",
static_cast<int>(messages.size()));
1067 formatter.
AddField(
"status",
"success");
1070 return absl::OkStatus();
1080 auto output_path = parser.
GetString(
"output").value();
1081 auto range = NormalizeRange(parser.
GetString(
"range").value_or(
"all"));
1082 if (!IncludeVanilla(range) && !IncludeExpanded(range)) {
1083 return absl::InvalidArgumentError(
1084 absl::StrFormat(
"Invalid range: %s", range));
1087 std::vector<editor::MessageData> vanilla;
1088 std::vector<editor::MessageData> expanded;
1090 if (IncludeVanilla(range)) {
1095 if (IncludeExpanded(range)) {
1096 expanded = ReadExpandedMessages(*rom);
1106 formatter.
AddField(
"output", output_path);
1107 formatter.
AddField(
"range", range);
1108 formatter.
AddField(
"vanilla_count",
static_cast<int>(vanilla.size()));
1109 formatter.
AddField(
"expanded_count",
static_cast<int>(expanded.size()));
1110 formatter.
AddField(
"status",
"success");
1113 return absl::OkStatus();
1123 auto file_path = parser.
GetString(
"file").value();
1124 const bool apply = parser.
HasFlag(
"apply");
1125 const bool strict = parser.
HasFlag(
"strict");
1126#ifdef __EMSCRIPTEN__
1128 return absl::FailedPreconditionError(
1129 "message-import-bundle --apply is unavailable in WebAssembly because "
1130 "the browser filesystem cannot provide durable backup and readback "
1134 auto range = NormalizeRange(parser.
GetString(
"range").value_or(
"all"));
1135 if (!IncludeVanilla(range) && !IncludeExpanded(range)) {
1136 return absl::InvalidArgumentError(
1137 absl::StrFormat(
"Invalid range: %s", range));
1141 if (!entries_or.ok()) {
1142 return entries_or.status();
1144 auto entries = entries_or.value();
1147 formatter.
AddField(
"file", file_path);
1148 formatter.
AddField(
"range", range);
1149 formatter.
AddField(
"apply", apply);
1150 formatter.
AddField(
"strict", strict);
1151 formatter.
AddField(
"entries",
static_cast<int>(entries.size()));
1153 bool has_errors =
false;
1154 int parse_error_count = 0;
1155 int error_count = 0;
1156 int applied_updates = 0;
1157 bool has_vanilla_entries =
false;
1158 bool has_expanded_entries =
false;
1160 struct ParsedEntry {
1163 std::vector<std::string> line_warnings;
1165 std::vector<ParsedEntry> parsed_entries;
1166 parsed_entries.reserve(entries.size());
1168 for (
const auto& entry : entries) {
1170 !IncludeVanilla(range)) ||
1172 !IncludeExpanded(range))) {
1176 ParsedEntry parsed{entry,
1179 if (!parsed.parse.ok()) {
1181 parse_error_count +=
static_cast<int>(parsed.parse.errors.size());
1182 error_count +=
static_cast<int>(parsed.parse.errors.size());
1185 has_vanilla_entries =
true;
1187 has_expanded_entries =
true;
1189 parsed_entries.push_back(std::move(parsed));
1193 for (
const auto& parsed : parsed_entries) {
1195 formatter.
AddField(
"id", parsed.entry.id);
1196 formatter.
AddField(
"bank", BankLabel(parsed.entry.bank));
1197 formatter.
AddField(
"text", parsed.entry.text);
1198 formatter.
AddField(
"encoded_length",
1199 static_cast<int>(parsed.parse.bytes.size()));
1200 if (!parsed.line_warnings.empty()) {
1202 for (
const auto& warning : parsed.line_warnings) {
1207 if (!parsed.parse.warnings.empty()) {
1209 for (
const auto& warning : parsed.parse.warnings) {
1214 if (!parsed.parse.errors.empty()) {
1216 for (
const auto& error : parsed.parse.errors) {
1226 Rom* active_rom = rom;
1229 if (active_rom ==
nullptr || !active_rom->
is_loaded()) {
1230 auto rom_path = parser.
GetString(
"rom");
1231 if (!rom_path.has_value() || rom_path->empty()) {
1232 std::string global_rom_path = absl::GetFlag(FLAGS_rom);
1233 if (!global_rom_path.empty()) {
1234 rom_path = global_rom_path;
1237 if (!rom_path.has_value() || rom_path->empty()) {
1239 formatter.
AddField(
"status",
"error");
1241 "ROM not loaded; provide --rom when using --apply");
1242 formatter.
AddField(
"parse_error_count", parse_error_count);
1243 formatter.
AddField(
"error_count", error_count);
1245 return absl::OkStatus();
1248 if (!load_status.ok()) {
1250 formatter.
AddField(
"status",
"error");
1251 formatter.
AddField(
"error", std::string(load_status.message()));
1252 formatter.
AddField(
"parse_error_count", parse_error_count);
1253 formatter.
AddField(
"error_count", error_count);
1255 return absl::OkStatus();
1257 active_rom = &owned_rom;
1261 formatter.
AddField(
"status",
"error");
1262 formatter.
AddField(
"error",
"Parse errors present; no changes applied");
1263 formatter.
AddField(
"parse_error_count", parse_error_count);
1264 formatter.
AddField(
"error_count", error_count);
1266 if (strict && parse_error_count > 0) {
1269 return absl::FailedPreconditionError(
1270 "Strict validation failed due to parse errors");
1272 return absl::OkStatus();
1275 std::optional<ProjectMutationContext> project_context;
1276 std::vector<uint8_t> disk_baseline;
1277 if (has_vanilla_entries || has_expanded_entries) {
1278 const absl::Status snapshot_status =
1279 VerifyCleanDiskSnapshot(*active_rom, &disk_baseline);
1280 if (!snapshot_status.ok()) {
1282 formatter.
AddField(
"status",
"error");
1283 formatter.
AddField(
"error", std::string(snapshot_status.message()));
1284 formatter.
AddField(
"parse_error_count", parse_error_count);
1285 formatter.
AddField(
"error_count", error_count);
1287 return snapshot_status;
1290 auto project_context_or = LoadProjectMutationContext(
1291 parser, *active_rom,
"Message bundle apply");
1292 if (!project_context_or.ok()) {
1294 formatter.
AddField(
"status",
"error");
1296 std::string(project_context_or.status().message()));
1297 formatter.
AddField(
"parse_error_count", parse_error_count);
1298 formatter.
AddField(
"error_count", error_count);
1300 return project_context_or.status();
1302 project_context.emplace(std::move(project_context_or.value()));
1305 std::optional<ExpandedMutationContext> expanded_context;
1306 if (IncludeExpanded(range) && has_expanded_entries) {
1308 PreflightExpandedMutation(project_context->project, *active_rom);
1309 if (!context_or.ok()) {
1311 formatter.
AddField(
"status",
"error");
1312 formatter.
AddField(
"error", std::string(context_or.status().message()));
1313 formatter.
AddField(
"parse_error_count", parse_error_count);
1314 formatter.
AddField(
"error_count", error_count);
1316 return context_or.status();
1318 expanded_context.emplace(std::move(context_or.value()));
1324 std::vector<editor::MessageData> vanilla_messages;
1325 std::vector<std::pair<int, std::vector<uint8_t>>> vanilla_edits;
1326 if (IncludeVanilla(range) && has_vanilla_entries) {
1327 if (active_rom->
size() >
1328 static_cast<size_t>(std::numeric_limits<int>::max())) {
1330 return absl::OutOfRangeError(
1331 "ROM is too large for bounded vanilla-message parsing");
1335 static_cast<int>(active_rom->
size()));
1338 std::vector<std::string> expanded_texts;
1339 if (expanded_context.has_value()) {
1340 const auto expanded_messages = ReadExpandedMessages(
1341 *active_rom, expanded_context->start, expanded_context->
end);
1342 auto bank_size_status = ValidateExpandedMessageBankSize(
1343 expanded_messages.size(), *expanded_context);
1344 if (!bank_size_status.ok()) {
1345 formatter.
AddField(
"status",
"error");
1346 formatter.
AddField(
"error", std::string(bank_size_status.message()));
1348 return bank_size_status;
1350 expanded_texts.reserve(expanded_messages.size());
1351 for (
const auto& message : expanded_messages) {
1352 expanded_texts.push_back(message.RawString);
1356 for (
const auto& parsed : parsed_entries) {
1358 if (parsed.entry.id < 0 ||
1359 parsed.entry.id >=
static_cast<int>(vanilla_messages.size())) {
1364 vanilla_edits.emplace_back(parsed.entry.id, parsed.parse.bytes);
1366 if (!expanded_context.has_value()) {
1369 const auto id_status =
1370 ValidateExpandedMessageId(parsed.entry.id, *expanded_context);
1371 if (!id_status.ok()) {
1376 if (parsed.entry.id >=
static_cast<int>(expanded_texts.size())) {
1377 expanded_texts.resize(parsed.entry.id + 1);
1379 expanded_texts[parsed.entry.id] = parsed.entry.text;
1384 std::optional<editor::VanillaMessageSavePlan> vanilla_plan;
1385 std::optional<size_t> expected_vanilla_count;
1386 std::string write_policy_warning;
1387 if (!has_errors && IncludeVanilla(range) && has_vanilla_entries) {
1388 auto edit_or = PlanVanillaEdits(project_context->project, *active_rom,
1389 vanilla_edits, &write_policy_warning);
1390 if (!edit_or.ok()) {
1391 formatter.
AddField(
"status",
"error");
1392 formatter.
AddField(
"error", std::string(edit_or.status().message()));
1394 return edit_or.status();
1396 AddVanillaPlanReport(formatter, *edit_or);
1397 expected_vanilla_count = edit_or->expected_count;
1398 vanilla_plan.emplace(std::move(edit_or->plan));
1401 if (expanded_context.has_value() &&
1402 !expanded_context->policy_warning.empty()) {
1403 if (!write_policy_warning.empty()) {
1404 absl::StrAppend(&write_policy_warning,
"; ");
1406 absl::StrAppend(&write_policy_warning, expanded_context->policy_warning);
1408 if (!write_policy_warning.empty()) {
1409 formatter.
AddField(
"write_policy_warning", write_policy_warning);
1413 formatter.
AddField(
"status",
"error");
1414 formatter.
AddField(
"error",
"Invalid message IDs; no changes applied");
1417 if (vanilla_plan.has_value()) {
1421 formatter.
AddField(
"status",
"error");
1422 formatter.
AddField(
"error", std::string(status.message()));
1428 if (expanded_context.has_value()) {
1430 active_rom, expanded_context->start, expanded_context->
end,
1433 formatter.
AddField(
"status",
"error");
1434 formatter.
AddField(
"error", std::string(status.message()));
1440 if (active_rom->
dirty() && project_context.has_value()) {
1441 const absl::Status unchanged_status = VerifyDiskSnapshotUnchanged(
1442 project_context->canonical_rom_path, disk_baseline);
1443 if (!unchanged_status.ok()) {
1444 formatter.
AddField(
"status",
"error");
1445 formatter.
AddField(
"error", std::string(unchanged_status.message()));
1447 return unchanged_status;
1452 save_settings.
filename = project_context->canonical_rom_path.string();
1453 auto save_status = active_rom->
SaveToFile(save_settings);
1454 if (!save_status.ok()) {
1455 formatter.
AddField(
"status",
"error");
1456 formatter.
AddField(
"error", std::string(save_status.message()));
1463 if (project_context.has_value()) {
1464 const absl::Status readback_status =
1465 VerifySavedRom(*active_rom, project_context->canonical_rom_path,
1466 vanilla_plan, expected_vanilla_count);
1467 if (!readback_status.ok()) {
1468 formatter.
AddField(
"status",
"error");
1469 formatter.
AddField(
"error", std::string(readback_status.message()));
1471 return readback_status;
1474 formatter.
AddField(
"status",
"success");
1475 formatter.
AddField(
"applied_messages", applied_updates);
1476 if (project_context.has_value()) {
1477 formatter.
AddField(
"readback_verified",
true);
1481 if (!has_errors && IncludeVanilla(range) && has_vanilla_entries) {
1482 std::vector<std::pair<int, std::vector<uint8_t>>> vanilla_edits;
1483 for (
const auto& parsed : parsed_entries) {
1485 vanilla_edits.emplace_back(parsed.entry.id, parsed.parse.bytes);
1488 AddVanillaApplyPreflight(parser, rom, vanilla_edits, formatter);
1490 formatter.
AddField(
"status", has_errors ?
"error" :
"success");
1493 formatter.
AddField(
"error_count", error_count);
1494 formatter.
AddField(
"parse_error_count", parse_error_count);
1496 if (strict && parse_error_count > 0) {
1499 return absl::FailedPreconditionError(
"Strict validation failed");
1501 return absl::OkStatus();
1516 const std::string& text) {
1517#ifdef __EMSCRIPTEN__
1518 return absl::FailedPreconditionError(
1519 "message-write --range vanilla is unavailable in WebAssembly because "
1520 "the browser filesystem cannot provide durable backup and readback "
1523 if (rom ==
nullptr || !rom->
is_loaded()) {
1524 return absl::FailedPreconditionError(
"ROM not loaded; provide --rom");
1529 return absl::InvalidArgumentError(absl::StrCat(
1530 "Message text does not encode: ", absl::StrJoin(parse.
errors,
"; ")));
1533 std::vector<uint8_t> disk_baseline;
1538 std::vector<std::pair<int, std::vector<uint8_t>>> edits;
1539 edits.emplace_back(
id, parse.
bytes);
1540 std::string policy_warning;
1546 formatter.
AddHexField(
"id",
static_cast<uint64_t
>(
id), 3);
1547 formatter.
AddField(
"range",
"vanilla");
1550 if (!policy_warning.empty()) {
1551 formatter.
AddField(
"write_policy_warning", policy_warning);
1554 if (!line_warnings.empty()) {
1556 for (
const auto& warning : line_warnings) {
1564 auto finish = [&formatter](absl::Status status) {
1566 formatter.
AddField(
"status",
"success");
1567 formatter.
AddField(
"readback_verified",
true);
1569 formatter.
AddField(
"status",
"error");
1570 formatter.
AddField(
"error", std::string(status.message()));
1578 if (status.ok() && rom->
dirty()) {
1590 return finish(status);
1603 const std::string& text) {
1604#ifdef __EMSCRIPTEN__
1605 return absl::FailedPreconditionError(
1606 "message-write --range expanded is unavailable in WebAssembly because "
1607 "the browser filesystem cannot provide durable backup and readback "
1610 if (rom ==
nullptr || !rom->
is_loaded()) {
1611 return absl::FailedPreconditionError(
"ROM not loaded; provide --rom");
1615 if (bytes.empty() && !text.empty()) {
1616 return absl::InvalidArgumentError(
"Encoding produced no bytes");
1625 std::vector<uint8_t> disk_baseline;
1630 std::vector<std::string> all_texts;
1631 all_texts.reserve(expanded.size());
1632 for (
const auto& msg : expanded) {
1633 all_texts.push_back(msg.RawString);
1635 if (
id >=
static_cast<int>(all_texts.size())) {
1636 all_texts.resize(
id + 1);
1638 all_texts[id] = text;
1642 formatter.
AddField(
"range",
"expanded");
1644 formatter.
AddField(
"encoded_length",
static_cast<int>(bytes.size()));
1648 if (!warnings.empty()) {
1650 for (
const auto& warning : warnings) {
1658 auto finish = [&formatter](absl::Status status) {
1660 formatter.
AddField(
"status",
"success");
1661 formatter.
AddField(
"readback_verified",
true);
1663 formatter.
AddField(
"status",
"error");
1664 formatter.
AddField(
"error", std::string(status.message()));
1671 absl::Status status =
1673 if (status.ok() && rom->
dirty()) {
1685 return finish(status);
1689 std::nullopt, std::nullopt));
1697 auto id_or = parser.
GetInt(
"id");
1699 return id_or.status();
1700 int msg_id = id_or.value();
1702 auto text = parser.
GetString(
"text").value();
1704 const std::string range =
1705 NormalizeRange(parser.
GetString(
"range").value_or(
"expanded"));
1706 if (range ==
"vanilla") {
1707 return WriteVanillaMessage(rom, parser, formatter, msg_id, text);
1709 if (range !=
"expanded") {
1710 return absl::InvalidArgumentError(absl::StrFormat(
1711 "message-write --range must be vanilla or expanded, got %s", range));
1713 return WriteExpandedMessage(rom, parser, formatter, msg_id, text);
1723 auto output_path = parser.
GetString(
"output").value();
1724 auto range = parser.
GetString(
"range").value_or(
"expanded");
1726 int start, end_addr;
1727 if (range ==
"expanded") {
1736 const uint8_t* data = rom->
data();
1737 int data_end = start;
1738 while (data_end <= end_addr && data[data_end] != 0xFF) {
1741 if (data_end <= end_addr) {
1745 int size = data_end - start;
1747 std::ofstream file(output_path, std::ios::binary);
1748 if (!file.is_open()) {
1749 return absl::InternalError(
1750 absl::StrFormat(
"Cannot write to file: %s", output_path));
1752 file.write(
reinterpret_cast<const char*
>(data + start), size);
1756 formatter.
AddField(
"output", output_path);
1757 formatter.
AddField(
"range", range);
1759 formatter.
AddHexField(
"end_address", data_end - 1, 6);
1760 formatter.
AddField(
"size_bytes", size);
1761 formatter.
AddField(
"status",
"success");
1764 return absl::OkStatus();
1774 auto output_path = parser.
GetString(
"output").value();
1775 auto range = parser.
GetString(
"range").value_or(
"expanded");
1779 if (range ==
"expanded") {
1781 snes_addr = 0x2F8000;
1784 snes_addr = 0x1C0000;
1791 std::ofstream file(output_path);
1792 if (!file.is_open()) {
1793 return absl::InternalError(
1794 absl::StrFormat(
"Cannot write to file: %s", output_path));
1798 file <<
"; Auto-generated message data\n";
1799 file << absl::StrFormat(
"; Source: %s region\n", range);
1800 file << absl::StrFormat(
"; Messages: %d\n\n", messages.size());
1801 file << absl::StrFormat(
"org $%06X\n\n", snes_addr);
1804 for (
const auto& msg : messages) {
1805 file << absl::StrFormat(
"; Message $%02X: %s\n", msg.ID,
1806 msg.ContentsParsed.substr(0, 60));
1808 for (
size_t i = 0; i < msg.Data.size(); ++i) {
1811 file << absl::StrFormat(
"$%02X", msg.Data[i]);
1813 file <<
", $7F ; terminator\n\n";
1817 file <<
"db $FF ; end of message data\n";
1821 formatter.
AddField(
"output", output_path);
1822 formatter.
AddField(
"range", range);
1823 formatter.
AddField(
"messages_exported",
static_cast<int>(messages.size()));
1824 formatter.
AddField(
"status",
"success");
1827 return absl::OkStatus();
1833 const std::string project_path = parser.
GetString(
"project").value();
1834 const std::string incoming_path = parser.
GetString(
"file").value();
1835 const bool write = parser.
HasFlag(
"write");
1839 absl::Status open_status;
1841 ScopedHackManifestBindingRestore restore_manifest(resource_labels);
1843 open_status = source_project.
Open(project_path);
1844 }
catch (
const std::exception& error) {
1845 return absl::InvalidArgumentError(absl::StrFormat(
1846 "Cannot load project '%s': %s", project_path, error.what()));
1848 return absl::InvalidArgumentError(absl::StrFormat(
1849 "Cannot load project '%s': unknown project parse error",
1853 if (!open_status.ok()) {
1854 return absl::Status(open_status.code(),
1855 absl::StrFormat(
"Cannot load project '%s': %s",
1856 project_path, open_status.message()));
1858 if (absl::Status manifest_status =
1859 EnsureConfiguredHackManifestLoaded(&source_project, project_path);
1860 !manifest_status.ok()) {
1861 return manifest_status;
1866 .expected_source_sha256 =
1867 parser.
GetString(
"expected-source-sha256").value_or(
""),
1871 if (!result_or.ok()) {
1872 return result_or.status();
1874 const auto& result = *result_or;
1877 formatter.
AddField(
"status",
"success");
1878 formatter.
AddField(
"mode", write ?
"write" :
"dry-run");
1879 formatter.
AddField(
"changed", result.changed);
1880 formatter.
AddField(
"wrote", result.wrote);
1881 formatter.
AddField(
"canonical_bundle", result.canonical_bundle_path.string());
1882 formatter.
AddField(
"generated_asm_include",
1883 result.generated_asm_include_path.string());
1884 formatter.
AddHexField(
"first_expanded_id", result.first_expanded_id, 3);
1885 formatter.
AddHexField(
"last_expanded_id", result.last_expanded_id, 3);
1886 formatter.
AddField(
"expanded_count", result.expanded_count);
1887 formatter.
AddField(
"incoming_updates", result.incoming_updates);
1888 formatter.
AddField(
"encoded_size",
static_cast<int>(result.encoded_size));
1889 formatter.
AddField(
"capacity",
static_cast<int>(result.capacity));
1890 formatter.
AddField(
"source_sha256_before", result.source_sha256_before);
1891 formatter.
AddField(
"proposed_source_sha256", result.proposed_source_sha256);
1893 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
const auto & vector() const
absl::Status SaveToFile(const SaveSettings &settings)
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
~ScopedHackManifestBindingRestore()
zelda3::ResourceLabelProvider & provider_
ScopedHackManifestBindingRestore(const ScopedHackManifestBindingRestore &)=delete
ScopedHackManifestBindingRestore & operator=(const ScopedHackManifestBindingRestore &)=delete
ScopedHackManifestBindingRestore(zelda3::ResourceLabelProvider &provider)
Utility for parsing common CLI argument patterns.
std::optional< std::string > GetString(const std::string &name) const
Parse a named argument (e.g., –format=json or –format json)
bool HasFlag(const std::string &name) const
Check if a flag is present.
absl::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
Loads and queries the hack manifest JSON for yaze-ASM integration.
const MessageLayout & message_layout() const
absl::Status LoadFromFile(const std::string &filepath)
Load manifest from a JSON file path.
std::vector< WriteConflict > AnalyzePcWriteRanges(const std::vector< std::pair< uint32_t, uint32_t > > &pc_ranges) const
Analyze a set of PC-offset ranges for write conflicts.
bool loaded() const
Check if the manifest has been loaded.
const std::vector< VanillaMessageWrite > & writes() const
Unified interface for accessing resource labels with project overrides.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
ABSL_DECLARE_FLAG(std::string, rom)
absl::Status EnsureConfiguredHackManifestLoaded(project::YazeProject *project, absl::string_view project_path)
absl::StatusOr< VanillaEditPlan > PlanVanillaEdits(const project::YazeProject &yaze_project, const Rom &rom, const std::vector< std::pair< int, std::vector< uint8_t > > > &edits, std::string *policy_warning)
std::string BankLabel(editor::MessageBank bank)
absl::Status ValidateExpandedMessageBankSize(size_t message_count, const ExpandedMutationContext &context)
void AddVanillaApplyPreflight(const resources::ArgumentParser &parser, Rom *rom, const std::vector< std::pair< int, std::vector< uint8_t > > > &edits, resources::OutputFormatter &formatter)
absl::Status VerifyDiskSnapshotUnchanged(const std::filesystem::path &rom_path, const std::vector< uint8_t > &expected_bytes)
void AddVanillaPlanReport(resources::OutputFormatter &formatter, const VanillaEditPlan &edit)
bool IncludeVanilla(const std::string &range)
absl::Status ValidateExpandedMessageId(int id, const ExpandedMutationContext &context)
absl::StatusOr< std::filesystem::path > CanonicalExistingPath(const std::string &path, absl::string_view label)
bool IncludeExpanded(const std::string &range)
absl::Status WriteVanillaMessage(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter, int id, const std::string &text)
std::string NormalizeRange(const std::string &range)
absl::StatusOr< ProjectMutationContext > LoadProjectMutationContext(const resources::ArgumentParser &parser, const Rom &rom, absl::string_view mutation_scope)
std::string FormatManifestConflict(const core::WriteConflict &conflict)
constexpr VanillaTextRegion kVanillaTextRegions[]
absl::Status WriteExpandedMessage(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter, int id, const std::string &text)
absl::StatusOr< ExpandedMutationContext > PreflightExpandedMutation(const project::YazeProject &yaze_project, const Rom &rom)
absl::Status VerifyCleanDiskSnapshot(const Rom &rom, std::vector< uint8_t > *baseline)
absl::Status ValidateVanillaMutation(const project::YazeProject &yaze_project, const std::vector< std::pair< uint32_t, uint32_t > > &changed_ranges, std::string *policy_warning)
absl::Status VerifySavedRom(const Rom &active_rom, const std::filesystem::path &saved_rom_path, const std::optional< editor::VanillaMessageSavePlan > &vanilla_plan, std::optional< size_t > expected_vanilla_count)
std::vector< editor::MessageData > ReadExpandedMessages(const Rom &rom)
std::string AddressOwnershipToString(AddressOwnership ownership)
int GetExpandedTextDataStart()
std::string ParseTextDataByte(uint8_t value)
std::vector< uint8_t > CompressMessageWithDictionary(const std::vector< uint8_t > &data, const std::vector< std::vector< uint8_t > > &dictionary)
std::string MessageBankToString(MessageBank bank)
absl::Status WriteExpandedTextData(Rom *rom, int start, int end, const std::vector< std::string > &messages)
constexpr int kPointersDictionaries
absl::StatusOr< std::vector< MessageBundleEntry > > LoadMessageBundleFromJson(const std::string &path)
std::vector< MessageData > ReadAllTextData(uint8_t *rom, int pos, int max_pos, bool allow_bank_switch)
constexpr int kTextData2End
constexpr uint8_t kBankSwitchCommand
std::vector< uint8_t > ParseMessageToData(std::string str)
absl::Status ExportMessageBundleToJson(const std::string &path, const std::vector< MessageData > &vanilla, const std::vector< MessageData > &expanded)
std::string ExportToOrgFormat(const std::vector< std::pair< int, std::string > > &messages, const std::vector< std::string > &labels)
absl::StatusOr< VanillaMessageSavePlan > BuildVanillaMessageSavePlan(const std::vector< MessageData > &messages, std::optional< size_t > expected_message_count)
constexpr char kOracleExpandedRomWriteRefusal[]
std::vector< MessageData > ReadExpandedTextData(uint8_t *rom, int pos)
std::vector< uint8_t > ExpandMessageDictionary(const std::vector< uint8_t > &data, const std::vector< std::vector< uint8_t > > &dictionary)
std::optional< TextElement > FindMatchingCommand(uint8_t b)
MessageParseResult ParseMessageToDataWithDiagnostics(std::string_view str)
int GetExpandedTextDataEnd()
absl::StatusOr< MessageSourceSyncResult > SyncMessageSource(const project::YazeProject &project, const fs::path &incoming_bundle_path, const MessageSourceSyncOptions &options)
std::vector< std::string > ValidateMessageLineWidths(const std::string &message)
std::vector< std::vector< uint8_t > > ReadDictionaryEntryBytes(const uint8_t *rom, size_t rom_size)
std::vector< std::pair< int, std::string > > ParseOrgContent(const std::string &content)
absl::Status ApplyVanillaMessageSavePlan(Rom *rom, const VanillaMessageSavePlan &plan)
constexpr int kTextDataEnd
ResourceLabelProvider & GetResourceLabels()
Get the global ResourceLabelProvider instance.
uint32_t PcToSnes(uint32_t addr)
constexpr size_t kCopierHeaderSize
Size of the SMC/SWC copier header that some ROM dumps start with.
uint32_t SnesToPc(uint32_t addr) noexcept
#define RETURN_IF_ERROR(expr)
bool load_resource_labels
std::string policy_warning
project::YazeProject project
std::filesystem::path canonical_rom_path
editor::VanillaMessageSavePlan current
bool secondary_length_pinned
editor::VanillaMessageSavePlan plan
std::vector< std::pair< uint32_t, uint32_t > > changed_ranges
std::vector< EditedVanillaMessage > edited
A conflict detected when yaze wants to write to an ASM-owned address.
AddressOwnership ownership
std::vector< uint8_t > Data
std::vector< uint8_t > DataParsed
std::vector< uint8_t > bytes
std::vector< std::string > errors
Modern project structure with comprehensive settings consolidation.
core::HackManifest hack_manifest
std::string hack_manifest_file
std::string GetAbsolutePath(const std::string &relative_path) const
absl::Status Open(const std::string &project_path)