From 4cd417a8fc41a9be13707aa229b385a2e4b7399c Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Wed, 15 Feb 2023 18:02:49 -0800 Subject: [PATCH] Finished Language Generation Tool --- src/dawnshared/util/Xml.cpp | 25 +++++- .../generatedlanguages/GeneratedLanguages.cpp | 81 ++++++++++++++++++- src/dawntools/util/File.cpp | 45 +++++++++++ src/dawntools/util/File.hpp | 20 ++++- 4 files changed, 166 insertions(+), 5 deletions(-) diff --git a/src/dawnshared/util/Xml.cpp b/src/dawnshared/util/Xml.cpp index bfb3cfcd..df9b341e 100644 --- a/src/dawnshared/util/Xml.cpp +++ b/src/dawnshared/util/Xml.cpp @@ -26,6 +26,8 @@ void Xml::load(Xml *xml, std::string data, size_t *j) { bool_t insideTag = false; std::string buffer = ""; std::string attrKey = ""; + std::string bufferWhitespaces; + bool_t valueIsInWhitespace = false; size_t i = *j; while(c = data[i++]) { @@ -131,10 +133,29 @@ void Xml::load(Xml *xml, std::string data, size_t *j) { // In HTML Spec there could be a child here but not in XML spec. doing = XML_PARSE_STATE_PARSING_CLOSE; xml->value = buffer; - buffer = ""; + buffer.clear(); + valueIsInWhitespace = false; + bufferWhitespaces.clear(); continue; } - buffer += c; + + if(Xml::isWhitespace(c)) { + if(!valueIsInWhitespace) { + bufferWhitespaces.clear(); + bufferWhitespaces += c; + valueIsInWhitespace = true; + } else { + if(c != ' ') bufferWhitespaces += c; + } + // TODO: I can maybe consider indentation here + } else { + if(valueIsInWhitespace) { + buffer += bufferWhitespaces; + valueIsInWhitespace = false; + } + buffer += c; + } + break; case XML_PARSE_STATE_PARSING_CHILD: diff --git a/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp b/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp index d9c55bea..b95122b8 100644 --- a/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp +++ b/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp @@ -25,7 +25,86 @@ int32_t GeneratedLanguages::start() { } // Now process each language file - + std::map> strings; + std::vector knownKeys; + auto itFiles = files.begin(); + while(itFiles != files.end()) { + File file(*itFiles); + file.open(FILE_MODE_READ); + std::string buffer; + size_t n = 0; + + while(n < file.length) { + char lang[32]; + char key[128]; + char string[32178]; + + // Read lang + if(n != 0) file.setPosition(n); + auto langSize = file.readAhead(lang, 32, '|'); + lang[langSize] = '\0'; + n += langSize + 1; + if(langSize <= 0) { + std::cout << "Error reading language name: " << langSize << std::endl; + return 1; + } + + // Read Key + file.setPosition(n); + auto keySize = file.readAhead(key, 128, '|'); + key[keySize] = '\0'; + n += keySize + 1; + if(keySize <= 0) { + std::cout << "Error reading language key: " << keySize << std::endl; + return 1; + } + + // Read String + file.setPosition(n); + auto stringSize = file.readAhead(string, 32178, '|'); + string[stringSize] = '\0'; + n += stringSize + 1; + if(stringSize <= 0) { + std::cout << "Error reading language string: " << stringSize << std::endl; + return 1; + } + + strings[lang][key] = string; + auto exist = std::find(knownKeys.begin(), knownKeys.end(), key); + if(exist == knownKeys.end()) knownKeys.push_back(key); + } + + ++itFiles; + } + + // Now prepare output file + auto itLang = strings.begin(); + std::string bufferOut = ""; + while(itLang != strings.end()) { + File langOut(flags["output"] + FILE_PATH_SEP + "language_" + itLang->first + ".language"); + bufferOut.clear(); + + auto itKeys = knownKeys.begin(); + while(itKeys != knownKeys.end()) { + auto key = *itKeys; + auto exist = itLang->second.find(key); + if(exist == itLang->second.end()) { + std::cout << "Language " << itLang->first << " is missing key " << key << std::endl; + return 1; + } + std::cout << "bruh " << exist->second << std::endl; + bufferOut += exist->first + "|" + exist->second + "|"; + ++itKeys; + } + + // Write out. + if(!langOut.writeString(bufferOut)) { + std::cout << "Failed to create output file \"" + langOut.filename + "\"" << std::endl; + return 1; + } + + ++itLang; + } return 0; } diff --git a/src/dawntools/util/File.cpp b/src/dawntools/util/File.cpp index f61da4de..56df6443 100644 --- a/src/dawntools/util/File.cpp +++ b/src/dawntools/util/File.cpp @@ -126,6 +126,46 @@ bool_t File::readString(std::string *out) { return true; } +size_t File::readAhead(char *buffer, size_t max, char needle) { + assertNotNull(buffer); + assertTrue(max > 0); + + if(!this->isOpen()) { + if(!this->open(FILE_MODE_READ)) return 0; + } + assertTrue(this->mode == FILE_MODE_READ); + + // Buffer + size_t pos = ftell(this->file); + size_t amountLeftToRead = mathMin(max, this->length - pos); + char temporary[FILE_BUFFER_SIZE]; + size_t n = 0; + + while(amountLeftToRead > 0) { + size_t toRead = mathMin(amountLeftToRead, FILE_BUFFER_SIZE); + amountLeftToRead -= toRead; + // Read bytes + size_t read = fread(temporary, sizeof(char), toRead, this->file); + + // Read error? + if(toRead != read) return 0; + + // Did we read the needle? + size_t i = 0; + while(i < read) { + char c = temporary[i++]; + if(c == needle) { + return n; + } else { + buffer[n++] = c; + } + } + } + + // Needle was not found. + return -1; +} + bool_t File::writeString(std::string in) { if(!this->isOpen()) { if(!this->open(FILE_MODE_WRITE)) return false; @@ -143,6 +183,11 @@ bool_t File::writeRaw(char *data, size_t len) { return true; } +void File::setPosition(size_t n) { + fseek(this->file, 0, SEEK_SET); + fseek(this->file, n, SEEK_CUR); +} + File::~File() { if(this->file != nullptr) this->close(); } \ No newline at end of file diff --git a/src/dawntools/util/File.hpp b/src/dawntools/util/File.hpp index 3972df93..b30b158f 100644 --- a/src/dawntools/util/File.hpp +++ b/src/dawntools/util/File.hpp @@ -33,14 +33,14 @@ namespace Dawn { class File { private: enum FileMode mode; - size_t length; public: - FILE *file = nullptr; static std::string normalizeSlashes(std::string str); static void mkdirp(std::string path); std::string filename; + size_t length; + FILE *file = nullptr; /** * Constructs a new File interface class. @@ -95,6 +95,20 @@ namespace Dawn { */ bool_t readString(std::string *out); + /** + * Reads ahead from the current position to a specific needle (character). + * + * @param buffer Buffer to output read chars to. + * @param max Max length of the buffer / amount of chars to read ahead. + * @param needle The character (needle) to look for. + * @return Amount of chars read, or <= 0 on error. + */ + size_t readAhead( + char *buffer, + size_t max, + char needle + ); + /** * Writes the entire contents of a string to a file. * @@ -111,6 +125,8 @@ namespace Dawn { */ bool_t writeRaw(char *data, size_t ); + void setPosition(size_t pos); + ~File(); }; } \ No newline at end of file