From c3dbacde767de97af26b941dbfd20734983aeca6 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 7 Feb 2023 21:22:57 -0800 Subject: [PATCH] Updated locale gen --- CMakeLists.txt | 4 +- src/dawn/CMakeLists.txt | 7 +- src/dawnshared/CMakeLists.txt | 9 + .../assert/CMakeLists.txt | 0 src/{dawn => dawnshared}/assert/assert.cpp | 0 src/{dawn => dawnshared}/assert/assert.hpp | 2 +- src/dawnshared/dawnsharedlibs.hpp | 4 +- src/dawnshared/util/array.hpp | 2 +- .../locale/languagegen/CMakeLists.txt | 5 + .../locale/languagegen/LanguageGen.cpp | 99 ++++---- .../locale/languagegen/LanguageGen.hpp | 5 +- src/dawntools/util/XmlNew.cpp | 223 ++++++++++++++++++ src/dawntools/util/XmlNew.hpp | 43 ++++ 13 files changed, 343 insertions(+), 60 deletions(-) rename src/{dawn => dawnshared}/assert/CMakeLists.txt (100%) rename src/{dawn => dawnshared}/assert/assert.cpp (100%) rename src/{dawn => dawnshared}/assert/assert.hpp (97%) create mode 100644 src/dawntools/util/XmlNew.cpp create mode 100644 src/dawntools/util/XmlNew.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 86b327ff..963dcdd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,9 @@ # https://opensource.org/licenses/MIT cmake_minimum_required(VERSION 3.13) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 20) set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt index 6630eb9d..fb66e0fd 100644 --- a/src/dawn/CMakeLists.txt +++ b/src/dawn/CMakeLists.txt @@ -17,8 +17,13 @@ target_include_directories(${DAWN_TARGET_NAME} ${CMAKE_CURRENT_LIST_DIR} ) + +target_sources(${DAWN_TARGET_NAME} + PRIVATE + ${DAWN_SHARED_SOURCES} +) + # Subdirs -add_subdirectory(assert) add_subdirectory(asset) add_subdirectory(display) add_subdirectory(input) diff --git a/src/dawnshared/CMakeLists.txt b/src/dawnshared/CMakeLists.txt index 203c8ff9..08003e0e 100644 --- a/src/dawnshared/CMakeLists.txt +++ b/src/dawnshared/CMakeLists.txt @@ -8,6 +8,15 @@ set( DAWN_SHARED_INCLUDES ${CMAKE_CURRENT_LIST_DIR} + CACHE INTERNAL + ${DAWN_CACHE_TARGET} +) + +set(D ${CMAKE_CURRENT_LIST_DIR}) +set( + DAWN_SHARED_SOURCES + ${D}/assert/assert.cpp + CACHE INTERNAL ${DAWN_CACHE_TARGET} ) \ No newline at end of file diff --git a/src/dawn/assert/CMakeLists.txt b/src/dawnshared/assert/CMakeLists.txt similarity index 100% rename from src/dawn/assert/CMakeLists.txt rename to src/dawnshared/assert/CMakeLists.txt diff --git a/src/dawn/assert/assert.cpp b/src/dawnshared/assert/assert.cpp similarity index 100% rename from src/dawn/assert/assert.cpp rename to src/dawnshared/assert/assert.cpp diff --git a/src/dawn/assert/assert.hpp b/src/dawnshared/assert/assert.hpp similarity index 97% rename from src/dawn/assert/assert.hpp rename to src/dawnshared/assert/assert.hpp index 879771cb..d8592812 100644 --- a/src/dawn/assert/assert.hpp +++ b/src/dawnshared/assert/assert.hpp @@ -6,7 +6,7 @@ */ #pragma once -#include "dawnlibs.hpp" +#include "dawnsharedlibs.hpp" #define ASSERTS_ENABLED 1 diff --git a/src/dawnshared/dawnsharedlibs.hpp b/src/dawnshared/dawnsharedlibs.hpp index 5a0a039e..55cde3de 100644 --- a/src/dawnshared/dawnsharedlibs.hpp +++ b/src/dawnshared/dawnsharedlibs.hpp @@ -19,6 +19,7 @@ extern "C" { #include typedef bool bool_t; + typedef char char_t; } #include @@ -28,4 +29,5 @@ extern "C" { #include #include #include -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/src/dawnshared/util/array.hpp b/src/dawnshared/util/array.hpp index ab64da5a..8b53ec7d 100644 --- a/src/dawnshared/util/array.hpp +++ b/src/dawnshared/util/array.hpp @@ -4,7 +4,7 @@ // https://opensource.org/licenses/MIT #pragma once -#include "dawnlibs.hpp" +#include "dawnsharedlibs.hpp" #include "assert/assert.hpp" namespace Dawn { diff --git a/src/dawntools/locale/languagegen/CMakeLists.txt b/src/dawntools/locale/languagegen/CMakeLists.txt index 1589d8e0..a209a524 100644 --- a/src/dawntools/locale/languagegen/CMakeLists.txt +++ b/src/dawntools/locale/languagegen/CMakeLists.txt @@ -8,18 +8,23 @@ project(languagegen VERSION 2.0) add_executable(languagegen) target_sources(languagegen PRIVATE + ${DAWN_SHARED_SOURCES} LanguageGen.cpp ../../util/DawnTool.cpp + ../../util/XmlNew.cpp ../../util/file.cpp ../../util/csv.cpp ../../util/xml.cpp ) + target_include_directories(languagegen PUBLIC ${DAWN_SHARED_INCLUDES} ${CMAKE_CURRENT_LIST_DIR}/../../ ${CMAKE_CURRENT_LIST_DIR} ) + + target_link_libraries(languagegen PUBLIC ${DAWN_BUILD_HOST_LIBS} diff --git a/src/dawntools/locale/languagegen/LanguageGen.cpp b/src/dawntools/locale/languagegen/LanguageGen.cpp index 9b44ec96..592076ce 100644 --- a/src/dawntools/locale/languagegen/LanguageGen.cpp +++ b/src/dawntools/locale/languagegen/LanguageGen.cpp @@ -14,7 +14,7 @@ LanguageGen::LanguageGen(const int argc, const char *argv[]) : { } -int32_t LanguageGen::start() { +int32_t LanguageGen::start() { if(this->args.size() != 3) { std::cout << "Invalid number of arguments provided to language gen!" << std::endl; return 1; @@ -32,53 +32,47 @@ int32_t LanguageGen::start() { if(buffer == NULL) { std::cout << "Failed to allocate memory for locale string XML" << std::endl; fclose(fileIn); - return 1; + return 1; } assetReadString(fileIn, buffer); fclose(fileIn); - - xml_t xml; - xmlLoad(&xml, buffer); + auto xml = Xml::load(std::string(buffer)); free(buffer); - + // Begin parsing. Start by looking for the tags std::vector languages; - for(int32_t i = 0; i < xml.childrenCount; i++) { - auto c = xml.children + i; - if(std::string(c->node) != "language") continue; - auto attrName = xmlGetAttributeByName(c, "name"); - - if(attrName == -1) { - std::cout << "Missing name param on language node" << std::endl; - xmlDispose(&xml); - return 1; + auto itChildren = xml.children.begin(); + while(itChildren != xml.children.end()) { + auto child = *itChildren; + if(child->node == "language") { + auto attrName = child->attributes.find("name"); + if(attrName == child->attributes.end()) { + std::cout << "Missing name param on language node" << std::endl; + return 1; + } + languages.push_back(attrName->second); } - languages.push_back(std::string(c->attributeDatas[attrName])); + ++itChildren; } // Now begin actually parsing std::map> strings; - for(int32_t i = 0; i < xml.childrenCount; i++) { - auto c = xml.children + i; - if(std::string(c->node) == "group") { - auto ret = this->parseGroup(c, "", &strings); - if(ret != 0) { - xmlDispose(&xml); - return ret; - } - } else if(std::string(c->node) == "string") { + itChildren = xml.children.begin(); + while(itChildren != xml.children.end()) { + auto child = *itChildren; + if(child->node == "group") { + auto ret = this->parseGroup(child, "", &strings); + if(ret != 0) return ret; + } else if(child->node == "string") { std::cout << "String cannot be a root node" << std::endl; - xmlDispose(&xml); return 1; } + ++itChildren; } - - xmlDispose(&xml); - + // Now we validate each lang has each key. std::vector keys; - auto it = strings.begin(); while(it != strings.end()) { auto it2 = it->second.begin(); @@ -98,7 +92,7 @@ int32_t LanguageGen::start() { while(it != strings.end()) { std::vector itKeys; - std::string bufferOut = ""; + std::string bufferOut; auto it2 = it->second.begin(); while(it2 != it->second.end()) { @@ -119,8 +113,8 @@ int32_t LanguageGen::start() { return 1; } - const char *strOut = bufferOut.c_str(); - fwrite(strOut, sizeof(char), strlen(strOut), fileOut); + const char_t *strOut = bufferOut.c_str(); + fwrite(strOut, sizeof(char_t), strlen(strOut), fileOut); fclose(fileOut); auto it3 = keys.begin(); @@ -144,54 +138,55 @@ int32_t LanguageGen::start() { } int32_t LanguageGen::parseString( - xml_t *stringNode, + Xml *stringNode, std::string key, std::map> *strings ) { - auto attrLang = xmlGetAttributeByName(stringNode, "lang"); - if(attrLang == -1) { + auto attrLang = stringNode->attributes.find("lang"); + if(attrLang == stringNode->attributes.end()) { std::cout << "String is missing lang parameter." << std::endl; return -1; } - std::string lang(stringNode->attributeDatas[attrLang]); struct LanguageString str; str.key = key; - str.value = std::string(stringNode->value); + str.value = stringNode->value; - auto existing = (*strings).find(lang); + auto existing = (*strings).find(attrLang->second); if(existing == (*strings).end()) { - (*strings).insert(std::make_pair(lang, std::vector())); + (*strings).insert(std::make_pair(attrLang->second, std::vector())); } - (*strings)[lang].push_back(str); + (*strings)[attrLang->second].push_back(str); return 0; } int32_t LanguageGen::parseGroup( - xml_t *groupNode, + Xml *groupNode, std::string key, - std::map> *strings + std::map> *strings ) { int32_t ret; - auto attrKey = xmlGetAttributeByName(groupNode, "key"); - if(attrKey == -1) { + auto attrKey = groupNode->attributes.find("key"); + if(attrKey == groupNode->attributes.end()) { std::cout << "Group node is missing key" << std::endl; return 1; } if(key.size() > 0) key += "."; - key += std::string(groupNode->attributeDatas[attrKey]); + key += attrKey->second; - for(int32_t i = 0; i < groupNode->childrenCount; i++) { - auto c = groupNode->children + i; - if(std::string(c->node) == "string") { - ret = this->parseString(c, key, strings); + auto itChildren = groupNode->children.begin(); + while(itChildren != groupNode->children.end()) { + auto child = *itChildren; + if(child->node == "string") { + ret = this->parseString(child, key, strings); if(ret != 0) return ret; - } else if(std::string(c->node) == "group") { - ret = this->parseGroup(c, key, strings); + } else if(child->node == "group") { + ret = this->parseGroup(child, key, strings); if(ret != 0) return ret; } + ++itChildren; } return 0; diff --git a/src/dawntools/locale/languagegen/LanguageGen.hpp b/src/dawntools/locale/languagegen/LanguageGen.hpp index d0331471..6651360c 100644 --- a/src/dawntools/locale/languagegen/LanguageGen.hpp +++ b/src/dawntools/locale/languagegen/LanguageGen.hpp @@ -5,6 +5,7 @@ #pragma once #include "util/DawnTool.hpp" +#include "util/XmlNew.hpp" namespace Dawn { struct LanguageString { @@ -15,13 +16,13 @@ namespace Dawn { class LanguageGen : public DawnTool { protected: int32_t parseGroup( - xml_t *node, + Xml *node, std::string key, std::map> *strings ); int32_t parseString( - xml_t *node, + Xml *node, std::string key, std::map> *strings ); diff --git a/src/dawntools/util/XmlNew.cpp b/src/dawntools/util/XmlNew.cpp new file mode 100644 index 00000000..f4a9dae6 --- /dev/null +++ b/src/dawntools/util/XmlNew.cpp @@ -0,0 +1,223 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "XmlNew.hpp" +#include "util/array.hpp" + +using namespace Dawn; + +bool_t Xml::isWhitespace(char_t c) { + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; +} + +Xml Xml::load(std::string data) { + size_t j = 0; + Xml xml; + Xml::load(&xml, data, &j); + return xml; +} + +void Xml::load(Xml *xml, std::string data, size_t *j) { + char_t c; + int32_t level = 0; + enum XmlParseState doing = XML_PARSE_STATE_DOING_NOTHING; + enum XmlParseState doingBeforeComment; + bool_t insideTag = false; + std::string buffer = ""; + std::string attrKey = ""; + size_t i = *j; + + while(c = data[i++]) { + switch(doing) { + case XML_PARSE_STATE_DOING_NOTHING: + if(c == '>') continue; + if(c == '<') { + if(data[i] == '!' && data[i+1] == '-' && data[i+2] == '-') { + doingBeforeComment = doing; + doing = XML_PARSE_STATE_PARSING_COMMENT; + i += 3; + } else if(insideTag) { + i -= 1; + auto child = new Xml(); + Xml::load(child, data, &i); + xml->children.push_back(child); + doing = XML_PARSE_STATE_PARSING_CHILD; + } else { + doing = XML_PARSE_STATE_PARSING_TAG_NAME; + level++; + insideTag = true; + } + continue; + } + + if(Xml::isWhitespace(c)) continue; + doing = XML_PARSE_STATE_PARSING_VALUE; + buffer += c; + break; + + case XML_PARSE_STATE_PARSING_TAG_NAME: + // Just keep reading until we either hit a space (end of the tag name) + // or a closing tag value, either / or > + if(Xml::isWhitespace(c) || c == '>' || c == '/') { + xml->node = buffer; + buffer = ""; + if(c == '/') { + level--; + insideTag = false; + doing = XML_PARSE_STATE_PARSING_CLOSE; + } else { + doing = c == '>' ? XML_PARSE_STATE_DOING_NOTHING : XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE; + } + continue; + } + buffer += c; + break; + + + case XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE: + // Look until we hit either the end of a tag, or the attribute itself + if(Xml::isWhitespace(c) || c == '>' || c == '/' || c == '=') { + if(c == '>' || c == '/') { + doing = XML_PARSE_STATE_DOING_NOTHING; + if(c == '/') { + level--; + insideTag = false; + doing = XML_PARSE_STATE_PARSING_CLOSE; + } + } else if(c == '=') { + doing = XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE_VALUE; + } else { + doing = XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE; + } + + if(buffer.size() > 0) { + attrKey = buffer; + xml->attributes[buffer] = ""; + buffer = ""; + } + continue; + } + buffer += c; + break; + + case XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE_VALUE: + // Keep looking until we find a quote mark + if(Xml::isWhitespace(c)) continue; + if(c == '>' || c == '/') { + doing = XML_PARSE_STATE_DOING_NOTHING; + insideTag = false; + continue; + } + + if(c != '"') continue; + doing = XML_PARSE_STATE_PARSING_ATTRIBUTE_VALUE; + break; + + case XML_PARSE_STATE_PARSING_ATTRIBUTE_VALUE: + // Parse the attribute value until we find a quote mark. + if(c == '"') { + doing = XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE; + xml->attributes[attrKey] = buffer; + buffer = ""; + continue; + } + buffer += c; + break; + + case XML_PARSE_STATE_PARSING_VALUE: + // Keep parsing child until we find a < for an opening/closing tag. + if(c == '<') { + // In HTML Spec there could be a child here but not in XML spec. + doing = XML_PARSE_STATE_PARSING_CLOSE; + xml->value = buffer; + buffer = ""; + continue; + } + buffer += c; + break; + + case XML_PARSE_STATE_PARSING_CHILD: + if(c == '<') { + // Read ahead and confirm this is a close or not + if(data[i] == '/') { + doing = XML_PARSE_STATE_PARSING_CLOSE; + continue; + } + + if(data[i] == '!' && data[i+1] == '-' && data[i+2] == '-') { + doingBeforeComment = doing; + doing = XML_PARSE_STATE_PARSING_COMMENT; + i += 3; + continue; + } + + // Likely another child. + auto child = new Xml(); + i -= 1; + Xml::load(child, data, &i); + xml->children.push_back(child); + } + + if(Xml::isWhitespace(c)) continue; + + // In HTML Spec there's a chance for there to be a value here, but not + // in the XML spec. + break; + + case XML_PARSE_STATE_PARSING_CLOSE: + // Just keep parsing until the tag closer finishes. + if(c != '>') continue; + doing = XML_PARSE_STATE_DOING_NOTHING; + + //TODO: Return index or something? + *j = i; + return; + + case XML_PARSE_STATE_PARSING_COMMENT: + if(c != '-') continue; + if(data[i] != '-') continue; + if(data[i+1] != '>') continue; + i += 2; + doing = doingBeforeComment; + break; + + default: + break; + } + } + + *j = i; +} + + +std::vector Xml::getChildrenOfType(std::string type) { + std::vector children; + auto itChildren = this->children.begin(); + while(itChildren != this->children.end()) { + auto child = *itChildren; + if(child->node == type) children.push_back(child); + ++itChildren; + } + return children; +} + +Xml * Xml::getFirstChildOfType(std::string type) { + auto itChildren = this->children.begin(); + while(itChildren != this->children.end()) { + auto child = *itChildren; + if(child->node == type) return child; + ++itChildren; + } + return nullptr; +} + + +Xml::~Xml() { + auto it = this->children.begin(); + while(it != this->children.end()) { + delete *it; + ++it; + } +} \ No newline at end of file diff --git a/src/dawntools/util/XmlNew.hpp b/src/dawntools/util/XmlNew.hpp new file mode 100644 index 00000000..3068314c --- /dev/null +++ b/src/dawntools/util/XmlNew.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "dawnsharedlibs.hpp" + +namespace Dawn { + enum XmlParseState { + XML_PARSE_STATE_DOING_NOTHING, + XML_PARSE_STATE_PARSING_TAG_NAME, + XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE, + XML_PARSE_STATE_PARSING_ATTRIBUTE_NAME, + XML_PARSE_STATE_LOOKING_FOR_ATTRIBUTE_VALUE, + XML_PARSE_STATE_PARSING_ATTRIBUTE_VALUE, + XML_PARSE_STATE_PARSING_VALUE, + XML_PARSE_STATE_PARSING_CHILD, + XML_PARSE_STATE_PARSING_CLOSE, + XML_PARSE_STATE_PARSING_COMMENT + }; + + class Xml { + protected: + static bool_t isWhitespace(char_t c); + + public: + static Xml load(std::string data); + static void load(Xml *xml, std::string data, size_t *j); + + + + std::string node; + std::string value; + std::map attributes; + std::vector children; + + std::vector getChildrenOfType(std::string type); + Xml * getFirstChildOfType(std::string type); + + ~Xml(); + }; +} \ No newline at end of file