From f1d13d2e45cba0f5e18ffb3699fd2e2c2f1a9d76 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Sun, 12 Feb 2023 21:38:12 -0800
Subject: [PATCH] Redid the VN Scene Parser

---
 src/dawnpokergame/CMakeLists.txt              |   4 +-
 src/dawnpokergame/game/DawnGame.cpp           |   4 +-
 src/dawnpokergame/scenes/Scene_1.hpp          |  58 ---------
 src/dawntools/util/CodeGen.hpp                | 100 +++++++++++++++
 src/dawntools/util/File.cpp                   |   4 +-
 src/dawntools/util/XmlParser.hpp              |  72 +++++++++++
 .../visualnovel/vnscenegen/CMakeLists.txt     |   5 +-
 .../visualnovel/vnscenegen/VnSceneGen.cpp     | 113 ++---------------
 .../visualnovel/vnscenegen/VnSceneGen.hpp     |   5 +-
 .../vnscenegen/parse/CMakeLists.txt           |  15 ---
 .../vnscenegen/parse/VnSceneParseAsset.cpp    |  27 -----
 .../vnscenegen/parse/VnSceneParseAsset.hpp    |  16 ---
 .../parse/VnSceneParseCharacter.cpp           |  27 -----
 .../parse/VnSceneParseCharacter.hpp           |  19 ---
 .../vnscenegen/parse/VnSceneParseEvent.cpp    | 105 ----------------
 .../vnscenegen/parse/VnSceneParseEvent.hpp    |  12 --
 .../vnscenegen/parse/VnSceneParseHeader.cpp   |  31 -----
 .../vnscenegen/parse/VnSceneParseHeader.hpp   |  22 ----
 .../vnscenegen/parse/VnSceneParseInclude.cpp  |  19 ---
 .../vnscenegen/parse/VnSceneParseInclude.hpp  |  11 --
 .../vnscenegen/parse/VnSceneParseScene.cpp    |  25 ----
 .../vnscenegen/parse/VnSceneParseScene.hpp    |  16 ---
 .../visualnovel/vnscenegen/parse/asset.hpp    |  56 +++++++++
 .../vnscenegen/parse/character.hpp            |  77 ++++++++++++
 .../parse/event/characterfadeevent.hpp        |  77 ++++++++++++
 .../vnscenegen/parse/event/pauseevent.hpp     |  48 ++++++++
 .../vnscenegen/parse/event/textevent.hpp      | 100 +++++++++++++++
 .../visualnovel/vnscenegen/parse/events.hpp   | 109 +++++++++++++++++
 .../visualnovel/vnscenegen/parse/header.hpp   |  67 ++++++++++
 .../visualnovel/vnscenegen/parse/include.hpp  |  59 +++++++++
 .../visualnovel/vnscenegen/parse/root.hpp     | 114 ++++++++++++++++++
 .../visualnovel/vnscenegen/parse/scene.hpp    |  40 ++++++
 32 files changed, 941 insertions(+), 516 deletions(-)
 delete mode 100644 src/dawnpokergame/scenes/Scene_1.hpp
 create mode 100644 src/dawntools/util/CodeGen.hpp
 create mode 100644 src/dawntools/util/XmlParser.hpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/CMakeLists.txt
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.cpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.hpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.cpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.hpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.cpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.hpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.cpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.hpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.cpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.hpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.cpp
 delete mode 100644 src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/asset.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/character.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/event/characterfadeevent.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/event/pauseevent.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/event/textevent.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/events.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/header.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/include.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/root.hpp
 create mode 100644 src/dawntools/visualnovel/vnscenegen/parse/scene.hpp

