hellbound/Assets/Scripts/Game/Save/Persister.cs

170 lines
6.0 KiB
C#
Raw Permalink Normal View History

2021-11-26 11:16:25 +03:00
using System;
using System.IO;
using game;
using metagen;
public static class Persister
{
private static readonly object SaveLock = new object();
public static PersistError SaveToFile(DataSave data, string file_path, string secret)
{
var file_path_tmp = file_path + ".~" + UnityEngine.Random.value;
var err = DoSaveToFile(data, file_path, file_path_tmp, secret);
Log.Info("Saved to file: " + file_path + ", " + err);
return err;
}
//TODO: not needed at the moment
//async static public UniTask<PersistError> SaveToFileAsync(DataSave data, string file_path, string secret)
//{
// var err = new PersistError();
// string file_path_tmp = file_path + ".~" + UnityEngine.Random.value;
// await UniTask.Run(() =>
// err = DoSaveToFile(data, file_path, file_path_tmp, secret)
// );
// Log.Info("Saved to file async: " + file_path + ", " + err);
// return err;
//}
static PersistError DoSaveToFile(DataSave data, string file_path, string file_path_tmp, string secret)
{
var err = new PersistError();
if (string.IsNullOrEmpty(secret))
{
err.Description = $"{nameof(secret)} is empty";
err.Code = PersistCode.NO_SECRET;
return err;
}
//NOTE: to be on the safe side
lock (SaveLock)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(file_path));
Directory.CreateDirectory(Path.GetDirectoryName(file_path_tmp));
using (var ms = new MemoryStream())
{
var writer = new MsgPackDataWriter(ms);
var data_err = Meta.SyncSafe(MetaSyncContext.NewForWrite(writer), ref data);
if (data_err == 0)
{
using (var fs = File.Open(file_path_tmp, FileMode.Create))
{
if (fs == null)
throw new Exception("Can't create file: " + file_path_tmp);
ms.Position = 0;
if (AES.Encrypt(ms, fs, secret) == 0)
{
err.Description = "Data error: empty data";
err.Code = PersistCode.DATA_ERR;
}
else
{
err.Bytes = ms.Position;
}
}
}
else
{
err.Description = "Data write error: " + data_err;
err.Code = PersistCode.DATA_ERR;
}
}
if (err == PersistCode.OK)
{
var full_file_path_tmp_bak = file_path_tmp + ".bak";
//NOTE: trying as hard as possible to postpone destruction of
// the original save file
if (File.Exists(full_file_path_tmp_bak))
File.Delete(full_file_path_tmp_bak);
if (File.Exists(file_path))
File.Move(file_path, full_file_path_tmp_bak);
File.Move(file_path_tmp, file_path);
if (File.Exists(full_file_path_tmp_bak))
File.Delete(full_file_path_tmp_bak);
}
}
catch (Exception e)
{
err.Description = "IO write error: " + e.Message;
err.Code = e.IsStorageFullException() ? PersistCode.IO_ERR_DISK_FULL : PersistCode.IO_ERR;
}
}
return err;
}
public static PersistError LoadFromFile(ref DataSave data, string file_path, string secret)
{
var err = new PersistError();
try
{
if (File.Exists(file_path))
{
using (var fs = File.Open(file_path, FileMode.Open))
{
if (fs == null)
throw new Exception("Can't open file for read: " + file_path);
if (string.IsNullOrEmpty(secret))
{
err.Description = "Sekret is empty";
err.Code = PersistCode.NO_SECRET;
return err;
}
using (var ms = new MemoryStream())
{
AES.Decrypt(fs, ms, secret);
ms.Position = 0;
var reader = new MsgPackDataReader(ms);
//var data_err = data.read(reader);
var data_err = Meta.SyncSafe(MetaSyncContext.NewForRead(AutogenBundle.createById, reader), ref data);
if (data_err != 0)
{
Log.Warn("IO read error: " + data_err);
err.Description = "MsgPackDataReader read error: " + data_err;
err.Code = PersistCode.DATA_ERR;
}
else
{
err.Bytes = ms.Position;
}
}
}
}
else
{
err.Code = PersistCode.NO_FILE;
}
}
catch (Exception e)
{
Log.Warn("IO read error: " + e.Message);
err.Code = PersistCode.IO_ERR;
err.Description = e.Message;
}
finally
{
Log.Info("Loading from file: " + file_path + "(" + err + ")");
}
return err;
}
}