using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using AibisDream.Utility;
using Newtonsoft.Json;
using UnityEngine;
namespace AibisDream.SaveSystem
{
///
/// 存档槽位目录与文件路径工具。
///
public static class SlotDirectory
{
public static string GetSlotPath(int slotIndex)
{
return Path.Combine(ConstRef.SaveFilePath, $"{ConstRef.SaveSlotDirPrefix}{slotIndex}");
}
public static string GetSnapshotPath(int slotIndex)
{
return Path.Combine(GetSlotPath(slotIndex), $"{ConstRef.SaveSnapshotFileName}.json");
}
public static string GetMetaPath(int slotIndex)
{
return Path.Combine(GetSlotPath(slotIndex), $"{ConstRef.SaveMetaFileName}.json");
}
public static string GetThumbnailPath(int slotIndex)
{
return Path.Combine(GetSlotPath(slotIndex), $"{ConstRef.SaveThumbnailFileName}.png");
}
public static string GetLatestSlotIndexPath()
{
return Path.Combine(ConstRef.SaveFilePath, $"{ConstRef.LatestSlotFileName}.json");
}
public static bool Exists(int slotIndex)
{
return File.Exists(GetMetaPath(slotIndex));
}
public static IEnumerable EnumerateExistingSlots()
{
if (!Directory.Exists(ConstRef.SaveFilePath))
{
yield break;
}
for (var i = SlotIndex.Auto; i <= SlotIndex.ManualEnd; i++)
{
if (Exists(i))
{
yield return i;
}
}
}
public static void EnsureSlotDirectory(int slotIndex)
{
Directory.CreateDirectory(GetSlotPath(slotIndex));
}
}
///
/// 槽位文件原子写工具。
/// 先写入临时文件,再重命名为最终文件,避免写入过程中崩溃导致存档损坏。
///
public static class SlotAtomicWriter
{
public static void WriteJson(T obj, string finalPath)
{
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
WriteText(json, finalPath);
}
public static void WriteText(string text, string finalPath)
{
var tempPath = finalPath + ".tmp";
var dir = Path.GetDirectoryName(finalPath);
if (!string.IsNullOrEmpty(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllText(tempPath, text);
CommitTempFile(tempPath, finalPath);
}
public static void WriteBytes(byte[] data, string finalPath)
{
if (data == null)
{
return;
}
var tempPath = finalPath + ".tmp";
var dir = Path.GetDirectoryName(finalPath);
if (!string.IsNullOrEmpty(dir))
{
Directory.CreateDirectory(dir);
}
File.WriteAllBytes(tempPath, data);
CommitTempFile(tempPath, finalPath);
}
private static void CommitTempFile(string tempPath, string finalPath)
{
const int maxAttempts = 5;
for (var attempt = 0; attempt < maxAttempts; attempt++)
{
try
{
if (File.Exists(finalPath))
{
// Unity 使用的 .NET Standard 2.0 无 File.Move(..., overwrite);Replace 为原子覆盖。
File.Replace(tempPath, finalPath, null);
}
else
{
File.Move(tempPath, finalPath);
}
return;
}
catch (IOException) when (attempt < maxAttempts - 1)
{
Thread.Sleep(20 * (attempt + 1));
}
}
}
}
}