Currently I creating a save\load system for my game. Its purpose is to be able to convert a Dictionary<string, object> to an encrypted string, and write it to disk file, and vice versa.
Saving functionality works just fine as follows:
public void Save()
{
PrepareSavableData();
using StreamWriter writer = new(SavegamePath);
string rawData = AuxMath.SerializeObjectToString(_storedStates);
string encodedData = AuxMath.Encode(rawData, PublicKey, PrivateKey);
writer.Write(encodedData);
writer.Close();
SavegameCompleted?.Invoke(this, EventArgs.Empty);
}
private void PrepareSavableData()
{
foreach (var entity in _registeredEntities)
{
_storedStates[entity.ID] = entity.GetState();
}
}
I fetch the data from every registered SavableEntitiy as follows:
public object GetState()
{
List<object> states = new(_savables.Count);
foreach (var savable in _savables)
{
states.Add(savable.CaptureState());
}
return states;
}
In the Save() above I use DES algorithm to encrypt data that is previously serialized to a string. No problems with that as well - I tested it thoroughly.
public static string SerializeObjectToString(object obj)
{
using MemoryStream memoryStream = new();
using StreamReader reader = new(memoryStream, Encoding.UTF8);
DataContractSerializer serializer = new(obj.GetType());
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
public static string Encode(string input, string publicKey, string privateKey)
{
try
{
byte[] inputAsBytes = Encoding.UTF8.GetBytes(input);
byte[] publicKeyAsBytes = Encoding.UTF8.GetBytes(publicKey);
byte[] privateKeyAsBytes = Encoding.UTF8.GetBytes(privateKey);
using DESCryptoServiceProvider cryptoServiceProvider = new();
using MemoryStream memoryStream = new();
using CryptoStream cryptoStream = new(memoryStream,
cryptoServiceProvider.CreateEncryptor(publicKeyAsBytes, privateKeyAsBytes),
CryptoStreamMode.Write);
cryptoStream.Write(inputAsBytes, 0, inputAsBytes.Length);
cryptoStream.FlushFinalBlock();
return Convert.ToBase64String(memoryStream.ToArray());
}
catch (Exception e)
{
return e.Message;
}
}
So fetching the savable data, serializing it, encoding, and writing to a disc file works without a hitch. At the game startup save file loading is performed, decoding, and deserialization, so that later any SavableEntity which registers itself can restore its previous state if one exists in the state dictionary. Reading the save file and decoding it works just fine.
private bool Load()
{
if (File.Exists(SavegamePath))
{
try
{
using StreamReader reader = new(SavegamePath);
string encodedData = reader.ReadToEnd();
string decodedData = AuxMath.Decode(encodedData, PublicKey, PrivateKey);
_storedStates = AuxMath.DeserializeStringToObject<Dictionary<string, object>>(decodedData, _knownSavableTypes);
SavegameLoadSuccesful?.Invoke(this, EventArgs.Empty);
return true;
}
catch (Exception e)
{
SavegameLoadFailed?.Invoke(this, new SavegameLoadFailedEventArgs(e.Message));
return false;
}
}
return false;
}
public static string Decode(string encodedInput, string publicKey, string privateKey)
{
try
{
byte[] encodedInputAsBytes = Convert.FromBase64String(encodedInput);
byte[] publicKeyAsBytes = Encoding.UTF8.GetBytes(publicKey);
byte[] privateKeyAsBytes = Encoding.UTF8.GetBytes(privateKey);
using DESCryptoServiceProvider cryptoServiceProvider = new();
using MemoryStream memoryStream = new();
using CryptoStream cryptoStream = new(memoryStream,
cryptoServiceProvider.CreateDecryptor(publicKeyAsBytes, privateKeyAsBytes),
CryptoStreamMode.Write);
cryptoStream.Write(encodedInputAsBytes, 0, encodedInputAsBytes.Length);
cryptoStream.FlushFinalBlock();
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
catch (Exception e)
{
return e.Message;
}
}
But, when it finally comes to the data deserialization phase from a successfully loaded and decoded file, an error occurs inside exactly this deserialization method right inside the if statement. DataContractSerializer cannot properly deserialize a states dictionary from a memory stream.
public static T DeserializeStringToObject<T>(string objectAsXml, IEnumerable<Type> knownTypes)
{
using MemoryStream memoryStream = new();
using StreamWriter writer = new(memoryStream, Encoding.UTF8);
DataContractSerializer deserializer = new(typeof(T), knownTypes);
writer.Write(objectAsXml);
memoryStream.Position = 0;
if (deserializer.ReadObject(memoryStream) is T value)
{
return value;
}
else
{
throw new Exception("Passed data is invalid or corrupted and cannot be properly restored!");
}
}
Currently this error occurs:
XmlException: Unexpected end of file. Following elements are not closed: Value, KeyValueOfstringanyType, ArrayOfKeyValueOfstringanyType. Line 1, position 197.
It may be something trivial, but I wasn't able to crack it on my own so far. Please, help!
Best regards, Vyacheslav.