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 == "tiletype")
46 else if (tok == "all")
48 }
49 return flags;
50}
51
52} // namespace
53
55 const resources::ArgumentParser& parser) {
56 RETURN_IF_ERROR(parser.RequireArgs({"room", "output"}));
57 if (parser.GetString("output")->empty()) {
58 return absl::InvalidArgumentError("--output cannot be empty");
59 }
60 if (const auto scale = parser.GetString("scale"); scale.has_value()) {
61 return app::service::ParseRenderScale(*scale).status();
62 }
63 return absl::OkStatus();
64}
65
67 Rom* rom, const resources::ArgumentParser& parser,
68 resources::OutputFormatter& formatter) {
69 resources::CommandInvocationContext invocation_context;
70 if (rom != nullptr && !rom->filename().empty()) {
71 invocation_context.source_rom_path = std::filesystem::path(rom->filename());
72 invocation_context.active_rom_path = std::filesystem::path(rom->filename());
73 }
74 return ExecuteWithContext(rom, parser, formatter, invocation_context);
75}
76
78 Rom* rom, const resources::ArgumentParser& parser,
80 const resources::CommandInvocationContext& invocation_context) {
82 // Parse --room (decimal or 0x-hex).
83 auto room_str = parser.GetString("room").value();
84 int room_id = 0;
85 try {
86 room_id = std::stoi(room_str, nullptr, 0);
87 } catch (...) {
88 return absl::InvalidArgumentError(
89 absl::StrFormat("Invalid room id: %s", room_str));
90 }
91
92 // Parse --output.
93 const std::string output_path = parser.GetString("output").value();
94 ASSIGN_OR_RETURN(const auto resolved_output,
97 "--output", resolved_output, invocation_context));
98
99 // Parse --overlays (optional, default none).
100 uint32_t overlay_flags = app::service::RenderOverlay::kNone;
101 if (auto ov = parser.GetString("overlays"); ov.has_value()) {
102 overlay_flags = ParseOverlays(ov.value());
103 }
104
105 // Parse --scale (optional, default 1.0, finite 0.25–8.0).
106 float scale = 1.0f;
107 if (auto sc = parser.GetString("scale"); sc.has_value()) {
109 }
110
111 if (!rom || !rom->is_loaded()) {
112 return absl::FailedPreconditionError("ROM not loaded");
113 }
114
115 // Load GameData (palette groups, tileset tables).
116 zelda3::GameData game_data;
117 auto gd_status = zelda3::LoadGameData(*rom, game_data);
118 if (!gd_status.ok()) {
119 return absl::InternalError(
120 absl::StrFormat("Failed to load game data: %s", gd_status.message()));
121 }
122
123 // Render.
124 app::service::RenderService render_service(rom, &game_data);
126 req.room_id = room_id;
127 req.overlay_flags = overlay_flags;
128 req.scale = scale;
129
130 auto result_or = render_service.RenderDungeonRoom(req);
131 if (!result_or.ok())
132 return result_or.status();
133 const auto& result = *result_or;
134
135 // Write PNG to disk.
136 // Recheck identity after rendering, before opening a truncating stream. Use
137 // the resolved path, not a caller-supplied parent symlink a second time.
139 "--output", resolved_output, invocation_context));
140 std::ofstream out(resolved_output, std::ios::binary);
141 if (!out) {
142 return absl::InternalError(
143 absl::StrFormat("Cannot open output file: %s", output_path));
144 }
145 out.write(reinterpret_cast<const char*>(result.png_data.data()),
146 static_cast<std::streamsize>(result.png_data.size()));
147 if (!out) {
148 return absl::InternalError(
149 absl::StrFormat("Write failed for: %s", output_path));
150 }
151 out.close();
152 if (!out) {
153 return absl::InternalError(
154 absl::StrFormat("Close failed for: %s", output_path));
155 }
156
157 formatter.AddField("room_id", absl::StrFormat("0x%02X", room_id));
158 formatter.AddField("output", output_path);
159 formatter.AddField("width", result.width);
160 formatter.AddField("height", result.height);
161 formatter.AddField("size_bytes", static_cast<int>(result.png_data.size()));
162 return absl::OkStatus();
163}
164
165} // namespace handlers
166} // namespace cli
167} // 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:49
auto filename() const
Definition rom.h:207
bool is_loaded() const
Definition rom.h:187
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