From dd430ad1b6d1212badcf0c24d2da195f26f2f8be Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 19 Aug 2024 11:36:30 -0400 Subject: [PATCH] decouple message data functions from message editor class --- src/app/editor/editor.cmake | 1 + src/app/editor/message/message_data.cc | 150 +++++++++++++++++++++++ src/app/editor/message/message_data.h | 91 +++++++++++++- src/app/editor/message/message_editor.cc | 147 +--------------------- src/app/editor/message/message_editor.h | 83 ------------- 5 files changed, 244 insertions(+), 228 deletions(-) create mode 100644 src/app/editor/message/message_data.cc diff --git a/src/app/editor/editor.cmake b/src/app/editor/editor.cmake index fd2f2011..af7b473a 100644 --- a/src/app/editor/editor.cmake +++ b/src/app/editor/editor.cmake @@ -6,6 +6,7 @@ set( app/editor/sprite/sprite_editor.cc app/editor/music/music_editor.cc app/editor/message/message_editor.cc + app/editor/message/message_data.cc app/editor/code/assembly_editor.cc app/editor/graphics/screen_editor.cc app/editor/graphics/graphics_editor.cc diff --git a/src/app/editor/message/message_data.cc b/src/app/editor/message/message_data.cc new file mode 100644 index 00000000..dc0d269a --- /dev/null +++ b/src/app/editor/message/message_data.cc @@ -0,0 +1,150 @@ +#include "message_data.h" + +#include "app/core/common.h" + +namespace yaze { +namespace app { +namespace editor { + +uint8_t FindMatchingCharacter(char value) { + for (const auto [key, char_value] : CharEncoder) { + if (value == char_value) { + return key; + } + } + return 0xFF; +} + +uint8_t FindDictionaryEntry(uint8_t value) { + if (value < DICTOFF || value == 0xFF) { + return -1; + } + return value - DICTOFF; +} + +TextElement FindMatchingCommand(uint8_t b) { + TextElement empty_element; + for (const auto& text_element : TextCommands) { + if (text_element.ID == b) { + return text_element; + } + } + return empty_element; +} + +TextElement FindMatchingSpecial(uint8_t value) { + auto it = std::find_if(SpecialChars.begin(), SpecialChars.end(), + [value](const TextElement& text_element) { + return text_element.ID == value; + }); + if (it != SpecialChars.end()) { + return *it; + } + + return TextElement(); +} + +ParsedElement FindMatchingElement(const std::string& str) { + std::smatch match; + for (auto& textElement : TextCommands) { + match = textElement.MatchMe(str); + if (match.size() > 0) { + if (textElement.HasArgument) { + return ParsedElement(textElement, + std::stoi(match[1].str(), nullptr, 16)); + } else { + return ParsedElement(textElement, 0); + } + } + } + + const auto dictionary_element = + TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary"); + + match = dictionary_element.MatchMe(str); + if (match.size() > 0) { + return ParsedElement(dictionary_element, + DICTOFF + std::stoi(match[1].str(), nullptr, 16)); + } + return ParsedElement(); +} + +std::string ParseTextDataByte(uint8_t value) { + if (CharEncoder.contains(value)) { + char c = CharEncoder.at(value); + std::string str = ""; + str.push_back(c); + return str; + } + + // Check for command. + TextElement textElement = FindMatchingCommand(value); + if (!textElement.Empty()) { + return textElement.GenericToken; + } + + // Check for special characters. + textElement = FindMatchingSpecial(value); + if (!textElement.Empty()) { + return textElement.GenericToken; + } + + // Check for dictionary. + int dictionary = FindDictionaryEntry(value); + if (dictionary >= 0) { + return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary); + } + + return ""; +} + +std::vector ParseMessageToData(std::string str) { + std::vector bytes; + std::string temp_string = str; + int pos = 0; + + while (pos < temp_string.size()) { + // Get next text fragment. + if (temp_string[pos] == '[') { + int next = temp_string.find(']', pos); + if (next == -1) { + break; + } + + ParsedElement parsedElement = + FindMatchingElement(temp_string.substr(pos, next - pos + 1)); + + const auto dictionary_element = + TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary"); + + if (!parsedElement.Active) { + core::logf("Error parsing message: %s", temp_string); + break; + } else if (parsedElement.Parent == dictionary_element) { + bytes.push_back(parsedElement.Value); + } else { + bytes.push_back(parsedElement.Parent.ID); + + if (parsedElement.Parent.HasArgument) { + bytes.push_back(parsedElement.Value); + } + } + + pos = next + 1; + continue; + } else { + uint8_t bb = FindMatchingCharacter(temp_string[pos++]); + + if (bb != 0xFF) { + core::logf("Error parsing message: %s", temp_string); + bytes.push_back(bb); + } + } + } + + return bytes; +} + +} // namespace editor +} // namespace app +} // namespace yaze \ No newline at end of file diff --git a/src/app/editor/message/message_data.h b/src/app/editor/message/message_data.h index 678f4d5f..922cb545 100644 --- a/src/app/editor/message/message_data.h +++ b/src/app/editor/message/message_data.h @@ -1,16 +1,46 @@ #ifndef YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H #define YAZE_APP_EDITOR_MESSAGE_MESSAGE_DATA_H +#include +#include #include #include #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" namespace yaze { namespace app { namespace editor { -const uint8_t MESSAGETERMINATOR = 0x7F; +const uint8_t kMessageTerminator = 0x7F; +const std::string BANKToken = "BANK"; +const std::string DICTIONARYTOKEN = "D"; +constexpr uint8_t DICTOFF = 0x88; + +static const std::unordered_map CharEncoder = { + {0x00, 'A'}, {0x01, 'B'}, {0x02, 'C'}, {0x03, 'D'}, {0x04, 'E'}, + {0x05, 'F'}, {0x06, 'G'}, {0x07, 'H'}, {0x08, 'I'}, {0x09, 'J'}, + {0x0A, 'K'}, {0x0B, 'L'}, {0x0C, 'M'}, {0x0D, 'N'}, {0x0E, 'O'}, + {0x0F, 'P'}, {0x10, 'Q'}, {0x11, 'R'}, {0x12, 'S'}, {0x13, 'T'}, + {0x14, 'U'}, {0x15, 'V'}, {0x16, 'W'}, {0x17, 'X'}, {0x18, 'Y'}, + {0x19, 'Z'}, {0x1A, 'a'}, {0x1B, 'b'}, {0x1C, 'c'}, {0x1D, 'd'}, + {0x1E, 'e'}, {0x1F, 'f'}, {0x20, 'g'}, {0x21, 'h'}, {0x22, 'i'}, + {0x23, 'j'}, {0x24, 'k'}, {0x25, 'l'}, {0x26, 'm'}, {0x27, 'n'}, + {0x28, 'o'}, {0x29, 'p'}, {0x2A, 'q'}, {0x2B, 'r'}, {0x2C, 's'}, + {0x2D, 't'}, {0x2E, 'u'}, {0x2F, 'v'}, {0x30, 'w'}, {0x31, 'x'}, + {0x32, 'y'}, {0x33, 'z'}, {0x34, '0'}, {0x35, '1'}, {0x36, '2'}, + {0x37, '3'}, {0x38, '4'}, {0x39, '5'}, {0x3A, '6'}, {0x3B, '7'}, + {0x3C, '8'}, {0x3D, '9'}, {0x3E, '!'}, {0x3F, '?'}, {0x40, '-'}, + {0x41, '.'}, {0x42, ','}, {0x44, '>'}, {0x45, '('}, {0x46, ')'}, + {0x4C, '"'}, {0x51, '\''}, {0x59, ' '}, {0x5A, '<'}, {0x5F, L'¡'}, + {0x60, L'¡'}, {0x61, L'¡'}, {0x62, L' '}, {0x63, L' '}, {0x64, L' '}, + {0x65, ' '}, {0x66, '_'}, +}; + +uint8_t FindMatchingCharacter(char value); +uint8_t FindDictionaryEntry(uint8_t value); std::string ReplaceAllDictionaryWords(std::string str); @@ -134,6 +164,59 @@ struct TextElement { bool operator==(const TextElement& other) const { return ID == other.ID; } }; +static const std::vector TextCommands = { + TextElement(0x6B, "W", true, "Window border"), + TextElement(0x6D, "P", true, "Window position"), + TextElement(0x6E, "SPD", true, "Scroll speed"), + TextElement(0x7A, "S", true, "Text draw speed"), + TextElement(0x77, "C", true, "Text color"), + TextElement(0x6A, "L", false, "Player name"), + TextElement(0x74, "1", false, "Line 1"), + TextElement(0x75, "2", false, "Line 2"), + TextElement(0x76, "3", false, "Line 3"), + TextElement(0x7E, "K", false, "Wait for key"), + TextElement(0x73, "V", false, "Scroll text"), + TextElement(0x78, "WT", true, "Delay X"), + TextElement(0x6C, "N", true, "BCD number"), + TextElement(0x79, "SFX", true, "Sound effect"), + TextElement(0x71, "CH3", false, "Choose 3"), + TextElement(0x72, "CH2", false, "Choose 2 high"), + TextElement(0x6F, "CH2L", false, "Choose 2 low"), + TextElement(0x68, "CH2I", false, "Choose 2 indented"), + TextElement(0x69, "CHI", false, "Choose item"), + TextElement(0x67, "IMG", false, "Next attract image"), + TextElement(0x80, BANKToken, false, "Bank marker (automatic)"), + TextElement(0x70, "NONO", false, "Crash"), +}; + +TextElement FindMatchingCommand(uint8_t b); + +static const std::vector SpecialChars = { + TextElement(0x43, "...", false, "Ellipsis …"), + TextElement(0x4D, "UP", false, "Arrow ↑"), + TextElement(0x4E, "DOWN", false, "Arrow ↓"), + TextElement(0x4F, "LEFT", false, "Arrow ←"), + TextElement(0x50, "RIGHT", false, "Arrow →"), + TextElement(0x5B, "A", false, "Button Ⓐ"), + TextElement(0x5C, "B", false, "Button Ⓑ"), + TextElement(0x5D, "X", false, "Button ⓧ"), + TextElement(0x5E, "Y", false, "Button ⓨ"), + TextElement(0x52, "HP1L", false, "1 HP left"), + TextElement(0x53, "HP1R", false, "1 HP right"), + TextElement(0x54, "HP2L", false, "2 HP left"), + TextElement(0x55, "HP3L", false, "3 HP left"), + TextElement(0x56, "HP3R", false, "3 HP right"), + TextElement(0x57, "HP4L", false, "4 HP left"), + TextElement(0x58, "HP4R", false, "4 HP right"), + TextElement(0x47, "HY0", false, "Hieroglyph ☥"), + TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"), + TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"), + TextElement(0x4A, "LFL", false, "Link face left"), + TextElement(0x4B, "LFR", false, "Link face right"), +}; + +TextElement FindMatchingSpecial(uint8_t b); + struct ParsedElement { TextElement Parent; uint8_t Value; @@ -147,6 +230,12 @@ struct ParsedElement { } }; +ParsedElement FindMatchingElement(const std::string& str); + +std::string ParseTextDataByte(uint8_t value); + +std::vector ParseMessageToData(std::string str); + } // namespace editor } // namespace app } // namespace yaze diff --git a/src/app/editor/message/message_editor.cc b/src/app/editor/message/message_editor.cc index 9ad018d9..592a69dc 100644 --- a/src/app/editor/message/message_editor.cc +++ b/src/app/editor/message/message_editor.cc @@ -307,7 +307,7 @@ void MessageEditor::ReadAllTextDataV2() { uint8_t current_byte = 0; while (current_byte != 0xFF) { current_byte = rom()->data()[pos++]; - if (current_byte == MESSAGETERMINATOR) { + if (current_byte == kMessageTerminator) { auto message = MessageData(message_id++, pos, current_raw_message, raw_message, current_parsed_message, parsed_message); @@ -403,7 +403,7 @@ void MessageEditor::ReadAllTextData() { while (true) { current_byte = rom()->data()[pos++]; - if (current_byte == MESSAGETERMINATOR) { + if (current_byte == kMessageTerminator) { auto message = MessageData(message_id++, pos, current_message_raw, temp_bytes_raw, current_message_parsed, temp_bytes_parsed); @@ -488,53 +488,6 @@ void MessageEditor::ReadAllTextData() { } } -TextElement MessageEditor::FindMatchingCommand(uint8_t b) { - TextElement empty_element; - for (const auto& text_element : TextCommands) { - if (text_element.ID == b) { - return text_element; - } - } - return empty_element; -} - -TextElement MessageEditor::FindMatchingSpecial(uint8_t value) { - auto it = std::find_if(SpecialChars.begin(), SpecialChars.end(), - [value](const TextElement& text_element) { - return text_element.ID == value; - }); - if (it != SpecialChars.end()) { - return *it; - } - - return TextElement(); -} - -ParsedElement MessageEditor::FindMatchingElement(std::string str) { - std::smatch match; - for (auto& textElement : TextCommands) { - match = textElement.MatchMe(str); - if (match.size() > 0) { - if (textElement.HasArgument) { - return ParsedElement(textElement, - std::stoi(match[1].str(), nullptr, 16)); - } else { - return ParsedElement(textElement, 0); - } - } - } - - const auto dictionary_element = - TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary"); - - match = dictionary_element.MatchMe(str); - if (match.size() > 0) { - return ParsedElement(dictionary_element, - DICTOFF + std::stoi(match[1].str(), nullptr, 16)); - } - return ParsedElement(); -} - std::string ReplaceAllDictionaryWords(std::string str) { std::string temp = str; for (const auto& entry : AllDictionaries) { @@ -542,57 +495,9 @@ std::string ReplaceAllDictionaryWords(std::string str) { temp = absl::StrReplaceAll(temp, {{entry.Contents, entry.Contents}}); } } - return temp; } -std::vector MessageEditor::ParseMessageToData(std::string str) { - std::vector bytes; - std::string temp_string = str; - int pos = 0; - - while (pos < temp_string.size()) { - // Get next text fragment. - if (temp_string[pos] == '[') { - int next = temp_string.find(']', pos); - if (next == -1) { - break; - } - - ParsedElement parsedElement = - FindMatchingElement(temp_string.substr(pos, next - pos + 1)); - - const auto dictionary_element = - TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary"); - - if (!parsedElement.Active) { - core::logf("Error parsing message: %s", temp_string); - break; - } else if (parsedElement.Parent == dictionary_element) { - bytes.push_back(parsedElement.Value); - } else { - bytes.push_back(parsedElement.Parent.ID); - - if (parsedElement.Parent.HasArgument) { - bytes.push_back(parsedElement.Value); - } - } - - pos = next + 1; - continue; - } else { - uint8_t bb = FindMatchingCharacter(temp_string[pos++]); - - if (bb != 0xFF) { - core::logf("Error parsing message: %s", temp_string); - bytes.push_back(bb); - } - } - } - - return bytes; -} - MessageEditor::DictionaryEntry MessageEditor::GetDictionaryFromID( uint8_t value) { if (value < 0 || value >= AllDictionaries.size()) { @@ -601,52 +506,6 @@ MessageEditor::DictionaryEntry MessageEditor::GetDictionaryFromID( return AllDictionaries[value]; } -uint8_t MessageEditor::FindDictionaryEntry(uint8_t value) { - if (value < DICTOFF || value == 0xFF) { - return -1; - } - - return value - DICTOFF; -} - -uint8_t MessageEditor::FindMatchingCharacter(char value) { - for (const auto [key, char_value] : CharEncoder) { - if (value == char_value) { - return key; - } - } - return 0xFF; -} - -std::string MessageEditor::ParseTextDataByte(uint8_t value) { - if (CharEncoder.contains(value)) { - char c = CharEncoder.at(value); - std::string str = ""; - str.push_back(c); - return str; - } - - // Check for command. - TextElement textElement = FindMatchingCommand(value); - if (!textElement.Empty()) { - return textElement.GenericToken; - } - - // Check for special characters. - textElement = FindMatchingSpecial(value); - if (!textElement.Empty()) { - return textElement.GenericToken; - } - - // Check for dictionary. - int dictionary = FindDictionaryEntry(value); - if (dictionary >= 0) { - return absl::StrFormat("[%s:%X]", DICTIONARYTOKEN, dictionary); - } - - return ""; -} - void MessageEditor::DrawTileToPreview(int x, int y, int srcx, int srcy, int pal, int sizex, int sizey) { int drawid = srcx + (srcy * 32); @@ -819,7 +678,7 @@ absl::Status MessageEditor::Save() { } RETURN_IF_ERROR( - rom()->Write(pos++, MESSAGETERMINATOR)); // , true, "Terminator text" + rom()->Write(pos++, kMessageTerminator)); // , true, "Terminator text" } // Verify that we didn't go over the space available for the second block. diff --git a/src/app/editor/message/message_editor.h b/src/app/editor/message/message_editor.h index d956f9ed..6ba6d61d 100644 --- a/src/app/editor/message/message_editor.h +++ b/src/app/editor/message/message_editor.h @@ -34,85 +34,12 @@ constexpr int kNumDictionaryEntries = 97; constexpr int kNumMessages = 396; constexpr uint8_t kBlockTerminator = 0x80; -constexpr uint8_t DICTOFF = 0x88; constexpr uint8_t BANKID = 0x80; constexpr uint8_t kScrollVertical = 0x73; constexpr uint8_t kLine1 = 0x74; constexpr uint8_t kLine2 = 0x75; constexpr uint8_t kLine3 = 0x76; -const std::string DICTIONARYTOKEN = "D"; -const std::string BANKToken = "BANK"; - -static const std::vector TextCommands = { - TextElement(0x6B, "W", true, "Window border"), - TextElement(0x6D, "P", true, "Window position"), - TextElement(0x6E, "SPD", true, "Scroll speed"), - TextElement(0x7A, "S", true, "Text draw speed"), - TextElement(0x77, "C", true, "Text color"), - TextElement(0x6A, "L", false, "Player name"), - TextElement(0x74, "1", false, "Line 1"), - TextElement(0x75, "2", false, "Line 2"), - TextElement(0x76, "3", false, "Line 3"), - TextElement(0x7E, "K", false, "Wait for key"), - TextElement(0x73, "V", false, "Scroll text"), - TextElement(0x78, "WT", true, "Delay X"), - TextElement(0x6C, "N", true, "BCD number"), - TextElement(0x79, "SFX", true, "Sound effect"), - TextElement(0x71, "CH3", false, "Choose 3"), - TextElement(0x72, "CH2", false, "Choose 2 high"), - TextElement(0x6F, "CH2L", false, "Choose 2 low"), - TextElement(0x68, "CH2I", false, "Choose 2 indented"), - TextElement(0x69, "CHI", false, "Choose item"), - TextElement(0x67, "IMG", false, "Next attract image"), - TextElement(0x80, BANKToken, false, "Bank marker (automatic)"), - TextElement(0x70, "NONO", false, "Crash"), -}; - -static const std::vector SpecialChars = { - TextElement(0x43, "...", false, "Ellipsis …"), - TextElement(0x4D, "UP", false, "Arrow ↑"), - TextElement(0x4E, "DOWN", false, "Arrow ↓"), - TextElement(0x4F, "LEFT", false, "Arrow ←"), - TextElement(0x50, "RIGHT", false, "Arrow →"), - TextElement(0x5B, "A", false, "Button Ⓐ"), - TextElement(0x5C, "B", false, "Button Ⓑ"), - TextElement(0x5D, "X", false, "Button ⓧ"), - TextElement(0x5E, "Y", false, "Button ⓨ"), - TextElement(0x52, "HP1L", false, "1 HP left"), - TextElement(0x53, "HP1R", false, "1 HP right"), - TextElement(0x54, "HP2L", false, "2 HP left"), - TextElement(0x55, "HP3L", false, "3 HP left"), - TextElement(0x56, "HP3R", false, "3 HP right"), - TextElement(0x57, "HP4L", false, "4 HP left"), - TextElement(0x58, "HP4R", false, "4 HP right"), - TextElement(0x47, "HY0", false, "Hieroglyph ☥"), - TextElement(0x48, "HY1", false, "Hieroglyph 𓈗"), - TextElement(0x49, "HY2", false, "Hieroglyph Ƨ"), - TextElement(0x4A, "LFL", false, "Link face left"), - TextElement(0x4B, "LFR", false, "Link face right"), -}; - -static const std::unordered_map CharEncoder = { - {0x00, 'A'}, {0x01, 'B'}, {0x02, 'C'}, {0x03, 'D'}, {0x04, 'E'}, - {0x05, 'F'}, {0x06, 'G'}, {0x07, 'H'}, {0x08, 'I'}, {0x09, 'J'}, - {0x0A, 'K'}, {0x0B, 'L'}, {0x0C, 'M'}, {0x0D, 'N'}, {0x0E, 'O'}, - {0x0F, 'P'}, {0x10, 'Q'}, {0x11, 'R'}, {0x12, 'S'}, {0x13, 'T'}, - {0x14, 'U'}, {0x15, 'V'}, {0x16, 'W'}, {0x17, 'X'}, {0x18, 'Y'}, - {0x19, 'Z'}, {0x1A, 'a'}, {0x1B, 'b'}, {0x1C, 'c'}, {0x1D, 'd'}, - {0x1E, 'e'}, {0x1F, 'f'}, {0x20, 'g'}, {0x21, 'h'}, {0x22, 'i'}, - {0x23, 'j'}, {0x24, 'k'}, {0x25, 'l'}, {0x26, 'm'}, {0x27, 'n'}, - {0x28, 'o'}, {0x29, 'p'}, {0x2A, 'q'}, {0x2B, 'r'}, {0x2C, 's'}, - {0x2D, 't'}, {0x2E, 'u'}, {0x2F, 'v'}, {0x30, 'w'}, {0x31, 'x'}, - {0x32, 'y'}, {0x33, 'z'}, {0x34, '0'}, {0x35, '1'}, {0x36, '2'}, - {0x37, '3'}, {0x38, '4'}, {0x39, '5'}, {0x3A, '6'}, {0x3B, '7'}, - {0x3C, '8'}, {0x3D, '9'}, {0x3E, '!'}, {0x3F, '?'}, {0x40, '-'}, - {0x41, '.'}, {0x42, ','}, {0x44, '>'}, {0x45, '('}, {0x46, ')'}, - {0x4C, '"'}, {0x51, '\''}, {0x59, ' '}, {0x5A, '<'}, {0x5F, L'¡'}, - {0x60, L'¡'}, {0x61, L'¡'}, {0x62, L' '}, {0x63, L' '}, {0x64, L' '}, - {0x65, ' '}, {0x66, '_'}, -}; - static TextElement DictionaryElement = TextElement(0x80, DICTIONARYTOKEN, true, "Dictionary"); @@ -144,12 +71,6 @@ class MessageEditor : public Editor, public SharedRom { void Delete(); void SelectAll(); - TextElement FindMatchingCommand(uint8_t byte); - TextElement FindMatchingSpecial(uint8_t value); - std::string ParseTextDataByte(uint8_t value); - - static std::vector ParseMessageToData(std::string str); - static ParsedElement FindMatchingElement(std::string str); struct DictionaryEntry { uint8_t ID; std::string Contents; @@ -179,8 +100,6 @@ class MessageEditor : public Editor, public SharedRom { } }; DictionaryEntry GetDictionaryFromID(uint8_t value); - static uint8_t FindDictionaryEntry(uint8_t value); - static uint8_t FindMatchingCharacter(char value); void DrawTileToPreview(int x, int y, int srcx, int srcy, int pal, int sizex = 1, int sizey = 1); void DrawCharacterToPreview(char c); @@ -194,8 +113,6 @@ class MessageEditor : public Editor, public SharedRom { void InsertSpecialButton_Click(); void InsertSelectedText(std::string str); - static const std::vector AllDicts; - private: bool skip_next = false; bool from_form = false;