yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
collision_source_pairing.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <iterator>
5#include <optional>
6#include <string>
7#include <system_error>
8#include <vector>
9
10#include "absl/strings/str_format.h"
12#include "util/macro.h"
14
15namespace yaze::editor {
16namespace {
17
18namespace fs = std::filesystem;
19
20absl::StatusOr<std::optional<std::string>> ReadOptionalFile(
21 const fs::path& path) {
22 std::error_code ec;
23 if (!fs::exists(path, ec)) {
24 if (ec) {
25 return absl::FailedPreconditionError(absl::StrFormat(
26 "Cannot inspect %s: %s", path.string(), ec.message()));
27 }
28 return std::optional<std::string>();
29 }
30 std::ifstream in(path, std::ios::binary);
31 if (!in) {
32 return absl::FailedPreconditionError(
33 absl::StrFormat("Cannot read %s", path.string()));
34 }
35 return std::optional<std::string>(
36 std::string(std::istreambuf_iterator<char>(in), {}));
37}
38
39// The collision source that the ROM currently on disk would produce.
40absl::StatusOr<std::string> CollisionSourceOfRomBytes(
41 const std::string& rom_bytes) {
42 Rom disk_rom;
43 Rom::LoadOptions options; // Load as yaze does, without resource labels.
44 options.load_resource_labels = false;
46 std::vector<uint8_t>(rom_bytes.begin(), rom_bytes.end()), options));
48}
49
50} // namespace
51
52absl::Status ValidateRomCollisionSourceBinding(Rom* replacement_rom,
53 const fs::path& json_path) {
54 if (replacement_rom == nullptr || !replacement_rom->is_loaded()) {
55 return absl::FailedPreconditionError("No replacement ROM loaded");
56 }
57 ASSIGN_OR_RETURN(const auto source, ReadOptionalFile(json_path));
58 if (!source.has_value()) {
59 return absl::FailedPreconditionError(absl::StrFormat(
60 "Configured collision source %s is missing; restore it before "
61 "swapping the project ROM. The project binding was not changed.",
62 json_path.string()));
63 }
64 ASSIGN_OR_RETURN(const std::string replacement_source,
66 if (*source != replacement_source) {
67 return absl::FailedPreconditionError(absl::StrFormat(
68 "Replacement ROM %s does not match configured collision source %s. "
69 "Swap ROM does not publish collision changes. Select a ROM matching "
70 "this source. For collision edits, use Save ROM on the bound project "
71 "and then Build Project. The saved copy and project binding were kept.",
72 replacement_rom->filename(), json_path.string()));
73 }
74 return absl::OkStatus();
75}
76
77absl::Status SaveRomWithCollisionSource(Rom* rom, const fs::path& json_path) {
78 if (rom == nullptr || !rom->is_loaded() || rom->filename().empty()) {
79 return absl::FailedPreconditionError("No ROM file to save");
80 }
82 const fs::path rom_path(rom->filename());
83
84 ASSIGN_OR_RETURN(const std::string new_json,
86 ASSIGN_OR_RETURN(const auto disk_json, ReadOptionalFile(json_path));
87
88 // A byte-identical source needs no second file: save the ROM as usual.
89 if (disk_json.has_value() && *disk_json == new_json) {
90 Rom::SaveSettings settings;
91 return rom->SaveToFile(settings);
92 }
93
95 .subject = "ROM and custom collision source",
96 .published_file = "custom collision source"};
98 {rom_path, json_path}, labels));
99
100 // Read both files under the lock so no other writer can slip in between.
101 ASSIGN_OR_RETURN(const auto disk_rom, ReadOptionalFile(rom_path));
102 if (!disk_rom.has_value()) {
103 return absl::FailedPreconditionError(absl::StrFormat(
104 "Cannot pair a custom collision source with %s: the ROM file does "
105 "not exist yet",
106 rom_path.string()));
107 }
108 ASSIGN_OR_RETURN(const auto locked_json, ReadOptionalFile(json_path));
109 if (locked_json != disk_json) {
110 return absl::AbortedError(absl::StrFormat(
111 "%s changed while saving; nothing was written. Save again.",
112 json_path.string()));
113 }
114 if (locked_json.has_value()) {
115 ASSIGN_OR_RETURN(const std::string disk_source,
116 CollisionSourceOfRomBytes(*disk_rom));
117 if (*locked_json != disk_source) {
118 return absl::FailedPreconditionError(absl::StrFormat(
119 "%s does not match the custom collision in %s. It was changed "
120 "outside yaze, or the two were already out of step. Nothing was "
121 "written; reconcile them first (Oracle: "
122 "Scripts/Generate/validate_custom_collision_source.py).",
123 json_path.string(), rom_path.string()));
124 }
125 }
126
127 const std::vector<uint8_t>& bytes = rom->vector();
128 std::vector<core::SourceArtifactUpdate> updates;
129 updates.push_back(core::SourceArtifactUpdate{
130 .target = rom_path,
131 .before = *disk_rom,
132 .after = std::string(bytes.begin(), bytes.end())});
133 updates.push_back(core::SourceArtifactUpdate{
134 .target = json_path, .before = locked_json, .after = new_json});
136 *lock, std::move(updates), core::ComputeSourceArtifactSha256(*disk_rom)));
137 rom->set_dirty(false);
138 return absl::OkStatus();
139}
140
141} // namespace yaze::editor
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:49
absl::Status CheckLayoutWritable() const
Definition rom.cc:429
auto filename() const
Definition rom.h:207
void set_dirty(bool dirty)
Definition rom.h:189
const auto & vector() const
Definition rom.h:205
absl::Status SaveToFile(const SaveSettings &settings)
Definition rom.cc:648
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
Definition rom.cc:606
bool is_loaded() const
Definition rom.h:187
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
absl::Status PublishSourceArtifacts(const SourceArtifactPublicationLock &lock, std::vector< SourceArtifactUpdate > updates, std::string_view expected_primary_sha256, SourceArtifactReadbackValidator readback_validator)
std::string ComputeSourceArtifactSha256(std::string_view content)
absl::StatusOr< std::unique_ptr< SourceArtifactPublicationLock > > AcquireSourceArtifactPublicationLock(const std::vector< fs::path > &targets, const SourceArtifactPublisherLabels &labels)
absl::StatusOr< std::string > CollisionSourceOfRomBytes(const std::string &rom_bytes)
absl::StatusOr< std::optional< std::string > > ReadOptionalFile(const fs::path &path)
Editors are the view controllers for the application.
absl::Status SaveRomWithCollisionSource(Rom *rom, const fs::path &json_path)
absl::Status ValidateRomCollisionSourceBinding(Rom *replacement_rom, const fs::path &json_path)
absl::StatusOr< std::string > DumpCustomCollisionSourceFromRom(Rom *rom)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
bool load_resource_labels
Definition rom.h:66