yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_render_commands.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <sstream>
5#include <string>
6#include <vector>
7
8#include "absl/strings/str_format.h"
11#include "rom/rom.h"
12#include "util/macro.h"
13#include "zelda3/game_data.h"
14
15namespace yaze {
16namespace cli {
17namespace handlers {
18
19namespace {
20
21// Parse overlays from a comma-separated string into RenderOverlay flags.
22uint32_t ParseOverlays(const std::string& s) {
23 if (s.empty())
26 std::istringstream ss(s);
27 std::string tok;
28 while (std::getline(ss, tok, ',')) {
29 tok.erase(0, tok.find_first_not_of(" \t"));
30 if (!tok.empty())
31 tok.erase(tok.find_last_not_of(" \t") + 1);
32 if (tok == "collision")
34 else if (tok == "sprites")
36 else if (tok == "objects")
38 else if (tok == "track")
40 else if (tok == "camera")
42 else if (tok == "grid")
44 else if (tok == "all")
46 }
47 return flags;
48}
49
50} // namespace
51
53 const resources::ArgumentParser& parser) {
54 RETURN_IF_ERROR(parser.RequireArgs({"room", "output"}));
55 if (parser.GetString("output")->empty()) {
56 return absl::InvalidArgumentError("--output cannot be empty");
57 }
58 if (const auto scale = parser.GetString("scale"); scale.has_value()) {
59 return app::service::ParseRenderScale(*scale).status();
60 }
61 return absl::OkStatus();
62}
63
65 Rom* rom, const resources::ArgumentParser& parser,
66 resources::OutputFormatter& formatter) {
67 resources::CommandInvocationContext invocation_context;
68 if (rom != nullptr && !rom->filename().empty()) {
69 invocation_context.source_rom_path = std::filesystem::path(rom->filename());
70 invocation_context.active_rom_path = std::filesystem::path(rom->filename());
71 }
72 return ExecuteWithContext(rom, parser, formatter, invocation_context);
73}
74
76 Rom* rom, const resources::ArgumentParser& parser,
78 const resources::CommandInvocationContext& invocation_context) {
80 // Parse --room (decimal or 0x-hex).
81 auto room_str = parser.GetString("room").value();
82 int room_id = 0;
83 try {
84 room_id = std::stoi(room_str, nullptr, 0);
85 } catch (...) {
86 return absl::InvalidArgumentError(
87 absl::StrFormat("Invalid room id: %s", room_str));
88 }
89
90 // Parse --output.
91 const std::string output_path = parser.GetString("output").value();
92 ASSIGN_OR_RETURN(const auto resolved_output,
95 "--output", resolved_output, invocation_context));
96
97 // Parse --overlays (optional, default none).
98 uint32_t overlay_flags = app::service::RenderOverlay::kNone;
99 if (auto ov = parser.GetString("overlays"); ov.has_value()) {
100 overlay_flags = ParseOverlays(ov.value());
101 }
102
103 // Parse --scale (optional, default 1.0, finite 0.25–8.0).
104 float scale = 1.0f;
105 if (auto sc = parser.GetString("scale"); sc.has_value()) {
107 }
108
109 if (!rom || !rom->is_loaded()) {
110 return absl::FailedPreconditionError("ROM not loaded");
111 }
112
113 // Load GameData (palette groups, tileset tables).
114 zelda3::GameData game_data;
115 auto gd_status = zelda3::LoadGameData(*rom, game_data);
116 if (!gd_status.ok()) {
117 return absl::InternalError(
118 absl::StrFormat("Failed to load game data: %s", gd_status.message()));
119 }
120
121 // Render.
122 app::service::RenderService render_service(rom, &game_data);
124 req.room_id = room_id;
125 req.overlay_flags = overlay_flags;
126 req.scale = scale;
127
128 auto result_or = render_service.RenderDungeonRoom(req);
129 if (!result_or.ok())
130 return result_or.status();
131 const auto& result = *result_or;
132
133 // Write PNG to disk.
134 // Recheck identity after rendering, before opening a truncating stream. Use
135 // the resolved path, not a caller-supplied parent symlink a second time.
137 "--output", resolved_output, invocation_context));
138 std::ofstream out(resolved_output, std::ios::binary);
139 if (!out) {
140 return absl::InternalError(
141 absl::StrFormat("Cannot open output file: %s", output_path));
142 }
143 out.write(reinterpret_cast<const char*>(result.png_data.data()),
144 static_cast<std::streamsize>(result.png_data.size()));
145 if (!out) {
146 return absl::InternalError(
147 absl::StrFormat("Write failed for: %s", output_path));
148 }
149 out.close();
150 if (!out) {
151 return absl::InternalError(
152 absl::StrFormat("Close failed for: %s", output_path));
153 }
154
155 formatter.AddField("room_id", absl::StrFormat("0x%02X", room_id));
156 formatter.AddField("output", output_path);
157 formatter.AddField("width", result.width);
158 formatter.AddField("height", result.height);
159 formatter.AddField("size_bytes", static_cast<int>(result.png_data.size()));
160 return absl::OkStatus();
161}
162
163} // namespace handlers
164} // namespace cli
165} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
auto filename() const
Definition rom.h:175
bool is_loaded() const
Definition rom.h:155
absl::StatusOr< RenderResult > RenderDungeonRoom(const RenderRequest &req)
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
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.
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)
absl::Status RequireArgs(const std::vector< std::string > &required) const
Validate that required arguments are present.
Utility for consistent output formatting across commands.
void AddField(const std::string &key, const std::string &value)
Add a key-value pair.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
absl::StatusOr< float > ParseRenderScale(absl::string_view 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::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
Definition game_data.cc:123
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::optional< std::filesystem::path > active_rom_path
std::optional< std::filesystem::path > source_rom_path