diff --git a/src/dawnpokergame/CMakeLists.txt b/src/dawnpokergame/CMakeLists.txt
index f48fd997..fac005f4 100644
--- a/src/dawnpokergame/CMakeLists.txt
+++ b/src/dawnpokergame/CMakeLists.txt
@@ -39,7 +39,7 @@ tool_truetype(truetype_alice ${DIR_GAME_ASSETS}/font/Alice-Regular.ttf truetype_
 
 tool_audio(audio_test borrowed/sample_short.wav)
 
-tool_vnscene(vnscene_1 ${DIR_GAME_ASSETS}/vn/Scene_1.xml)
+tool_vnscene(Scene_1 ${DIR_GAME_ASSETS}/vn/Scene_1.xml)
 
 add_dependencies(${DAWN_TARGET_NAME}
   locale_poker
@@ -53,5 +53,5 @@ add_dependencies(${DAWN_TARGET_NAME}
 
   audio_test
 
-  vnscene_1
+  Scene_1
 )
\ No newline at end of file
diff --git a/src/dawnpokergame/game/DawnGame.cpp b/src/dawnpokergame/game/DawnGame.cpp
index 39f57f40..74ce541e 100644
--- a/src/dawnpokergame/game/DawnGame.cpp
+++ b/src/dawnpokergame/game/DawnGame.cpp
@@ -4,7 +4,7 @@
 // https://opensource.org/licenses/MIT
 
 #include "DawnGame.hpp"
-#include "scenes/vnscene_1.hpp"
+#include "scenes/Scene_1.hpp"
 
 using namespace Dawn;
 
@@ -24,7 +24,7 @@ int32_t DawnGame::init() {
   this->renderManager.init();
   this->audioManager.init();
 
-  this->scene = new vnscene_1(this);
+  this->scene = new Scene_1(this);
   
   return DAWN_GAME_INIT_RESULT_SUCCESS;
 }
diff --git a/src/dawnpokergame/scenes/Scene_1.hpp b/src/dawnpokergame/scenes/Scene_1.hpp
deleted file mode 100644
index 2c2e7d34..00000000
--- a/src/dawnpokergame/scenes/Scene_1.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2022 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "visualnovel/scene/SimpleVNScene.hpp"
-#include "scenes/Scene_2.hpp"
-#include "prefabs/characters/DeathPrefab.hpp"
-#include "visualnovel/events/characters/VisualNovelFadeCharacterEvent.hpp"
-#include "visualnovel/events/characters/VisualNovelTransformItemEvent.hpp"
-#include "visualnovel/events/timing/VisualNovelBatchEvent.hpp"
-
-namespace Dawn {
-  class Scene_1 : public SimpleVNScene {
-    protected:
-      DeathPrefab *death;
-
-      void vnStage() override {
-        
-        this->death = DeathPrefab::create(this);
-        this->death->material->color.a = 0;
-      }
-
-      void onSceneEnded() {
-        auto scene = new Scene_2(this->game);
-        game->assetManager.queueSwap(
-          scene->getRequiredAssets(), this->getRequiredAssets()
-        );
-        game->assetManager.syncLoad();
-        scene->stage();
-        this->game->sceneCutover(scene);
-      }
-
-    public:
-      Scene_1(DawnGame *game) : SimpleVNScene(game) {
-        
-      }
-
-      std::vector<Asset*> getRequiredAssets() override {
-        auto man = &this->game->assetManager;
-        std::vector<Asset*> assets = SimpleVNScene::getRequiredAssets();
-        vectorAppend(&assets, DeathPrefab::getRequiredAssets(man));
-        assets.push_back(man->get<AudioAsset>("audio_test"));
-        return assets;
-      }
-
-      IVisualNovelEvent * getVNEvent() override {
-        auto start = new VisualNovelPauseEvent(vnManager, 0.1f);
-        start
-          ->then(new VisualNovelTextboxEvent(vnManager, this->death->vnCharacter, this->death->emotionHappy, "scene.1.1"))
-          ->then(new VisualNovelFadeCharacterEvent(vnManager, this->death->vnCharacter, true, &easeOutQuad, 1.0f))
-          // ->then(new VisualNovelCallbackEvent<Scene_1>(vnManager, this, &Scene_1::onSceneEnded))
-        ;
-        return start;
-      } 
-  };
-}
\ No newline at end of file
diff --git a/src/dawntools/util/CodeGen.hpp b/src/dawntools/util/CodeGen.hpp
new file mode 100644
index 00000000..2fcd15e8
--- /dev/null
+++ b/src/dawntools/util/CodeGen.hpp
@@ -0,0 +1,100 @@
+// 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 {
+  struct ClassGenInfo {
+    std::vector<std::string> includes;
+    std::string clazz = "Unknown";
+    std::string extend = "";
+    std::string constructorArgs = "";
+    std::string extendArgs = "";
+
+    std::vector<std::string> protectedCode;
+    std::vector<std::string> protectedProperties;
+
+    std::vector<std::string> publicCode;
+    std::vector<std::string> publicProperties;
+  };
+
+  struct MethodGenInfo {
+    std::string name;
+    std::string type = "void";
+    std::vector<std::string> body;
+    std::string args = "";
+    bool_t isStatic = false;
+    bool_t isOverride = false;
+  };
+  
+  class CodeGen {
+    protected:
+      static void line(
+        std::vector<std::string> *out,
+        std::string contents,
+        std::string tabs
+      ) {
+        out->push_back(tabs + contents);
+      }
+
+      static void lines(
+        std::vector<std::string> *out,
+        std::vector<std::string> lines,
+        std::string tabs
+      ) {
+        auto itLine = lines.begin();
+        while(itLine != lines.end()) {
+          line(out, *itLine, tabs);
+          ++itLine;
+        }
+      }
+
+      static void classGen(
+        std::vector<std::string> *out,
+        struct ClassGenInfo info
+      ) {
+        std::vector<std::string> buffer;
+
+        line(out, "#pragma once", "");
+        line(out, "", "");
+        if(info.includes.size() > 0) {
+          lines(out, info.includes, "");
+          line(out, "", "");
+        }
+        line(out, "namespace Dawn {", "");
+          line(out, "class " + info.clazz + (info.extend.size() == 0 ? "{" : " : public " + info.extend + " {" ), "  ");
+          if(info.protectedCode.size() > 0) {
+            line(out, "protected:", "    ");
+              lines(out, info.protectedProperties, "      ");
+              line(out, "", "      ");
+              lines(out, info.protectedCode, "      ");
+          }
+
+          if(info.publicCode.size() > 0 || info.constructorArgs.size() > 0) {
+            line(out, "public:", "    ");
+              lines(out, info.publicProperties, "      ");
+              line(out, "", "      ");
+              line(out, info.clazz + "(" + info.constructorArgs + ")" + (info.extend.size() > 0 ? " : " + info.extend + "(" + info.extendArgs + ")" : "") + " {", "      ");
+              line(out, "}",  "      ");
+              if(info.publicCode.size() > 0) {
+                line(out, "", "      ");
+                lines(out, info.publicCode, "      ");
+              }
+          }
+          line(out, "};", "  ");
+        line(out, "}", "");
+      }
+
+      static void methodGen(
+        std::vector<std::string> *out,
+        struct MethodGenInfo info
+      ) {
+        line(out, info.type + " " + info.name + "(" + info.args + ") " + ( info.isOverride ? "override" : "" ) + "{", "");
+        lines(out, info.body, "  ");
+        line(out, "}", "");
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/util/File.cpp b/src/dawntools/util/File.cpp
index 8423f99d..fa0ed16f 100644
--- a/src/dawntools/util/File.cpp
+++ b/src/dawntools/util/File.cpp
@@ -63,13 +63,13 @@ bool_t File::readString(std::string *out) {
   out->clear();
 
   size_t i = 0;
-  char buffer[FILE_BUFFER_SIZE + 1];
+  char buffer[FILE_BUFFER_SIZE + 1];// +1 for null term
   while(i != this->length) {
     size_t amt = mathMin<size_t>(FILE_BUFFER_SIZE, (this->length - i));
     auto amtRead = fread(buffer, sizeof(char), amt, this->file);
     if(amtRead != amt) return false;
     i += amtRead;
-    buffer[amtRead + 1] = '\0';
+    buffer[amtRead] = '\0';
     out->append(buffer);
   }
 
diff --git a/src/dawntools/util/XmlParser.hpp b/src/dawntools/util/XmlParser.hpp
new file mode 100644
index 00000000..0221c997
--- /dev/null
+++ b/src/dawntools/util/XmlParser.hpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/Xml.hpp"
+
+namespace Dawn {
+  template<typename T>
+  class XmlParser {
+    protected:
+      virtual std::vector<std::string> getRequiredAttributes() = 0;
+      virtual std::map<std::string, std::string> getOptionalAttributes() = 0;
+      virtual int32_t onParse(
+        Xml *node,
+        std::map<std::string, std::string> values,
+        T *output,
+        std::string *error
+      ) = 0;
+
+    public:
+      static std::string parseDuration(std::string duration) {
+        std::string dur = duration;
+        if(dur.find('.') == std::string::npos) dur += ".0";
+        return dur + "f";
+      }
+
+      static std::string parseEase(std::string e) {
+        if(e == "out-quad") return "&easeOutQuad";
+        if(e == "linear") return "&easeLinear";
+        return "";
+      }
+
+      int32_t parse(Xml *xml, T *output, std::string *error) {
+        std::map<std::string, std::string> values;
+
+        // First get the required attributes
+        auto required = this->getRequiredAttributes();
+        auto itRequired = required.begin();
+        while(itRequired != required.end()) {
+          auto s = *itRequired;
+          auto attr = xml->attributes.find(s);
+          if(attr == xml->attributes.end()) {
+            std::cout << "Missing required attribute \"" << s << "\"" << std::endl;
+            return 1;
+          }
+          values[s] = attr->second;
+          ++itRequired;
+        }
+
+        // Now get the optional attributes
+        auto optional = this->getOptionalAttributes();
+        auto itOptional = optional.begin();
+        while(itOptional != optional.end()) {
+          auto key = itOptional->first;
+          auto defaultValue = itOptional->second;
+          
+          auto attr = xml->attributes.find(key);
+          if(attr == xml->attributes.end()) {
+            values[key] = defaultValue;
+          } else {
+            values[key] = attr->second;
+          }
+          ++itOptional;
+        }
+
+        // Now send to parser
+        return this->onParse(xml, values, output, error);
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/CMakeLists.txt b/src/dawntools/visualnovel/vnscenegen/CMakeLists.txt
index 36fbcbfa..b4ec3631 100644
--- a/src/dawntools/visualnovel/vnscenegen/CMakeLists.txt
+++ b/src/dawntools/visualnovel/vnscenegen/CMakeLists.txt
@@ -32,7 +32,4 @@ target_compile_definitions(vnscenegen
 target_link_libraries(vnscenegen
   PUBLIC
     ${DAWN_BUILD_HOST_LIBS}
-)
-
-# Subdirs
-add_subdirectory(parse)
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/VnSceneGen.cpp b/src/dawntools/visualnovel/vnscenegen/VnSceneGen.cpp
index 22c4fa4d..eec43f5e 100644
--- a/src/dawntools/visualnovel/vnscenegen/VnSceneGen.cpp
+++ b/src/dawntools/visualnovel/vnscenegen/VnSceneGen.cpp
@@ -23,111 +23,24 @@ int32_t VnSceneGen::start() {
 
   // Parse XML
   Xml xml = Xml::load(buffer);
-
-  // First, read the header information
-  struct HeaderInformation header;
-  auto itXml = xml.children.begin();
-  while(itXml != xml.children.end()) {
-    auto child = *itXml;
-    if(child->node != "head") {
-      ++itXml;
-      continue;
-    }
-
-    auto ret = parseHeader(&header, child);
-    if(ret != 0) return ret;
-    break;
-  }
-  
-  // Validate header
-  if(header.sceneInfo.name.size() == 0 || header.sceneInfo.type.size() == 0) {
-    std::cout << "VN Scene header wasn't parsed properly." << std::endl;
-    return 1;
+  std::string error;
+  struct RootInformation info;
+  auto ret = (RootParser()).parse(&xml, &info, &error);
+  if(ret != 0) {
+    std::cout << error << std::endl;
+    return ret;
   }
 
-  // Parse and load events.
-  std::string bufferEvents;
-  itXml = xml.children.begin();
-  while(itXml != xml.children.end()) {
-    auto events = *itXml;
-    if(events->node != "events") {
-      ++itXml;
-      continue;
-    }
-    bufferEvents += "\n        start\n";
+  std::vector<std::string> lines;
+  RootGen::generate(&lines, &info, "");
 
-    auto itChildren = events->children.begin();
-    while(itChildren != events->children.end()) {
-      auto evt = *itChildren;
-      if(evt->node != "scene-transition") {
-        auto evtParsed = parseEvent(&header, evt);
-        if(evtParsed.size() == 0) return 1;
-        bufferEvents += "          ->then(" + evtParsed + ")\n";
-      }
-      ++itChildren;
-    }
-    bufferEvents += "        ;\n";
-    ++itXml;
-  }
-
-  // Now render output to file.
+  // Generate buffer
   std::string bufferOut;
-  bufferOut += "#pragma once\n\n";
-  auto itInclude = header.includes.begin();
-  while(itInclude != header.includes.end()) {
-    bufferOut += "#include \"" + (*itInclude) + "\"\n";
-    ++itInclude;
+  auto itLine = lines.begin();
+  while(itLine != lines.end()) {
+    bufferOut += *itLine + "\n";
+    ++itLine;
   }
-  bufferOut += "\nnamespace Dawn{\n";
-  bufferOut += "  class " + header.sceneInfo.name + " : public " + header.sceneInfo.type + " {\n";
-  bufferOut += "    protected:\n";
-
-  // Characters (As properties)
-  auto itCharacters = header.characters.begin();
-  while(itCharacters != header.characters.end()) {
-    auto c = *itCharacters;
-    bufferOut += "      " + c.clazz + " *" + c.name + ";\n";
-    ++itCharacters;
-  }
-  bufferOut += "\n      void vnStage() override {\n";
-  bufferOut += "        " + header.sceneInfo.type + "::vnStage();\n";
-  
-  // Initialize the characters
-  itCharacters = header.characters.begin();
-  while(itCharacters != header.characters.end()) {
-    auto c = *itCharacters;
-    bufferOut += "        this->" + c.name + " = " + c.clazz + "::create(this);\n";
-    ++itCharacters;
-  }
-
-  bufferOut += "      }\n";
-
-  bufferOut += "\n    public:\n";
-  bufferOut += "      " + header.sceneInfo.name + "(DawnGame *game) : " + header.sceneInfo.type + "(game) {\n";
-  bufferOut += "      }\n";
-
-  // Assets
-  bufferOut += "\n      std::vector<Asset*> getRequiredAssets() override{\n";
-  bufferOut += "        auto man = &this->game->assetManager;\n";
-  bufferOut += "        std::vector<Asset*> assets = " + header.sceneInfo.type + "::getRequiredAssets();\n";
-  itCharacters = header.characters.begin();
-  while(itCharacters != header.characters.end()) {
-    auto c = *itCharacters;
-    bufferOut += "        vectorAppend(&assets, " + c.clazz + "::getRequiredAssets(man));\n"; 
-    ++itCharacters;
-  }
-  bufferOut += "        return assets;\n";
-  bufferOut += "      }\n";
-
-  // VN Events
-  bufferOut += "\n      IVisualNovelEvent * getVNEvent() override {\n";
-  bufferOut += "        auto start = new VisualNovelPauseEvent(vnManager, 0.01f);\n";
-  bufferOut += bufferEvents;
-  bufferOut += "\n        return start;\n";
-  bufferOut += "      }\n";
-
-  bufferOut += "  };\n";
-  bufferOut += "}";
 
   // Finished with XML data, now we can write data out.
   File fileOut(this->args[2] + ".hpp");
diff --git a/src/dawntools/visualnovel/vnscenegen/VnSceneGen.hpp b/src/dawntools/visualnovel/vnscenegen/VnSceneGen.hpp
index 05b5e97c..8f5a1a06 100644
--- a/src/dawntools/visualnovel/vnscenegen/VnSceneGen.hpp
+++ b/src/dawntools/visualnovel/vnscenegen/VnSceneGen.hpp
@@ -6,14 +6,13 @@
 #pragma once
 #include "util/DawnTool.hpp"
 #include "util/File.hpp"
-#include "parse/VnSceneParseEvent.hpp"
-#include "parse/VnSceneParseHeader.hpp"
+#include "parse/root.hpp"
 
 namespace Dawn {
   class VnSceneGen : public DawnTool {
     protected:
 
     public:
-      int32_t start() override;
+      int32_t start();
   };
 }
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/CMakeLists.txt b/src/dawntools/visualnovel/vnscenegen/parse/CMakeLists.txt
deleted file mode 100644
index 10679721..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2023 Dominic Msters
-# 
-# This software is released under the MIT License.
-# https://opensource.org/licenses/MIT
-
-# Sources
-target_sources(vnscenegen
-  PRIVATE
-    VnSceneParseAsset.cpp
-    VnSceneParseCharacter.cpp
-    VnSceneParseEvent.cpp
-    VnSceneParseScene.cpp
-    VnSceneParseHeader.cpp
-    VnSceneParseInclude.cpp
-)
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.cpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.cpp
deleted file mode 100644
index e42fd7c0..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "VnSceneParseAsset.hpp"
-
-using namespace Dawn;
-
-int32_t parseAsset(std::vector<struct Asset> *assets, Xml *node) {
-  auto attrType = node->attributes.find("type");
-  if(attrType == node->attributes.end()) {
-    std::cout << "VN Scene Asset missing type attribute" << std::endl;
-    return 1;
-  }
-  auto attrName = node->attributes.find("name");
-  if(attrName == node->attributes.end()) {
-    std::cout << "VN Scene Asset missing name attribute" << std::endl;
-    return 1;
-  }
-
-  struct Asset ass;
-  ass.type = attrType->second;
-  ass.name = attrType->second;
-  assets->push_back(ass);
-  return 0;
-}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.hpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.hpp
deleted file mode 100644
index c4ac27da..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseAsset.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "util/Xml.hpp"
-
-using namespace Dawn;
-
-struct Asset {
-  std::string type;
-  std::string name;
-};
-
-int32_t parseAsset(std::vector<struct Asset> *assets, Xml *node);
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.cpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.cpp
deleted file mode 100644
index 6a841860..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "VnSceneParseCharacter.hpp"
-
-using namespace Dawn;
-
-int32_t parseCharacter(std::vector<struct CharacterInformation> *characters, Xml *node) {
-  auto attrClass = node->attributes.find("class");
-  if(attrClass == node->attributes.end()) {
-    std::cout << "Character definition is missing class" << std::endl;
-    return 1;
-  }
-
-  auto attrName = node->attributes.find("name");
-  if(attrName == node->attributes.end()) {
-    std::cout << "Character definition is missing name" << std::endl;
-  }
-
-  struct CharacterInformation character;
-  character.clazz = attrClass->second;
-  character.name = attrName->second;
-  characters->push_back(character);
-  return 0;
-}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.hpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.hpp
deleted file mode 100644
index 1a5b9b48..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseCharacter.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "util/Xml.hpp"
-
-using namespace Dawn;
-
-struct CharacterInformation {
-  std::string clazz;
-  std::string name;
-};
-
-int32_t parseCharacter(
-  std::vector<struct CharacterInformation> *characters,
-  Xml *node
-);
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.cpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.cpp
deleted file mode 100644
index 3708ae3c..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "VnSceneParseEvent.hpp"
-
-using namespace Dawn;
-
-std::string parseEase(std::string e) {
-  if(e == "out-quad") return "easeOutQuad";
-  std::cout << "Invalid ease defined" << std::endl;
-  return "";
-}
-
-std::string parseEvent(struct HeaderInformation *header, Xml *evt) {
-  std::string buffer;
-  std::string node(evt->node);
-
-  if(node == "pause") {
-    // Pause 
-    header->includes.push_back("visualnovel/events/timing/VisualNovelPauseEvent.hpp");
-    
-    auto attrDuration = evt->attributes.find("duration");
-    if(attrDuration == evt->attributes.end()) {
-      std::cout << "Pause event is missing duration argument." << std::endl;
-      return "";
-    }
-    auto dur = attrDuration->second;
-    if(dur.find('.') == std::string::npos) dur += ".0";
-    buffer += "new VisualNovelPauseEvent(vnManager, " + dur + "f)";
-
-
-  } else if(node == "text") {
-    // Text
-    header->includes.push_back("visualnovel/events/VisualNovelTextboxEvent.hpp");
-
-    auto attrChar = evt->attributes.find("character");
-    if(attrChar == evt->attributes.end()) {
-      std::cout << "Text event missing character attribute." << std::endl;
-      return "";
-    }
-
-    auto attrEmotion = evt->attributes.find("emotion");
-    if(attrEmotion == evt->attributes.end()) {
-      std::cout << "Text event missing emotion attribute." << std::endl;
-      return "";
-    }
-
-    auto attrString = evt->attributes.find("string");
-    if(attrString == evt->attributes.end()) {
-      std::cout << "Text event missing string attribute." << std::endl;
-      return "";
-    }
-
-    std::string emo = attrEmotion->second;
-    emo[0] = toupper(emo[0]);
-    
-    buffer += "new VisualNovelTextboxEvent(vnManager, ";
-    buffer += "this->" + attrChar->second + "->vnCharacter, ";
-    buffer += "this->" + attrChar->second + "->emotion" + emo + ", ";
-    buffer += "\"" + attrString->second + "\"" ;
-    buffer += ")";
-
-
-  } else if(node == "character-fade") {
-    // Character Fade
-    header->includes.push_back("visualnovel/events/characters/VisualNovelFadeCharacterEvent.hpp");
-    auto attrChar = evt->attributes.find("character");
-    auto attrDuration = evt->attributes.find("duration");
-    auto attrEase = evt->attributes.find("ease");
-    auto attrFade = evt->attributes.find("fade");
-
-    if(attrChar == evt->attributes.end()) {
-      std::cout << "Character fade event missing character attribute." << std::endl;
-      return "";
-    }
-    
-    std::string character = attrChar->second;
-    std::string easeIn = "true";
-    std::string ease = "easeLinear";
-    std::string duration = "1.0";
-
-    if(attrFade != evt->attributes.end()) easeIn = attrFade->second == "in" ? "true" : "false";
-
-    if(attrDuration != evt->attributes.end()) duration = attrDuration->second;
-    if(duration.find('.') == std::string::npos) duration += ".0";
-
-    if(attrEase != evt->attributes.end()) {
-      ease = parseEase(attrEase->second);
-      if(ease.size() == 0) return "";
-    }
-
-    buffer += "new VisualNovelFadeCharacterEvent(vnManager, ";
-    buffer += "this->" + character + "->vnCharacter, ";
-    buffer += easeIn + ", ";
-    buffer += "&" + ease + ", ";
-    buffer += duration + "f";
-    buffer += ")";
-  } else if(node == "scene-transition") {
-    buffer += " ";
-  }
-
-  return buffer;
-}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.hpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.hpp
deleted file mode 100644
index ee444d8d..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseEvent.hpp
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "util/Xml.hpp"
-#include "parse/VnSceneParseHeader.hpp"
-
-using namespace Dawn;
-
-std::string parseEvent(struct HeaderInformation *header, Xml *evt);
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.cpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.cpp
deleted file mode 100644
index c19c588b..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "VnSceneParseHeader.hpp"
-
-using namespace Dawn;
-
-int32_t parseHeader(struct HeaderInformation *info, Xml *node) {
-  auto itChildren = node->children.begin();
-  while(itChildren != node->children.end()) {
-    auto c = *itChildren;
-    int32_t ret = 0;
-
-    if(c->node == "include") {
-      ret = parseInclude(&info->includes, c);
-    } else if(c->node == "character") {
-      ret = parseCharacter(&info->characters, c);
-    } else if(c->node == "scene") {
-      ret = parseScene(&info->sceneInfo, c);
-    } else if (c->node == "asset") {
-      ret = parseAsset(&info->assets, c);
-    } else {
-      std::cout << "Parsing VN Scene header, unknown node " << c->node << std::endl;
-    }
-    if(ret != 0) return ret;
-    ++itChildren;
-  }
-  return 0;
-}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.hpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.hpp
deleted file mode 100644
index 530bad8f..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseHeader.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "util/Xml.hpp"
-#include "parse/VnSceneParseCharacter.hpp"
-#include "parse/VnSceneParseInclude.hpp"
-#include "parse/VnSceneParseScene.hpp"
-#include "parse/VnSceneParseAsset.hpp"
-
-using namespace Dawn;
-
-struct HeaderInformation {
-  std::vector<std::string> includes;
-  std::vector<struct CharacterInformation> characters;
-  std::vector<struct Asset> assets;
-  struct SceneInformation sceneInfo;
-};
-
-int32_t parseHeader(struct HeaderInformation *info, Xml *node);
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.cpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.cpp
deleted file mode 100644
index 7a69bf17..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "VnSceneParseInclude.hpp"
-
-using namespace Dawn;
-
-int32_t parseInclude(std::vector<std::string> *includes, Xml *node) {
-  auto attrPath = node->attributes.find("path");
-  if(attrPath == node->attributes.end()) {
-    std::cout << "Missing include path in VN Header Defintions." << std::endl;
-    return 1;
-  }
-
-  includes->push_back(attrPath->second);
-  return 0;
-}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.hpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.hpp
deleted file mode 100644
index 8e310af2..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseInclude.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "util/Xml.hpp"
-
-using namespace Dawn;
-
-int32_t parseInclude(std::vector<std::string> *includes, Xml *node);
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.cpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.cpp
deleted file mode 100644
index 2c342b68..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#include "VnSceneParseScene.hpp"
-
-using namespace Dawn;
-
-int32_t parseScene(struct SceneInformation *info, Xml *node) {
-  auto attrName = node->attributes.find("name");
-  if(attrName == node->attributes.end()) {
-    std::cout << "VN Scene <scene> definition is missing name attribute" << std::endl;
-    return 1;
-  }
-  info->name = attrName->second;
-
-  auto attrType = node->attributes.find("type");
-  if(attrType == node->attributes.end()) {
-    info->type = "SimpleVNScene";
-  } else {
-    info->type = attrType->second;
-  }
-  return 0;
-}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.hpp b/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.hpp
deleted file mode 100644
index 244c956b..00000000
--- a/src/dawntools/visualnovel/vnscenegen/parse/VnSceneParseScene.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2023 Dominic Masters
-// 
-// This software is released under the MIT License.
-// https://opensource.org/licenses/MIT
-
-#pragma once
-#include "util/Xml.hpp"
-
-using namespace Dawn;
-
-struct SceneInformation {
-  std::string type;
-  std::string name;
-};
-
-int32_t parseScene(struct SceneInformation *info, Xml *node);
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/asset.hpp b/src/dawntools/visualnovel/vnscenegen/parse/asset.hpp
new file mode 100644
index 00000000..4951f0b1
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/asset.hpp
@@ -0,0 +1,56 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "util/CodeGen.hpp"
+
+namespace Dawn {
+  struct AssetInformation {
+    std::string type;
+    std::string name;
+  };
+
+  class AssetParser : public XmlParser<struct AssetInformation> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{
+          "name",
+          "type"
+        };
+      }
+
+      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 AssetInformation *out,
+        std::string *error
+      ) {
+        out->name = values["name"];
+        out->type = values["type"];
+        return 0;
+      }
+
+      std::string convert(struct AssetInformation info) {
+        std::string out;
+        return out;
+      }
+  };
+
+  class AssetGen : public CodeGen {
+    public:
+      static void generate(
+        std::vector<std::string> *out,
+        struct AssetInformation *info,
+        std::string tabs
+      ) {
+        return line(out, "// Asset will be generated here", tabs);
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/character.hpp b/src/dawntools/visualnovel/vnscenegen/parse/character.hpp
new file mode 100644
index 00000000..ea2d81c6
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/character.hpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "util/CodeGen.hpp"
+
+namespace Dawn {
+  struct CharacterInformation {
+    std::string clazz;
+    std::string name;
+  };
+
+  class CharacterParser : public XmlParser<struct CharacterInformation> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{
+          "class",
+          "name"
+        };
+      }
+
+      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 CharacterInformation *out,
+        std::string *error
+      ) {
+        out->clazz = values["class"];
+        out->name = values["name"];
+
+        if(out->clazz.size() == 0) {
+          *error = "Character class cannot be empty.";
+          return 1;
+        }
+        if(out->name.size() == 0) {
+          *error = "Character name cannot be empty.";
+          return 1;
+        }
+
+        return 0;
+      }
+  };
+
+  class CharacterGen : public CodeGen {
+    public:
+      static void generateProperty(
+        std::vector<std::string> *out,
+        struct CharacterInformation info,
+        std::string tabs
+      ) {
+        line(out, info.clazz + " *" + info.name + ";", tabs);
+      }
+
+      static void generateInitializer(
+        std::vector<std::string> *out,
+        struct CharacterInformation info,
+        std::string tabs
+      ) {
+        line(out, "this->" + info.name + " = " + info.clazz + "::create(this);", tabs);
+      }
+
+      static void generateAssets(
+        std::vector<std::string> *out,
+        struct CharacterInformation info,
+        std::string tabs
+      ) {
+        line(out, "vectorAppend(&assets, " + info.clazz + "::getRequiredAssets(man));", "");
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/event/characterfadeevent.hpp b/src/dawntools/visualnovel/vnscenegen/parse/event/characterfadeevent.hpp
new file mode 100644
index 00000000..48becb8d
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/event/characterfadeevent.hpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "util/CodeGen.hpp"
+
+namespace Dawn {
+  struct CharacterFadeEventInfo {
+    std::string character;
+    std::string duration;
+    std::string ease;
+    std::string fade;
+    std::string include;
+  };
+
+  class CharacterFadeParser : public XmlParser<struct CharacterFadeEventInfo> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{
+          "character"
+        };
+      }
+
+      std::map<std::string, std::string> getOptionalAttributes() {
+        return std::map<std::string, std::string>{
+          { "fade", "in" },
+          { "ease", "linear" },
+          { "duration", "1" }
+        };
+      }
+
+      int32_t onParse(
+        Xml *node,
+        std::map<std::string, std::string> values,
+        struct CharacterFadeEventInfo *out,
+        std::string *error
+      ) {
+        out->character = values["character"];
+        out->duration = parseDuration(values["duration"]);
+        out->ease = parseEase(values["ease"]);
+        out->fade = values["fade"] == "in" ? "true" : "false";
+        out->include = "visualnovel/events/characters/VisualNovelFadeCharacterEvent.hpp";
+
+        if(out->ease.size() == 0) {
+          *error = "Invalid ease";
+          return 1;
+        }
+
+        return 0;
+      }
+  };
+
+
+  class CharacterFadeGen : public CodeGen {
+    public:
+      static void generate(
+        std::vector<std::string> *out,
+        struct CharacterFadeEventInfo *info,
+        std::string tabs = ""
+      ) {
+        line(out, "new VisualNovelFadeCharacterEvent(vnManager,", tabs + "  ");
+        line(out, "this->" + info->character + "->vnCharacter,", tabs + "  ");
+        line(out, info->fade + ",", tabs + "  ");
+        line(out, info->ease + ",", tabs + "  ");
+        line(out, info->duration, tabs + "  ");
+        line(out, ")", tabs);
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/event/pauseevent.hpp b/src/dawntools/visualnovel/vnscenegen/parse/event/pauseevent.hpp
new file mode 100644
index 00000000..6eb4b1dc
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/event/pauseevent.hpp
@@ -0,0 +1,48 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "util/CodeGen.hpp"
+
+namespace Dawn {
+  struct PauseEventInfo {
+    std::string duration;
+    std::string include;
+  };
+
+  class PauseEventParser : public XmlParser<struct PauseEventInfo> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{ "duration" };
+      }
+
+      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 PauseEventInfo *out,
+        std::string *error
+      ) {
+        out->duration = parseDuration(values["duration"]);
+        out->include = "visualnovel/events/timing/VisualNovelPauseEvent.hpp";
+        return 0;
+      }
+  };
+
+  class PauseEventGen : public CodeGen {
+    public:
+      static void generate(
+        std::vector<std::string> *out,
+        struct PauseEventInfo *info,
+        std::string tabs = ""
+      ) {
+        line(out, "new VisualNovelPauseEvent(vnManager, " + info->duration + ")", tabs);
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/event/textevent.hpp b/src/dawntools/visualnovel/vnscenegen/parse/event/textevent.hpp
new file mode 100644
index 00000000..be45a7a2
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/event/textevent.hpp
@@ -0,0 +1,100 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "util/CodeGen.hpp"
+
+namespace Dawn {
+  struct TextStringInfo {
+    std::string lang;
+    std::string text;
+  };
+
+  struct TextEventInfo {
+    std::string character;
+    std::string emotion;
+    std::vector<struct TextStringInfo> strings;
+
+    std::string key = "scene.1.1";
+  };
+
+  class TextStringParser : public XmlParser<struct TextStringInfo> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{ "lang" };
+      }
+
+      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 TextStringInfo *out,
+        std::string *error
+      ) {
+        out->lang = values["lang"];
+        out->text = node->value;
+        return 0;
+      }
+  };
+
+  class TextEventParser : public XmlParser<struct TextEventInfo> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{
+          "character",
+          "emotion"
+        };
+      }
+
+      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 TextEventInfo *out,
+        std::string *error
+      ) {
+        int32_t ret = 0;
+        out->character = values["character"];
+        out->emotion = values["emotion"];
+
+        auto itChildren = node->children.begin();
+        while(itChildren != node->children.end()) {
+          auto c = *itChildren;
+          if(c->node == "string") {
+            struct TextStringInfo str;
+            ret = (TextStringParser()).parse(c, &str, error);
+            out->strings.push_back(str);
+          }
+          ++itChildren;
+        }
+        return ret;
+      }
+  };
+
+  class TextEventGen : public CodeGen {
+    public:
+      static void generate(
+        std::vector<std::string> *out,
+        struct TextEventInfo *info,
+        std::string tabs = ""
+      ) {
+        std::string emo = info->emotion;
+        emo[0] = toupper(emo[0]);
+
+        line(out, "new VisualNovelTextboxEvent(vnManager,", tabs);
+        line(out, "this->" + info->character + "->vnCharacter, ", tabs + "  ");
+        line(out, "this->" + info->character + "->emotion" + emo + ", ", tabs + "  ");
+        line(out, "\"" + info->key  + "\"", tabs + "  ");
+        line(out, ")", tabs);
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/events.hpp b/src/dawntools/visualnovel/vnscenegen/parse/events.hpp
new file mode 100644
index 00000000..d727c88b
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/events.hpp
@@ -0,0 +1,109 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "parse/event/textevent.hpp"
+#include "parse/event/characterfadeevent.hpp"
+#include "parse/event/pauseevent.hpp"
+
+namespace Dawn {
+  enum EventType {
+    EVENT_TYPE_TEXT,
+    EVENT_TYPE_CHARACTER_FADE,
+    EVENT_TYPE_PAUSE
+  };
+
+  struct EventsInformation {
+    std::map<int32_t, enum EventType> eventTypes;
+    std::map<int32_t, struct TextEventInfo> textEvents;
+    std::map<int32_t, struct CharacterFadeEventInfo> characterFadeEvents;
+    std::map<int32_t, struct PauseEventInfo> pauseEvents;
+    std::vector<std::string> includes;
+  };
+
+  class EventsParser : public XmlParser<struct EventsInformation> {
+    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 EventsInformation *out,
+        std::string *error
+      ) {
+        int32_t ret = 0;
+        int32_t i = 0;
+        auto itChildren = node->children.begin();
+        while(itChildren != node->children.end()) {
+          auto c = *itChildren;
+          
+          if(c->node == "text") {
+            struct TextEventInfo textEvent;
+            ret = (TextEventParser()).parse(c, &textEvent, error);
+            out->eventTypes[i] = EVENT_TYPE_TEXT;
+            out->textEvents[i++] = textEvent;
+
+          } else if(c->node == "character-fade") {
+            struct CharacterFadeEventInfo charFadeEvent;
+            ret = (CharacterFadeParser()).parse(c, &charFadeEvent, error);
+            out->eventTypes[i] = EVENT_TYPE_CHARACTER_FADE;
+            out->characterFadeEvents[i++] = charFadeEvent;
+            out->includes.push_back(charFadeEvent.include);
+
+          } else if(c->node == "pause") {
+            struct PauseEventInfo pauseEvent;
+            ret = (PauseEventParser()).parse(c, &pauseEvent, error);
+            out->eventTypes[i] = EVENT_TYPE_PAUSE;
+            out->pauseEvents[i++] = pauseEvent;
+            out->includes.push_back(pauseEvent.include);
+
+          }
+
+          ++itChildren;
+        }
+
+        return ret;
+      }
+  };
+
+  class EventsGen : public CodeGen {
+    public:
+      static void generate(
+        std::vector<std::string> *out,
+        struct EventsInformation *info,
+        std::string tabs
+      ) {
+        auto itEvents = info->eventTypes.begin();
+        while(itEvents != info->eventTypes.end()) {
+          auto e = *itEvents;
+          line(out, "->then(", tabs);
+          switch(e.second) {
+            case EVENT_TYPE_TEXT:
+              TextEventGen::generate(out, &info->textEvents[e.first], tabs + "  ");
+              break;
+            
+            case EVENT_TYPE_CHARACTER_FADE:
+              CharacterFadeGen::generate(out, &info->characterFadeEvents[e.first], tabs + "  ");
+              break;
+
+            case EVENT_TYPE_PAUSE:
+              PauseEventGen::generate(out, &info->pauseEvents[e.first], tabs + "  ");
+              break;
+
+          }
+          line(out, ")", tabs);
+          ++itEvents;
+        }
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/header.hpp b/src/dawntools/visualnovel/vnscenegen/parse/header.hpp
new file mode 100644
index 00000000..097fe744
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/header.hpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "parse/include.hpp"
+#include "parse/character.hpp"
+#include "parse/scene.hpp"
+#include "parse/asset.hpp"
+
+namespace Dawn {
+  struct HeaderInformation {
+    std::vector<std::string> includes;
+    std::vector<struct CharacterInformation> characters;
+    std::vector<struct AssetInformation> assets;
+    std::map<std::string, std::map<std::string, std::string>> languages;
+    struct SceneInformation scene;
+  };
+
+  class HeaderParser : public XmlParser<struct HeaderInformation> {
+    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 HeaderInformation *out,
+        std::string *error
+      ) {
+        int32_t ret = 0;
+        auto itChildren = node->children.begin();
+        while(itChildren != node->children.end()) {
+          auto c = *itChildren;
+
+          if(c->node == "include") {
+            ret = (IncludeParser()).parse(c, &out->includes, error);
+
+          } else if (c->node == "character") {
+            struct CharacterInformation character;
+            ret = (CharacterParser()).parse(c, &character, error);
+            if(ret != 0) return ret;
+            out->characters.push_back(character);
+
+          } else if(c->node == "asset") {
+            struct AssetInformation asset;
+            ret = (AssetParser()).parse(c, &asset, error);
+            if(ret != 0) return ret;
+            out->assets.push_back(asset);
+
+          } else if(c->node == "scene") {
+            ret = (SceneParser()).parse(c, &out->scene, error);
+          }
+          
+          if(ret != 0) return ret;
+          ++itChildren;
+        }
+        return ret;
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/include.hpp b/src/dawntools/visualnovel/vnscenegen/parse/include.hpp
new file mode 100644
index 00000000..3a6960a4
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/include.hpp
@@ -0,0 +1,59 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "util/XmlParser.hpp"
+#include "util/CodeGen.hpp"
+
+namespace Dawn {
+  typedef std::vector<std::string> include_t;
+
+  class IncludeParser : public XmlParser<include_t> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{
+          "path"
+        };
+      }
+
+      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,
+        include_t *out,
+        std::string *error
+      ) {
+        if(values["path"].size() == 0) {
+          *error = "";
+          return 1;
+        }
+
+        out->push_back(values["path"]);
+        return 0;
+      }
+  };
+
+  class IncludeGen : public CodeGen {
+    public:
+      static void generate(
+        std::vector<std::string> *out,
+        include_t includes,
+        std::string tabs
+      ) {
+        std::vector<std::string> generated;
+        auto it = includes.begin();
+        while(it != includes.end()) {
+          if(std::find(generated.begin(), generated.end(), *it) == generated.end()) {
+            line(out, "#include \"" + *it + "\"", tabs);
+            generated.push_back(*it);
+          }
+          ++it;
+        }
+      }
+  };
+}
\ No newline at end of file
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/root.hpp b/src/dawntools/visualnovel/vnscenegen/parse/root.hpp
new file mode 100644
index 00000000..add302a0
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/root.hpp
@@ -0,0 +1,114 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "parse/header.hpp"
+#include "parse/events.hpp"
+
+namespace Dawn {
+  struct RootInformation {
+    struct HeaderInformation header;
+    struct EventsInformation events;
+  };
+
+  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 = 0;
+        auto itChildren = node->children.begin();
+
+        while(itChildren != node->children.end()) {
+          auto c = *itChildren;
+
+          if(c->node == "head") {
+            ret = (HeaderParser()).parse(c, &out->header, error);
+          } else if(c->node == "events") {
+            ret = (EventsParser()).parse(c, &out->events, error);
+          }
+
+          if(ret != 0) return ret;
+          ++itChildren;
+        }
+
+        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 VisualNovelPauseEvent(vnManager, 1.0f);", "");
+
+        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
diff --git a/src/dawntools/visualnovel/vnscenegen/parse/scene.hpp b/src/dawntools/visualnovel/vnscenegen/parse/scene.hpp
new file mode 100644
index 00000000..47fc7603
--- /dev/null
+++ b/src/dawntools/visualnovel/vnscenegen/parse/scene.hpp
@@ -0,0 +1,40 @@
+// 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 SceneInformation {
+    std::string type;
+    std::string name;
+  };
+
+  class SceneParser : public XmlParser<struct SceneInformation> {
+    protected:
+      std::vector<std::string> getRequiredAttributes() {
+        return std::vector<std::string>{
+          "name",
+        };
+      }
+
+      std::map<std::string, std::string> getOptionalAttributes() {
+        return std::map<std::string, std::string>{
+          { "type", "SimpleVNScene" }
+        };
+      }
+
+      int32_t onParse(
+        Xml *node,
+        std::map<std::string, std::string> values,
+        struct SceneInformation *out,
+        std::string *error
+      ) {
+        out->name = values["name"];
+        out->type = values["type"];
+        return 0;
+      }
+  };
+}
\ No newline at end of file