using System.Runtime.InteropServices; namespace Theriapolis.Game.Platform; /// /// OS-aware save directory resolution. Per the implementation plan ยง4.2, /// saves live under the platform-appropriate user data directory. /// public static class SavePaths { /// Top-level Theriapolis save directory. Created on first call if missing. public static string SavesDir { get { string dir = ResolveBase(); Directory.CreateDirectory(dir); return dir; } } public static string SlotPath(int slot) => Path.Combine(SavesDir, $"slot_{slot:D2}.trps"); public static string AutosavePath() => Path.Combine(SavesDir, "autosave.trps"); private static string ResolveBase() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Theriapolis", "Saves"); if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Theriapolis", "Saves"); // Linux + others: respect XDG_DATA_HOME, fall back to ~/.local/share. string xdg = Environment.GetEnvironmentVariable("XDG_DATA_HOME") ?? ""; if (string.IsNullOrEmpty(xdg)) xdg = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"); return Path.Combine(xdg, "Theriapolis", "saves"); } /// Atomic-rename file write so a crash mid-save can't corrupt the slot. public static void WriteAtomic(string path, byte[] bytes) { string dir = Path.GetDirectoryName(path)!; Directory.CreateDirectory(dir); string tmp = path + ".tmp"; File.WriteAllBytes(tmp, bytes); if (File.Exists(path)) File.Replace(tmp, path, destinationBackupFileName: null); else File.Move(tmp, path); } }