diff --git a/.gitmodules b/.gitmodules
index e4fb1601..9a8c6484 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,3 +5,4 @@
 [submodule "third_party/sm"]
 	path = third_party/sm
 	url = https://github.com/Ewpratten/sm
+	ignore = dirty
diff --git a/game/game_logic/Cargo.toml b/game/game_logic/Cargo.toml
index 19eeddf1..071d3ac1 100644
--- a/game/game_logic/Cargo.toml
+++ b/game/game_logic/Cargo.toml
@@ -8,3 +8,8 @@ edition = "2021"
 [dependencies]
 raylib = { version = "3.7", path = "../../third_party/raylib-rs/raylib" }
 sad_machine = { version = "1.0", path = "../../third_party/sm" }
+log = "0.4.14"
+profiling = "1.0.5"
+serde = { version = "1.0.136", features = ["derive"] }
+serde_json = "1.0.79"
+directories = "4.0.1"
\ No newline at end of file
diff --git a/game/game_logic/src/lib.rs b/game/game_logic/src/lib.rs
index 3f59d96a..b1f9531b 100644
--- a/game/game_logic/src/lib.rs
+++ b/game/game_logic/src/lib.rs
@@ -1,3 +1,27 @@
-/// This is the game logic entrypoint. Despite being async, 
+//! This file is the main entry point for the game logic.
+
+pub(crate) mod persistent;
+
+/// This is the game logic entrypoint. Despite being async,
 /// this is expected to block the main thread for rendering and stuff.
