yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld_allocation_commands.cc
Go to the documentation of this file.
2
3#include <map>
4#include <memory>
5#include <optional>
6#include <string>
7#include <utility>
8
9#include "absl/status/status.h"
10#include "absl/strings/numbers.h"
11#include "absl/strings/str_format.h"
13#include "core/hack_manifest.h"
14#include "core/project.h"
15#include "nlohmann/json.hpp"
16#include "rom/rom.h"
19
20namespace yaze::cli {
21
22namespace {
23
24// YazeProject::Open installs its manifest in the global label provider;
25// restore the previous binding when the command finishes.
27 public:
29 : provider_(provider), previous_(provider.hack_manifest()) {}
30 ~ScopedLabelManifestRestore() { provider_.SetHackManifest(previous_); }
33 delete;
34
35 private:
37 const core::HackManifest* previous_ = nullptr;
38};
39
40std::map<int, std::string> AreaNamesFromManifest(
41 const core::HackManifest& manifest) {
42 std::map<int, std::string> names;
43 const auto& registry = manifest.project_registry();
44 if (auto it = registry.all_resource_labels.find("overworld_map");
45 it != registry.all_resource_labels.end()) {
46 for (const auto& [key, label] : it->second) {
47 int id = 0;
48 if (absl::SimpleAtoi(key, &id) && id >= 0 &&
49 id < zelda3::kAllocationAreaCount && !label.empty()) {
50 names[id] = label;
51 }
52 }
53 }
54 for (const auto& area : registry.overworld_areas) {
55 if (area.area_id >= 0 && area.area_id < zelda3::kAllocationAreaCount &&
56 !area.name.empty()) {
57 names[area.area_id] = area.name;
58 }
59 }
60 return names;
61}
62
64 const core::HackManifest* manifest,
65 const emu::debug::SymbolProvider* symbols) {
67 if (manifest != nullptr && manifest->loaded()) {
68 annotator.ownership =
69 [manifest](
70 uint32_t snes) -> std::optional<std::pair<std::string, bool>> {
71 const auto ownership = manifest->ClassifyAddress(snes);
72 if (ownership == core::AddressOwnership::kVanillaSafe) {
73 return std::nullopt;
74 }
75 std::string note = core::AddressOwnershipToString(ownership);
76 if (ownership == core::AddressOwnership::kHookPatched) {
77 const uint32_t address = snes & 0x7FFFFF;
78 for (const auto& hook : manifest->protected_regions()) {
79 if (address >= hook.start && address < hook.end &&
80 !hook.module.empty()) {
81 note = absl::StrFormat("%s (%s)", note, hook.module);
82 break;
83 }
84 }
85 } else {
86 const auto& banks = manifest->owned_banks();
87 if (auto it = banks.find(static_cast<uint8_t>((snes >> 16) & 0x7F));
88 it != banks.end() && !it->second.ownership_note.empty()) {
89 note = absl::StrFormat("%s: %s", note, it->second.ownership_note);
90 }
91 }
92 const bool blocking =
96 return std::make_pair(note, blocking);
97 };
98 }
99 if (symbols != nullptr && symbols->HasSymbols()) {
100 // Name the nearest labelled symbol at or before the address, skipping
101 // asar's anonymous +/- labels (pos_N_M, neg_N_M).
102 annotator.symbol = [symbols](uint32_t snes) -> std::string {
103 auto anonymous = [](const std::string& name) {
104 return name.empty() || name[0] == ':' || name.rfind("pos_", 0) == 0 ||
105 name.rfind("neg_", 0) == 0;
106 };
107 const uint32_t start = snes >= 0x1000 ? snes - 0x1000 : 0;
108 auto candidates = symbols->GetSymbolsInRange(start, snes);
109 const emu::debug::Symbol* best = nullptr;
110 for (const auto& symbol : candidates) {
111 if (symbol.address <= snes && !anonymous(symbol.name) &&
112 (best == nullptr || symbol.address >= best->address)) {
113 best = &symbol;
114 }
115 }
116 if (best == nullptr) {
117 return std::string();
118 }
119 return best->address == snes
120 ? best->name
121 : absl::StrFormat("%s+$%X", best->name, snes - best->address);
122 };
123 }
124 return annotator;
125}
126
127} // namespace
128
130 Rom* rom, const resources::ArgumentParser& parser,
131 resources::OutputFormatter& formatter) {
132 if (rom == nullptr || !rom->is_loaded()) {
133 return absl::FailedPreconditionError("ROM not loaded");
134 }
135
136 // Declared before the label restore so the restore runs first on exit.
137 std::unique_ptr<project::YazeProject> opened_project;
138 std::optional<ScopedLabelManifestRestore> restore_labels;
139 const core::HackManifest* manifest = nullptr;
140 if (auto project_arg = parser.GetString("project"); project_arg.has_value()) {
141 opened_project = std::make_unique<project::YazeProject>();
142 restore_labels.emplace(zelda3::GetResourceLabels());
143 absl::Status status;
144 try {
145 status = opened_project->Open(*project_arg);
146 } catch (const std::exception& error) {
147 return absl::InvalidArgumentError(absl::StrFormat(
148 "Cannot load project '%s': %s", *project_arg, error.what()));
149 }
150 if (!status.ok()) {
151 return status;
152 }
153 manifest = &opened_project->hack_manifest;
154 } else if (project_ != nullptr) {
155 manifest = &project_->hack_manifest;
156 }
157
159 if (manifest != nullptr && manifest->loaded()) {
160 options.area_names = AreaNamesFromManifest(*manifest);
161 }
162
163 auto report_or = zelda3::BuildOverworldAllocationReport(rom, options);
164 if (!report_or.ok()) {
165 return report_or.status();
166 }
167 const zelda3::OverworldAllocationReport& report = *report_or;
168
169 std::optional<zelda3::TailMapExpansionCheck> tail_check;
170 if (parser.HasFlag("tail-check")) {
173 MakeAnnotator(manifest, symbol_provider_));
174 }
176 tail_check.has_value() ? &*tail_check : nullptr;
177
178 if (formatter.IsJson()) {
179 const auto json = nlohmann::ordered_json::parse(
181 for (auto it = json.begin(); it != json.end(); ++it) {
182 formatter.AddRawJsonField(it.key(), it.value().dump());
183 }
184 } else {
185 formatter.AddRawText(zelda3::FormatOverworldAllocationText(report, tail));
186 }
187 return absl::OkStatus();
188}
189
190} // namespace yaze::cli
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
bool is_loaded() const
Definition rom.h:187
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
ScopedLabelManifestRestore & operator=(const ScopedLabelManifestRestore &)=delete
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.
emu::debug::SymbolProvider * symbol_provider_
Utility for consistent output formatting across commands.
void AddRawText(const std::string &text)
Append preformatted text (for example an aligned table). Ignored in JSON mode.
void AddRawJsonField(const std::string &key, const std::string &json)
Add a field whose value is already-serialized JSON (numbers, nested arrays, null)....
bool IsJson() const
Check if using JSON format.
Loads and queries the hack manifest JSON for yaze-ASM integration.
AddressOwnership ClassifyAddress(uint32_t address) const
Classify a ROM address by ownership.
const std::unordered_map< uint8_t, OwnedBank > & owned_banks() const
const ProjectRegistry & project_registry() const
const std::vector< ProtectedRegion > & protected_regions() const
bool loaded() const
Check if the manifest has been loaded.
Provider for symbol (label) resolution in disassembly.
std::vector< Symbol > GetSymbolsInRange(uint32_t start, uint32_t end) const
Get all symbols in an address range.
bool HasSymbols() const
Check if any symbols are loaded.
Unified interface for accessing resource labels with project overrides.
zelda3::TailCheckAnnotator MakeAnnotator(const core::HackManifest *manifest, const emu::debug::SymbolProvider *symbols)
std::map< int, std::string > AreaNamesFromManifest(const core::HackManifest &manifest)
std::string AddressOwnershipToString(AddressOwnership ownership)
constexpr int kAllocationAreaCount
TailMapExpansionCheck CheckTailMapExpansion(const Rom &rom, const TailMapExpansionPlan &plan, const TailCheckAnnotator &annotator)
std::string FormatOverworldAllocationText(const OverworldAllocationReport &report, const TailMapExpansionCheck *tail_check)
std::string OverworldAllocationToJson(const OverworldAllocationReport &report, const TailMapExpansionCheck *tail_check)
absl::StatusOr< OverworldAllocationReport > BuildOverworldAllocationReport(Rom *rom, const OverworldAllocationOptions &options)
ResourceLabelProvider & GetResourceLabels()
Get the global ResourceLabelProvider instance.
Read-only map of overworld area IDs $00-$BF: which IDs are used, which per-area tables cover each ID,...
Information about a symbol (label, constant, or address)
core::HackManifest hack_manifest
Definition project.h:360
std::function< std::string(uint32_t)> symbol
Returns a symbol for a SNES address (empty when none).
std::function< std::optional< std::pair< std::string, bool > >(uint32_t)> ownership