From 7e09b1760eb2195f71d766a49af6e71e922036c9 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Thu, 9 Mar 2023 22:52:22 -0800
Subject: [PATCH] POC

---
 assets/games/tictactoe/test.xml               |   5 +
 src/dawn/asset/Asset.hpp                      |   2 +-
 src/dawn/display/animation/Animation.hpp      |   2 +-
 src/dawn/locale/LocaleManager.hpp             | 134 +++----
 src/dawn/scene/Scene.hpp                      |   2 +-
 src/dawn/state/StateProvider.hpp              |  53 +++
 src/dawntictactoe/CMakeLists.txt              |   1 +
 .../components/TicTacToeGame.cpp              |   5 +
 src/dawntictactoe/scenes/TicTacToeScene.hpp   |   4 +-
 src/dawntools/tools/CMakeLists.txt            |   4 +-
 src/dawntools/tools/uigen/CMakeLists.txt      |  24 +-
 src/dawntools/tools/uigen/UIGen.cpp           |  57 +++
 src/dawntools/tools/uigen/UIGen.hpp           |  20 +
 src/dawntools/tools/uigen/main.cpp            | 343 ------------------
 .../tools/uigen/parse/elements/children.hpp   |  68 ++++
 .../tools/uigen/parse/elements/label.hpp      |  46 +++
 src/dawntools/tools/uigen/parse/root.hpp      | 106 ++++++
 17 files changed, 453 insertions(+), 423 deletions(-)
 create mode 100644 assets/games/tictactoe/test.xml
 create mode 100644 src/dawn/state/StateProvider.hpp
 create mode 100644 src/dawntools/tools/uigen/UIGen.cpp
 create mode 100644 src/dawntools/tools/uigen/UIGen.hpp
 delete mode 100644 src/dawntools/tools/uigen/main.cpp
 create mode 100644 src/dawntools/tools/uigen/parse/elements/children.hpp
 create mode 100644 src/dawntools/tools/uigen/parse/elements/label.hpp
 create mode 100644 src/dawntools/tools/uigen/parse/root.hpp

diff --git a/assets/games/tictactoe/test.xml b/assets/games/tictactoe/test.xml
new file mode 100644
index 00000000..e34d4901
--- /dev/null
+++ b/assets/games/tictactoe/test.xml
@@ -0,0 +1,5 @@
+<root>
+  <label>
+    Hello World!
+  </label>
+</root>
\ No newline at end of file
diff --git a/src/dawn/asset/Asset.hpp b/src/dawn/asset/Asset.hpp
index 7111d09d..87047d80 100644
--- a/src/dawn/asset/Asset.hpp
+++ b/src/dawn/asset/Asset.hpp
@@ -4,7 +4,7 @@
 // https://opensource.org/licenses/MIT
 
 #pragma once
