yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
message_data.cc
Go to the documentation of this file.
1#include "message_data.h"
2
3#include <optional>
4#include <string>
5
6#include "app/snes.h"
7#include "util/hex.h"
8#include "util/log.h"
9
10namespace yaze {
11namespace editor {
12
13uint8_t FindMatchingCharacter(char value) {
14 for (const auto [key, char_value] : CharEncoder) {
15 if (value == char_value) {
16 return key;
17 }
18 }
19 return 0xFF;
20}
21
22int8_t FindDictionaryEntry(uint8_t value) {
23 if (value < DICTOFF || value == 0xFF) {
24 return -1;
25 }
26 return value - DICTOFF;
27}
28
29std::optional<TextElement> FindMatchingCommand(uint8_t b) {
30 for (const auto& text_element : TextCommands) {
31 if (text_element.ID == b) {
32 return text_element;
33 }
34 }
35 return std::nullopt;
36}
37
38std::optional<TextElement> FindMatchingSpecial(uint8_t value) {
39 auto it = std::ranges::find_if(SpecialChars,
40 [value](const TextElement& text_element) {
41 return text_element.ID == value;
42 });
43 if (it != SpecialChars.end()) {
44 return *it;
45 }
46 return std::nullopt;
47}
48
49ParsedElement FindMatchingElement(const std::string& str) {
50 std::smatch match;
51 std::vector<TextElement> commands_and_chars = TextCommands;
52 commands_and_chars.insert(commands_and_chars.end(), SpecialChars.begin(),
53 SpecialChars.end());
54 for (auto& text_element : commands_and_chars) {
55 match = text_element.MatchMe(str);
56 if (match.size() > 0) {
57 if (text_element.HasArgument) {
58 std::string arg = match[1].str().substr(1);
59 return ParsedElement(text_element, std::stoi(arg, nullptr, 16));
60 } else {
61 return ParsedElement(text_element, 0);
62 }
63 }
64 }
65
66 const auto dictionary_element =
67 TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
68
69 match = dictionary_element.MatchMe(str);
70 if (match.size() > 0) {
71 return ParsedElement(dictionary_element,
72 DICTOFF + std::stoi(match[1].str(), nullptr, 16));
73 }
74 return ParsedElement();
75}
76
77std::string ParseTextDataByte(uint8_t value) {
78 if (CharEncoder.contains(value)) {
79 char c = CharEncoder.at(value);
80 std::string str = "";
81 str.push_back(c);
82 return str;
83 }
84
85 // Check for command.
86 if (auto text_element = FindMatchingCommand(value);
87 text_element != std::nullopt) {
88 return text_element->GenericToken;
89 }
90
91 // Check for special characters.
92 if (auto special_element = FindMatchingSpecial(value);
93 special_element != std::nullopt) {
94 return special_element->GenericToken;
95 }
96
97 // Check for dictionary.
98 int8_t dictionary = FindDictionaryEntry(value);
99 if (dictionary >= 0) {
100 return absl::StrFormat("[%s:%02X]", DICTIONARYTOKEN,
101 static_cast<unsigned char>(dictionary));
102 }
103
104 return "";
105}
106
107std::vector<uint8_t> ParseMessageToData(std::string str) {
108 std::vector<uint8_t> bytes;
109 std::string temp_string = std::move(str);
110 int pos = 0;
111 while (pos < temp_string.size()) {
112 // Get next text fragment.
113 if (temp_string[pos] == '[') {
114 int next = temp_string.find(']', pos);
115 if (next == -1) {
116 break;
117 }
118
119 ParsedElement parsedElement =
120 FindMatchingElement(temp_string.substr(pos, next - pos + 1));
121
122 const auto dictionary_element =
123 TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary");
124
125 if (!parsedElement.Active) {
126 util::logf("Error parsing message: %s", temp_string);
127 break;
128 } else if (parsedElement.Parent == dictionary_element) {
129 bytes.push_back(parsedElement.Value);
130 } else {
131 bytes.push_back(parsedElement.Parent.ID);
132
133 if (parsedElement.Parent.HasArgument) {
134 bytes.push_back(parsedElement.Value);
135 }
136 }
137
138 pos = next + 1;
139 continue;
140 } else {
141 uint8_t bb = FindMatchingCharacter(temp_string[pos++]);
142
143 if (bb != 0xFF) {
144 bytes.push_back(bb);
145 }
146 }
147 }
148
149 return bytes;
150}
151
152std::vector<DictionaryEntry> BuildDictionaryEntries(Rom* rom) {
153 std::vector<DictionaryEntry> AllDictionaries;
154 for (int i = 0; i < kNumDictionaryEntries; i++) {
155 std::vector<uint8_t> bytes;
156 std::stringstream stringBuilder;
157
158 int address = SnesToPc(
159 kTextData + (rom->data()[kPointersDictionaries + (i * 2) + 1] << 8) +
160 rom->data()[kPointersDictionaries + (i * 2)]);
161
162 int temppush_backress =
164 (rom->data()[kPointersDictionaries + ((i + 1) * 2) + 1] << 8) +
165 rom->data()[kPointersDictionaries + ((i + 1) * 2)]);
166
167 while (address < temppush_backress) {
168 uint8_t uint8_tDictionary = rom->data()[address++];
169 bytes.push_back(uint8_tDictionary);
170 stringBuilder << ParseTextDataByte(uint8_tDictionary);
171 }
172
173 AllDictionaries.push_back(DictionaryEntry{(uint8_t)i, stringBuilder.str()});
174 }
175
176 std::ranges::sort(AllDictionaries,
177 [](const DictionaryEntry& a, const DictionaryEntry& b) {
178 return a.Contents.size() > b.Contents.size();
179 });
180
181 return AllDictionaries;
182}
183
184std::string ReplaceAllDictionaryWords(std::string str,
185 const std::vector<DictionaryEntry>& dictionary) {
186 std::string temp = std::move(str);
187 for (const auto& entry : dictionary) {
188 if (entry.ContainedInString(temp)) {
189 temp = entry.ReplaceInstancesOfIn(temp);
190 }
191 }
192 return temp;
193}
194
196 uint8_t value, const std::vector<DictionaryEntry>& dictionary) {
197 for (const auto& entry : dictionary) {
198 if (entry.ID + DICTOFF == value) {
199 return entry;
200 }
201 }
202 return DictionaryEntry();
203}
204
205absl::StatusOr<MessageData> ParseSingleMessage(
206 const std::vector<uint8_t>& rom_data, int* current_pos) {
207 MessageData message_data;
208 int pos = *current_pos;
209 uint8_t current_byte;
210 std::vector<uint8_t> temp_bytes_raw;
211 std::vector<uint8_t> temp_bytes_parsed;
212 std::string current_message_raw;
213 std::string current_message_parsed;
214
215 // Read the message data
216 while (true) {
217 current_byte = rom_data[pos++];
218
219 if (current_byte == kMessageTerminator) {
220 message_data.ID = message_data.ID + 1;
221 message_data.Address = pos;
222 message_data.RawString = current_message_raw;
223 message_data.Data = temp_bytes_raw;
224 message_data.DataParsed = temp_bytes_parsed;
225 message_data.ContentsParsed = current_message_parsed;
226
227 temp_bytes_raw.clear();
228 temp_bytes_parsed.clear();
229 current_message_raw.clear();
230 current_message_parsed.clear();
231
232 break;
233 } else if (current_byte == 0xFF) {
234 break;
235 }
236
237 temp_bytes_raw.push_back(current_byte);
238
239 // Check for command.
240 auto text_element = FindMatchingCommand(current_byte);
241 if (text_element != std::nullopt) {
242 current_message_raw.append(text_element->GetParamToken());
243 current_message_parsed.append(text_element->GetParamToken());
244 temp_bytes_parsed.push_back(current_byte);
245 continue;
246 }
247
248 // Check for dictionary.
249 int8_t dictionary = FindDictionaryEntry(current_byte);
250 if (dictionary >= 0) {
251 current_message_raw.append("[");
252 current_message_raw.append(DICTIONARYTOKEN);
253 current_message_raw.append(":");
254 current_message_raw.append(util::HexWord(static_cast<unsigned char>(dictionary)));
255 current_message_raw.append("]");
256
257 auto mutable_rom_data = const_cast<uint8_t*>(rom_data.data());
258 uint32_t address = Get24LocalFromPC(
259 mutable_rom_data, kPointersDictionaries + (dictionary * 2));
260 uint32_t address_end = Get24LocalFromPC(
261 mutable_rom_data, kPointersDictionaries + ((dictionary + 1) * 2));
262
263 for (uint32_t i = address; i < address_end; i++) {
264 temp_bytes_parsed.push_back(rom_data[i]);
265 current_message_parsed.append(ParseTextDataByte(rom_data[i]));
266 }
267
268 continue;
269 }
270
271 // Everything else.
272 if (CharEncoder.contains(current_byte)) {
273 std::string str = "";
274 str.push_back(CharEncoder.at(current_byte));
275 current_message_raw.append(str);
276 current_message_parsed.append(str);
277 temp_bytes_parsed.push_back(current_byte);
278 }
279 }
280
281 *current_pos = pos;
282 return message_data;
283}
284
285std::vector<std::string> ParseMessageData(
286 std::vector<MessageData>& message_data,
287 const std::vector<DictionaryEntry>& dictionary_entries) {
288 std::vector<std::string> parsed_messages;
289
290 for (auto& message : message_data) {
291 std::string parsed_message = "";
292 // Use index-based loop to properly skip argument bytes
293 for (size_t pos = 0; pos < message.Data.size(); ++pos) {
294 uint8_t byte = message.Data[pos];
295
296 // Check for text commands first (they may have arguments to skip)
297 auto text_element = FindMatchingCommand(byte);
298 if (text_element != std::nullopt) {
299 // Add newline for certain commands
300 if (text_element->ID == kScrollVertical ||
301 text_element->ID == kLine2 || text_element->ID == kLine3) {
302 parsed_message.append("\n");
303 }
304 // If command has an argument, get it from next byte and skip it
305 if (text_element->HasArgument && pos + 1 < message.Data.size()) {
306 uint8_t arg_byte = message.Data[pos + 1];
307 parsed_message.append(text_element->GetParamToken(arg_byte));
308 pos++; // Skip the argument byte
309 } else {
310 parsed_message.append(text_element->GetParamToken());
311 }
312 continue; // Move to next byte
313 }
314
315 // Check for special characters
316 auto special_element = FindMatchingSpecial(byte);
317 if (special_element != std::nullopt) {
318 parsed_message.append(special_element->GetParamToken());
319 continue;
320 }
321
322 // Check for dictionary entries
323 if (byte >= DICTOFF && byte < (DICTOFF + 97)) {
324 DictionaryEntry dic_entry;
325 for (const auto& entry : dictionary_entries) {
326 if (entry.ID == byte - DICTOFF) {
327 dic_entry = entry;
328 break;
329 }
330 }
331 parsed_message.append(dic_entry.Contents);
332 continue;
333 }
334
335 // Finally check for regular characters
336 if (CharEncoder.contains(byte)) {
337 parsed_message.push_back(CharEncoder.at(byte));
338 }
339 }
340 parsed_messages.push_back(parsed_message);
341 }
342
343 return parsed_messages;
344}
345
346std::vector<MessageData> ReadAllTextData(uint8_t* rom, int pos) {
347 std::vector<MessageData> list_of_texts;
348 int message_id = 0;
349
350 std::vector<uint8_t> raw_message;
351 std::vector<uint8_t> parsed_message;
352 std::string current_raw_message;
353 std::string current_parsed_message;
354
355 uint8_t current_byte = 0;
356 while (current_byte != 0xFF) {
357 current_byte = rom[pos++];
358 if (current_byte == kMessageTerminator) {
359 list_of_texts.push_back(
360 MessageData(message_id++, pos, current_raw_message, raw_message,
361 current_parsed_message, parsed_message));
362 raw_message.clear();
363 parsed_message.clear();
364 current_raw_message.clear();
365 current_parsed_message.clear();
366 continue;
367 } else if (current_byte == 0xFF) {
368 break;
369 }
370
371 raw_message.push_back(current_byte);
372
373 auto text_element = FindMatchingCommand(current_byte);
374 if (text_element != std::nullopt) {
375 parsed_message.push_back(current_byte);
376 if (text_element->HasArgument) {
377 current_byte = rom[pos++];
378 raw_message.push_back(current_byte);
379 parsed_message.push_back(current_byte);
380 }
381
382 current_raw_message.append(text_element->GetParamToken(current_byte));
383 current_parsed_message.append(text_element->GetParamToken(current_byte));
384
385 if (text_element->Token == kBankToken) {
386 pos = kTextData2;
387 }
388
389 continue;
390 }
391
392 // Check for special characters.
393 auto special_element = FindMatchingSpecial(current_byte);
394 if (special_element != std::nullopt) {
395 current_raw_message.append(special_element->GetParamToken());
396 current_parsed_message.append(special_element->GetParamToken());
397 parsed_message.push_back(current_byte);
398 continue;
399 }
400
401 // Check for dictionary.
402 int8_t dictionary = FindDictionaryEntry(current_byte);
403 if (dictionary >= 0) {
404 current_raw_message.append(absl::StrFormat("[%s:%s]", DICTIONARYTOKEN,
405 util::HexByte(static_cast<unsigned char>(dictionary))));
406
407 uint32_t address =
408 Get24LocalFromPC(rom, kPointersDictionaries + (dictionary * 2));
409 uint32_t address_end =
410 Get24LocalFromPC(rom, kPointersDictionaries + ((dictionary + 1) * 2));
411
412 for (uint32_t i = address; i < address_end; i++) {
413 parsed_message.push_back(rom[i]);
414 current_parsed_message.append(ParseTextDataByte(rom[i]));
415 }
416
417 continue;
418 }
419
420 // Everything else.
421 if (CharEncoder.contains(current_byte)) {
422 std::string str = "";
423 str.push_back(CharEncoder.at(current_byte));
424 current_raw_message.append(str);
425 current_parsed_message.append(str);
426 parsed_message.push_back(current_byte);
427 }
428 }
429
430 return list_of_texts;
431}
432
433absl::Status LoadExpandedMessages(std::string& expanded_message_path,
434 std::vector<std::string>& parsed_messages,
435 std::vector<MessageData>& expanded_messages,
436 std::vector<DictionaryEntry>& dictionary) {
437 static Rom expanded_message_rom;
438 if (!expanded_message_rom.LoadFromFile(expanded_message_path, false).ok()) {
439 return absl::InternalError("Failed to load expanded message ROM");
440 }
441 expanded_messages = ReadAllTextData(expanded_message_rom.mutable_data(), 0);
442 auto parsed_expanded_messages =
443 ParseMessageData(expanded_messages, dictionary);
444 // Insert into parsed_messages
445 for (const auto& expanded_message : expanded_messages) {
446 parsed_messages.push_back(parsed_expanded_messages[expanded_message.ID]);
447 }
448 return absl::OkStatus();
449}
450
451} // namespace editor
452} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
auto mutable_data()
Definition rom.h:204
auto data() const
Definition rom.h:203
std::vector< MessageData > ReadAllTextData(uint8_t *rom, int pos)
uint8_t FindMatchingCharacter(char value)
const std::string kBankToken
DictionaryEntry FindRealDictionaryEntry(uint8_t value, const std::vector< DictionaryEntry > &dictionary)
const std::string DICTIONARYTOKEN
constexpr uint8_t kScrollVertical
std::string ParseTextDataByte(uint8_t value)
absl::Status LoadExpandedMessages(std::string &expanded_message_path, std::vector< std::string > &parsed_messages, std::vector< MessageData > &expanded_messages, std::vector< DictionaryEntry > &dictionary)
constexpr int kTextData
constexpr int kTextData2
std::string ReplaceAllDictionaryWords(std::string str, const std::vector< DictionaryEntry > &dictionary)
constexpr uint8_t kLine2
constexpr int kPointersDictionaries
constexpr int kNumDictionaryEntries
absl::StatusOr< MessageData > ParseSingleMessage(const std::vector< uint8_t > &rom_data, int *current_pos)
std::vector< std::string > ParseMessageData(std::vector< MessageData > &message_data, const std::vector< DictionaryEntry > &dictionary_entries)
std::optional< TextElement > FindMatchingSpecial(uint8_t value)
constexpr uint8_t kMessageTerminator
std::vector< DictionaryEntry > BuildDictionaryEntries(Rom *rom)
std::vector< uint8_t > ParseMessageToData(std::string str)
constexpr uint8_t DICTOFF
std::optional< TextElement > FindMatchingCommand(uint8_t b)
ParsedElement FindMatchingElement(const std::string &str)
constexpr uint8_t kLine3
int8_t FindDictionaryEntry(uint8_t value)
std::string HexWord(uint16_t word, HexStringParams params)
Definition hex.cc:41
std::string HexByte(uint8_t byte, HexStringParams params)
Definition hex.cc:30
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
Definition log.h:116
Main namespace for the application.
uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc=true)
Definition snes.h:30
uint32_t SnesToPc(uint32_t addr) noexcept
Definition snes.h:8
std::vector< uint8_t > Data
std::vector< uint8_t > DataParsed