9#include "absl/status/status.h"
10#include "absl/strings/ascii.h"
11#include "absl/strings/match.h"
12#include "absl/strings/str_format.h"
27 absl::AsciiStrToLower(&value);
28 if (absl::StartsWith(value,
"0x")) {
29 value = value.substr(2);
31 value.erase(std::remove_if(value.begin(), value.end(),
32 [](
unsigned char c) { return std::isspace(c); }),
41 std::filesystem::path path(value);
42 path = path.lexically_normal();
43 path.make_preferred();
48 absl::AsciiStrToLower(&value);
52bool PathsMatch(
const std::string& lhs,
const std::string& rhs) {
53 if (lhs.empty() || rhs.empty()) {
58 if (normalized_lhs == normalized_rhs) {
61 return Lowercase(std::filesystem::path(normalized_lhs).filename().
string()) ==
62 Lowercase(std::filesystem::path(normalized_rhs).filename().
string());
70 bool matches_configured_project =
false;
71 bool matches_editable_target =
false;
72 bool matches_build_output =
false;
100 const std::string& loaded_path) {
133 : rom_file_manager_(deps.rom_file_manager),
134 session_coordinator_(deps.session_coordinator),
135 toast_manager_(deps.toast_manager),
136 popup_manager_(deps.popup_manager),
137 project_(deps.project) {}
166 if (target_info.matches_editable_target &&
167 !target_info.matches_build_output) {
172 if (expected.empty() || actual.empty()) {
175 return expected != actual;
180 return absl::OkStatus();
184 if (!target_info.matches_build_output) {
185 return absl::OkStatus();
190 return absl::OkStatus();
194 return absl::OkStatus();
197 const std::string editable_target = GetEditableTargetPath(target_info);
200 absl::StrFormat(
"Project opened a build-output ROM; edit '%s' instead",
204 return absl::FailedPreconditionError(absl::StrFormat(
205 "Loaded ROM '%s' matches the project's build output. Edit '%s' instead.",
206 target_info.loaded_path.empty() ? rom->
filename()
207 : target_info.loaded_path,
212 Rom* rom,
const std::optional<std::string>& target_filename) {
214 return absl::OkStatus();
218 const std::string target_path =
219 target_filename.has_value()
223 const std::string source_path =
225 const auto target_info = GetRomTargetInfo(
project_, target_path);
226 const auto source_info = GetRomTargetInfo(
project_, source_path);
227 if (target_info.matches_build_output) {
229 return absl::OkStatus();
234 "ROM write blocked: '%s' is the project's build output",
235 target_info.loaded_path.empty() ? target_path
236 : target_info.loaded_path),
239 return absl::PermissionDeniedError(absl::StrFormat(
240 "ROM write blocked: '%s' is the project's build output. Edit '%s' "
242 target_info.loaded_path.empty() ? target_path : target_info.loaded_path,
243 GetEditableTargetPath(target_info)));
246 if (source_info.matches_editable_target &&
247 !source_info.matches_build_output) {
248 return absl::OkStatus();
254 if (expected.empty() || actual.empty() || expected == actual) {
255 return absl::OkStatus();
259 return absl::OkStatus();
265 "ROM write blocked: project expects a different ROM hash",
268 return absl::PermissionDeniedError(
269 "ROM write blocked by project write policy");
281 return absl::CancelledError(
"ROM write confirmation required");
284 return absl::OkStatus();
289 return absl::InvalidArgumentError(
"ROM not loaded");
293 return absl::OkStatus();
301 const std::string manifest_path =
306 "Save blocked: configured hack manifest is unavailable: %s",
310 return absl::FailedPreconditionError(absl::StrFormat(
311 "Configured hack manifest is missing or malformed: %s", manifest_path));
316 return absl::OkStatus();
327 if (preflight.ok()) {
328 return absl::OkStatus();
331 const auto& first = preflight.errors.front();
334 first.code, first.message.c_str()),
337 return preflight.ToStatus();
397 return absl::FailedPreconditionError(
"No ROM loaded");
400 return absl::FailedPreconditionError(
"No ROM file manager");
406 const std::string& folder,
409 int keep_daily_days) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
bool HasProjectRegistry() const
bool loaded() const
Check if the manifest has been loaded.
const BuildPipeline & build_pipeline() const
void SetBackupRetentionCount(int count)
absl::Status PruneBackups(const std::string &rom_filename) const
std::vector< BackupEntry > ListBackups(const std::string &rom_filename) const
void SetBackupBeforeSave(bool enabled)
void SetBackupFolder(const std::string &folder)
void SetBackupKeepDailyDays(int days)
void SetBackupKeepDaily(bool enabled)
void UpdateCurrentRomHash(Rom *rom)
Recompute the hash of the current ROM.
std::vector< RomFileManager::BackupEntry > GetRomBackups(Rom *rom) const
void SetPotItemConfirmPending(int unloaded_rooms, int total_rooms)
Set pot-item confirmation pending (called by SaveRom when needed).
bool pending_rom_write_confirm_
absl::Status CheckRomWritePolicy(Rom *rom, const std::optional< std::string > &target_filename=std::nullopt)
Enforce project write policy; may set pending_rom_write_confirm.
const project::YazeProject * project_
int pending_pot_item_total_rooms_
bool pending_pot_item_save_confirm_
bool IsRomHashMismatch() const
Check whether the ROM hash mismatches the project's expected hash.
std::string current_rom_hash_
RomFileManager * rom_file_manager_
PopupManager * popup_manager_
void CancelRomWriteConfirm()
std::string current_rom_path_
absl::Status CheckRomOpenPolicy(Rom *rom)
Validate that the loaded ROM is a safe project target to open/edit.
bool suppress_pot_item_save_once_
PotItemSaveDecision ResolvePotItemSaveConfirmation(PotItemSaveDecision decision)
RomLifecycleManager()=default
int pending_pot_item_unloaded_rooms_
bool bypass_build_output_open_once_
bool bypass_rom_write_confirm_once_
absl::Status CheckOracleRomSafetyPreSave(Rom *rom)
Run Oracle-specific ROM safety preflight before saving.
bool bypass_pot_item_confirm_once_
void Initialize(Dependencies deps)
absl::Status PruneRomBackups(Rom *rom)
ToastManager * toast_manager_
void ApplyDefaultBackupPolicy(bool enabled, const std::string &folder, int retention_count, bool keep_daily, int keep_daily_days)
Apply default backup policy from user settings.
SessionCoordinator * session_coordinator_
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
std::string NormalizePath(std::string value)
std::string NormalizeHash(std::string value)
std::string GetBuildOutputProjectRomPath(const project::YazeProject *project)
std::string Lowercase(std::string value)
std::string GetEditableProjectRomPath(const project::YazeProject *project)
RomTargetInfo GetRomTargetInfo(const project::YazeProject *project, const std::string &loaded_path)
bool PathsMatch(const std::string &lhs, const std::string &rhs)
std::string GetEditableTargetPath(const RomTargetInfo &info)
Editors are the view controllers for the application.
std::string ComputeRomHash(const uint8_t *data, size_t size)
OracleRomSafetyPreflightResult RunOracleRomSafetyPreflight(Rom *rom, const OracleRomSafetyPreflightOptions &options)
SessionCoordinator * session_coordinator
RomFileManager * rom_file_manager
project::YazeProject * project
PopupManager * popup_manager
ToastManager * toast_manager
std::string configured_project_path
bool matches_build_output
bool matches_editable_target
std::string build_output_path
bool matches_configured_project
std::string editable_target_path
Modern project structure with comprehensive settings consolidation.
bool project_opened() const
core::HackManifest hack_manifest
std::string hack_manifest_file
std::string GetAbsolutePath(const std::string &relative_path) const
bool validate_custom_collision_maps
bool require_custom_collision_write_support
bool require_water_fill_reserved_region
bool validate_water_fill_table