-#include "state/State.hpp"
+#include "state/StateEvent.hpp"
 
 namespace Dawn {
   class AssetManager;
diff --git a/src/dawn/display/animation/Animation.hpp b/src/dawn/display/animation/Animation.hpp
index 416dc48e..048be736 100644
--- a/src/dawn/display/animation/Animation.hpp
+++ b/src/dawn/display/animation/Animation.hpp
@@ -4,7 +4,7 @@
 // https://opensource.org/licenses/MIT
 
 #pragma once
-#include "state/State.hpp"
+#include "state/StateEvent.hpp"
 #include "Easing.hpp"
 #include "util/mathutils.hpp"
 
diff --git a/src/dawn/locale/LocaleManager.hpp b/src/dawn/locale/LocaleManager.hpp
index e341fe16..f6523604 100644
--- a/src/dawn/locale/LocaleManager.hpp
+++ b/src/dawn/locale/LocaleManager.hpp
@@ -1,68 +1,68 @@
-// Copyright (c) 2022 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "state/State.hpp"
-#include "asset/AssetManager.hpp"
-
-namespace Dawn {
-  class DawnGame;
-
-  struct Locale {
-    std::string language;
-  };
-
-  class LocaleManager : public StateOwner {
-    private:
-      DawnGame *game;
-      LanguageAsset *asset;
-      struct Locale locale;
-      LanguageAsset *currentlyLoadedAsset = nullptr;
-      LanguageAsset *loadingAsset = nullptr;
-
-      /** Listens for when the pending language loads. */
-      void onLanguageLoaded();
-
-    public:
-      StateEvent<> eventLocaleChanged;
-      StateEvent<> eventLanguageUpdated;
-
-      /**
-       * Initializes the Locale Manager Instance. Locale Manager is responsible
-       * for handling anything that will change depending on which region the
-       * player is in.
-       * 
-       * @param game Game instance this manager belongs to.
-       */
-      LocaleManager(DawnGame *game);
-
-      /**
-       * Initializes the LocaleManager and loads the default language.
-       */
-      void init();
-
-      /**
-       * Change the locale and begin loading the new language.
-       * 
-       * @param locale Locale to switch to.
-       */
-      void setLocale(struct Locale locale);
-
-      /**
-       * Gets the current locale.
-       * 
-       * @return Current locale.
-       */
-      struct Locale getLocale();
-    
-      /**
-       * Returns a language string from the language CSV file.
-       * 
-       * @param key Key of the string to get.
-       * @return The translated string.
-       */
-      std::string getString(std::string key); 
-  };
+// Copyright (c) 2022 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "state/StateEvent.hpp"
+#include "asset/AssetManager.hpp"
+
+namespace Dawn {
+  class DawnGame;
+
+  struct Locale {
+    std::string language;
+  };
+
+  class LocaleManager {
+    private:
+      DawnGame *game;
+      LanguageAsset *asset;
+      struct Locale locale;
+      LanguageAsset *currentlyLoadedAsset = nullptr;
+      LanguageAsset *loadingAsset = nullptr;
+
+      /** Listens for when the pending language loads. */
+      void onLanguageLoaded();
+
+    public:
+      StateEvent<> eventLocaleChanged;
+      StateEvent<> eventLanguageUpdated;
+
+      /**
+       * Initializes the Locale Manager Instance. Locale Manager is responsible
+       * for handling anything that will change depending on which region the
+       * player is in.
+       * 
+       * @param game Game instance this manager belongs to.
+       */
+      LocaleManager(DawnGame *game);
+
+      /**
+       * Initializes the LocaleManager and loads the default language.
+       */
+      void init();
+
+      /**
+       * Change the locale and begin loading the new language.
+       * 
+       * @param locale Locale to switch to.
+       */
+      void setLocale(struct Locale locale);
+
+      /**
+       * Gets the current locale.
+       * 
+       * @return Current locale.
+       */
+      struct Locale getLocale();
+    
+      /**
+       * Returns a language string from the language CSV file.
+       * 
+       * @param key Key of the string to get.
+       * @return The translated string.
+       */
+      std::string getString(std::string key); 
+  };
 }
\ No newline at end of file
diff --git a/src/dawn/scene/Scene.hpp b/src/dawn/scene/Scene.hpp
index 3d8913aa..ea8d9d65 100644
--- a/src/dawn/scene/Scene.hpp
+++ b/src/dawn/scene/Scene.hpp
@@ -8,7 +8,7 @@
 #include "asset/Asset.hpp"
 #include "scene/debug/SceneDebugLine.hpp"
 #include "physics/ScenePhysicsManager.hpp"
-#include "state/State.hpp"
+#include "state/StateEvent.hpp"
 
 namespace Dawn {
   class DawnGame;
diff --git a/src/dawn/state/StateProvider.hpp b/src/dawn/state/StateProvider.hpp
new file mode 100644
index 00000000..6508dc2b
--- /dev/null
+++ b/src/dawn/state/StateProvider.hpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "state/State.hpp"
+
+namespace Dawn {
+  class SceneItemComponent;
+
+  template<typename D, typename...A>
+  struct StateListener {
+    std::function<void(A...)> callback;
+    D data;
+  };
+
+  template<typename D, typename...A>
+  struct StateProviderSet {
+    private:
+      std::vector<StateListener<D,A...>> listeners;
+
+    public:
+      std::function<void()> addEffect(
+        std::function<void(A...)> callback,
+        D data
+      ) {
+        struct StateListener<D, A...> l;
+        l.callback = callback;
+        l.data = data;
+        this->listeners.push_back(l);
+
+        l.callback();
+
+        return std::bind([&](struct StateListener<D, A...> listener) {
+          assertUnreachable();
+        }, l);
+      }
+  };
+
+  class TimeProvider {
+    public:
+      StateProviderSet<float_t> effect;
+  };
+
+  std::function<void()> useTimeout(
+    std::function<void()> someCallback,
+    float_t timeout,
+    SceneItemComponent *context
+  ) {
+    return (TimeProvider()).effect.addEffect(someCallback, timeout);
+  }
+}
\ No newline at end of file
diff --git a/src/dawntictactoe/CMakeLists.txt b/src/dawntictactoe/CMakeLists.txt
index ec04854e..1635654a 100644
--- a/src/dawntictactoe/CMakeLists.txt
+++ b/src/dawntictactoe/CMakeLists.txt
@@ -26,3 +26,4 @@ set(DIR_GAME_ASSETS games/tictactoe)
 tool_language(locale_en ${DIR_GAME_ASSETS}/locale/en.xml)
 tool_tileset(tileset_xo texture_xo ${DIR_GAME_ASSETS}/xo.png 1 4)
 tool_truetype(truetype_bizudp ${DIR_GAME_ASSETS}/font/BIZUDPGothic-Bold.ttf truetype_bizudp 2048 2048 120)
+tool_ui(ui_test ${DIR_GAME_ASSETS}/test.xml ui_test)
\ No newline at end of file
diff --git a/src/dawntictactoe/components/TicTacToeGame.cpp b/src/dawntictactoe/components/TicTacToeGame.cpp
index 1da6e217..7bc111a0 100644
--- a/src/dawntictactoe/components/TicTacToeGame.cpp
+++ b/src/dawntictactoe/components/TicTacToeGame.cpp
@@ -7,6 +7,7 @@
 #include "game/DawnGame.hpp"
 #include "scene/components/example/ExampleSpin.hpp"
 #include "scene/components/physics/3d/CubeCollider.hpp"
+#include "state/StateProvider.hpp"
 
 using namespace Dawn;
 
@@ -29,6 +30,10 @@ void TicTacToeGame::onStart() {
     ++itTiles;
   }
 
+  useTimeout([&]{
+    std::cout << "Timeout" << std::endl;
+  }, 1000, this)();
+
   useEffect([&]{
     if(!gameOver) return;
     
diff --git a/src/dawntictactoe/scenes/TicTacToeScene.hpp b/src/dawntictactoe/scenes/TicTacToeScene.hpp
index e1331b71..cb79f8bb 100644
--- a/src/dawntictactoe/scenes/TicTacToeScene.hpp
+++ b/src/dawntictactoe/scenes/TicTacToeScene.hpp
@@ -12,10 +12,8 @@
 #include "prefabs/SimpleLabel.hpp"
 #include "scene/components/ui/menu/UISimpleMenu.hpp"
 
-#include "state/State.hpp"
-
 namespace Dawn {
-  class TicTacToeScene : public Scene, public StateOwner {
+  class TicTacToeScene : public Scene {
     protected:
       Camera *camera;
       std::function<void()> evtUnsub;
diff --git a/src/dawntools/tools/CMakeLists.txt b/src/dawntools/tools/CMakeLists.txt
index 8d671409..d862041a 100644
--- a/src/dawntools/tools/CMakeLists.txt
+++ b/src/dawntools/tools/CMakeLists.txt
@@ -50,8 +50,8 @@ endfunction()
 # UI Tool
 function(tool_ui target in)
   add_custom_target(${target}
-    COMMAND uigen "${DAWN_ASSETS_SOURCE_DIR}/${in}" "${DAWN_GENERATED_DIR}/ui/${target}"
-    COMMENT "Generating ui ${target} from ${in}"
+    COMMAND uigen --input="${DAWN_ASSETS_SOURCE_DIR}/${in}" --output="${DAWN_GENERATED_DIR}/prefabs/ui/${target}"
+    COMMENT "Generating UI ${target} from ${in}"
     DEPENDS uigen
   )
   target_include_directories(${DAWN_TARGET_NAME}
diff --git a/src/dawntools/tools/uigen/CMakeLists.txt b/src/dawntools/tools/uigen/CMakeLists.txt
index 3fcd0242..535d254b 100644
--- a/src/dawntools/tools/uigen/CMakeLists.txt
+++ b/src/dawntools/tools/uigen/CMakeLists.txt
@@ -3,21 +3,35 @@
 # This software is released under the MIT License.
 # https://opensource.org/licenses/MIT
 
-# Texture Build Tool
 project(uigen VERSION 1.0)
 add_executable(uigen)
+
+
+# Sources
 target_sources(uigen
   PRIVATE
-    main.cpp
-    ../../util/file.cpp
-    ../../util/xml.cpp
+    ${DAWN_SHARED_SOURCES}
+    ${DAWN_TOOL_SOURCES}
+    UIGen.cpp
 )
+
+# Includes
 target_include_directories(uigen
   PUBLIC
     ${DAWN_SHARED_INCLUDES}
-    ${CMAKE_CURRENT_LIST_DIR}/../../
+    ${DAWN_TOOL_INCLUDES}
     ${CMAKE_CURRENT_LIST_DIR}
 )
+
+# Definitions
+target_compile_definitions(uigen
+  PUBLIC
+    ${DAWN_SHARED_DEFINITIONS}
+    DAWN_TOOL_INSTANCE=UIGen
+    DAWN_TOOL_HEADER="UIGen.hpp"
+)
+
+# Libraries
 target_link_libraries(uigen
   PUBLIC
     ${DAWN_BUILD_HOST_LIBS}
diff --git a/src/dawntools/tools/uigen/UIGen.cpp b/src/dawntools/tools/uigen/UIGen.cpp
new file mode 100644
index 00000000..989e9c31
--- /dev/null
+++ b/src/dawntools/tools/uigen/UIGen.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#include "UIGen.hpp"
+
+using namespace Dawn;
+
+std::vector<std::string> UIGen::getRequiredFlags() {
+  return std::vector<std::string>{ "input", "output" };
+}
+
+int32_t UIGen::start() {
+  // Open input file.
+  File file(flags["input"]);
+  std::string buffer;
+  if(!file.readString(&buffer)) {
+    std::cout << "Failed to read " << file.filename << std::endl;
+    return 1;
+  }
+
+  // Parse XML
+  Xml xml = Xml::load(buffer);
+
+  std::string error;
+  struct RootInformation info;
+  auto ret = (RootParser()).parse(&xml, &info, &error);
+  if(ret != 0) {
+    std::cout << error << std::endl;
+    return ret;
+  }
+
+  // std::vector<std::string> lines;
+  // RootGen::generate(&lines, &info, "");
+
+  // // Generate buffer
+  // std::string bufferOut;
+  // auto itLine = lines.begin();
+  // while(itLine != lines.end()) {
+  //   bufferOut += *itLine + "\n";
+  //   ++itLine;
+  // }
+
+  // // Finished with XML data, now we can write data out.
+  File fileOut(flags["output"] + ".hpp");
+  if(!fileOut.mkdirp()) {
+    std::cout << "Failed to make ui output dir" << std::endl;
+    return 1;
+  }
+  // if(!fileOut.writeString(bufferOut)) {
+  //   std::cout << "Failed to generate scene " << fileOut.filename << std::endl;
+  //   return 1;
+  // }
+
+  return 0;
+}
\ No newline at end of file
diff --git a/src/dawntools/tools/uigen/UIGen.hpp b/src/dawntools/tools/uigen/UIGen.hpp
new file mode 100644
index 00000000..4a1da836
--- /dev/null
+++ b/src/dawntools/tools/uigen/UIGen.hpp
@@ -0,0 +1,20 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/DawnTool.hpp"
+#include "util/File.hpp"
+#include "parse/root.hpp"
+#include "util/Language.cpp"
+
+namespace Dawn {
+  class UIGen : public DawnTool {
+    protected:
+      std::vector<std::string> getRequiredFlags() override;
+
+    public:
+      int32_t start();
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/tools/uigen/main.cpp b/src/dawntools/tools/uigen/main.cpp
deleted file mode 100644
index d49e783a..00000000
--- a/src/dawntools/tools/uigen/main.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-/**
- * Copyright (c) 2023 Dominic Masters
- * 
- * This software is released under the MIT License.
- * https://opensource.org/licenses/MIT
- */
-
-#include "../../util/xml.hpp"
-#include "../../util/file.hpp"
-#include <iostream>
-#include <vector>
-
-struct UIGenerated {
-  bool align = false;
-  bool alignGrid = false;
-  std::string alignX;
-  std::string alignY;
-  std::string align0;
-  std::string align1;
-  std::string align2;
-  std::string align3;
-
-  bool isGrid = false;
-  std::string rows;
-  std::string columns;
-  std::string gutterX;
-  std::string gutterY;
-
-
-  std::string type;
-  std::string name;
-
-  std::string parent;
-
-  // Optionals
-  bool hasColor = false;
-  std::string color;
-};
-
-std::vector<std::string> split(std::string s, std::string delimiter) {
-  size_t pos_start = 0, pos_end, delim_len = delimiter.length();
-  std::string token;
-  std::vector<std::string> res;
-
-  while ((pos_end = s.find (delimiter, pos_start)) != std::string::npos) {
-    token = s.substr (pos_start, pos_end - pos_start);
-    pos_start = pos_end + delim_len;
-    res.push_back(token);
-  }
-
-  res.push_back (s.substr (pos_start));
-  return res;
-}
-
-std::string alignmentFromRaw(std::string strRaw) {
-  if(strRaw == "start") return "UI_COMPONENT_ALIGN_START";
-  if(strRaw == "middle") return "UI_COMPONENT_ALIGN_MIDDLE";
-  if(strRaw == "end") return "UI_COMPONENT_ALIGN_END";
-  if(strRaw == "stretch") return "UI_COMPONENT_ALIGN_STRETCH";
-  return "";
-}
-
-static bool parseChildren(
-  xml_t *currentNode,
-  std::string parent,
-  std::vector<struct UIGenerated> *items
-) {
-  // Confirm attributes
-  auto attrName = xmlGetAttributeByName(currentNode, "name");
-  if(attrName == -1) {
-    std::cout << "Missing name attribute." << std::endl;
-    return false;
-  }
-
-  struct UIGenerated item;
-  item.name = std::string(currentNode->attributeDatas[attrName] );
-  item.type = std::string(currentNode->node);
-  item.parent = parent;
-
-  // Standard Align
-  auto attrAlign = xmlGetAttributeByName(currentNode, "align");
-  if(attrAlign != -1) {
-    // Parse alignment
-    std::string alignRaw(currentNode->attributeDatas[attrAlign]);
-    std::vector<std::string> alignmentParts = split(alignRaw, " ");
-    if(alignmentParts.size() != 6) {
-      std::cout << "Alignment is invalid" << std::endl;
-      return false;
-    }
-
-    item.align = true;
-    item.alignX = alignmentFromRaw(alignmentParts[0]);
-    item.alignY = alignmentFromRaw(alignmentParts[1]);
-    item.align0 = alignmentParts[2];
-    item.align1 = alignmentParts[3];
-    item.align2 = alignmentParts[4];
-    item.align3 = alignmentParts[5];
-  
-    if(item.alignX.size() == 0) {
-      std::cout << "X Align is invalid" << std::endl;
-      return "";
-    }
-    if(item.alignY.size() == 0) {
-      std::cout << "Y Align is invalid" << std::endl;
-      return "";
-    }
-  }
-
-  // Grid Align
-  auto attrGridAlign = xmlGetAttributeByName(currentNode, "grid");
-  if(attrGridAlign != -1) {
-    // Parse alignment
-    std::string alignRaw(currentNode->attributeDatas[attrGridAlign]);
-    std::vector<std::string> alignmentParts = split(alignRaw, " ");
-    if(alignmentParts.size() != 4) {
-      std::cout << "Grid alignment is invalid" << std::endl;
-      return false;
-    }
-
-    item.alignGrid = true;
-    item.alignX = alignmentFromRaw(alignmentParts[0]);
-    item.alignY = alignmentFromRaw(alignmentParts[1]);
-    item.align0 = alignmentParts[2];
-    item.align1 = alignmentParts[3];
-  
-    if(item.alignX.size() == 0) {
-      std::cout << "X Align is invalid" << std::endl;
-      return "";
-    }
-    if(item.alignY.size() == 0) {
-      std::cout << "Y Align is invalid" << std::endl;
-      return "";
-    }
-  }
-
-  // Parse color
-  auto attrColor = xmlGetAttributeByName(currentNode, "color");
-  if(attrColor != -1) {
-    item.hasColor = true;
-    item.color = currentNode->attributeDatas[attrColor];
-  }
-
-  
-  // Grid
-  if(item.type == "UIGrid") {
-    auto attrRows = xmlGetAttributeByName(currentNode, "rows");
-    auto attrCols = xmlGetAttributeByName(currentNode, "columns");
-    auto attrGutter = xmlGetAttributeByName(currentNode, "gutter");
-    if(attrRows == -1 || attrCols == -1) {
-      std::cout << "Grid is invalid" << std::endl;
-      return false;
-    }
-    item.isGrid = true;
-    item.rows = currentNode->attributeDatas[attrRows];
-    item.columns = currentNode->attributeDatas[attrCols];
-    if(attrGutter != -1) {
-      auto gutterParts = split(currentNode->attributeDatas[attrGutter], " ");
-      if(gutterParts.size() != 2) {
-        std::cout << "Gutter is invalid" << std::endl;
-        return false;
-      }
-      item.gutterX = gutterParts[0];
-      item.gutterY = gutterParts[1];
-    } else {
-      item.gutterX = "0";
-      item.gutterY = "0";
-    }
-  }
-
-  // Self
-  items->push_back(item);
-
-  // Children
-  for(int32_t i = 0; i < currentNode->childrenCount; i++) {
-    if(!parseChildren(currentNode->children + i, item.name, items)) return false;
-  }
-
-  return true;
-}
-
-int main(int argc, char *args[]) {
-  if(argc != 3) {
-    std::cout << "Invalid number of args for ui gen" << std::endl;
-    return 1;
-  }
-
-  // Open input file.
-  char fileIn[FILENAME_MAX];
-  fileNormalizeSlashes(args[1]);
-  sprintf(fileIn, "%s", args[1]);
-  FILE *fin = fopen(fileIn, "rb");
-  if(fin == NULL) {
-    std::cout << "Failed to open input file " << fileIn << std::endl;
-    return 1;
-  }
-
-  // Tell file len
-  fseek(fin, 0, SEEK_END);
-  auto len = ftell(fin);
-  fseek(fin, 0, SEEK_SET);
-
-  // Read data.
-  char *buffer = (char *)malloc(sizeof(char) * (len + 1));
-  if(buffer == NULL) {
-    std::cout << "Failed to create temporary memory." << std::endl;
-    fclose(fin);
-    return 1;
-  }
-  assetReadString(fin, buffer);
-  fclose(fin);
-
-  // Parse XML
-  xml_t xml;
-  xmlLoad(&xml, buffer);
-  free(buffer);
-
-  // Begin output
-  std::string bufferOut = "";
-
-  // Imports
-  bufferOut += "#pragma once\n";
-  for(int32_t i = 0; i < xml.attributeCount; i++) {
-    std::string attrName = xml.attributeNames[i];
-
-    if(
-      attrName == "name"
-    ) continue;
-
-    bufferOut += "#include \"";
-    bufferOut += xml.attributeDatas[i];
-    bufferOut += "\"\n";
-  }
-  bufferOut += "\n";
-
-  // Now prep class itself.
-  auto attrName = xmlGetAttributeByName(&xml, "name");
-  if(attrName == -1) {
-    std::cout << "Missing " << std::endl;
-    xmlDispose(&xml);
-    return 1;
-  }
-  std::string name = xml.attributeDatas[attrName];
-
-  // Children
-  std::vector<struct UIGenerated> items;
-  for(int32_t j = 0; j < xml.childrenCount; j++) {
-    if(parseChildren(xml.children + j, "", &items)) continue;
-    xmlDispose(&xml);
-    return 1;
-  }
-
-  // Generate strings.
-  bufferOut += "namespace Dawn {\n";
-  bufferOut += "  class " + name + " : public UIEmpty {\n";
-  bufferOut += "    public:\n";
-  auto it = items.begin();
-  while(it != items.end()) {
-    auto c = *it;
-    bufferOut += "      " + c.type + " " + c.name + ";\n";
-    ++it;
-  }
-  bufferOut += "\n";
-  bufferOut += "      " + name + "(UICanvas *canvas) : UIEmpty(canvas),\n";
-  it = items.begin();
-  while(it != items.end()) {
-    auto c = *it;
-    bufferOut += "        " + c.name + "(canvas)";
-    if(it != items.end() - 1) bufferOut += ",";
-    bufferOut += "\n";
-    ++it;
-  }
-  bufferOut += "      {\n";
-  
-  it = items.begin();
-  while(it != items.end()) {
-    auto c = *it;
-    bufferOut += "\n";
-
-    // Transform
-    if(c.align) {
-      bufferOut += "        " + c.name + ".setTransform(\n";
-      bufferOut += "          " + c.alignX + ", " + c.alignY + ",\n";
-      bufferOut += "          glm::vec4(" + c.align0 + ", " + c.align1 + ", " + c.align2 + ", " + c.align3 + "),\n";
-      bufferOut += "          0.0f\n";
-      bufferOut += "        );\n";
-    }
-
-    // Color
-    if(c.hasColor) {
-      bufferOut += "        " + c.name + ".color = " + c.color + ";\n";
-    }
-
-    // Grid
-    if(c.isGrid) {
-      bufferOut += "        " + c.name + ".setGridSize(\n";
-      bufferOut += "          " + c.rows + ", " + c.columns + ",\n";
-      bufferOut += "          " + c.gutterX + ", " + c.gutterY + "\n";
-      bufferOut += "        );\n";
-    }
-
-    // Parent setting
-    if(c.alignGrid) {
-      bufferOut += "        " + c.parent + ".addToGrid(\n";
-      bufferOut += "          &" + c.name + ",\n";
-      bufferOut += "          " + c.align0 + ", " + c.align1 + ",\n";
-      bufferOut += "          " + c.alignX + ", " + c.alignY + "\n";
-      bufferOut += "        );\n";
-    } else {
-      if(c.parent == "") {
-        bufferOut += "        this->addChild(&" + c.name + ");\n";
-      } else {
-        bufferOut += "        " + c.parent + ".addChild(&"  + c.name + ");\n";
-      }
-    }
-    ++it;
-  }
-
-  bufferOut += "      }\n";
-  bufferOut += "  };\n";
-  bufferOut += "}";
-
-  xmlDispose(&xml);
-
-  // Finished with XML data, now we can write data out.
-  char fileOut[FILENAME_MAX];
-  fileNormalizeSlashes(args[2]);
-  sprintf(fileOut, "%s.hpp", args[2]);
-  fileMkdirp(fileOut);
-  FILE *fout = fopen(fileOut, "wb");
-  if(fout == NULL) {
-    std::cout << "Failed to open output file." << std::endl;
-    return 1;
-  }
-
-  // Buffer out data.
-  const char *bufferOutStr = bufferOut.c_str();
-  fwrite(bufferOutStr, sizeof(char), strlen(bufferOutStr), fout);
-  fclose(fout);
-  std::cout << "Generated UI " << fileOut << std::endl;
-
-  // Cleanup
-  return 0;
-}
\ No newline at end of file
diff --git a/src/dawntools/tools/uigen/parse/elements/children.hpp b/src/dawntools/tools/uigen/parse/elements/children.hpp
new file mode 100644
index 00000000..4bfe42ba
--- /dev/null
+++ b/src/dawntools/tools/uigen/parse/elements/children.hpp
@@ -0,0 +1,68 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "label.hpp"
+
+namespace Dawn {
+  enum ChildType {
+    CHILD_TYPE_LABEL
+  };
+
+  struct ChildInfo;
+
+  struct ChildrenInfo {
+    std::vector<struct ChildInfo> children;
+  };
+
+  struct ChildInfo {
+    enum ChildType type;
+    struct LabelInfo label;
+    struct ChildrenInfo children;
+  };
+
+  class ChildrenParser : public XmlParser<struct ChildrenInfo> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>();
+      }
+
+      std::map<std::string, std::string> getOptionalAttributes() {
+        return std::map<std::string, std::string>();
+      }
+
+      int32_t onParse(
+        Xml *node,
+        std::map<std::string, std::string> values,
+        struct ChildrenInfo *out,
+        std::string *error
+      ) {
+        // Parse children of self.
+        int32_t ret = 0;
+        auto itChildren = node->children.begin();
+        while(itChildren != node->children.end()) {
+          auto c = *itChildren;
+          struct ChildInfo child;
+
+          if(c->node == "label") {
+            child.type = CHILD_TYPE_LABEL;
+            ret = (LabelParser()).parse(c, &child.label, error);
+          } else {
+            *error = "Unrecognized UI Element " + c->node;
+            return 1;
+          }
+          if(ret != 0) return ret;
+
+          // Now Parse children of children
+          ret = (ChildrenParser()).parse(c, &child.children, error);
+          if(ret != 0) return ret;
+          
+          ++itChildren;
+        }
+
+        return ret;
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/tools/uigen/parse/elements/label.hpp b/src/dawntools/tools/uigen/parse/elements/label.hpp
new file mode 100644
index 00000000..f82e0f31
--- /dev/null
+++ b/src/dawntools/tools/uigen/parse/elements/label.hpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+
+namespace Dawn {
+  struct LabelInfo {
+    std::string text = "";
+    std::string fontSize = "";
+  };
+
+  class LabelParser : public XmlParser<struct LabelInfo> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>();
+      }
+
+      std::map<std::string, std::string> getOptionalAttributes() {
+        return {
+          { "fontSize", "" }
+        };
+      }
+
+      int32_t onParse(
+        Xml *node,
+        std::map<std::string, std::string> values,
+        struct LabelInfo *out,
+        std::string *error
+      ) {
+        int32_t ret = 0;
+
+        if(values["fontSize"].size() > 0) {
+          out->fontSize = values["fontSize"];
+        }
+
+        if(node->value.size() > 0) {
+          out->text = node->value;
+        }
+
+        return ret;
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/tools/uigen/parse/root.hpp b/src/dawntools/tools/uigen/parse/root.hpp
new file mode 100644
index 00000000..35701c72
--- /dev/null
+++ b/src/dawntools/tools/uigen/parse/root.hpp
@@ -0,0 +1,106 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "parse/elements/children.hpp"
+
+namespace Dawn {
+  struct RootInformation {
+    std::vector<std::string> includes;
+    struct ChildrenInfo children;
+  };
+
+  class RootParser : public XmlParser<struct RootInformation> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>();
+      }
+
+      std::map<std::string, std::string> getOptionalAttributes() {
+        return std::map<std::string, std::string>();
+      }
+
+      int32_t onParse(
+        Xml *node,
+        std::map<std::string, std::string> values,
+        struct RootInformation *out,
+        std::string *error
+      ) {
+        int32_t ret;
+        if(node->node != "root") {
+          *error = "Root node is of an invalid type";
+          return 1;
+        }
+
+        ret = (ChildrenParser()).parse(node, &out->children, error);
+        if(ret != 0) return ret;
+
+        return ret;
+      }
+  };
+
+  // class RootGen : public CodeGen {
+  //   public:
+  //     static void generate(
+  //       std::vector<std::string> *out,
+  //       struct RootInformation *info,
+  //       std::string tabs = ""
+  //     ) {
+  //       struct ClassGenInfo c;
+  //       c.clazz = info->header.scene.name;
+  //       c.extend = info->header.scene.type;
+  //       c.constructorArgs = "DawnGame *game";
+  //       c.extendArgs = "game";
+
+  //       struct MethodGenInfo vnStage;
+  //       vnStage.name = "vnStage";
+  //       vnStage.type = "void";
+  //       vnStage.isOverride = true;
+  //       line(&vnStage.body, info->header.scene.type+  "::vnStage();", "");
+
+  //       struct MethodGenInfo getAssets;
+  //       getAssets.name = "getRequiredAssets";
+  //       getAssets.type = "std::vector<Asset*>";
+  //       getAssets.isOverride = true;
+  //       line(&getAssets.body, "auto man = &this->game->assetManager;", "");
+  //       line(&getAssets.body, "auto assets = " + info->header.scene.type + "::getRequiredAssets();", "");
+
+  //       struct MethodGenInfo getVNEvent;
+  //       getVNEvent.name = "getVNEvent";
+  //       getVNEvent.type = "IVisualNovelEvent *";
+  //       getVNEvent.isOverride = true;
+  //       line(&getVNEvent.body, "auto start = new VisualNovelEmptyEvent(vnManager);", "");
+
+  //       IncludeGen::generate(&c.includes, info->header.includes, "");
+  //       IncludeGen::generate(&c.includes, info->events.includes, "");
+        
+  //       // Characters
+  //       auto itChar = info->header.characters.begin();
+  //       while(itChar != info->header.characters.end()) {
+  //         CharacterGen::generateProperty(&c.publicProperties, *itChar, "");
+  //         CharacterGen::generateInitializer(&vnStage.body, *itChar, "");
+  //         CharacterGen::generateAssets(&getAssets.body, *itChar, "");
+  //         ++itChar;
+  //       }
+
+  //       // Events
+  //       if(info->events.eventTypes.size() > 0) {
+  //         line(&getVNEvent.body, "start", "");
+  //         EventsGen::generate(&getVNEvent.body, &info->events, "  ");
+  //         line(&getVNEvent.body, ";", "");
+  //       }
+
+  //       // Wrap up methods
+  //       line(&getAssets.body, "return assets;", "");
+  //       line(&getVNEvent.body, "return start;", "");
+
+  //       methodGen(&c.publicCode, vnStage);
+  //       line(&c.publicCode, "", "");
+  //       methodGen(&c.publicCode, getAssets);
+  //       methodGen(&c.publicCode, getVNEvent);
+  //       classGen(out, c);
+  //     }
+  // };
+}
\ No newline at end of file