yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
hex_commands.cc
Go to the documentation of this file.
2
3#include <cctype>
4
5#include "absl/strings/str_format.h"
6#include "absl/strings/str_split.h"
8
9namespace yaze {
10namespace cli {
11namespace handlers {
12
14 Rom* rom, const resources::ArgumentParser& parser,
15 resources::OutputFormatter& formatter) {
16 auto address_str = parser.GetString("address").value();
17 auto length = parser.GetInt("length").value_or(16);
18 std::string format = "both";
19 if (auto data_format = parser.GetString("data-format");
20 data_format.has_value()) {
21 format = *data_format;
22 } else if (auto fmt = parser.GetString("format"); fmt.has_value()) {
23 if (*fmt == "hex" || *fmt == "ascii" || *fmt == "both" ||
24 *fmt == "binary") {
25 format = *fmt;
26 }
27 }
28
29 // Parse address
30 uint32_t address;
31 try {
32 size_t pos;
33 address = std::stoul(address_str, &pos, 16);
34 if (pos != address_str.size()) {
35 return absl::InvalidArgumentError("Invalid hex address format");
36 }
37 } catch (const std::exception& e) {
38 return absl::InvalidArgumentError(absl::StrFormat(
39 "Failed to parse address '%s': %s", address_str, e.what()));
40 }
41
42 // Validate range
43 if (address + length > rom->size()) {
44 return absl::OutOfRangeError(absl::StrFormat(
45 "Read beyond ROM: 0x%X+%d > %zu", address, length, rom->size()));
46 }
47
48 // Read and format data
49 const uint8_t* data = rom->data() + address;
50
51 formatter.BeginObject("Hex Read");
52 formatter.AddHexField("address", address, 6);
53 formatter.AddField("length", length);
54 formatter.AddField("format", format);
55
56 bool include_hex =
57 (format == "hex" || format == "both" || format == "binary");
58 bool include_ascii = (format == "ascii" || format == "both");
59
60 if (include_hex) {
61 std::string hex_data;
62 for (int i = 0; i < length; ++i) {
63 absl::StrAppendFormat(&hex_data, "%02X", data[i]);
64 if (i < length - 1)
65 hex_data += " ";
66 }
67 formatter.AddField("hex_data", hex_data);
68 }
69
70 if (include_ascii) {
71 std::string ascii_data;
72 for (int i = 0; i < length; ++i) {
73 char c = static_cast<char>(data[i]);
74 ascii_data += (std::isprint(static_cast<unsigned char>(c)) ? c : '.');
75 }
76 formatter.AddField("ascii_data", ascii_data);
77 }
78 formatter.EndObject();
79
80 return absl::OkStatus();
81}
82
84 Rom* rom, const resources::ArgumentParser& parser,
85 resources::OutputFormatter& formatter) {
86 auto address_str = parser.GetString("address").value();
87 auto data_str = parser.GetString("data").value();
88
89 // Parse address
90 uint32_t address;
91 try {
92 size_t pos;
93 address = std::stoul(address_str, &pos, 16);
94 if (pos != address_str.size()) {
95 return absl::InvalidArgumentError("Invalid hex address format");
96 }
97 } catch (const std::exception& e) {
98 return absl::InvalidArgumentError(absl::StrFormat(
99 "Failed to parse address '%s': %s", address_str, e.what()));
100 }
101
102 // Parse data bytes
103 std::vector<std::string> byte_strs = absl::StrSplit(data_str, ' ');
104 std::vector<uint8_t> bytes;
105
106 for (const auto& byte_str : byte_strs) {
107 if (byte_str.empty())
108 continue;
109 try {
110 uint8_t value = static_cast<uint8_t>(std::stoul(byte_str, nullptr, 16));
111 bytes.push_back(value);
112 } catch (const std::exception& e) {
113 return absl::InvalidArgumentError(
114 absl::StrFormat("Invalid byte '%s': %s", byte_str, e.what()));
115 }
116 }
117
118 formatter.BeginObject("Hex Write");
119 auto status = ApplyRawRomWrite(rom, address, bytes, parser, formatter);
120 formatter.EndObject();
121 return status;
122}
123
125 Rom* rom, const resources::ArgumentParser& parser,
126 resources::OutputFormatter& formatter) {
127 auto pattern_str = parser.GetString("pattern").value();
128 auto start_str = parser.GetString("start").value_or("0x000000");
129 auto end_str = parser.GetString("end").value_or(
130 absl::StrFormat("0x%06X", static_cast<int>(rom->size())));
131
132 // Parse addresses
133 uint32_t start_address, end_address;
134 try {
135 start_address = std::stoul(start_str, nullptr, 16);
136 end_address = std::stoul(end_str, nullptr, 16);
137 } catch (const std::exception& e) {
138 return absl::InvalidArgumentError(
139 absl::StrFormat("Invalid address format: %s", e.what()));
140 }
141
142 // Parse pattern
143 std::vector<std::string> byte_strs = absl::StrSplit(pattern_str, ' ');
144 std::vector<std::pair<uint8_t, bool>> pattern; // (value, is_wildcard)
145
146 for (const auto& byte_str : byte_strs) {
147 if (byte_str.empty())
148 continue;
149 if (byte_str == "??" || byte_str == "**") {
150 pattern.push_back({0, true}); // Wildcard
151 } else {
152 try {
153 uint8_t value = static_cast<uint8_t>(std::stoul(byte_str, nullptr, 16));
154 pattern.push_back({value, false});
155 } catch (const std::exception& e) {
156 return absl::InvalidArgumentError(absl::StrFormat(
157 "Invalid pattern byte '%s': %s", byte_str, e.what()));
158 }
159 }
160 }
161
162 if (pattern.empty()) {
163 return absl::InvalidArgumentError("Empty pattern");
164 }
165
166 // Search for pattern
167 std::vector<uint32_t> matches;
168 const uint8_t* rom_data = rom->data();
169
170 for (uint32_t i = start_address; i <= end_address - pattern.size(); ++i) {
171 bool match = true;
172 for (size_t j = 0; j < pattern.size(); ++j) {
173 if (!pattern[j].second && // If not wildcard
174 rom_data[i + j] != pattern[j].first) {
175 match = false;
176 break;
177 }
178 }
179 if (match) {
180 matches.push_back(i);
181 }
182 }
183
184 formatter.BeginObject("Hex Search Results");
185 formatter.AddField("pattern", pattern_str);
186 formatter.AddHexField("start_address", start_address, 6);
187 formatter.AddHexField("end_address", end_address, 6);
188 formatter.AddField("matches_found", static_cast<int>(matches.size()));
189
190 formatter.BeginArray("matches");
191 for (uint32_t match : matches) {
192 formatter.AddArrayItem(absl::StrFormat("0x%06X", match));
193 }
194 formatter.EndArray();
195 formatter.EndObject();
196
197 return absl::OkStatus();
198}
199
200} // namespace handlers
201} // namespace cli
202} // 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:52
auto data() const
Definition rom.h:213
auto size() const
Definition rom.h:212
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
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::StatusOr< int > GetInt(const std::string &name) const
Parse an integer argument (supports hex with 0x prefix)
Utility for consistent output formatting across commands.
void BeginArray(const std::string &key)
Begin an array.
void AddArrayItem(const std::string &item)
Add an item to current array.
void BeginObject(const std::string &title="")
Start a JSON object or text section.
void EndObject()
End a JSON object or text section.
void AddField(const std::string &key, const std::string &value)
Add a key-value pair.
void AddHexField(const std::string &key, uint64_t value, int width=2)
Add a hex-formatted field.
absl::Status ApplyRawRomWrite(Rom *rom, uint32_t address, const std::vector< uint8_t > &bytes, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter)
Shared body of rom-write and hex-write.