From bc9623bbdf635cc16cc95f8a86a241a6933a8fdd Mon Sep 17 00:00:00 2001 From: RogueAI42 Date: Sun, 23 Jul 2017 22:09:41 +1000 Subject: i goofed This reverts commit c446a76e0c5b7b35638f1e223ad167b46f30941e. --- TimeHACK.Engine/SaveSystem.cs | 370 +++++------------------------------------- 1 file changed, 43 insertions(+), 327 deletions(-) (limited to 'TimeHACK.Engine/SaveSystem.cs') diff --git a/TimeHACK.Engine/SaveSystem.cs b/TimeHACK.Engine/SaveSystem.cs index cda4464..81aa322 100644 --- a/TimeHACK.Engine/SaveSystem.cs +++ b/TimeHACK.Engine/SaveSystem.cs @@ -1,10 +1,4 @@ -// Define BINARY_SAVE before release so the player has -// to put some effort into cheating ;) -// During development, leave it undefined to use the -// easily modifiable JSON serialised format -//#define BINARY_SAVE - -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -13,7 +7,6 @@ using System.Threading.Tasks; using Newtonsoft.Json; using System.Diagnostics; using System.Windows.Forms; -using System.Runtime.CompilerServices; namespace TimeHACK.Engine { @@ -26,11 +19,6 @@ namespace TimeHACK.Engine public static Theme currentTheme { get; set; } -#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); -#endif - public static string GameDirectory { get @@ -114,6 +102,36 @@ namespace TimeHACK.Engine } } + public static bool LoadSave() + { + try + { + // ON A FINAL RELEASE USE THE "FINAL RELEASE THINGS" + #region Final Release Things + //Read base64 string from file + //string b64 = File.ReadAllText(Path.Combine(ProfileDirectory, ProfileFile)); + //Get Unicode byte array + //byte[] bytes = Convert.FromBase64String(b64); + //Decode the Unicode + //string json = Encoding.UTF8.GetString(bytes); + //Deserialize save object. + #endregion + // USE THE THINGS IN THE "DEVELOPER THINGS" FOR A DEVELOPMENT RELEASE + #region Developer Things + string json = File.ReadAllText(Path.Combine(ProfileDirectory, ProfileFile)); + #endregion + CurrentSave = JsonConvert.DeserializeObject(json); + + } catch + { + MessageBox.Show("WARNING! It looks like this save is corrupt!"); + MessageBox.Show("We will now open the Save troubleshooter"); + + troubleshooter.ShowDialog(); + } + return true; + } + public static void NewGame() { var save = new Save(); @@ -229,297 +247,21 @@ namespace TimeHACK.Engine File.WriteAllText(Path.Combine(directory, "_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)) - { - 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)) - throw new InvalidDataException("This is not a TimeHACK 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; - } -#else - return JsonConvert.DeserializeObject(File.ReadAllText(fname)); -#endif - } - - public static void WriteSave(string fname, Save save) - { -#if BINARY_SAVE - using (var fobj = File.OpenWrite(fname)) - { - 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); - } -#else - // Serialize the save to JSON. - File.WriteAllText(fname, JsonConvert.SerializeObject(save, Formatting.Indented)); -#endif - } public static void SaveGame() { - WriteSave(Path.Combine(ProfileDirectory, ProfileFile), CurrentSave); - } - - public static bool LoadSave() - { - string savefile = Path.Combine(ProfileDirectory, ProfileFile); - try - { - CurrentSave = ReadSave(savefile); - } - catch - { - MessageBox.Show("WARNING! It looks like this save is corrupt! We will now open the Save troubleshooter"); - - troubleshooter.ShowDialog(); - } - return true; + //Serialize the save to JSON. + string json = JsonConvert.SerializeObject(CurrentSave, Formatting.Indented); + + // ADD THE TWO LINES OF CODE BELOW ON A FINAL RELEASE + //Get JSON bytes (Unicode format). + //var bytes = Encoding.UTF8.GetBytes(json); + //Encode the array into Base64. + //string b64 = Convert.ToBase64String(bytes); + //Write to disk. + + // CHANGE THE "JSON" TO "B64" ON A FINAL RELEASE! + File.WriteAllText(Path.Combine(ProfileDirectory, ProfileFile), json); } public static byte[] GetAchievements() @@ -553,40 +295,14 @@ namespace TimeHACK.Engine } } - - // 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. - - [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 string ThemeName { get; set; } } -- cgit v1.2.3