Skip to content

Instantly share code, notes, and snippets.

@gering
Last active July 11, 2024 14:05
Show Gist options
  • Save gering/f37082d8dd45a3ae4f2c to your computer and use it in GitHub Desktop.
Save gering/f37082d8dd45a3ae4f2c to your computer and use it in GitHub Desktop.
persitent data path handling in Unity with fallback
private static string[] _persistentDataPaths;
public static bool IsDirectoryWritable(string path) {
try {
if (!Directory.Exists(path)) return false;
string file = Path.Combine(path, Path.GetRandomFileName());
using (FileStream fs = File.Create(file, 1)) {}
File.Delete(file);
return true;
} catch {
return false;
}
}
private static string GetPersistentDataPath(params string[] components) {
try {
string path = Path.DirectorySeparatorChar + string.Join("" + Path.DirectorySeparatorChar, components);
if (!Directory.GetParent(path).Exists) return null;
if (!Directory.Exists(path)) {
Debug.Log("creating directory: " + path);
Directory.CreateDirectory(path);
}
if (!IsDirectoryWritable(path)) {
Debug.LogWarning("persistent data path not writable: " + path);
return null;
}
return path;
} catch (Exception ex) {
Debug.LogException(ex);
return null;
}
}
public static string persistentDataPathInternal {
#if UNITY_ANDROID
get {
if (Application.isEditor || !Application.isPlaying) return Application.persistentDataPath;
string path = null;
if (string.IsNullOrEmpty(path)) path = GetPersistentDataPath("storage", "emulated", "0", "Android", "data", Application.bundleIdentifier, "files");
if (string.IsNullOrEmpty(path)) path = GetPersistentDataPath("data", "data", Application.bundleIdentifier, "files");
return path;
}
#else
get { return Application.persistentDataPath; }
#endif
}
public static string persistentDataPathExternal {
#if UNITY_ANDROID
get {
if (Application.isEditor || !Application.isPlaying) return null;
string path = null;
if (string.IsNullOrEmpty(path)) path = GetPersistentDataPath("storage", "sdcard0", "Android", "data", Application.bundleIdentifier, "files");
if (string.IsNullOrEmpty(path)) path = GetPersistentDataPath("storage", "sdcard1", "Android", "data", Application.bundleIdentifier, "files");
if (string.IsNullOrEmpty(path)) path = GetPersistentDataPath("mnt", "sdcard", "Android", "data", Application.bundleIdentifier, "files");
return path;
}
#else
get { return null; }
#endif
}
public static string[] persistentDataPaths {
get {
if (_persistentDataPaths == null) {
List<string> paths = new List<string>();
if (!string.IsNullOrEmpty(persistentDataPathInternal)) paths.Add(persistentDataPathInternal);
if (!string.IsNullOrEmpty(persistentDataPathExternal)) paths.Add(persistentDataPathExternal);
if (!string.IsNullOrEmpty(Application.persistentDataPath) && !paths.Contains(Application.persistentDataPath)) paths.Add(Application.persistentDataPath);
_persistentDataPaths = paths.ToArray();
}
return _persistentDataPaths;
}
}
// returns best persistent data path
public static string persistentDataPath {
get { return persistentDataPaths.Length > 0 ? persistentDataPaths[0] : null; }
}
public static string GetPersistentFile(string relativePath) {
if (string.IsNullOrEmpty(relativePath)) return null;
foreach (string path in persistentDataPaths) {
if (FileExists(path, relativePath)) return Path.Combine(path, relativePath);
}
return null;
}
public static bool SaveData(string relativePath, byte[] data) {
string path = GetPersistentFile(relativePath);
if (string.IsNullOrEmpty(path)) {
return SaveData(relativePath, data, 0);
} else {
try {
File.WriteAllBytes(path, data);
return true;
} catch (Exception ex) {
Debug.LogWarning("couldn't save data to: " + path);
Debug.LogException(ex);
// try to delete file again
if (File.Exists(path)) File.Delete(path);
return SaveData(relativePath, data, 0);
}
}
}
private static bool SaveData(string relativePath, byte[] data, int pathIndex) {
if (pathIndex < persistentDataPaths.Length) {
string path = Path.Combine(persistentDataPaths[pathIndex], relativePath);
try {
string dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir)) {
Debug.Log("creating directory: " + dir);
Directory.CreateDirectory(dir);
}
File.WriteAllBytes(path, data);
return true;
} catch (Exception ex) {
Debug.LogWarning("couldn't save data to: " + path);
Debug.LogException(ex);
if (File.Exists(path)) File.Delete(path); // try to delete file again
return SaveData(relativePath, data, pathIndex + 1); // try next persistent path
}
} else {
Debug.LogWarning("couldn't save data to any persistent data path");
return false;
}
}
public static bool FileExists(string path, string relativePath) {
return Directory.Exists(path) && File.Exists(Path.Combine(path, relativePath));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment