diff --git a/Nibriboard/Nibriboard.csproj b/Nibriboard/Nibriboard.csproj
index 1a6f01f..e219c87 100644
--- a/Nibriboard/Nibriboard.csproj
+++ b/Nibriboard/Nibriboard.csproj
@@ -7,7 +7,7 @@
     <OutputType>Exe</OutputType>
     <RootNamespace>Nibriboard</RootNamespace>
     <AssemblyName>Nibriboard</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
     <DebugSymbols>true</DebugSymbols>
@@ -40,6 +40,7 @@
     <Compile Include="RippleSpace\ChunkReference.cs" />
     <Compile Include="RippleSpace\DrawnLine.cs" />
     <Compile Include="RippleSpace\Reference.cs" />
+    <Compile Include="Utilities.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="RippleSpace\" />
diff --git a/Nibriboard/RippleSpace/Chunk.cs b/Nibriboard/RippleSpace/Chunk.cs
index ba67166..5f6263d 100644
--- a/Nibriboard/RippleSpace/Chunk.cs
+++ b/Nibriboard/RippleSpace/Chunk.cs
@@ -1,32 +1,34 @@
 ﻿using System;
 using System.IO;
 using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
 using System.Collections.Generic;
 using System.Collections;
+using System.Runtime.Serialization;
 
 namespace Nibriboard.RippleSpace
 {
 	/// <summary>
 	/// Represents a single chunk of an infinite <see cref="Nibriboard.RippleSpace.Plane" />.
 	/// </summary>
-	public class Chunk : IEnumerable<DrawnLine>
+	[Serializable]
+	public class Chunk : IEnumerable<DrawnLine>, IDeserializationCallback
 	{
 		/// <summary>
 		/// The lines that this chunk currently contains.
 		/// </summary>
 		private List<DrawnLine> lines = new List<DrawnLine>();
 
-		/// <summary>
-		/// The plane that this chunk is located on.
-		/// </summary>
-		public readonly Plane Plane;
-
 		/// <summary>
 		/// The size of this chunk.
 		/// </summary>
 		public readonly int Size;
 
+		/// <summary>
+		/// The location of this chunk chunk on the plane.
+		/// </summary>
+		public readonly ChunkReference Location;
+
+
 		/// <summary>
 		/// The time at which this chunk was loaded.
 		/// </summary>
@@ -36,17 +38,80 @@ namespace Nibriboard.RippleSpace
 		/// </summary>
 		public DateTime TimeLastAccessed { get; private set; } = DateTime.Now;
 
-		public Chunk(Plane inPlane, int inSize)
+		/// <summary>
+		/// Whether this <see cref="T:Nibriboard.RippleSpace.Chunk"/> is primary chunk.
+		/// Primary chunks are always loaded.
+		/// </summary>
+		public bool IsPrimaryChunk
+		{
+			get {
+				if(Location.X < Location.Plane.PrimaryChunkAreaSize &&
+				   Location.X > -Location.Plane.PrimaryChunkAreaSize &&
+				   Location.Y < Location.Plane.PrimaryChunkAreaSize &&
+				   Location.Y > -Location.Plane.PrimaryChunkAreaSize)
+				{
+					return true;
+				}
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Whether this chunk is inactive or not.
+		/// </summary>
+		/// <remarks>
+		/// Note that even if a chunk is inactive, it's not guaranteed that
+		/// it will be unloaded. It's possible that the server will keep it
+		/// loaded anyway - it could be a primary chunk, or the server may not
+		/// have many chunks loaded at a particular time.
+		/// </remarks>
+		public bool Inactive
+		{
+			get {
+				// If the time we were last accessed + the inactive timer is
+				// still less than the current time, then we're inactive.
+				if (TimeLastAccessed.AddMilliseconds(Plane.InactiveMillisecs) < DateTime.Now)
+					return false;
+				
+				return true;
+			}
+		}
+
+		/// <summary>
+		/// Whether this chunk could, theorectically, be unloaded. Of course,
+		/// the server may decide it doesn't need to unload us even if we're
+		/// inactive.
+		/// </summary>
+		public bool CouldUnload
+		{
+			get {
+				// If we're a primary chunk or not inactive, then we shouldn't
+				// unload it.
+				if (IsPrimaryChunk || !Inactive)
+					return false;
+				
+				return true;
+			}
+		}
+
+		public Chunk(Plane inPlane, int inSize, ChunkReference inLocation)
 		{
 			Plane = inPlane;
 			Size = inSize;
+			Location = inLocation;
 		}
 
+		/// <summary>
+		/// Updates the time the chunk was last accessed, thereby preventing it
+		/// from becoming inactive.
+		/// </summary>
 		public void UpdateAccessTime()
 		{
 			TimeLastAccessed = DateTime.Now;
 		}
 
+		#region Enumerator
+
 		public DrawnLine this[int i]
 		{
 			get {
@@ -70,25 +135,28 @@ namespace Nibriboard.RippleSpace
 			return GetEnumerator();
 		}
 
+		#endregion
+
+		#region Serialisation
+
 		public static async Task<Chunk> FromFile(Plane plane, string filename)
 		{
-			StreamReader chunkSource = new StreamReader(filename);
+			FileStream chunkSource = new FileStream(filename, FileMode.Open);
 			return await FromStream(plane, chunkSource);
 		}
-		public static async Task<Chunk> FromStream(Plane plane, StreamReader chunkSource)
+		public static async Task<Chunk> FromStream(Plane plane, Stream chunkSource)
 		{
-			Chunk result = new Chunk(
-				plane,
-				int.Parse(chunkSource.ReadLine())
-			);
+			Chunk loadedChunk = await Utilities.DeserialiseBinaryObject<Chunk>(chunkSource);
+			loadedChunk.Plane = plane;
 
-			string nextLine = string.Empty;
-			while((nextLine = await chunkSource.ReadLineAsync()) != null)
-			{
-				throw new NotImplementedException();
-			}
-
-			return result;
+			return loadedChunk;
 		}
+
+		public void OnDeserialization(object sender)
+		{
+			UpdateAccessTime();
+		}
+
+		#endregion
 	}
 }
diff --git a/Nibriboard/RippleSpace/ChunkReference.cs b/Nibriboard/RippleSpace/ChunkReference.cs
index a768070..7777aad 100644
--- a/Nibriboard/RippleSpace/ChunkReference.cs
+++ b/Nibriboard/RippleSpace/ChunkReference.cs
@@ -1,5 +1,6 @@
 ﻿using System;
 using System.Security.Policy;
+using System.IO;
 namespace Nibriboard.RippleSpace
 {
 	/// <summary>
@@ -34,5 +35,23 @@ namespace Nibriboard.RippleSpace
 		{
 			return $"{Plane.Name}-{X},{Y}.chunk";
 		}
+
+		public static ChunkReference Parse(Plane plane, string source)
+		{
+			if (!source.StartsWith("ChunkReference:"))
+				throw new InvalidDataException($"Error: That isn't a valid chunk reference. Chunk references start with 'ChunkReference:'.");
+
+			// Trim the extras off the reference
+			source = source.Substring("ChunkReference:".Length);
+			source = source.Trim("() \v\t\r\n".ToCharArray());
+
+			int x = source.Substring(0, source.IndexOf(","));
+			int y = source.Substring(source.IndexOf(",") + 1);
+			return new ChunkReference(
+				plane,
+				x,
+				y
+			);
+		}
 	}
 }
diff --git a/Nibriboard/RippleSpace/LocationReference.cs b/Nibriboard/RippleSpace/LocationReference.cs
index 9944f14..97b60f4 100644
--- a/Nibriboard/RippleSpace/LocationReference.cs
+++ b/Nibriboard/RippleSpace/LocationReference.cs
@@ -10,5 +10,24 @@ namespace Nibriboard.RippleSpace
 		{
 			
 		}
+
+		public static LocationReference Parse(Plane plane, string source)
+		{
+			// TODO: Decide if this is the format that we want to use for location references
+			if (!source.StartsWith("LocationReference:"))
+				throw new InvalidDataException($"Error: That isn't a valid location reference. Location references start with 'ChunkReference:'.");
+
+			// Trim the extras off the reference
+			source = source.Substring("LocationReference:".Length);
+			source = source.Trim("() \v\t\r\n".ToCharArray());
+
+			int x = source.Substring(0, source.IndexOf(","));
+			int y = source.Substring(source.IndexOf(",") + 1);
+			return new LocationReference(
+				plane,
+				x,
+				y
+			);
+		}
 	}
 }
