using System; using System.Collections; using System.IO; using System.Text; using UnityEngine; namespace AutoGroupGenerator { /// /// File system helpers for synchronous and coroutine-based IO. /// public static class FileUtils { #region Static Methods /// /// Loads all text from a file. /// /// Path to the file to read. /// The file contents as text. public static string LoadFromFile(string filePath) { using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) using (BufferedStream bs = new BufferedStream(fs)) using (StreamReader sr = new StreamReader(bs, Encoding.UTF8)) { return sr.ReadToEnd(); } } /// /// Saves text to a file, creating directories as needed. /// /// Path to the file to write. /// Text to write. public static void SaveToFile(string filePath, string data) { EnsureDirectoryExist(filePath); using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) using (BufferedStream bs = new BufferedStream(fs)) using (StreamWriter sw = new StreamWriter(bs, Encoding.UTF8)) { sw.Write(data); } } /// /// Loads JSON data from a file asynchronously as a coroutine. /// /// Type to deserialize. /// Path to the file to read. /// Callback invoked with the deserialized data. /// Maximum characters read per frame. /// An enumerator for coroutine execution. public static IEnumerator LoadFromFileAsync(string filePath, Action onComplete, int bytesPerStep = 1024 * 1024) { if (!File.Exists(filePath)) { Debug.LogError("File not found: " + filePath); yield break; } StreamReader streamReader = new StreamReader(filePath); StringBuilder data = new StringBuilder(); while (true) { try { char[] buffer = new char[bytesPerStep]; int bytesRead = streamReader.Read(buffer, 0, bytesPerStep); if (bytesRead > 0) { data.Append(buffer, 0, bytesRead); } else { streamReader.Close(); onComplete?.Invoke(JsonUtility.FromJson(data.ToString())); break; } } catch (Exception ex) { Debug.LogError($"{nameof(LoadFromFileAsync)} Error : {ex.Message}"); streamReader.Close(); break; } yield return null; } } /// /// Saves text to a file asynchronously as a coroutine. /// /// Text to write. /// Path to the file to write. /// Callback invoked with success state. /// Maximum characters written per frame. /// An enumerator for coroutine execution. public static IEnumerator SaveToFileAsync(string serializedData, string filePath, Action onComplete = null, int bytesPerStep = 1024 * 1024) { EnsureDirectoryExist(filePath); var data = new StringBuilder(serializedData); StreamWriter streamWriter = new StreamWriter(filePath, false, Encoding.UTF8); while (data.Length > 0) { try { int lengthToWrite = Math.Min(bytesPerStep, data.Length); char[] buffer = new char[lengthToWrite]; data.CopyTo(0, buffer, 0, lengthToWrite); streamWriter.Write(buffer, 0, lengthToWrite); data.Remove(0, lengthToWrite); } catch (Exception ex) { Debug.LogError($"{nameof(SaveToFileAsync)} Error : {ex.Message}"); streamWriter?.Close(); onComplete?.Invoke(false); yield break; } yield return null; } streamWriter.Close(); onComplete?.Invoke(true); } /// /// Ensures the directory for the specified file path exists. /// /// File path whose directory should exist. public static void EnsureDirectoryExist(string filePath) { string directoryPath = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); } } #endregion } }