9#include "absl/strings/str_format.h"
10#include "absl/strings/str_split.h"
15#include "yaze_config.h"
25namespace fs = std::filesystem;
26namespace atlas = zelda3::atlas;
33 : provider_(provider), previous_(provider.hack_manifest()) {}
44std::vector<std::string>
SplitList(
const std::string& value) {
45 std::vector<std::string> out;
46 for (absl::string_view part : absl::StrSplit(value,
',')) {
47 std::string item(part);
48 item.erase(0, item.find_first_not_of(
" \t"));
49 item.erase(item.find_last_not_of(
" \t") + 1);
57bool Exists(
const std::string& path) {
59 return !path.empty() && fs::is_regular_file(path, ec);
66 const std::string view = parser.
GetString(
"view").value_or(
"dungeon");
67 if (view ==
"overworld") {
68 return absl::UnimplementedError(
69 "--view overworld is not implemented yet (Atlas P2); use --view "
72 if (view !=
"dungeon") {
73 return absl::InvalidArgumentError(
"--view must be dungeon or overworld");
75 if (parser.
GetString(
"out").has_value()) {
76 return absl::UnimplementedError(
77 "PNG output (--out) is not implemented yet (Atlas P3); use --json "
81 if (!
json.has_value() ||
json->empty()) {
82 return absl::InvalidArgumentError(
"--json <path> is required");
84 if (
auto defaults = parser.
GetString(
"defaults");
85 defaults.has_value() && *defaults !=
"vanilla" && *defaults !=
"none") {
86 return absl::InvalidArgumentError(
"--defaults must be vanilla or none");
88 return absl::OkStatus();
95 if (rom !=
nullptr && !rom->
filename().empty()) {
107 if (rom ==
nullptr || !rom->
is_loaded()) {
108 return absl::FailedPreconditionError(
"ROM not loaded");
111 const std::string json_arg = *parser.
GetString(
"json");
115 invocation_context));
118 std::vector<std::string> annotation_files;
119 std::string defaults =
"vanilla";
120 std::vector<std::string> symbol_candidates;
122 const std::string project_arg = parser.
GetString(
"project").value_or(
"");
123 if (!project_arg.empty()) {
125 if (!fs::exists(project_arg, ec)) {
126 return absl::NotFoundError(
127 absl::StrFormat(
"--project path does not exist: %s", project_arg));
129 if (fs::is_directory(project_arg, ec) &&
130 fs::path(project_arg).extension() !=
".yazeproj") {
131 return absl::InvalidArgumentError(absl::StrFormat(
132 "--project %s is a folder; pass the .yaze project file",
140 status = project.
Open(project_arg);
141 }
catch (
const std::exception& error) {
142 return absl::InvalidArgumentError(absl::StrFormat(
143 "Cannot load project '%s': %s", project_arg, error.what()));
150 for (
const auto& file : annotation_files) {
152 return absl::NotFoundError(absl::StrFormat(
153 "--project %s: [atlas] annotations file %s does not exist",
158 annotation_files.empty()) {
159 return absl::FailedPreconditionError(absl::StrFormat(
160 "--project %s: no dungeon registry (Docs/Dev/Planning/"
161 "dungeons.json beside the code folder, hack manifest or project "
162 "file) and no [atlas] annotations; nothing project-specific would "
163 "load. Drop --project for a ROM-only Atlas",
170 symbol_candidates.push_back(
174 if (
auto extra = parser.
GetString(
"annotations"); extra.has_value()) {
175 for (
const auto& file : SplitList(*extra)) {
176 annotation_files.push_back(file);
179 if (
auto value = parser.
GetString(
"defaults"); value.has_value()) {
186 std::string symbols_path;
187 if (
auto symbols = parser.
GetString(
"symbols"); symbols.has_value()) {
188 if (!Exists(*symbols)) {
189 return absl::NotFoundError(
190 absl::StrFormat(
"--symbols file does not exist: %s", *symbols));
192 symbols_path = *symbols;
193 }
else if (!annotations.symbol_links.empty()) {
194 std::vector<std::string> candidates;
196 candidates.push_back(
197 fs::path(rom->
filename()).replace_extension(
".sym").string());
199 candidates.insert(candidates.end(), symbol_candidates.begin(),
200 symbol_candidates.end());
201 for (
const auto& candidate : candidates) {
202 if (Exists(candidate)) {
203 symbols_path = candidate;
218 if (
auto vanilla = parser.
GetString(
"vanilla"); vanilla.has_value()) {
231 invocation_context));
233 std::ofstream out(json_path, std::ios::binary | std::ios::trunc);
235 return absl::PermissionDeniedError(
236 absl::StrFormat(
"Cannot write %s", json_arg));
240 return absl::InternalError(absl::StrFormat(
"Write failed: %s", json_arg));
246 formatter.
AddField(
"view",
"dungeon");
247 formatter.
AddField(
"json", json_arg);
250 formatter.
AddField(
"project", project_arg);
256 formatter.
AddField(
"defaults", defaults);
260 : fs::path(symbols_path).filename().string());
263 :
"derived_from_entrances");
271 formatter.
AddField(
"areas",
static_cast<int>(model.
areas.size()));
272 formatter.
AddField(
"door_edges",
static_cast<int>(model.
edges.size()));
275 for (
const auto& warning : model.
warnings) {
279 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())
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status ExecuteWithContext(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter, const resources::CommandInvocationContext &invocation_context) override
Execute with immutable, invocation-scoped ROM path identity.
ScopedLabelManifestRestore(const ScopedLabelManifestRestore &)=delete
ScopedLabelManifestRestore(zelda3::ResourceLabelProvider &provider)
zelda3::ResourceLabelProvider & provider_
ScopedLabelManifestRestore & operator=(const ScopedLabelManifestRestore &)=delete
~ScopedLabelManifestRestore()
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)
Loads and queries the hack manifest JSON for yaze-ASM integration.
const ProjectRegistry & project_registry() const
Unified interface for accessing resource labels with project overrides.
#define YAZE_VERSION_STRING
#define ASSIGN_OR_RETURN(type_variable_name, expression)
bool Exists(const std::string &path)
std::vector< std::string > SplitList(const std::string &value)
absl::StatusOr< std::filesystem::path > ResolveStableArtifactPath(const std::filesystem::path &path)
absl::Status RejectArtifactRomAliases(absl::string_view option_name, const std::filesystem::path &artifact_path, const CommandInvocationContext &invocation_context)
absl::StatusOr< DungeonAtlasInput > CollectDungeonAtlasInput(Rom *rom, const DungeonAtlasCollectOptions &options)
DungeonAtlas BuildDungeonAtlas(const DungeonAtlasInput &input, const AtlasAnnotations &annotations)
absl::StatusOr< AtlasAnnotations > LoadAtlasAnnotations(const std::vector< std::string > &files, const std::string &defaults)
std::string DungeonAtlasToJson(const DungeonAtlas &atlas, const DungeonAtlasJsonContext &context, int indent)
constexpr char kDungeonAtlasJsonSchema[]
ResourceLabelProvider & GetResourceLabels()
Get the global ResourceLabelProvider instance.
#define RETURN_IF_ERROR(expr)
std::optional< std::filesystem::path > active_rom_path
std::optional< std::filesystem::path > source_rom_path
std::vector< DungeonEntry > dungeons
Modern project structure with comprehensive settings consolidation.
core::HackManifest hack_manifest
std::vector< std::string > AtlasAnnotationPaths() const
std::string AtlasSymbolsPath() const
std::string GetAbsolutePath(const std::string &relative_path) const
absl::Status Open(const std::string &project_path)
std::string symbols_filename
std::vector< SymbolLinkAnnotation > symbol_links
const core::ProjectRegistry * registry
const core::HackManifest * manifest
std::vector< DoorEdge > edges
std::vector< AtlasArea > areas
std::vector< std::string > annotation_files
std::vector< std::string > warnings
DungeonAtlasCounts counts