// Created by SwanDEV 2017 using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.IO; using System.Text; #if UNITY_2017_3_OR_NEWER using UnityEngine.Networking; #endif /// Files, Paths, Names and IO common methods. (Last update: 2021-03-19).. /// *** Reminder! File IO methods do not work with streamingAssetsPath on Android. /// public class FilePathName { private static string _lastGeneratedFileNameWithoutExt_fff = ""; private static int _lastSameFileNameCounter_fff = 1; private static string _lastGeneratedFileNameWithoutExt = ""; private static int _lastSameFileNameCounter = 1; public enum SaveFormat { NONE = -1, GIF = 0, JPG = 1, PNG = 2, } #region ----- Instance ----- private static FilePathName fpn = null; public static FilePathName Instance { get { if (fpn == null) fpn = new FilePathName(); return fpn; } } #endregion #region ----- Path & FileName ----- public enum AppPath { /// The directory path where you can store data that you want to be kept between runs. PersistentDataPath = 0, /// The directory path where temporary data can be stored. TemporaryCachePath, /// The folder located at /Assets/StreamingAssets in the project. (Not work with System.IO methods when running on Android/WebGL) StreamingAssetsPath, /// The folder located at /Assets in the project. (Work on the Unity editor only) DataPath } public string GetAppPath(AppPath appPath) { string directory = ""; switch (appPath) { case AppPath.PersistentDataPath: directory = Application.persistentDataPath; break; case AppPath.TemporaryCachePath: directory = Application.temporaryCachePath; break; case AppPath.StreamingAssetsPath: directory = Application.streamingAssetsPath; break; case AppPath.DataPath: directory = Application.dataPath; break; } return directory; } public string GetSaveDirectory(bool isTemporaryPath = false, string subFolder = "", bool createDirectoryIfNotExist = false) { string result = ""; if (isTemporaryPath) { result = Application.temporaryCachePath; } else { //Available path: Application.persistentDataPath, Application.temporaryCachePath, Application.dataPath. //Do not allow sub-folder when a GIF is being created, but you can move it to any accessible sub-folder after the GIF is completely saved. //And/Or, you can filter the file names to include .gif only. #if UNITY_EDITOR result = Application.dataPath; //result = Application.persistentDataPath; #else result = Application.persistentDataPath; #endif } result = string.IsNullOrEmpty(subFolder) ? result : Path.Combine(result, subFolder); if (createDirectoryIfNotExist && !Directory.Exists(result)) { Directory.CreateDirectory(result); } return result; } public string GetFileNameWithoutExt(bool millisecond = false) { if (millisecond) { return _GetComparedFileName(DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff"), _lastGeneratedFileNameWithoutExt_fff, _lastSameFileNameCounter_fff, out _lastGeneratedFileNameWithoutExt_fff, out _lastSameFileNameCounter_fff); } return _GetComparedFileName(DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"), _lastGeneratedFileNameWithoutExt, _lastSameFileNameCounter, out _lastGeneratedFileNameWithoutExt, out _lastSameFileNameCounter); } private string _GetComparedFileName(string newFileName, string lastGeneratedFileName, int sameFileNameCounter, out string outLastGeneratedFileName, out int outSameFileNameCounter) { if (lastGeneratedFileName == newFileName) { sameFileNameCounter++; } else { sameFileNameCounter = 1; } outLastGeneratedFileName = newFileName; outSameFileNameCounter = sameFileNameCounter; if (sameFileNameCounter > 1) { newFileName += " " + sameFileNameCounter; } return newFileName; } public string GenerateFileNameWithDateTimeParameters(int YYYY, int MM, int DD, int hh = 0, int mm = 0, int ss = 0, int fff = -1) { DateTime dateTime = new DateTime(Mathf.Clamp(YYYY, 1, 9999), Mathf.Clamp(MM, 1, 12), Mathf.Clamp(DD, 1, 31), Mathf.Clamp(hh, 0, 23), Mathf.Clamp(mm, 0, 59), Mathf.Clamp(ss, 0, 59), Mathf.Clamp(fff, 0, 999)); return dateTime.ToString("yyyy-MM-dd-HH-mm-ss" + (fff < 0 ? "" : "-fff")); } /// /// Compare the filename (generated by FilePathName class) with the provided DateTime. Return -1, 0, 1. (-1: fileName time is earlier; 0: equals; 1 fileName time is later) /// /// FileName generated by FilePathName class (e.g: Photo_2019-08-31-00-00-00-000.jpg/.png) /// The DateTime to be compared with the time formated file name string. public int CompareFileNameWithTime(string fileName_FPN, DateTime dateTime) { string[] texts1 = fileName_FPN.Split('_'); string[] texts2 = texts1[1].Split('-'); int YYYY = 0; int.TryParse(texts2[0], out YYYY); int MM = 0; int.TryParse(texts2[1], out MM); int DD = 0; int.TryParse(texts2[2], out DD); int hh = 0; int.TryParse(texts2[3], out hh); int mm = 0; int.TryParse(texts2[4], out mm); int ss = 0; int.TryParse(texts2[5], out ss); int fff = -1; if (texts2.Length > 6) int.TryParse(texts2[6].Substring(0, 3), out fff); DateTime dt = fff == -1 ? new DateTime(YYYY, MM, DD, hh, mm, ss) : new DateTime(YYYY, MM, DD, hh, mm, ss, fff); if (dt == dateTime) return 0; else if (dt > dateTime) return 1; else return -1; } /// /// Check if the filename (generated by FilePathName class) within the Start and End DateTime interval. /// /// FileName generated by FilePathName class (e.g: Photo_2019-08-31-00-00-00-000.jpg/.png) /// Start date time. /// End date time. public bool CheckFileNameWithinDateTimeInterval(string fileName_FPN, DateTime startDateTime, DateTime endDateTime) { return CompareFileNameWithTime(fileName_FPN, startDateTime) >= 0 && CompareFileNameWithTime(fileName_FPN, endDateTime) <= 0; } public string EnsureValidPath(string pathOrUrl) { string path = pathOrUrl; if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { // WEB } else if (path.StartsWith("/idbfs/", StringComparison.OrdinalIgnoreCase)) { // WebGL index DB } else { // Local path path = EnsureLocalPath(path); } return path; } public string EnsureLocalPath(string path) { if (path.StartsWith("jar:", StringComparison.OrdinalIgnoreCase)) // Android streamingAssetsPath { } else if (!path.StartsWith("file:///", StringComparison.OrdinalIgnoreCase)) { while (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { path = path.Remove(0, 1); } path = "file:///" + path; } return path; } public string EnsureValidFileName(string fileName) { string replaceChars = "[:\\\\/*\"?|<>']"; for (int i = 0; i < replaceChars.Length; i++) { if (fileName.Contains(replaceChars[i].ToString())) { fileName = fileName.Replace(replaceChars[i], '_'); } } return fileName; } public string GetGifFileName() { string timestamp = GetFileNameWithoutExt(); return "GIF_" + timestamp; } public string GetGifFullPath(string subFolder = "", bool createDirectoryIfNotExist = false) { return GetSaveDirectory(false, subFolder, createDirectoryIfNotExist) + "/" + GetGifFileName() + ".gif"; } public string GetDownloadedGifSaveFullPath(string subFolder = "", bool createDirectoryIfNotExist = false) { return GetSaveDirectory(false, subFolder, createDirectoryIfNotExist) + "/" + GetGifFileName() + ".gif"; } public string GetJpgFileName() { string timestamp = GetFileNameWithoutExt(true); return "Photo_" + timestamp; } public string GetJpgFullPath(string subFolder = "", bool createDirectoryIfNotExist = false) { return GetSaveDirectory(false, subFolder, createDirectoryIfNotExist) + "/" + GetJpgFileName() + ".jpg"; } public string GetPngFileName() { string timestamp = GetFileNameWithoutExt(true); return "Photo_" + timestamp; } public string GetPngFullPath(string subFolder = "", bool createDirectoryIfNotExist = false) { return GetSaveDirectory(false, subFolder, createDirectoryIfNotExist) + "/" + GetPngFileName() + ".png"; } #endregion #region ----- IO ----- public byte[] ReadFileToBytes(string fromFullPath) { if (!File.Exists(fromFullPath)) { #if UNITY_EDITOR Debug.LogWarning("File Not Found: " + fromFullPath); #endif return null; } return File.ReadAllBytes(fromFullPath); } public void WriteBytesToFile(string toFullPath, byte[] byteArray) { CheckToCreateDirectory(Path.GetDirectoryName(toFullPath)); File.WriteAllBytes(toFullPath, byteArray); } public void CopyFile(string fromFullPath, string toFullPath, bool overwrite = false) { if (!File.Exists(fromFullPath)) { #if UNITY_EDITOR Debug.LogWarning("File Not Found: " + fromFullPath); #endif return; } CheckToCreateDirectory(Path.GetDirectoryName(toFullPath)); File.Copy(fromFullPath, toFullPath, overwrite); } public void MoveFile(string fromFullPath, string toFullPath, bool replaceIfExist = false) { if (!File.Exists(fromFullPath)) { #if UNITY_EDITOR Debug.LogWarning("File Not Found: " + fromFullPath); #endif return; } if (replaceIfExist && File.Exists(toFullPath)) File.Delete(toFullPath); CheckToCreateDirectory(Path.GetDirectoryName(toFullPath)); File.Move(fromFullPath, toFullPath); } public void DeleteFile(string fileFullPath) { if (!File.Exists(fileFullPath)) { #if UNITY_EDITOR Debug.LogWarning("File Not Found: " + fileFullPath); #endif return; } File.Delete(fileFullPath); } public void CheckToCreateDirectory(string directory) { if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); } /// Determine whether a given path is a directory. public bool PathIsDirectory(string path) { FileAttributes attr = File.GetAttributes(path); if ((attr & FileAttributes.Directory) == FileAttributes.Directory) return true; else return false; } public void RenameFile(string originFilePath, string newFileName) { string directory = Path.GetDirectoryName(originFilePath); string newFilePath = Path.Combine(directory, newFileName); CopyFile(originFilePath, newFilePath, true); } public bool FileStreamTo(string fileFullpath, byte[] byteArray) { try { CheckToCreateDirectory(Path.GetDirectoryName(fileFullpath)); using (FileStream fs = new FileStream(fileFullpath, FileMode.Create, FileAccess.Write)) { fs.Write(byteArray, 0, byteArray.Length); return true; // success } } catch (Exception e) { Console.WriteLine("Exception caught in process: {0}", e); return false; // fail } } public void WriteBytesToText(byte[] bytes, string toFileFullPath, string separator = "", bool toChar = true) { // int bkCount = 0; StringBuilder sb = new StringBuilder(); if (string.IsNullOrEmpty(separator)) { if (toChar) { for (int i = 0; i < bytes.Length; i++) { sb.Append((char)bytes[i]); } } else { for (int i = 0; i < bytes.Length; i++) { sb.Append(bytes[i]); //Test // bkCount++; // if(bkCount == 3) // { // bkCount = 0; // sb.Append(" (" + (i+1)/3 + ")\n"); // } } } } else { if (toChar) { for (int i = 0; i < bytes.Length; i++) { sb.Append((char)bytes[i]); sb.Append(separator); } } else { for (int i = 0; i < bytes.Length; i++) { sb.Append(bytes[i]); sb.Append(separator); //Test // bkCount++; // if(bkCount == 3) // { // bkCount = 0; // sb.Append(" (" + ((i+1)/3-1) + ")\n"); // } } } } CheckToCreateDirectory(Path.GetDirectoryName(toFileFullPath)); File.WriteAllText(toFileFullPath, sb.ToString()); } /// /// Saves texture as JPG/PNG on Windows/Mac. /// public string SaveImage(Texture2D texture, string subFolder, string fileName, SaveFormat imageFormat, Environment.SpecialFolder specialFolder = Environment.SpecialFolder.MyPictures, int jpgQuality = 90) { string extensionName = "." + imageFormat.ToString().ToLower(); byte[] bytes = null; string gifPath = ""; switch (imageFormat) { case SaveFormat.JPG: bytes = texture.EncodeToJPG(jpgQuality); break; case SaveFormat.PNG: bytes = texture.EncodeToPNG(); break; case SaveFormat.GIF: #if PRO_GIF gifPath = SaveBytes(new byte[] { 0 }, subFolder, fileName + extensionName, specialFolder); ProGifTexturesToGIF tex2Gif = ProGifTexturesToGIF.Instance; tex2Gif.Save(new List { texture }, texture.width, texture.height, 1, -1, 10, (id, path) => { Debug.Log("Save GIF.." + path + "\n" + gifPath); CopyFile(path, gifPath, true); DeleteFile(path); }, null, ProGifTexturesToGIF.ResolutionHandle.ResizeKeepRatio, false); #endif break; } if(!string.IsNullOrEmpty(gifPath)) { return gifPath; } return SaveBytes(bytes, subFolder, fileName + extensionName, specialFolder); } /// /// Saves file byte array on Windows/Mac. /// public string SaveBytes(byte[] bytes, string subFolder, string fileNameWithExtension, Environment.SpecialFolder specialFolder = Environment.SpecialFolder.MyPictures) { string directory = Path.Combine(Environment.GetFolderPath(specialFolder), subFolder); string savePath = Path.Combine(directory, fileNameWithExtension); WriteBytesToFile(savePath, bytes); return savePath; } public string SaveTextureAs(Texture2D texture2D, SaveFormat format = SaveFormat.JPG) { string savePath = string.Empty; switch (format) { case SaveFormat.JPG: savePath = GetJpgFullPath(); WriteBytesToFile(savePath, texture2D.EncodeToJPG(90)); break; case SaveFormat.PNG: savePath = GetPngFullPath(); WriteBytesToFile(savePath, texture2D.EncodeToPNG()); break; case SaveFormat.GIF: #if PRO_GIF savePath = ProGifTexturesToGIF.Instance.Save(new List { texture2D }, texture2D.width, texture2D.height, 1, -1, 10, null, null, ProGifTexturesToGIF.ResolutionHandle.ResizeKeepRatio, false); #endif break; } return savePath; } public string SaveTextureAs(Texture2D texture2D, AppPath appPath, string subFolder, bool isJPG, string optionalFileName_WithoutExtension = "") { string savePath = GetAppPath(appPath); if (!string.IsNullOrEmpty(subFolder)) savePath = Path.Combine(savePath, subFolder); if (!Directory.Exists(savePath)) Directory.CreateDirectory(savePath); savePath = Path.Combine(savePath, (string.IsNullOrEmpty(optionalFileName_WithoutExtension) ? GetFileNameWithoutExt(true) : optionalFileName_WithoutExtension) + (isJPG ? ".jpg" : ".png")); WriteBytesToFile(savePath, (isJPG ? texture2D.EncodeToJPG(90) : texture2D.EncodeToPNG())); return savePath; } #if PRO_GIF public string SaveTexturesAsGIF(List textureList, int width, int height, int fps, int loop, int quality, Action onFileSaved = null, Action onFileSaveProgress = null, ProGifTexturesToGIF.ResolutionHandle resolutionHandle = ProGifTexturesToGIF.ResolutionHandle.ResizeKeepRatio) { return ProGifTexturesToGIF.Instance.Save(textureList, width, height, fps, loop, quality, onFileSaved, onFileSaveProgress, resolutionHandle, false); } #endif public Texture2D LoadImage(string fullFilePath) { if (!File.Exists(fullFilePath)) { #if UNITY_EDITOR Debug.LogWarning("File not exist! " + fullFilePath); #endif return null; } else { Texture2D tex2D = new Texture2D(1, 1); //, TextureFormat.ARGB32, false); tex2D.LoadImage(ReadFileToBytes(fullFilePath)); return tex2D; } } /// /// Load images in the target directory, to a texture2D list. /// /// The images. /// Directory. /// A list of file extension names, indicating the type of files to be loaded. Load jpg, png and gif if Null or Empty. public List LoadImages(string directory, List fileExtensions = null) { if (fileExtensions != null && fileExtensions.Count > 0) { } else { fileExtensions = new List { ".jpg", ".png", ".gif" }; } List textureList = new List(); foreach (string f in GetFilePaths(directory, fileExtensions)) { if (fileExtensions.Contains(Path.GetExtension(f).ToLower())) { textureList.Add(LoadImage(f)); } } return textureList; } /// /// Load files in the target directory, to a byte[] list. /// /// Files in byte[]. /// Directory. /// A list of file extension names, indicating the type of files to be loaded. Load all files if Null or Empty. public List LoadFiles(string directory, List fileExtensions = null) { List fileByteList = new List(); foreach (string f in GetFilePaths(directory, fileExtensions)) { fileByteList.Add(ReadFileToBytes(f)); } return fileByteList; } /// /// Get file paths in the target directory. /// /// File paths list. /// Directory. /// A list of file extension names, indicating the type of file paths to get. Get all file paths if Null or Empty. public List GetFilePaths(string directory, List fileExtensions = null) { if (!Directory.Exists(directory)) { #if UNITY_EDITOR Debug.Log("Directory Not Found: " + directory); #endif return null; } string[] allFiles_src = Directory.GetFiles(directory); bool loadAllFile = (fileExtensions == null) ? true : ((fileExtensions.Count <= 0) ? true : false); if (loadAllFile) { #if UNITY_EDITOR Debug.Log("Load ALL"); #endif List fileList = new List(); int len = allFiles_src.Length; for (int i = 0; i < len; i++) { fileList.Add(allFiles_src[i]); } return fileList; } #if UNITY_EDITOR Debug.Log("Load Filtered"); #endif if (fileExtensions == null) { fileExtensions = new List(); } else { for (int i = 0; i < fileExtensions.Count; i++) { fileExtensions[i] = fileExtensions[i].ToLower(); } } List filteredFilePathList = new List(); foreach (string f in allFiles_src) { if (fileExtensions.Contains(Path.GetExtension(f).ToLower())) { filteredFilePathList.Add(f); } } return filteredFilePathList; } #if UNITY_2017_3_OR_NEWER /// /// Loads file using UnityWebRequest. Return the byte array of the file in onLoadCompleted callback. /// ( IEnumerator: Remember to call this method in StartCoroutine ) /// /// The file byte array. /// Local file path or http/https link. /// On load completed callback. Return the byte array of the downloaded file. /// On load completed callback. Return the UnityWebRequest object. public IEnumerator LoadFileUWR(string url, Action onLoadCompleted = null, Action onLoadCompletedUWR = null) { string path = EnsureValidPath(url); using (UnityWebRequest uwr = UnityWebRequest.Get(path)) { uwr.SendWebRequest(); while (!uwr.isDone) { yield return null; } #if UNITY_2020_1_OR_NEWER if (uwr.result == UnityWebRequest.Result.ConnectionError || uwr.result == UnityWebRequest.Result.ProtocolError || uwr.result == UnityWebRequest.Result.DataProcessingError) #else if (uwr.isNetworkError || uwr.isHttpError) #endif { if (onLoadCompleted != null) onLoadCompleted(null); if (onLoadCompletedUWR != null) onLoadCompletedUWR(null); Debug.LogError("File load error.\n" + uwr.error); } else { if (onLoadCompleted != null) onLoadCompleted(uwr.downloadHandler.data); if (onLoadCompletedUWR != null) onLoadCompletedUWR(uwr); } } } #else /// /// Loads file using WWW. Return the byte array of the file in onLoadCompleted callback. /// ( IEnumerator: Remember to call this method in StartCoroutine ) /// /// The file byte array. /// Local file path or http/https link. /// On load completed callback. Return the byte array of the downloaded file. /// On load completed callback. Return the WWW object. public IEnumerator LoadFileWWW(string url, Action onLoadCompleted = null, Action onLoadCompletedWWW = null) { string path = EnsureValidPath(url); using (WWW www = new WWW(path)) { yield return www; if (string.IsNullOrEmpty(www.error) == false) { if (onLoadCompleted != null) onLoadCompleted(null); if (onLoadCompletedWWW != null) onLoadCompletedWWW(null); Debug.LogError("File load error.\n" + www.error); yield break; } if (onLoadCompleted != null) onLoadCompleted(www.bytes); if (onLoadCompletedWWW != null) onLoadCompletedWWW(www); } } #endif #endregion public Sprite Texture2DToSprite(Texture2D texture2D) { if (texture2D == null) return null; Vector2 pivot = new Vector2(0.5f, 0.5f); float pixelPerUnit = 100; return Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), pivot, pixelPerUnit); } }