yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_coverage_model.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_DUNGEON_OBJECT_COVERAGE_MODEL_H_
2#define YAZE_APP_EDITOR_DUNGEON_OBJECT_COVERAGE_MODEL_H_
3
4#include <cstddef>
5#include <cstdint>
6#include <filesystem>
7#include <map>
8#include <optional>
9#include <string>
10#include <string_view>
11#include <utility>
12#include <vector>
13
14#include "absl/status/status.h"
15#include "absl/status/statusor.h"
16#include "core/hack_manifest.h"
19
20namespace yaze::editor {
21
22// Evidence states for one dungeon object ID. The names and meanings follow the
23// "Object coverage checklist" in
24// docs/internal/plans/dungeon-0.8.0-issue-test-backlog-2026-06-28.md.
26 kUntriaged, // Nobody has compared it against the game yet.
27 kReproduced, // Compared, and it draws wrong.
28 kFixedAwaitingProof, // A fix landed; it has not been re-checked.
29 kVerified, // Compared against the game and it matches.
30 kIntentionalPreviewLimit // Cannot match by design (HDMA, animation, ...).
31};
32
40
41// Stable key written to the evidence file, e.g. "fixed-awaiting-proof".
43// Short label for the UI, e.g. "Broken".
45std::optional<ObjectEvidenceState> ParseObjectEvidenceState(
46 std::string_view key);
47
50 std::string note;
51 int room_id = -1; // Room the verdict was made in, or -1.
52 std::string rom_sha1; // ROM the verdict was made against.
53 std::string updated_utc; // ISO-8601 UTC time of the last change.
54};
55
56// Per-object verdicts, saved as JSON:
57// {"version": 1, "objects": {"0x04C": {"state": "verified", ...}}}
59 public:
60 const ObjectEvidence* Find(int object_id) const;
61 ObjectEvidenceState StateOf(int object_id) const;
62 // An untriaged entry with no note is removed rather than stored.
63 void Set(int object_id, ObjectEvidence evidence);
64 const std::map<int, ObjectEvidence>& entries() const { return entries_; }
65
66 // Folder of game tilemap captures used by the automatic check, or empty.
67 const std::string& capture_dir() const { return capture_dir_; }
68 void set_capture_dir(std::string dir) { capture_dir_ = std::move(dir); }
69
70 std::string ToJson() const;
71 static absl::StatusOr<ObjectEvidenceStore> FromJson(std::string_view json);
72
73 // A missing file is an empty store, not an error.
74 static absl::StatusOr<ObjectEvidenceStore> LoadFromFile(
75 const std::filesystem::path& path);
76 // Writes a sibling temporary file and renames it over `path`.
77 absl::Status SaveToFile(const std::filesystem::path& path) const;
78
79 private:
80 std::map<int, ObjectEvidence> entries_;
81 std::string capture_dir_;
82};
83
84// One placed object in one room.
86 int room_id = -1;
87 size_t object_index = 0; // Index into Room::GetTileObjects().
88 int x = 0;
89 int y = 0;
90 int size = 0;
91 int layer = 0;
92};
93
94// Which rooms use which object IDs.
96 public:
97 void AddRoom(int room_id, const std::vector<zelda3::RoomObject>& objects);
98 // Occurrences in room order, or nullptr when the object is never placed.
99 const std::vector<ObjectOccurrence>* Find(int object_id) const;
100 int RoomCountFor(int object_id) const;
101 int rooms_scanned() const { return rooms_scanned_; }
102
103 private:
104 std::map<int, std::vector<ObjectOccurrence>> by_object_;
106};
107
108// A family the 0.8.0 backlog names as unproven, with what to look at.
110 const char* name;
111 const char* what_to_check;
112};
113
114const std::vector<ReleaseFocusGroup>& ReleaseFocusGroups();
115// Index into ReleaseFocusGroups(), or -1 when the object is in none.
116int ReleaseFocusGroupFor(int object_id);
117
118// Review order: release focus groups first, in backlog order, then every
119// other object by ID.
120std::vector<int> OrderObjectsForReview(const std::vector<int16_t>& object_ids);
121
122// The next untriaged object that is placed in at least one room, searching
123// `review_order` after `after_object_id` and wrapping. Returns nullopt when
124// every placed object has a verdict.
125std::optional<int> NextObjectToCheck(const std::vector<int>& review_order,
126 const ObjectEvidenceStore& evidence,
127 const ObjectUsageIndex& usage,
128 std::optional<int> after_object_id);
129
130// A folder written by scripts/agents/capture-game-room-tilemaps.py.
132 std::string rom_sha1;
133 std::vector<int> captured_rooms; // rooms with status "ok", ascending
134};
135
136// Reads <dir>/manifest.json.
137absl::StatusOr<GameCaptureManifest> LoadGameCaptureManifest(
138 const std::filesystem::path& dir);
139
140// <dir>/room_XXX.tilemap
141std::filesystem::path GameCaptureRoomPath(const std::filesystem::path& dir,
142 int room_id);
143
144// Automatic check result for one placement.
146 int object_id = -1;
147 int tiles_owned = 0;
149 uint16_t difference_bits = 0;
150};
151
152// Automatic check results, keyed by room and object index.
154 public:
155 void Clear();
156 void Record(int room_id, size_t object_index, int object_id,
157 const PlacementAutoResult& result);
158 void RecordRoom(bool exact) {
160 rooms_exact_ += exact ? 1 : 0;
161 }
162
163 const PlacementAutoResult* Find(int room_id, size_t object_index) const;
164
166 int placements_checked = 0; // placements with at least one owned tile
168 uint16_t difference_bits = 0;
170 std::vector<int> rooms_checked; // ascending, unique
171 };
172 // Null when no placement of the object was checked.
173 const ObjectSummary* Summary(int object_id) const;
174 const std::map<int, ObjectSummary>& summaries() const { return summaries_; }
175
176 int rooms_compared() const { return rooms_compared_; }
177 int rooms_exact() const { return rooms_exact_; }
178
179 private:
180 std::map<std::pair<int, size_t>, PlacementAutoResult> placements_;
181 std::map<int, ObjectSummary> summaries_;
184};
185
186// Verdicts the automatic check supports for objects nobody has judged yet:
187// kVerified when every checked placement matches, kReproduced when any
188// differs. Objects that already have a verdict are never included.
189std::map<int, ObjectEvidence> ProposeAutomaticVerdicts(
190 const ObjectAutoCheckResults& results, const ObjectEvidenceStore& evidence,
191 const std::string& rom_sha1);
192
193// "0x04C" for type 1, "0x12D" for type 2, "0xFD6" for type 3.
194std::string FormatObjectId(int object_id);
195
196// Lowercases letters and keeps only [a-z0-9_-], so a name taken from a
197// project or ROM can never walk out of the folder it is joined onto.
198std::string SafeEvidenceFileName(std::string_view name);
199
200// True when every component of `dir` is a plain name, i.e. the path contains
201// no ".." that could climb out of the folder the user picked.
202bool IsPlainCaptureDir(const std::filesystem::path& dir);
203
204// An object whose draw code a ROM hack changed, so the game (and a capture of
205// it) may draw it differently from yaze's vanilla routine.
207 bool replaced = false; // The routine jumps straight into the hack's code.
208 uint32_t address = 0; // Jump target, or the first hooked address.
209 std::string module; // Hack module that hooks the routine, if known.
210};
211
212// Flags objects whose routine starts with a long jump into expanded code, or
213// whose routine bytes overlap a hook region from the hack manifest.
214std::map<int, ObjectCustomDrawCode> FindObjectsWithCustomDrawCode(
215 const std::vector<zelda3::ObjectDrawCode>& draw_code,
216 const std::vector<core::ProtectedRegion>& hooks);
217
218} // namespace yaze::editor
219
220#endif // YAZE_APP_EDITOR_DUNGEON_OBJECT_COVERAGE_MODEL_H_
void Record(int room_id, size_t object_index, int object_id, const PlacementAutoResult &result)
const PlacementAutoResult * Find(int room_id, size_t object_index) const
const std::map< int, ObjectSummary > & summaries() const
std::map< std::pair< int, size_t >, PlacementAutoResult > placements_
std::map< int, ObjectSummary > summaries_
const ObjectSummary * Summary(int object_id) const
const std::string & capture_dir() const
const ObjectEvidence * Find(int object_id) const
absl::Status SaveToFile(const std::filesystem::path &path) const
static absl::StatusOr< ObjectEvidenceStore > FromJson(std::string_view json)
const std::map< int, ObjectEvidence > & entries() const
ObjectEvidenceState StateOf(int object_id) const
static absl::StatusOr< ObjectEvidenceStore > LoadFromFile(const std::filesystem::path &path)
std::map< int, ObjectEvidence > entries_
void Set(int object_id, ObjectEvidence evidence)
int RoomCountFor(int object_id) const
const std::vector< ObjectOccurrence > * Find(int object_id) const
void AddRoom(int room_id, const std::vector< zelda3::RoomObject > &objects)
std::map< int, std::vector< ObjectOccurrence > > by_object_
Editors are the view controllers for the application.
std::optional< ObjectEvidenceState > ParseObjectEvidenceState(std::string_view key)
int ReleaseFocusGroupFor(int object_id)
absl::StatusOr< GameCaptureManifest > LoadGameCaptureManifest(const std::filesystem::path &dir)
const char * ObjectEvidenceStateLabel(ObjectEvidenceState state)
std::filesystem::path GameCaptureRoomPath(const std::filesystem::path &dir, int room_id)
bool IsPlainCaptureDir(const std::filesystem::path &dir)
const char * ObjectEvidenceStateKey(ObjectEvidenceState state)
std::string FormatObjectId(int object_id)
const std::vector< ReleaseFocusGroup > & ReleaseFocusGroups()
std::map< int, ObjectEvidence > ProposeAutomaticVerdicts(const ObjectAutoCheckResults &results, const ObjectEvidenceStore &evidence, const std::string &rom_sha1)
std::map< int, ObjectCustomDrawCode > FindObjectsWithCustomDrawCode(const std::vector< zelda3::ObjectDrawCode > &draw_code, const std::vector< core::ProtectedRegion > &hooks)
std::vector< int > OrderObjectsForReview(const std::vector< int16_t > &object_ids)
std::string SafeEvidenceFileName(std::string_view name)
std::optional< int > NextObjectToCheck(const std::vector< int > &review_order, const ObjectEvidenceStore &evidence, const ObjectUsageIndex &usage, std::optional< int > after_object_id)
constexpr ObjectEvidenceState kAllObjectEvidenceStates[]