diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt
index ef24eb1d..071da5d2 100644
--- a/src/dawn/CMakeLists.txt
+++ b/src/dawn/CMakeLists.txt
@@ -23,6 +23,7 @@ add_subdirectory(asset)
 # add_subdirectory(audio)
 add_subdirectory(component)
 add_subdirectory(display)
+add_subdirectory(environment)
 add_subdirectory(game)
 # add_subdirectory(games)
 # add_subdirectory(input)
diff --git a/src/dawn/asset/AssetDataLoader.cpp b/src/dawn/asset/AssetDataLoader.cpp
index ba959cd6..32609382 100644
--- a/src/dawn/asset/AssetDataLoader.cpp
+++ b/src/dawn/asset/AssetDataLoader.cpp
@@ -152,7 +152,7 @@ size_t AssetDataLoader::readUntil(
     if(buffer[i] == delimiter) break;
     i++;
   }
-  buffer[i] = '\0';
+  buffer[i++] = '\0';
   return i;
 }
 
diff --git a/src/dawn/asset/AssetDataLoader.hpp b/src/dawn/asset/AssetDataLoader.hpp
index 61b7941d..33394d2d 100644
--- a/src/dawn/asset/AssetDataLoader.hpp
+++ b/src/dawn/asset/AssetDataLoader.hpp
@@ -116,7 +116,7 @@ namespace Dawn {
        * @param buffer Buffer to read into.
        * @param maxSize Maximum size of the buffer.
        * @param delimiter Delimiter to read until.
-       * @return The count of bytes read.
+       * @return The count of bytes read (including null terminator)
        */
       size_t readUntil(
         uint8_t *buffer,
diff --git a/src/dawn/asset/loaders/TextureLoader.cpp b/src/dawn/asset/loaders/TextureLoader.cpp
index c48dc3c0..14f2cbc8 100644
--- a/src/dawn/asset/loaders/TextureLoader.cpp
+++ b/src/dawn/asset/loaders/TextureLoader.cpp
@@ -14,6 +14,7 @@ TextureLoader::TextureLoader(const std::string name) :
   state(TextureLoaderLoadState::INITIAL)
 {
   sharedTexture = std::make_shared<Texture>();
+  weakTexture = sharedTexture;
 }
 
 void TextureLoader::updateAsync() {
@@ -23,23 +24,124 @@ void TextureLoader::updateAsync() {
 
   // Read in the header.
   uint8_t buffer[TEXTURE_LOADER_HEADER_SIZE];
-  this->loader.read(buffer, TEXTURE_LOADER_HEADER_SIZE);
   size_t pos = 0;
   
   // Read Version
   pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
-  std::string version = std::string((char*)buffer, pos);
+  std::string version = std::string((char*)buffer);
   assertTrue(version == "DT_2.00", "Invalid Texture Version!");
 
   // Read Texture Width
   this->loader.setPosition(pos);
   pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  width = std::stoi(std::string((char*)buffer));
+  assertTrue(width > 0, "Invalid Texture Width!");
+
+  // Read Texture Height
+  this->loader.setPosition(pos);
+  pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  height = std::stoi(std::string((char*)buffer));
+  assertTrue(height > 0, "Invalid Texture Height!");
+
+  // Texture Format (RGBA, RGB, etc)
+  this->loader.setPosition(pos);
+  pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  int32_t iFormat = std::stoi(std::string((char*)buffer));
+  switch(iFormat) {
+    case 1: format = TextureFormat::R; break;
+    case 2: format = TextureFormat::RG; break;
+    case 3: format = TextureFormat::RGB; break;
+    case 4: format = TextureFormat::RGBA; break;
+    default: assertUnreachable("Invalid Texture Format %i!", iFormat);
+  }
+
+  // Wrap X
+  this->loader.setPosition(pos);
+  pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  int32_t iWrapX = std::stoi(std::string((char*)buffer));
+  switch(iWrapX) {
+    case 0: wrapX = TextureWrapMode::REPEAT; break;
+    case 1: wrapX = TextureWrapMode::MIRRORED_REPEAT; break;
+    case 2: wrapX = TextureWrapMode::CLAMP_TO_EDGE; break;
+    case 3: wrapX = TextureWrapMode::CLAMP_TO_BORDER; break;
+    default: assertUnreachable("Invalid Texture Wrap X %i!", iWrapX);
+  }
+
+  // Wrap Y
+  this->loader.setPosition(pos);
+  pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  int32_t iWrapY = std::stoi(std::string((char*)buffer));
+  switch(iWrapY) {
+    case 0: wrapY = TextureWrapMode::REPEAT; break;
+    case 1: wrapY = TextureWrapMode::MIRRORED_REPEAT; break;
+    case 2: wrapY = TextureWrapMode::CLAMP_TO_EDGE; break;
+    case 3: wrapY = TextureWrapMode::CLAMP_TO_BORDER; break;
+    default: assertUnreachable("Invalid Texture Wrap Y %i!", iWrapY);
+  }
+
+  // Filter Min
+  this->loader.setPosition(pos);
+  pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  int32_t iFilterMin = std::stoi(std::string((char*)buffer));
+  switch(iFilterMin) {
+    case 0: filterMin = TextureFilterMode::NEAREST; break;
+    case 1: filterMin = TextureFilterMode::LINEAR; break;
+    default: assertUnreachable("Invalid Texture Filter Min %i!", iFilterMin);
+  }
+
+  // Filter Mag
+  this->loader.setPosition(pos);
+  pos += this->loader.readUntil(buffer, TEXTURE_LOADER_HEADER_SIZE, '|');
+  int32_t iFilterMag = std::stoi(std::string((char*)buffer));
+  switch(iFilterMag) {
+    case 0: filterMag = TextureFilterMode::NEAREST; break;
+    case 1: filterMag = TextureFilterMode::LINEAR; break;
+    default: assertUnreachable("Invalid Texture Filter Mag %i!", iFilterMag);
+  }
+
+  // Data begins here. This part is done synchronously directly to the GPU.
+  this->loader.setPosition(pos);
+  size_t bufferSize = width * height * iFormat;
+  data = new uint8_t[bufferSize];
+  assertNotNull(data, "Failed to allocate texture data!");
+  this->loader.read(data, bufferSize);
+
+  // Handoff to sync to buffer to GPU.
+  this->state = TextureLoaderLoadState::ASYNC_DONE;
 }
 
 void TextureLoader::updateSync() {
+  if(this->state != TextureLoaderLoadState::ASYNC_DONE) return;
+  this->state = TextureLoaderLoadState::SYNC_LOADING;
 
+  assertNotNull(this->sharedTexture, "Texture is null!");
+  assertNotNull(this->data, "Texture data is null!");
+
+  // Setup Texture
+  this->sharedTexture->setSize(
+    this->width,
+    this->height,
+    this->format,
+    TextureDataFormat::UNSIGNED_BYTE
+  );
+  this->sharedTexture->buffer(this->data);
+
+  // Free data buffer
+  delete[] this->data;
+  this->data = nullptr;
+
+  // Leat go of the held pointer
+  this->sharedTexture = nullptr;
+
+  // Hand off and call done
+  this->state = TextureLoaderLoadState::SYNC_DONE;
+  this->loaded = true;
 }
 
 TextureLoader::~TextureLoader() {
+  if(this->data != nullptr) {
+    delete[] this->data;
+    this->data = nullptr;
+  }
   this->sharedTexture = nullptr;
 }
\ No newline at end of file
diff --git a/src/dawn/asset/loaders/TextureLoader.hpp b/src/dawn/asset/loaders/TextureLoader.hpp
index 83e78bf4..d5b19ddf 100644
--- a/src/dawn/asset/loaders/TextureLoader.hpp
+++ b/src/dawn/asset/loaders/TextureLoader.hpp
@@ -11,34 +11,25 @@
 #define TEXTURE_LOADER_HEADER_SIZE 256
 
 namespace Dawn {
-  enum class TextureLoaderHeaderParseState {
-    VERSION,
-    WIDTH,
-    HEIGHT,
-    FORMAT,
-    WRAP_MODE_X,
-    WRAP_MODE_Y,
-    FILTER_MODE_MIN,
-    FILTER_MODE_MAG,
-    DONE
-  };
-
   enum class TextureLoaderLoadState {
     INITIAL,
     ASYNC_LOADING,
+    ASYNC_DONE,
+    SYNC_LOADING,
+    SYNC_DONE
   };
 
   class TextureLoader : public AssetLoader {
     protected:
       AssetDataLoader loader;
-      // int32_t width = -1, height = -1;
-      // uint8_t *colors;
       enum TextureLoaderLoadState state;
-      // enum TextureFormat format;
-      // enum TextureWrapMode wrapModeX;
-      // enum TextureWrapMode wrapModeY;
-      // enum TextureFilterMode filterModeMin;
-      // enum TextureFilterMode filterModeMag;
+      uint8_t *data = nullptr;
+      int32_t width = -1, height = -1;
+      enum TextureFormat format;
+      enum TextureWrapMode wrapX;
+      enum TextureWrapMode wrapY;
+      enum TextureFilterMode filterMin;
+      enum TextureFilterMode filterMag;
 
     public:
       std::shared_ptr<Texture> sharedTexture;
diff --git a/src/dawn/dawnlibs.hpp b/src/dawn/dawnlibs.hpp
index 05ed6c48..3a4c2081 100644
--- a/src/dawn/dawnlibs.hpp
+++ b/src/dawn/dawnlibs.hpp
@@ -17,6 +17,7 @@ extern "C" {
   #include <stdlib.h>
   #include <assert.h>
   #include <float.h>
+  #include <errno.h>
 
   typedef bool bool_t;
   typedef char char_t;
diff --git a/src/dawn/display/ITexture.hpp b/src/dawn/display/ITexture.hpp
index 506aed57..dc0952e8 100644
--- a/src/dawn/display/ITexture.hpp
+++ b/src/dawn/display/ITexture.hpp
@@ -27,8 +27,8 @@ namespace Dawn {
   };
 
   enum class TextureDataFormat {
-    UNSIGNED_BYTE = 0,
-    FLOAT = 1
+    UNSIGNED_BYTE = sizeof(uint8_t),
+    FLOAT = sizeof(float_t)
   };
 
   class ITexture {
diff --git a/src/dawn/environment/CMakeLists.txt b/src/dawn/environment/CMakeLists.txt
new file mode 100644
index 00000000..5893dcaa
--- /dev/null
+++ b/src/dawn/environment/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (c) 2023 Dominic Msters
+# 
+# This software is released under the MIT License.
+# https://opensource.org/licenses/MIT
+
+# Sources
+target_sources(${DAWN_TARGET_NAME}
+  PRIVATE
+    Environment.cpp
+)
\ No newline at end of file
diff --git a/src/dawn/environment/Environment.cpp b/src/dawn/environment/Environment.cpp
new file mode 100644
index 00000000..023d4431
--- /dev/null
+++ b/src/dawn/environment/Environment.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 2023 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#include "Environment.hpp"
+#include "assert/assert.hpp"
+
+using namespace Dawn;
+
+Environment environment;
+
+bool_t Environment::hasVariable(const std::string &key) {
+  assertTrue(key.length() > 0, "Key must be at least 1 character long.");
+  return this->variables.find(key) != this->variables.end();
+}
+
+void Environment::setVariable(
+  const std::string &key,
+  const std::string &value
+) {
+  assertTrue(key.length() > 0, "Key must be at least 1 character long.");
+
+  this->variables[key] = value;
+}
+
+std::string Environment::getVariable(const std::string &key) {
+  assertTrue(key.length() > 0, "Key must be at least 1 character long.");
+  assertTrue(this->hasVariable(key), "Variable does not exist.");
+  return this->variables[key];
+}
diff --git a/src/dawn/environment/Environment.hpp b/src/dawn/environment/Environment.hpp
index 39c62e61..e4fd3c25 100644
--- a/src/dawn/environment/Environment.hpp
+++ b/src/dawn/environment/Environment.hpp
@@ -8,8 +8,34 @@
 
 namespace Dawn {
   class Environment {
-    public:
+    private:
+      std::map<std::string, std::string> variables;
 
+    public:
+      /**
+       * Checks if the environment has a variable.
+       * 
+       * @param key Variable key to check.
+       * @return True if the variable exists, false otherwise.
+       */
+      bool_t hasVariable(const std::string &key);
+
+      /**
+       * Sets a variable in the environment.
+       * 
+       * @param key Variable key to set.
+       * @param value Variable value to set.
+       */
+      void setVariable(const std::string &key, const std::string &value);
+
+      /**
+       * Gets a variable from the environment.
+       * 
+       * @param key Variable key to get.
+       * @return Variable value, or empty string if not found.
+       */
+      std::string getVariable(const std::string &key);
   };
-  extern Dawn::Environment environment;
 }
+
+extern Dawn::Environment environment;
\ No newline at end of file
diff --git a/src/dawnhelloworld/scene/HelloWorldScene.cpp b/src/dawnhelloworld/scene/HelloWorldScene.cpp
index 0acadcc4..65c87596 100644
--- a/src/dawnhelloworld/scene/HelloWorldScene.cpp
+++ b/src/dawnhelloworld/scene/HelloWorldScene.cpp
@@ -23,7 +23,7 @@ void Dawn::helloWorldScene(Scene &s) {
   cubeMesh->createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT);
   CubeMesh::buffer(cubeMesh, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1), 0, 0);
 
-  auto texture = s.getGame()->assetManager.get<Texture>("test_texture");
+  auto texture = s.getGame()->assetManager.get<Texture>("texture_border");
 
   auto cubeItem = s.createSceneItem();
   auto cubeMeshRenderer = cubeItem->addComponent<MeshRenderer>();
diff --git a/src/dawnlinux/main.cpp b/src/dawnlinux/main.cpp
index 0a95ccbc..47e26ed8 100644
--- a/src/dawnlinux/main.cpp
+++ b/src/dawnlinux/main.cpp
@@ -4,21 +4,60 @@
 // https://opensource.org/licenses/MIT
 
 #include "main.hpp"
+#include "assert/assert.hpp"
 #include "display/RenderHost.hpp"
 #include "game/Game.hpp"
 #include "asset/AssetDataLoader.hpp"
+#include "environment/Environment.hpp"
+#include <filesystem>
 
 using namespace Dawn;
-std::string executablePath;
+namespace fs = std::filesystem;
 
 FILE * AssetDataLoader::openAssetArchiveFile() {
-  std::string path = 
-  return fopen((path + "../../assets.tar").c_str(), "rb");
+  auto path = environment.getVariable("assetsPath");
+  auto file = fopen(path.c_str(), "rb");
+  if(!file) {
+    assertUnreachable("Failed to open assets file.\n%s!", strerror(errno));
+    return nullptr;
+  }
+  return file;
 }
 
 int32_t main(int32_t argc, const char **argv) {
   //Set the path
-  path = argv[0];
+  assertTrue(argc > 0, "No executable path provided.");
+  environment.setVariable("executablePath", argv[0]);
+
+  auto execPath = fs::path(environment.getVariable("executablePath"));
+  auto execDir = execPath;
+  while(fs::is_directory(execDir)) execDir = execDir.parent_path();
+  environment.setVariable("executableDir", execDir.string());
+
+  // Find the assets
+  std::vector<fs::path> pathsToTryAndFindAssets = {
+    execDir / "assets.tar",
+    execDir / "assets.zip",
+    execDir / "data" / "assets.tar",
+    execDir / "data" / "assets.zip",
+    execDir.parent_path() / "assets.tar",
+    execDir.parent_path() / "assets.zip",
+    execDir.parent_path() / "data" / "assets.tar",
+    execDir.parent_path() / "data" / "assets.zip",
+    execDir.parent_path().parent_path() / "assets.tar",
+    execDir.parent_path().parent_path() / "assets.zip",
+    execDir.parent_path().parent_path() / "data" / "assets.tar",
+    execDir.parent_path().parent_path() / "data" / "assets.zip"
+  };
+  auto matchingPathIfAny = std::find_if(pathsToTryAndFindAssets.begin(), pathsToTryAndFindAssets.end(), [](const fs::path &p) {
+    return fs::exists(p);
+  });
+
+  if(matchingPathIfAny == pathsToTryAndFindAssets.end()) {
+    std::cout << "Could not find game assets" << std::endl;
+    return 1;
+  }
+  environment.setVariable("assetsPath", matchingPathIfAny->string());
 
   //Create the game
   auto game = std::make_shared<Game>();