diff --git a/Histacom2.Engine/Histacom2.Engine.csproj b/Histacom2.Engine/Histacom2.Engine.csproj
index 1d36223..69443b1 100644
--- a/Histacom2.Engine/Histacom2.Engine.csproj
+++ b/Histacom2.Engine/Histacom2.Engine.csproj
@@ -44,6 +44,9 @@
+
+ ..\packages\Whoa.1.5.0\lib\net45\Whoa.dll
+
diff --git a/Histacom2.Engine/SaveSystem.cs b/Histacom2.Engine/SaveSystem.cs
index a3fd2ef..b4cd56f 100644
--- a/Histacom2.Engine/SaveSystem.cs
+++ b/Histacom2.Engine/SaveSystem.cs
@@ -2,7 +2,7 @@
// to put some effort into cheating ;)
// During development, leave it undefined to use the
// easily modifiable JSON serialised format
-//#define BINARY_SAVE
+#define BINARY_SAVE
using System;
using System.Collections.Generic;
@@ -18,6 +18,10 @@
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
+#if BINARY_SAVE
+using Whoa;
+#endif
+
namespace Histacom2.Engine
{
public static class SaveSystem
@@ -36,8 +40,7 @@ public static class SaveSystem
#endif
#if BINARY_SAVE
- private static readonly byte[] magic = Encoding.UTF8.GetBytes("THSv");
- private static readonly IOrderedEnumerable properties = typeof(Save).GetProperties().OrderBy(p => (p.GetCustomAttributes(typeof(OrderAttribute), false).SingleOrDefault() as OrderAttribute).Order);
+ private static readonly int magic = 0x76534854; // 'THSv'
#endif
public static string GameDirectory
@@ -65,7 +68,11 @@ public static string AllProfilesDirectory
}
public static string ProfileName = "";
- public static string ProfileFile = "main.save";
+#if BINARY_SAVE
+ public static string ProfileFile = "main.whoa";
+#else
+ public static string ProfileFile = "main.json";
+#endif
public static string ProfileDirectory
{
@@ -404,162 +411,15 @@ public static void SaveDirectoryInfo(string parent, string dirname, bool isProte
File.WriteAllText(Path.Combine(Path.Combine(parent, dirname), "_data.info"), toWrite);
}
-#if BINARY_SAVE
- // Be careful with this... it trusts that the calling code has already checked
- // that T can be written by BinaryWriter.
- // No generics, because that'd be near-impossible to read back.
- private static void WriteList(BinaryWriter write, List list)
- {
- if (list == null)
- write.Write(0);
- else
- {
- write.Write(list.Count);
- foreach (T obj in list)
- ((dynamic)write).Write(obj);
- }
- }
-
- private static List ReadList(BinaryReader read, string reader)
- {
- int count = read.ReadInt32();
- var ret = new List(count);
- var function = typeof(BinaryReader).GetMethod(reader);
- for (int i = 0; i < count; i++)
- ret.Add((T) function.Invoke(read, new object[] { }));
- return ret;
- }
-
- private static void WriteBitfield(Stream fobj, IEnumerable bools)
- {
- sbyte bit = 7;
- int cur = 0;
- var bitfields = new byte[bools.Count() / 8 + 1];
- foreach (bool mybool in bools)
- {
- if (mybool)
- bitfields[cur] |= (byte) (1 << bit);
- bit--;
- if (bit < 0)
- {
- bit = 7;
- cur++;
- }
- }
- fobj.Write(bitfields, 0, bitfields.Length);
- }
-
- private static List ReadBitfield(Stream fobj, int count)
- {
- sbyte bit = 7;
- int cur = 0;
- var bitfields = new byte[count / 8 + 1];
- var bools = new List(count);
- byte val = (byte) fobj.ReadByte();
- fobj.Read(bitfields, 0, bitfields.Length);
- for (int i = 0; i < count; i++)
- {
- bools.Add(((val >> bit) & 1) == 1);
- bit--;
- if (bit < 0)
- {
- bit = 7;
- cur++;
- }
- }
- return bools;
- }
-#endif
-
public static Save ReadSave(string fname)
{
#if BINARY_SAVE
using (var fobj = File.OpenRead(fname))
+ using (var read = new BinaryReader(fobj))
{
- var save = new Save();
- var header = new byte[magic.Length];
- var read = new BinaryReader(fobj);
- fobj.Read(header, 0, magic.Length);
- if (!magic.SequenceEqual(header))
+ if (read.ReadInt32() != magic)
throw new InvalidDataException("This is not a Histacom2 binary save");
- int numprops = read.ReadInt32();
- var bools = new List();
- // Holy code duplication, Batman.
- // If you know a better way to get C# to do this, I'm all ears.
- foreach (var property in properties.Take(numprops))
- {
- if (property.PropertyType == typeof(string))
- property.SetValue(save, read.ReadString());
- else if (property.PropertyType == typeof(int))
- property.SetValue(save, read.ReadInt32());
- else if (property.PropertyType == typeof(uint))
- property.SetValue(save, read.ReadUInt32());
- else if (property.PropertyType == typeof(long))
- property.SetValue(save, read.ReadInt64());
- else if (property.PropertyType == typeof(ulong))
- property.SetValue(save, read.ReadUInt64());
- else if (property.PropertyType == typeof(short))
- property.SetValue(save, read.ReadInt16());
- else if (property.PropertyType == typeof(ushort))
- property.SetValue(save, read.ReadUInt16());
- else if (property.PropertyType == typeof(byte))
- property.SetValue(save, read.ReadByte());
- else if (property.PropertyType == typeof(sbyte))
- property.SetValue(save, read.ReadSByte());
- else if (property.PropertyType == typeof(char))
- property.SetValue(save, read.ReadChar());
- else if (property.PropertyType == typeof(float))
- property.SetValue(save, read.ReadSingle());
- else if (property.PropertyType == typeof(double))
- property.SetValue(save, read.ReadDouble());
- else if (property.PropertyType == typeof(decimal))
- property.SetValue(save, read.ReadDecimal());
-
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadString"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadInt32"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadUInt32"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadInt64"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadUInt64"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadInt16"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadUInt16"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadByte"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadSByte"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadChar"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadSingle"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadDouble"));
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadList(read, "ReadDecimal"));
-
- // Remember to read this boolean from the bitfield at the end.
- else if (property.PropertyType == typeof(bool))
- bools.Add(property);
-
- else if (property.PropertyType == typeof(List))
- property.SetValue(save, ReadBitfield(fobj, read.ReadInt32()));
-
- // RIP
- else
- throw new InvalidDataException("There is no deserialisation method specified for " + property.PropertyType.ToString());
- }
-
- // Let's read the ultra tiny bitfield.
- var loaded = ReadBitfield(fobj, bools.Count);
- foreach (var item in bools.Zip(loaded, (p, b) => new { Property = p, Value = b }))
- item.Property.SetValue(save, item.Value);
-
- return save;
+ return Whoa.Whoa.DeserialiseObject(fobj, SerialisationOptions.NonSerialized);
}
#else
return JsonConvert.DeserializeObject(File.ReadAllText(fname));
@@ -570,105 +430,10 @@ public static void WriteSave(string fname, Save save)
{
#if BINARY_SAVE
using (var fobj = File.OpenWrite(fname))
+ using (var write = new BinaryWriter(fobj))
{
- var write = new BinaryWriter(fobj);
- var bools = new List();
- fobj.Write(magic, 0, magic.Length);
- write.Write(properties.Count()); // The number of properties basically acts as the version number.
-
- foreach (var property in properties)
- {
- if (property == null)
- continue;
-
- // Types that can be written by BinaryWriter, except booleans.
- if (property.PropertyType == typeof(string))
- {
- var val = property.GetValue(save) as string;
- if (val == null)
- write.Write("");
- else
- write.Write(val);
- }
- else if (property.PropertyType == typeof(int))
- write.Write((int) property.GetValue(save));
- else if (property.PropertyType == typeof(uint))
- write.Write((uint) property.GetValue(save));
- else if (property.PropertyType == typeof(long))
- write.Write((long) property.GetValue(save));
- else if (property.PropertyType == typeof(ulong))
- write.Write((ulong) property.GetValue(save));
- else if (property.PropertyType == typeof(short))
- write.Write((short) property.GetValue(save));
- else if (property.PropertyType == typeof(ushort))
- write.Write((ushort) property.GetValue(save));
- else if (property.PropertyType == typeof(byte))
- write.Write((byte) property.GetValue(save));
- else if (property.PropertyType == typeof(sbyte))
- write.Write((sbyte) property.GetValue(save));
- else if (property.PropertyType == typeof(char))
- write.Write((char) property.GetValue(save));
- else if (property.PropertyType == typeof(float))
- write.Write((float) property.GetValue(save));
- else if (property.PropertyType == typeof(double))
- write.Write((double) property.GetValue(save));
- else if (property.PropertyType == typeof(decimal))
- write.Write((double) property.GetValue(save));
-
- // ... and their lists.
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
- else if (property.PropertyType == typeof(List))
- WriteList(write, property.GetValue(save) as List);
-
- // Booleans - they go in the bitfield at the end.
- else if (property.PropertyType == typeof(bool))
- bools.Add((bool) property.GetValue(save));
-
- // List of booleans - it gets its own bitfield.
- else if (property.PropertyType == typeof(List))
- {
- var val = property.GetValue(save) as List;
- if (val == null)
- write.Write(0);
- else
- {
- write.Write(val.Count());
- WriteBitfield(fobj, val);
- }
- }
-
- // Now what?
- else
- throw new InvalidDataException("There is no serialisation method specified for " + property.PropertyType.ToString());
- }
-
- // In order to save space, we store bools in a bitfield at the end.
- // One byte can store 8 bools, saving a whopping 7 bytes which can then be used for
- // extremely short text documents or something.
- WriteBitfield(fobj, bools);
+ write.Write(magic);
+ Whoa.Whoa.SerialiseObject(fobj, save, SerialisationOptions.NonSerialized);
}
#else
// Serialize the save to JSON.
@@ -772,61 +537,25 @@ public static void SetTheme()
}
}
-
- // This lets us preserve the order of properties.
- // Thanks to "ghord" from StackOverflow.
- public sealed class OrderAttribute : Attribute
- {
- private readonly int order_;
- public OrderAttribute([CallerLineNumber]int order = 0)
- {
- order_ = order;
- }
- public int Order { get { return order_; } }
- }
-
public class Save
{
// To maintain binary save compatibility,
- // add all new properties to the end and don't remove any.
- // Also, every property needs an "Order" attribute.
+ // add all new properties/fields to the end and don't remove any.
- [Order]
public string Username { get; set; }
-
- [Order]
public string CurrentOS { get; set; }
// public Dictionary InstalledPrograms { get; set; } InstallProgram is no longer needed... we have that data in the FileSystem
- [Order]
public List ExperiencedStories { get; set; }
-
- [Order]
public bool FTime95 { get; set; }
-
- [Order]
public int mineSweepE { get; set; } = 999;
-
- [Order]
public int mineSweepI { get; set; } = 999;
-
- [Order]
public int mineSweepH { get; set; } = 999;
-
- [Order]
public string ThemeName { get; set; }
-
- [Order]
public int BytesLeft { get; set; }
-
- [Order]
public Theme customTheme { get; set; }
-
- [Order]
public bool FTime98 { get; set; }
-
- [Order]
public bool[] installed95 { get; set; } = new bool[7]; // 0: WC98, 1: FTP, 2: SR, 3: EB, 4: SKNDWS, 5: TD0.1, 6: GTN
}
diff --git a/Histacom2.Engine/packages.config b/Histacom2.Engine/packages.config
index 810e559..de52fcd 100644
--- a/Histacom2.Engine/packages.config
+++ b/Histacom2.Engine/packages.config
@@ -1,4 +1,5 @@
+
\ No newline at end of file
diff --git a/Histacom2/SaveDialogs/SaveFileTroubleShooter.cs b/Histacom2/SaveDialogs/SaveFileTroubleShooter.cs
index 3283ca1..7fe258f 100644
--- a/Histacom2/SaveDialogs/SaveFileTroubleShooter.cs
+++ b/Histacom2/SaveDialogs/SaveFileTroubleShooter.cs
@@ -42,12 +42,12 @@ void BeginScan()
// Check if the main.save file exists
- string savefile = Path.Combine(SaveSystem.ProfileDirectory, "main.save");
- string oldsavefile = Path.Combine(SaveSystem.ProfileDirectory, "oldmain.save");
+ string savefile = Path.Combine(SaveSystem.ProfileDirectory, SaveSystem.ProfileFile);
+ string oldsavefile = Path.Combine(SaveSystem.ProfileDirectory, "old" + SaveSystem.ProfileFile);
if (!File.Exists(savefile))
{
- WriteToLog("ISSUE FOUND! File main.save doesn't exist");
+ WriteToLog($"ISSUE FOUND! File {SaveSystem.ProfileFile} doesn't exist");
WriteToLog("Creating one...");
@@ -58,7 +58,7 @@ void BeginScan()
EndScan(true);
return;
} else {
- WriteToLog("File main.save does exist - checking contents");
+ WriteToLog($"File {SaveSystem.ProfileFile} does exist - checking contents");
bool readable = false;
try
@@ -136,11 +136,11 @@ void BeginScan()
} catch {
// It's unusable...
- WriteToLog("ISSUE FOUND! File main.save is unreadable");
+ WriteToLog($"ISSUE FOUND! File {SaveSystem.ProfileFile} is unreadable");
WriteToLog("Sorry, there is no repairing it easily, your data will be lost");
- string backupfile = Path.Combine(SaveSystem.ProfileDirectory, "main.backup");
+ string backupfile = savefile + ".bak";
if (Directory.Exists(backupfile)) Directory.Delete(backupfile);