diff --git a/cmake/targets/CMakeLists.txt b/cmake/targets/CMakeLists.txt index d8224db6..93698f07 100644 --- a/cmake/targets/CMakeLists.txt +++ b/cmake/targets/CMakeLists.txt @@ -8,7 +8,7 @@ if(NOT DEFINED DAWN_BUILD_TARGET) if(WIN32) set(DAWN_BUILD_TARGET "target-pokergame-win32-glfw") elseif(UNIX AND NOT APPLE) - set(DAWN_BUILD_TARGET "target-pokergame-linux64-glfw") + set(DAWN_BUILD_TARGET "target-tictactoe-linux64-glfw") endif() endif() diff --git a/cmake/targets/target-tictactoe-linux64-glfw/CMakeLists.txt b/cmake/targets/target-tictactoe-linux64-glfw/CMakeLists.txt new file mode 100644 index 00000000..07a95c6e --- /dev/null +++ b/cmake/targets/target-tictactoe-linux64-glfw/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +set(DAWN_BUILDING dawntictactoe CACHE INTERNAL ${DAWN_CACHE_TARGET}) +set(DAWN_TARGET_LINUX64 true CACHE INTERNAL ${DAWN_CACHE_TARGET}) +set(DAWN_TARGET_GLFW true CACHE INTERNAL ${DAWN_CACHE_TARGET}) \ No newline at end of file diff --git a/src/dawn/prefabs/SimpleSpinningCubePrefab.hpp b/src/dawn/prefabs/SimpleSpinningCubePrefab.hpp index 06cf45e6..09ad7243 100644 --- a/src/dawn/prefabs/SimpleSpinningCubePrefab.hpp +++ b/src/dawn/prefabs/SimpleSpinningCubePrefab.hpp @@ -1,38 +1,38 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#pragma once -#include "prefab/SceneItemPrefab.hpp" -#include "display/mesh/CubeMesh.hpp" -#include "scene/components/display/MeshRenderer.hpp" -#include "scene/components/display/MeshHost.hpp" -#include "scene/components/display/material/SimpleTexturedMaterial.hpp" -#include "scene/components/example/ExampleSpin.hpp" - -namespace Dawn { - class SimpleSpinningCubePrefab : - public SceneItemPrefab - { - public: - std::vector prefabAssets(AssetManager *man) { - return std::vector(); - } - - SimpleSpinningCubePrefab(Scene *s, sceneitemid_t i) : - SceneItemPrefab(s, i) - { - } - - void prefabInit(AssetManager *man) override { - auto meshRenderer = this->addComponent(); - auto meshHost = this->addComponent(); - auto spinning = this->addComponent(); - auto material = this->addComponent(); - - meshHost->mesh.createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT); - CubeMesh::buffer(&meshHost->mesh, glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1, 1, 1), 0, 0); - } - }; +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "prefab/SceneItemPrefab.hpp" +#include "display/mesh/CubeMesh.hpp" +#include "scene/components/display/MeshRenderer.hpp" +#include "scene/components/display/MeshHost.hpp" +#include "scene/components/display/material/SimpleTexturedMaterial.hpp" +#include "scene/components/example/ExampleSpin.hpp" + +namespace Dawn { + class SimpleSpinningCubePrefab : + public SceneItemPrefab + { + public: + static std::vector prefabAssets(AssetManager *man) { + return std::vector(); + } + + SimpleSpinningCubePrefab(Scene *s, sceneitemid_t i) : + SceneItemPrefab(s, i) + { + } + + void prefabInit(AssetManager *man) override { + auto meshRenderer = this->addComponent(); + auto meshHost = this->addComponent(); + auto spinning = this->addComponent(); + auto material = this->addComponent(); + + meshHost->mesh.createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT); + CubeMesh::buffer(&meshHost->mesh, glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1, 1, 1), 0, 0); + } + }; } \ No newline at end of file diff --git a/src/dawn/scenes/TestScene.hpp b/src/dawn/scenes/TestScene.hpp new file mode 100644 index 00000000..adc552d0 --- /dev/null +++ b/src/dawn/scenes/TestScene.hpp @@ -0,0 +1,33 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "scene/Scene.hpp" +#include "prefabs/SimpleSpinningCubePrefab.hpp" + +namespace Dawn { + class TestScene : public Scene { + protected: + Camera *camera; + SimpleSpinningCubePrefab *cube; + + void stage() override { + camera = Camera::create(this); + camera->transform->lookAt(glm::vec3(5, 5, 5), glm::vec3(0, 0, 0)); + + cube = SimpleSpinningCubePrefab::prefabCreate(this); + } + + std::vector getRequiredAssets() override { + auto assMan = &this->game->assetManager; + std::vector assets; + vectorAppend(&assets, SimpleSpinningCubePrefab::getRequiredAssets(assMan)); + return assets; + } + + public: + TestScene(DawnGame *game) : Scene(game) {} + }; +} \ No newline at end of file diff --git a/src/dawntictactoe/CMakeLists.txt b/src/dawntictactoe/CMakeLists.txt new file mode 100644 index 00000000..c31f3496 --- /dev/null +++ b/src/dawntictactoe/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Set up the executable +set(DAWN_TARGET_NAME "TicTacToe" CACHE INTERNAL ${DAWN_CACHE_TARGET}) + +# Build Project +add_executable(${DAWN_TARGET_NAME}) + +# Includes +target_include_directories(${DAWN_TARGET_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) + +# Subdirs +add_subdirectory(game) +add_subdirectory(save) + +# Assets +set(DIR_GAME_ASSETS games/tictactoe) + +tool_language(locale_en ${DIR_GAME_ASSETS}/locale/en.xml) \ No newline at end of file diff --git a/src/dawntictactoe/game/CMakeLists.txt b/src/dawntictactoe/game/CMakeLists.txt new file mode 100644 index 00000000..8ca94ca8 --- /dev/null +++ b/src/dawntictactoe/game/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DAWN_TARGET_NAME} + PRIVATE + DawnGame.cpp +) \ No newline at end of file diff --git a/src/dawntictactoe/game/DawnGame.cpp b/src/dawntictactoe/game/DawnGame.cpp new file mode 100644 index 00000000..d99f1e70 --- /dev/null +++ b/src/dawntictactoe/game/DawnGame.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "DawnGame.hpp" +#include "scenes/TestScene.hpp" + +using namespace Dawn; + +DawnGame::DawnGame(DawnHost *host) : + host(host), + renderManager(this), + inputManager(this), + localeManager(this), + physicsManager(this), + saveManager(this) +{ +} + +int32_t DawnGame::init() { + this->assetManager.init(); + this->localeManager.init(); + this->renderManager.init(); + + this->scene = new TestScene(this); + + return DAWN_GAME_INIT_RESULT_SUCCESS; +} + +int32_t DawnGame::update(float_t delta) { + this->assetManager.update(); + this->inputManager.update(); + this->timeManager.update(delta); + + if(this->scene != nullptr) this->scene->update(); + + this->renderManager.update(); + + return DAWN_GAME_UPDATE_RESULT_SUCCESS; +} + +void DawnGame::sceneCutover(Scene *scene) { + if(scene == nullptr) scene = this->scene; + this->sceneToCutTo = scene; +} \ No newline at end of file diff --git a/src/dawntictactoe/game/DawnGame.hpp b/src/dawntictactoe/game/DawnGame.hpp new file mode 100644 index 00000000..8839502f --- /dev/null +++ b/src/dawntictactoe/game/DawnGame.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "game/_DawnGame.hpp" +#include "scene/components/Components.hpp" +#include "save/DawnGameSaveManager.hpp" + +namespace Dawn { + class DawnGame : public IDawnGame { + private: + Scene *sceneToCutTo = nullptr; + + public: + DawnHost *host; + RenderManager renderManager; + AssetManager assetManager; + InputManager inputManager; + TimeManager timeManager; + LocaleManager localeManager; + DawnGameSaveManager saveManager; + PhysicsManager physicsManager; + + DawnGame(DawnHost *host); + int32_t init() override; + int32_t update(float_t delta) override; + void sceneCutover(Scene *scene) override; + }; +} \ No newline at end of file diff --git a/src/dawntictactoe/input/InputBinds.hpp b/src/dawntictactoe/input/InputBinds.hpp new file mode 100644 index 00000000..89145de6 --- /dev/null +++ b/src/dawntictactoe/input/InputBinds.hpp @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "input/InputManager.hpp" + +#define INPUT_BIND(n) ((inputbind_t)n) +#define INPUT_BIND_ACCEPT INPUT_BIND(1) +#define INPUT_BIND_NEGATIVE_X INPUT_BIND(2) +#define INPUT_BIND_POSITIVE_X INPUT_BIND(3) +#define INPUT_BIND_NEGATIVE_Y INPUT_BIND(4) +#define INPUT_BIND_POSITIVE_Y INPUT_BIND(5) \ No newline at end of file diff --git a/src/dawntictactoe/save/CMakeLists.txt b/src/dawntictactoe/save/CMakeLists.txt new file mode 100644 index 00000000..2d057169 --- /dev/null +++ b/src/dawntictactoe/save/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DAWN_TARGET_NAME} + PRIVATE + DawnGameSaveManager.cpp +) \ No newline at end of file diff --git a/src/dawntictactoe/save/DawnGameSaveManager.cpp b/src/dawntictactoe/save/DawnGameSaveManager.cpp new file mode 100644 index 00000000..75a006b4 --- /dev/null +++ b/src/dawntictactoe/save/DawnGameSaveManager.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "DawnGameSaveManager.hpp" + +using namespace Dawn; + +DawnGameSaveManager::DawnGameSaveManager(DawnGame *game) : SaveManager(game) { +} + +bool_t DawnGameSaveManager::validateSave(struct SaveFile raw) { + if(!raw.has(POKER_SAVE_KEY_EXAMPLE)) return true; + this->currentSave.copy(raw, POKER_SAVE_KEY_EXAMPLE); + + return false; +} + +void DawnGameSaveManager::setExample(int32_t val) { + savedata_t value; + value.i32 = val; + this->currentSave.set(POKER_SAVE_KEY_EXAMPLE, value); +} + +int32_t DawnGameSaveManager::getExample() { + return this->currentSave.get(POKER_SAVE_KEY_EXAMPLE).i32; +} \ No newline at end of file diff --git a/src/dawntictactoe/save/DawnGameSaveManager.hpp b/src/dawntictactoe/save/DawnGameSaveManager.hpp new file mode 100644 index 00000000..bc946262 --- /dev/null +++ b/src/dawntictactoe/save/DawnGameSaveManager.hpp @@ -0,0 +1,22 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "save/SaveManager.hpp" + +#define POKER_SAVE_KEY_EXAMPLE "poker.example" + +namespace Dawn { + class DawnGameSaveManager : public SaveManager { + protected: + virtual bool_t validateSave(struct SaveFile raw) override; + + public: + DawnGameSaveManager(DawnGame *game); + + void setExample(int32_t value); + int32_t getExample(); + }; +} \ No newline at end of file diff --git a/src/dawntools/tools/CMakeLists.txt b/src/dawntools/tools/CMakeLists.txt index 9eccd122..90998bce 100644 --- a/src/dawntools/tools/CMakeLists.txt +++ b/src/dawntools/tools/CMakeLists.txt @@ -79,10 +79,12 @@ endfunction() # Language Tool function(tool_language target in) add_custom_target(${target} - COMMAND languagegen "${DAWN_ASSETS_SOURCE_DIR}/${in}" "${DAWN_ASSETS_BUILD_DIR}" + COMMAND languagegen --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_TOOL_GENERATED_LANG_DIR}/${target}.language" COMMENT "Generating language set ${target} from ${in}" DEPENDS languagegen ) + tool_generatedlanguages("${DAWN_TOOL_GENERATED_LANG_DIR}/${target}.language") + add_dependencies(${DAWN_TARGET_NAME} ${target}) endfunction() # Audio Tool diff --git a/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp b/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp index 97a43679..ded35f47 100644 --- a/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp +++ b/src/dawntools/tools/generatedlanguages/GeneratedLanguages.cpp @@ -26,6 +26,7 @@ int32_t GeneratedLanguages::start() { // Now process each language file std::map> strings; + std::vector knownKeys; auto itFiles = files.begin(); while(itFiles != files.end()) { @@ -84,6 +85,7 @@ int32_t GeneratedLanguages::start() { File langOut(flags["output"] + FILE_PATH_SEP + "language_" + itLang->first + ".language"); bufferOut.clear(); + auto itKeys = knownKeys.begin(); while(itKeys != knownKeys.end()) { auto key = *itKeys; @@ -97,6 +99,7 @@ int32_t GeneratedLanguages::start() { } // Write out. + langOut.mkdirp(); if(!langOut.writeString(bufferOut)) { std::cout << "Failed to create output file \"" + langOut.filename + "\"" << std::endl; return 1; diff --git a/src/dawntools/tools/languagegen/CMakeLists.txt b/src/dawntools/tools/languagegen/CMakeLists.txt index 4d20d747..4b32b157 100644 --- a/src/dawntools/tools/languagegen/CMakeLists.txt +++ b/src/dawntools/tools/languagegen/CMakeLists.txt @@ -6,31 +6,32 @@ # Texture Build Tool project(languagegen VERSION 2.0) add_executable(languagegen) + + +# Sources target_sources(languagegen PRIVATE ${DAWN_SHARED_SOURCES} + ${DAWN_TOOL_SOURCES} LanguageGen.cpp - ../../util/DawnTool.cpp - ../../util/File.cpp - - ../../util/file.cpp - ../../util/csv.cpp - ../../util/xml.cpp ) +# Includes target_include_directories(languagegen PUBLIC ${DAWN_SHARED_INCLUDES} - ${CMAKE_CURRENT_LIST_DIR}/../../ + ${DAWN_TOOL_INCLUDES} ${CMAKE_CURRENT_LIST_DIR} ) +# Definitions target_compile_definitions(languagegen PUBLIC DAWN_TOOL_INSTANCE=LanguageGen DAWN_TOOL_HEADER="LanguageGen.hpp" ) +# Libraries target_link_libraries(languagegen PUBLIC ${DAWN_BUILD_HOST_LIBS} diff --git a/src/dawntools/tools/languagegen/LanguageGen.cpp b/src/dawntools/tools/languagegen/LanguageGen.cpp index e2e8994a..7cdc8076 100644 --- a/src/dawntools/tools/languagegen/LanguageGen.cpp +++ b/src/dawntools/tools/languagegen/LanguageGen.cpp @@ -9,162 +9,144 @@ using namespace Dawn; -int32_t LanguageGen::start() { - if(this->args.size() != 3) { - std::cout << "Invalid number of arguments provided to language gen!" << std::endl; - return 1; +std::vector LanguageParser::getRequiredAttributes() { + return std::vector{ "key" }; +} + +std::map LanguageParser::getOptionalAttributes() { + return std::map(); +} + +int32_t LanguageParser::onParse( + Xml *node, + std::map values, + struct LanguageString *out, + std::string *error +) { + out->key = values["key"]; + out->text = node->value; + return 0; +} + + + +std::vector LanguageGroupParser::getRequiredAttributes() { + return std::vector{ "key" }; +} + +std::map LanguageGroupParser::getOptionalAttributes() { + return std::map(); +} + +int32_t LanguageGroupParser::onParse( + Xml *node, + std::map values, + struct LanguageGroup *out, + std::string *error +) { + std::string key = values["key"]; + out->key += key; + + auto it = node->children.begin(); + int32_t ret; + while(it != node->children.end()) { + auto c = *it; + if(c->node == "string") { + struct LanguageString string; + ret = (LanguageParser()).parse(c, &string, error); + if(ret != 0) return ret; + + string.lang = out->lang; + string.key = out->key + "." + string.key; + out->strings.push_back(string); + + } else if(c->node == "group") { + struct LanguageGroup group; + group.key += key + "."; + group.lang = out->lang; + + ret = (LanguageGroupParser()).parse(c, &group, error); + if(ret != 0) return ret; + vectorAppend(&out->strings, group.strings); + + } + ++it; + } + return 0; +} + + + +std::vector LanguageRootParser::getRequiredAttributes() { + return std::vector{ "lang" }; +} + +std::map LanguageRootParser::getOptionalAttributes() { + return std::map(); +} + +int32_t LanguageRootParser::onParse( + Xml *node, + std::map values, + struct LanguageRoot *out, + std::string *error +) { + int32_t ret; + out->lang = values["lang"]; + + auto it = node->children.begin(); + while(it != node->children.end()) { + auto c = *it; + if(c->node == "string") { + struct LanguageString string; + ret = (LanguageParser()).parse(c, &string, error); + if(ret != 0) return ret; + string.lang = out->lang; + out->strings.push_back(string); + + } else if(c->node == "group") { + struct LanguageGroup group; + group.lang = out->lang; + ret = (LanguageGroupParser()).parse(c, &group, error); + if(ret != 0) return ret; + vectorAppend(&out->strings, group.strings); + + } + + ++it; } - auto fileIn = File(this->args[1]); + return 0; +} + + + +std::vector LanguageGen::getRequiredFlags() { + return std::vector{ "input", "output" }; +} + +int32_t LanguageGen::start() { + auto fileIn = File(flags["input"]); std::string buffer; if(!fileIn.readString(&buffer)) { std::cout << "Failed to open/read input file " << fileIn.filename << std::endl; return 1; } + + auto xml = Xml::load(buffer); + std::string error; + struct LanguageRoot root; - // Begin parsing. Start by looking for the tags - std::vector languages; - 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); - } - ++itChildren; + auto ret = (LanguageRootParser()).parse(&xml, &root, &error); + if(ret != 0) { + std::cout << error << std::endl; + return ret; } - // Now begin actually parsing - std::map> strings; - 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; - return 1; - } - ++itChildren; - } - - // Now we validate each lang has each key. - std::vector keys; - auto it = strings.begin(); - while(it != strings.end()) { - auto it2 = it->second.begin(); - while(it2 != it->second.end()) { - auto key = it2->key; - auto exist = std::find(keys.begin(), keys.end(), key); - if(exist == keys.end()) { - keys.push_back(key); - } - it2++; - } - ++it; - } - - // Now we actually parse each string, validating as we go. - it = strings.begin(); - while(it != strings.end()) { - std::vector itKeys; - - std::string bufferOut; - - auto it2 = it->second.begin(); - while(it2 != it->second.end()) { - auto l = *it2; - itKeys.push_back(l.key); - bufferOut += l.key + "|" + l.value + "|"; - it2++; - } - - File fileOut(this->args[2] + "/language_" + it->first + ".language"); - if(!fileOut.mkdirp()) { - std::cout << "Failed to create output folder" << std::endl; - } - if(!fileOut.writeString(bufferOut)) { - std::cout << "Failed to write to output file " << fileOut.filename << std::endl; - return 1; - } - - auto it3 = keys.begin(); - while(it3 != keys.end()) { - auto key = *it3; - auto inIt = std::find(itKeys.begin(), itKeys.end(), key); - if(inIt == itKeys.end()) { - std::cout << "Locale " << it->first << " missing key " << key << std::endl; - } - it3++; - } - - if(itKeys.size() != keys.size()) { - std::cout << "Locale is missing some keys, see above" << std::endl; - return 1; - } - ++it; - } - - return 0; -} - -int32_t LanguageGen::parseString( - Xml *stringNode, - std::string key, - std::map> *strings -) { - auto attrLang = stringNode->attributes.find("lang"); - if(attrLang == stringNode->attributes.end()) { - std::cout << "String is missing lang parameter." << std::endl; - return -1; - } - - struct LanguageString str; - str.key = key; - str.value = stringNode->value; - - auto existing = (*strings).find(attrLang->second); - if(existing == (*strings).end()) { - (*strings).insert(std::make_pair(attrLang->second, std::vector())); - } - (*strings)[attrLang->second].push_back(str); - return 0; -} - -int32_t LanguageGen::parseGroup( - Xml *groupNode, - std::string key, - std::map> *strings -) { - int32_t ret; - - 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 += attrKey->second; - - 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(child->node == "group") { - ret = this->parseGroup(child, key, strings); - if(ret != 0) return ret; - } - ++itChildren; - } + // Now dump out the language strings to be picked up later. + ret = languageSaveStrings(flags["output"], root.strings); + if(ret != 0) return ret; return 0; } \ No newline at end of file diff --git a/src/dawntools/tools/languagegen/LanguageGen.hpp b/src/dawntools/tools/languagegen/LanguageGen.hpp index e15e4d59..c643baff 100644 --- a/src/dawntools/tools/languagegen/LanguageGen.hpp +++ b/src/dawntools/tools/languagegen/LanguageGen.hpp @@ -5,17 +5,62 @@ #pragma once #include "util/DawnTool.hpp" -#include "util/Xml.hpp" #include "util/File.hpp" +#include "util/XmlParser.hpp" +#include "util/Language.cpp" namespace Dawn { - struct LanguageString { + struct LanguageGroup { + std::vector strings; std::string key; - std::string value; + std::string lang; + }; + + struct LanguageRoot { + std::string lang; + std::vector strings; + }; + + class LanguageParser : public XmlParser { + protected: + std::vector getRequiredAttributes(); + std::map getOptionalAttributes(); + int32_t onParse( + Xml *node, + std::map values, + struct LanguageString *out, + std::string *error + ); + }; + + class LanguageGroupParser : public XmlParser { + protected: + std::vector getRequiredAttributes(); + std::map getOptionalAttributes(); + int32_t onParse( + Xml *node, + std::map values, + struct LanguageGroup *out, + std::string *error + ); + }; + + class LanguageRootParser : public XmlParser { + protected: + std::vector getRequiredAttributes(); + std::map getOptionalAttributes(); + int32_t onParse( + Xml *node, + std::map values, + struct LanguageRoot *out, + std::string *error + ) override; }; class LanguageGen : public DawnTool { protected: + std::vector getRequiredFlags() override; + int32_t parseGroup( Xml *node, std::string key,