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"
7
8namespace yaze {
9namespace cli {
10namespace handlers {
11
13 Rom* rom, const resources::ArgumentParser& parser,
14 resources::OutputFormatter& formatter) {
15 auto address_str = parser.GetString("address").value();
16 auto length = parser.GetInt("length").value_or(16);
17 std::string format = "both";
18 if (auto data_format = parser.GetString("data-format");
19 data_format.has_value()) {
20 format = *data_format;
21 } else if (auto fmt = parser.GetString("format"); fmt.has_value()) {
22 if (*fmt == "hex" || *fmt == "ascii" || *fmt == "both" ||
23 *fmt == "binary") {
24 format = *fmt;
25 }
26 }
27
28 // Parse address
29 uint32_t address;
30 try {
31 size_t pos;
32 address = std::stoul(address_str, &pos, 16);
33 if (pos != address_str.size()) {
34 return absl::InvalidArgumentError("Invalid hex address format");
35 }
36 } catch (const std::exception& e) {
37 return absl::InvalidArgumentError(absl::StrFormat(
38 "Failed to parse address '%s': %s", address_str, e.what()));
39 }
40
41 // Validate range
42 if (address + length > rom->size()) {
43 return absl::OutOfRangeError(absl::StrFormat(
44 "Read beyond ROM: 0x%X+%d > %zu", address, length, rom->size()));
45 }
46
47 // Read and format data
48 const uint8_t* data = rom->data() + address;
49
50 formatter.BeginObject("Hex Read");
51 formatter.AddHexField("address", address, 6);
52 formatter.AddField("length", length);
53 formatter.AddField("format", format);
54
55 bool include_hex =
56 (format == "hex" || format == "both" || format == "binary");
57 bool include_ascii = (format == "ascii" || format == "both");
58
59 if (include_hex) {
60 std::string hex_data;
61 for (int i = 0; i < length; ++i) {
62 absl::StrAppendFormat(&hex_data, "%02X", data[i]);
63 if (i < length - 1)
64 hex_data += " ";
65 }
66 formatter.AddField("hex_data", hex_data);
67 }
68
69 if (include_ascii) {
70 std::string ascii_data;
71 for (int i = 0; i < length; ++i) {
72 char c = static_cast<char>(data[i]);
73 ascii_data += (std::isprint(static_cast<unsigned char>(c)) ? c : '.');
74 }
75 formatter.AddField("ascii_data", ascii_data);
76 }
77 formatter.EndObject();
78
79 return absl::OkStatus();
80}
81
83 Rom* rom, const resources::ArgumentParser& parser,
84 resources::OutputFormatter& formatter) {
85 auto address_str = parser.GetString("address").value();
86 auto data_str = parser.GetString("data").value();
87
88 // Parse address
89 uint32_t address;
90 try {
91 size_t pos;
92 address = std::stoul(address_str, &pos, 16);
93 if (pos != address_str.size()) {
94 return absl::InvalidArgumentError("Invalid hex address format");
95 }
96 } catch (const std::exception& e) {
97 return absl::InvalidArgumentError(absl::StrFormat(
98 "Failed to parse address '%s': %s", address_str, e.what()));
99 }
100
101 // Parse data bytes
102 std::vector<std::string> byte_strs = absl::StrSplit(data_str, ' ');
103 std::vector<uint8_t> bytes;
104
105 for (const auto& byte_str : byte_strs) {
106 if (byte_str.empty())
107 continue;
108 try {
109 uint8_t value = static_cast<uint8_t>(std::stoul(byte_str, nullptr, 16));
110 bytes.push_back(value);
111 } catch (const std::exception& e) {
112 return absl::InvalidArgumentError(
113 absl::StrFormat("Invalid byte '%s': %s", byte_str, e.what()));
114 }
115 }
116
117 // Validate range
118 if (address + bytes.size() > rom->size()) {
119 return absl::OutOfRangeError(
120 absl::StrFormat("Write beyond ROM: 0x%X+%zu > %zu", address,
121 bytes.size(), rom->size()));
122 }
123
124 // Write data
125 if (auto status = rom->CheckLayoutWritable(); !status.ok()) {
126 return status;
127 }
128 for (size_t i = 0; i < bytes.size(); ++i) {
129 if (auto status = rom->WriteByte(address + i, bytes[i]); !status.ok()) {
130 return status;
131 }
132 }
133
134 // Format written data
135 std::string hex_data;
136 for (size_t i = 0; i < bytes.size(); ++i) {
137 absl::StrAppendFormat(&hex_data, "%02X", bytes[i]);
138 if (i < bytes.size() - 1)
139 hex_data += " ";
140 }
141
142 formatter.BeginObject("Hex Write");
143 formatter.AddHexField("address", address, 6);
144 formatter.AddField("bytes_written", static_cast<int>(bytes.size()));
145 formatter.AddField("hex_data", hex_data);
146 formatter.EndObject();
147
148 return absl::OkStatus();
149}
150
152 Rom* rom, const resources::ArgumentParser& parser,
153 resources::OutputFormatter& formatter) {
154 auto pattern_str = parser.GetString("pattern").value();
155 auto start_str = parser.GetString("start").value_or("0x000000");
156 auto end_str = parser.GetString("end").value_or(
157 absl::StrFormat("0x%06X", static_cast<int>(rom->size())));
158
159 // Parse addresses
160 uint32_t start_address, end_address;
161 try {
162 start_address = std::stoul(start_str, nullptr, 16);
163 end_address = std::stoul(end_str, nullptr, 16);
164 } catch (const std::exception& e) {
165 return absl::InvalidArgumentError(
166 absl::StrFormat("Invalid address format: %s", e.what()));
167 }
168
169 // Parse pattern
170 std::vector<std::string> byte_strs = absl::StrSplit(pattern_str, ' ');
171 std::vector<std::pair<uint8_t, bool>> pattern; // (value, is_wildcard)
172
173 for (const auto& byte_str : byte_strs) {
174 if (byte_str.empty())
175 continue;
176 if (byte_str == "??" || byte_str == "**") {
177 pattern.push_back({0, true}); // Wildcard
178 } else {
179 try {
180 uint8_t value = static_cast<uint8_t>(std::stoul(byte_str, nullptr, 16));
181 pattern.push_back({value, false});
182 } catch (const std::exception& e) {
183 return absl::InvalidArgumentError(absl::StrFormat(
184 "Invalid pattern byte '%s': %s", byte_str, e.what()));
185 }
186 }
187 }
188
189 if (pattern.empty()) {
190 return absl::InvalidArgumentError("Empty pattern");
191 }
192
193 // Search for pattern
194 std::vector<uint32_t> matches;
195 const uint8_t* rom_data = rom->data();
196
197 for (uint32_t i = start_address; i <= end_address - pattern.size(); ++i) {
198 bool match = true;
199 for (size_t j = 0; j < pattern.size(); ++j) {
200 if (!pattern[j].second && // If not wildcard
201 rom_data[i + j] != pattern[j].first) {
202 match = false;
203 break;
204 }
205 }
206 if (match) {
207 matches.push_back(i);
208 }
209 }
210
211 formatter.BeginObject("Hex Search Results");
212 formatter.AddField("pattern", pattern_str);
213 formatter.AddHexField("start_address", start_address, 6);
214 formatter.AddHexField("end_address", end_address, 6);
215 formatter.AddField("matches_found", static_cast<int>(matches.size()));
216
217 formatter.BeginArray("matches");
218 for (uint32_t match : matches) {
219 formatter.AddArrayItem(absl::StrFormat("0x%06X", match));
220 }
221 formatter.EndArray();
222 formatter.EndObject();
223
224 return absl::OkStatus();
225}
226
227} // namespace handlers
228} // namespace cli
229} // 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
absl::Status CheckLayoutWritable() const
Definition rom.cc:429
absl::Status WriteByte(int addr, uint8_t value)
Definition rom.cc:853
auto data() const
Definition rom.h:201
auto size() const
Definition rom.h:200
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.