170 lines
6.0 KiB
C#
170 lines
6.0 KiB
C#
|
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;
|
|||
|
}
|
|||
|
}
|