-pub async fn entrypoint() {}
+pub async fn entrypoint() {
+    log::info!("Game main thread handed off to logic crate.");
+
+    // Load the game settings
+    let mut settings = persistent::settings::PersistentGameSettings::load_or_create()
+        .expect("Failed to parse game settings from disk. Possibly corrupt file?");
+
+    // Load the game save state
+    let mut save_state = persistent::save_state::GameSaveState::load_or_create()
+        .expect("Failed to parse game save state from disk. Possibly corrupt file?");
+
+    // TODO: Blocking game loop goes here
+
+    // Clean up any resources
+    settings
+        .save()
+        .expect("Could not save game settings to disk.");
+    save_state
+        .save()
+        .expect("Could not save game save state to disk.");
+}
diff --git a/game/game_logic/src/persistent/mod.rs b/game/game_logic/src/persistent/mod.rs
new file mode 100644
index 00000000..51ea2683
--- /dev/null
+++ b/game/game_logic/src/persistent/mod.rs
@@ -0,0 +1,6 @@
+//! This module contains the datastructure backing persistent data.
+//! 
+//! This includes stuff like settings and game save state.
+
+pub mod settings;
+pub mod save_state;
\ No newline at end of file
diff --git a/game/game_logic/src/persistent/save_state.rs b/game/game_logic/src/persistent/save_state.rs
new file mode 100644
index 00000000..762bd32a
--- /dev/null
+++ b/game/game_logic/src/persistent/save_state.rs
@@ -0,0 +1,75 @@
+use std::path::PathBuf;
+
+use directories::ProjectDirs;
+use serde::{Deserialize, Serialize};
+
+/// Game save state.
+///
+/// This can be used for health, coins, inventory, progress, high scores, etc.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GameSaveState {
+    // TODO: Add data here.
+}
+
+// Add any default values here.
+impl Default for GameSaveState {
+    fn default() -> Self {
+        Self {}
+    }
+}
+
+/* ----------------------- You likely are looking for code above this line ----------------------- */
+
+// This is the code for actually saving and loading the file from disk.
+impl GameSaveState {
+    /// Returns the optimal path for storing settings data.
+    #[profiling::function]
+    fn get_save_location() -> PathBuf {
+        // We should allow this path to be overridden through an environment variable.
+        let preferences_dir = match std::env::var("OVERRIDE_GAME_SAVE_STATE_LOCATION") {
+            Ok(path) => PathBuf::from(path),
+            Err(_) => {
+                // If there is no override, we shall ask `directories` for the appropriate location.
+                ProjectDirs::from("com", "va3zza", "ludum-dare-50")
+                    .unwrap()
+                    .data_local_dir()
+                    .to_path_buf()
+            }
+        };
+
+        return preferences_dir.join("progress.json");
+    }
+
+    /// Loads the savestate from disk.
+    #[profiling::function]
+    pub fn load_or_create() -> Result<Self, serde_json::Error> {
+        // Attempt to load the savestate from the save location.
+        let save_location = Self::get_save_location();
+        log::debug!(
+            "Attempting to load game savestate from: {}",
+            save_location.display()
+        );
+
+        if save_location.is_file() {
+            log::debug!("Found existing savestate file.");
+            return serde_json::from_str(std::fs::read_to_string(&save_location).unwrap().as_str());
+        }
+
+        // If we got here, we need to create a new savestate file. In this case, we can just init the default savestate.
+        log::debug!("No existing savestate file found.");
+        return Ok(Self::default());
+    }
+
+    /// Saves the savestate to disk.
+    #[profiling::function]
+    pub fn save(&self) -> Result<(), serde_json::Error> {
+        // Get the save location
+        let save_location = Self::get_save_location();
+        log::debug!("Saving game savestate to: {}", save_location.display());
+
+        // Write the savestate to disk.
+        std::fs::write(save_location, serde_json::to_string(self).unwrap()).unwrap();
+
+        return Ok(());
+    }
+}
diff --git a/game/game_logic/src/persistent/settings.rs b/game/game_logic/src/persistent/settings.rs
new file mode 100644
index 00000000..6d2e7268
--- /dev/null
+++ b/game/game_logic/src/persistent/settings.rs
@@ -0,0 +1,76 @@
+use std::path::PathBuf;
+
+use directories::ProjectDirs;
+use serde::{Deserialize, Serialize};
+
+/// Settings for the game.
+///
+/// You can put whatever you want in here.
+/// Please don't add anything relating to gameplay though (no coins, health, etc.).
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct PersistentGameSettings {
+    // TODO: Add settings here.
+}
+
+// Add any default values here.
+impl Default for PersistentGameSettings {
+    fn default() -> Self {
+        Self {}
+    }
+}
+
+/* ----------------------- You likely are looking for code above this line ----------------------- */
+
+// This is the code for actually saving and loading the file from disk.
+impl PersistentGameSettings {
+    /// Returns the optimal path for storing settings data.
+    #[profiling::function]
+    fn get_save_location() -> PathBuf {
+        // We should allow this path to be overridden through an environment variable.
+        let preferences_dir = match std::env::var("OVERRIDE_GAME_SETTINGS_SAVE_LOCATION") {
+            Ok(path) => PathBuf::from(path),
+            Err(_) => {
+                // If there is no override, we shall ask `directories` for the appropriate location.
+                ProjectDirs::from("com", "va3zza", "ludum-dare-50")
+                    .unwrap()
+                    .preference_dir()
+                    .to_path_buf()
+            }
+        };
+
+        return preferences_dir.join("settings.json");
+    }
+
+    /// Loads the settings from disk.
+    #[profiling::function]
+    pub fn load_or_create() -> Result<Self, serde_json::Error> {
+        // Attempt to load the settings from the save location.
+        let save_location = Self::get_save_location();
+        log::debug!(
+            "Attempting to load game settings from: {}",
+            save_location.display()
+        );
+
+        if save_location.is_file() {
+            log::debug!("Found existing settings file.");
+            return serde_json::from_str(std::fs::read_to_string(&save_location).unwrap().as_str());
+        }
+
+        // If we got here, we need to create a new settings file. In this case, we can just init the default settings.
+        log::debug!("No existing settings file found.");
+        return Ok(Self::default());
+    }
+
+    /// Saves the settings to disk.
+    #[profiling::function]
+    pub fn save(&self) -> Result<(), serde_json::Error> {
+        // Get the save location
+        let save_location = Self::get_save_location();
+        log::debug!("Saving game settings to: {}", save_location.display());
+
+        // Write the settings to disk.
+        std::fs::write(save_location, serde_json::to_string(self).unwrap()).unwrap();
+
+        return Ok(());
+    }
+}
diff --git a/third_party/raylib-rs b/third_party/raylib-rs
index 67775a41..3aff1382 160000
--- a/third_party/raylib-rs
+++ b/third_party/raylib-rs
@@ -1 +1 @@
-Subproject commit 67775a41ab64aae22951455fdd45147b9cbb749a
+Subproject commit 3aff138276b374f5e07187a652a71d9eb59e97d1