diff --git a/Nibriboard/RippleSpace/Plane.cs b/Nibriboard/RippleSpace/Plane.cs
index 792915b..5b6d900 100644
--- a/Nibriboard/RippleSpace/Plane.cs
+++ b/Nibriboard/RippleSpace/Plane.cs
@@ -1,6 +1,7 @@
 ﻿using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
+using System.IO;
 namespace Nibriboard.RippleSpace
 {
 	/// <summary>
@@ -18,6 +19,23 @@ namespace Nibriboard.RippleSpace
 		/// </summary>
 		public readonly int ChunkSize;
 
+		/// <summary>
+		/// The path to the directory that the plane's information will be stored in.
+		/// </summary>
+		public readonly string StorageDirectory;
+
+		/// <summary>
+		/// The number of milliseconds that should pass since a chunk's last
+		/// access in order for it to be considered inactive.
+		/// </summary>
+		public int InactiveMillisecs = 60 * 1000;
+
+		/// <summary>
+		/// The number of chunks in a square around (0, 0) that should always be
+		/// loaded.
+		/// </summary>
+		public int PrimaryChunkAreaSize = 10;
+
 		/// <summary>
 		/// The chunkspace that holds the currently loaded and active chunks.
 		/// </summary>
@@ -27,6 +45,8 @@ namespace Nibriboard.RippleSpace
 		{
 			Name = inName;
 			ChunkSize = inChunkSize;
+
+			StorageDirectory = $"./Planes/{Name}";
 		}
 
 		public async Task<Chunk> FetchChunk(ChunkReference chunkLocation)
diff --git a/Nibriboard/Utilities.cs b/Nibriboard/Utilities.cs
new file mode 100644
index 0000000..d829ecd
--- /dev/null
+++ b/Nibriboard/Utilities.cs
@@ -0,0 +1,17 @@
+﻿using System;
+using System.Threading.Tasks;
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
+namespace Nibriboard
+{
+	public static class Utilities
+	{
+		public static async Task<T> DeserialiseBinaryObject<T>(Stream sourceStream)
+		{
+			return await Task.Run(() => {
+				BinaryFormatter formatter = new BinaryFormatter();
+				return (T)formatter.Deserialize(sourceStream);
+			});
+		}
+	}
+}
