PageRenderTime 26ms CodeModel.GetById 37ms RepoModel.GetById 1ms app.codeStats 1ms

/BucketPreviewer/Assets/SkywardFileBrowser/Plugins/SkywardFileBrowser/SfbFileSystem.cs

https://gitlab.com/cygnusfear/bucketpreviewer
C# | 563 lines | 460 code | 95 blank | 8 comment | 134 complexity | 3446d23b634c22a9b116c638c75a2237 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. using System.Threading;
  8. using SimpleJSON;
  9. using Debug = UnityEngine.Debug;
  10. namespace SkywardRay.FileBrowser {
  11. public class SfbFileSystem {
  12. public bool IsFake { get; private set; }
  13. private Dictionary<string, SfbFileSystemEntry> entries = new Dictionary<string, SfbFileSystemEntry>();
  14. public SfbFileSystemEntry root;
  15. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  16. private SfbFileSystemThread thread;
  17. #endif
  18. public SfbFileSystem (bool isFake) {
  19. IsFake = isFake;
  20. entries.Add("/", new SfbFileSystemEntry("/", false, SfbFileSystemEntryType.Root));
  21. root = entries["/"];
  22. if (isFake == false) {
  23. ReadLogicalDrives();
  24. }
  25. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  26. thread = new SfbFileSystemThread();
  27. #endif
  28. }
  29. public void Update () {
  30. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  31. thread.MainThreadUpdate();
  32. #endif
  33. }
  34. public void AddEntry (SfbFileSystemEntry entry) {
  35. if (entry.parent == null) {
  36. if (entry.type == SfbFileSystemEntryType.LogicalDrive) {
  37. entry.parent = root;
  38. root.AddChild(entry);
  39. }
  40. else {
  41. string s = GetParentPath(entry.path);
  42. if (entries.ContainsKey(s)) {
  43. entry.parent = entries[s];
  44. entries[s].AddChild(entry);
  45. }
  46. }
  47. }
  48. var children = entries.Where(a => GetParentPath(a.Key) == entry.path).Select(b => b.Value);
  49. foreach (var child in children) {
  50. entry.AddChild(child);
  51. }
  52. if (!entries.ContainsKey(entry.path)) {
  53. entries.Add(entry.path, entry);
  54. }
  55. }
  56. public void RemoveEntry (SfbFileSystemEntry entry) {
  57. entry.children.RemoveAll(a => a == entry);
  58. if (entry.parent != null) {
  59. entry.parent.RemoveChild(entry);
  60. }
  61. else {
  62. string s = GetParentPath(entry.path);
  63. Debug.Log(entries.ContainsKey(s));
  64. }
  65. entries.Remove(entry.path);
  66. }
  67. public void DeleteEntryOnDiskAndRemove (SfbFileSystemEntry entry) {
  68. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  69. if (IsFake) {
  70. return;
  71. }
  72. if (!File.Exists(entry.path) && !Directory.Exists(entry.path)) {
  73. return;
  74. }
  75. FileAttributes attr = File.GetAttributes(entry.path);
  76. if ((attr & FileAttributes.Directory) == FileAttributes.Directory) {
  77. Directory.Delete(entry.path, true);
  78. }
  79. else {
  80. File.Delete(entry.path);
  81. }
  82. RemoveEntry(entry);
  83. #endif
  84. }
  85. public void NewDirectory (string path) {
  86. if (DirectoryExists(path)) {
  87. return;
  88. }
  89. path = GetNormalizedPath(path);
  90. if (!IsFake) {
  91. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  92. Directory.CreateDirectory(path);
  93. #endif
  94. }
  95. AddEntry(new SfbFileSystemEntry(path, false, SfbFileSystemEntryType.Folder));
  96. }
  97. public void NewDirectory (string parentPath, string name) {
  98. parentPath = GetNormalizedPath(parentPath);
  99. NewDirectory(parentPath + "/" + name);
  100. }
  101. public static bool RealDirectoryExists (string path) {
  102. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  103. return Directory.Exists(path) && (new DirectoryInfo(path).Attributes & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint;
  104. #else
  105. return false;
  106. #endif
  107. }
  108. public bool DirectoryExists (string path) {
  109. if (IsFake) {
  110. return entries.ContainsKey(path);
  111. }
  112. return RealDirectoryExists(path);
  113. }
  114. public bool DirectoryExists (SfbFileSystemEntry entry) {
  115. return entries.ContainsKey(entry.path) && entries[entry.path] == entry;
  116. }
  117. public bool FileExists (string path) {
  118. if (IsFake) {
  119. return !entries.ContainsKey(path);
  120. }
  121. if (entries.ContainsKey(path)) {
  122. return true;
  123. }
  124. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  125. return File.Exists(path);
  126. #else
  127. return false;
  128. #endif
  129. }
  130. public bool FileExists (SfbFileSystemEntry entry) {
  131. if (entry.type == SfbFileSystemEntryType.File) {
  132. return false;
  133. }
  134. return entries.ContainsKey(entry.path) && entries[entry.path] == entry;
  135. }
  136. public SfbFileSystemEntry GetDirectory (string path) {
  137. if (!entries.ContainsKey(path)) {
  138. if (IsFake) {
  139. return null;
  140. }
  141. return ReadDirectory(path);
  142. }
  143. return entries[path];
  144. }
  145. public SfbFileSystemEntry GetFile (string path) {
  146. if (!entries.ContainsKey(path)) {
  147. return IsFake ? null : ReadFile(path);
  148. }
  149. return entries[path];
  150. }
  151. public List<SfbFileSystemEntry> GetDirectoryContents (SfbFileSystemEntry entry) {
  152. if (!entries.ContainsKey(entry.path)) {
  153. return new List<SfbFileSystemEntry>();
  154. }
  155. if (entry.type == SfbFileSystemEntryType.Root) {
  156. if (root.children.Count == 0) {
  157. ReadLogicalDrives();
  158. }
  159. return root.children;
  160. }
  161. return entry.children;
  162. }
  163. public List<SfbFileSystemEntry> GetDirectoryContents (SfbFileSystemEntry entry, SfbFileSortingOrder sortingOrder) {
  164. switch (sortingOrder) {
  165. case SfbFileSortingOrder.FolderThenFileThenName:
  166. return GetDirectoryContents(entry).OrderByDescending(a => a.type).ThenBy(b => b.name).ToList();
  167. }
  168. return null;
  169. }
  170. public static char[] GetInvalidFileNameChars () {
  171. //#if !UNITY_WEBGL && !UNITY_WEBPLAYER
  172. return Path.GetInvalidFileNameChars();
  173. //#else
  174. // return new char[0];
  175. //#endif
  176. }
  177. public static string GetExtension (string path) {
  178. //#if !UNITY_WEBGL && !UNITY_WEBPLAYER
  179. return Path.GetExtension(path);
  180. //#else
  181. // return "";
  182. //#endif
  183. }
  184. public static string GetFileName (string path) {
  185. string s = path;
  186. if (s.EndsWith("/")) {
  187. s = path.Remove(s.Length - 1);
  188. }
  189. s = Regex.Match(s, @"([^/]*$)").Groups[1].Value;
  190. if (s == "" || Regex.IsMatch(s, @"[a-zA-Z]:")) {
  191. s += "/";
  192. }
  193. return s;
  194. }
  195. public static string GetParentPath (string path) {
  196. if (path.EndsWith("/")) {
  197. path = path.Remove(path.Length - 1);
  198. }
  199. path = Regex.Replace(path, @"[^/]*$", "");
  200. if (path == "") {
  201. path = "/";
  202. }
  203. return GetNormalizedPath(path);
  204. }
  205. public static string GetNormalizedPath (string path) {
  206. path = path.Replace('\\', '/');
  207. if (path.EndsWith("/")) {
  208. path = path.Remove(path.Length - 1);
  209. }
  210. if (path == "" || Regex.IsMatch(path, @"[a-zA-Z]:$")) {
  211. path += "/";
  212. }
  213. return path;
  214. }
  215. public static SfbFileSystem CreateFromJSON (string json) {
  216. var N = JSON.Parse(json);
  217. var fileSystem = new SfbFileSystem(true);
  218. if (N["fileSystem"] != null && N["fileSystem"]["root"] != null) {
  219. ParseJSONInto(N["fileSystem"]["root"], "/", fileSystem);
  220. }
  221. else {
  222. Debug.Log("No root found: " + N.ToString(""));
  223. }
  224. return fileSystem;
  225. }
  226. public static void ParseJSONInto (JSONNode N, string parentPath, SfbFileSystem fileSystem) {
  227. if (N["contents"] == null || N["contents"].Count == 0) {
  228. return;
  229. }
  230. foreach (var child in N["contents"].Childs) {
  231. if (child["logicalDrive"] != null) {
  232. if (child["logicalDrive"]["name"] == null) {
  233. return;
  234. }
  235. var path = parentPath + child["logicalDrive"]["name"].Value + "/";
  236. fileSystem.AddEntry(new SfbFileSystemEntry(path, false, SfbFileSystemEntryType.LogicalDrive));
  237. ParseJSONInto(child["logicalDrive"], path, fileSystem);
  238. }
  239. if (child["folder"] != null) {
  240. if (child["folder"]["name"] == null) {
  241. return;
  242. }
  243. var path = parentPath + child["folder"]["name"].Value + "/";
  244. var hidden = child["folder"]["hidden"] != null && child["folder"]["hidden"].AsBool;
  245. fileSystem.AddEntry(new SfbFileSystemEntry(path, hidden, SfbFileSystemEntryType.Folder));
  246. ParseJSONInto(child["folder"], path, fileSystem);
  247. }
  248. if (child["file"] != null) {
  249. if (child["file"]["name"] == null || child["file"]["extension"] == null) {
  250. return;
  251. }
  252. var path = parentPath + child["file"]["name"].Value + "." + child["file"]["extension"].Value;
  253. var hidden = child["file"]["hidden"] != null && child["file"]["hidden"].AsBool;
  254. fileSystem.AddEntry(new SfbFileSystemEntry(path, hidden, SfbFileSystemEntryType.File));
  255. }
  256. }
  257. }
  258. public SfbFileSystemEntry GetParentDirectory (string path) {
  259. if (entries.ContainsKey(path) && entries[path].parent != null) {
  260. return entries[path].parent;
  261. }
  262. if (path.EndsWith("/")) {
  263. path = path.Remove(path.Length - 1);
  264. }
  265. path = Regex.Replace(path, @"[^/]*$", "");
  266. if (path == "") {
  267. path = "/";
  268. }
  269. if (entries.ContainsKey(path)) {
  270. return entries[path];
  271. }
  272. if (IsFake) {
  273. return null;
  274. }
  275. return ReadDirectory(path);
  276. }
  277. private SfbFileSystemEntry ReadFile (string path) {
  278. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  279. if (IsFake) {
  280. return null;
  281. }
  282. string normalizedPath = GetNormalizedPath(path);
  283. if (!File.Exists(normalizedPath)) {
  284. return null;
  285. }
  286. SfbFileSystemEntry entry;
  287. if (!entries.ContainsKey(normalizedPath)) {
  288. bool hidden = (File.GetAttributes(normalizedPath) & FileAttributes.Hidden) != 0;
  289. entry = new SfbFileSystemEntry(normalizedPath, hidden, SfbFileSystemEntryType.File);
  290. AddEntry(entry);
  291. }
  292. else {
  293. entry = entries[normalizedPath];
  294. }
  295. return entry;
  296. #else
  297. return null;
  298. #endif
  299. }
  300. private SfbFileSystemEntry ReadDirectory (string path) {
  301. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  302. if (IsFake) {
  303. return null;
  304. }
  305. string normalizedPath = GetNormalizedPath(path);
  306. SfbFileSystemEntry entry;
  307. if (!entries.ContainsKey(normalizedPath)) {
  308. bool hidden = (new DirectoryInfo(normalizedPath).Attributes & FileAttributes.Hidden) != 0;
  309. entry = new SfbFileSystemEntry(normalizedPath, hidden, SfbFileSystemEntryType.Folder);
  310. AddEntry(entry);
  311. }
  312. else {
  313. entry = entries[normalizedPath];
  314. }
  315. return entry;
  316. #else
  317. return null;
  318. #endif
  319. }
  320. private void ReadLogicalDrives () {
  321. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  322. if (IsFake) {
  323. return;
  324. }
  325. var drives = new List<string>();
  326. foreach (string drive in Environment.GetLogicalDrives()) {
  327. bool accessible = true;
  328. try {
  329. Directory.GetFileSystemEntries(drive);
  330. }
  331. catch (Exception) {
  332. accessible = false;
  333. }
  334. if (accessible) {
  335. string normalizedPath = drive.Replace('\\', '/');
  336. drives.Add(normalizedPath);
  337. }
  338. }
  339. var remove = root.children.Select(a => a.path).Except(drives);
  340. foreach (var s in remove) {
  341. RemoveEntry(entries[s]);
  342. }
  343. foreach (var drive in drives) {
  344. if (entries.ContainsKey(drive)) {
  345. continue;
  346. }
  347. var entry = new SfbFileSystemEntry(drive, false, SfbFileSystemEntryType.LogicalDrive);
  348. AddEntry(entry);
  349. }
  350. root.readContents = true;
  351. #endif
  352. }
  353. public SfbFileSystemEntry CreateNewFileAndAddEntry (string path) {
  354. if (entries.ContainsKey(path)) {
  355. Debug.LogWarning("File already exists");
  356. return null;
  357. }
  358. SfbFileSystemEntry entry = new SfbFileSystemEntry(path, false, SfbFileSystemEntryType.File);
  359. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  360. if (!IsFake) {
  361. try {
  362. File.Create(path);
  363. }
  364. catch (Exception e) {
  365. Debug.LogException(e);
  366. Debug.LogError("Could not create file");
  367. return null;
  368. }
  369. }
  370. #endif
  371. AddEntry(entry);
  372. return entry;
  373. }
  374. public void ReadFileOrBackupFromDisk (SfbFileSystemEntry fileSystemEntry, string backupExtension) {
  375. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  376. if (string.IsNullOrEmpty(backupExtension)) {
  377. Debug.LogError("Cannot read backup file bacause no backup extension was set.");
  378. return;
  379. }
  380. if (fileSystemEntry.type != SfbFileSystemEntryType.File) {
  381. return;
  382. }
  383. if (!backupExtension.StartsWith(".")) {
  384. backupExtension = "." + backupExtension;
  385. }
  386. if (!File.Exists(fileSystemEntry.path)) {
  387. if (File.Exists(fileSystemEntry.path + backupExtension)) {
  388. fileSystemEntry.SetContents(File.ReadAllBytes(fileSystemEntry.path + backupExtension));
  389. }
  390. return;
  391. }
  392. fileSystemEntry.ReadContentsFromDisk();
  393. #endif
  394. }
  395. public void CreateBackup (string path, string backupExtension) {
  396. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  397. if (string.IsNullOrEmpty(backupExtension)) {
  398. Debug.LogError("Cannot write backup file bacause no backup extension was set.");
  399. return;
  400. }
  401. if (!backupExtension.StartsWith(".")) {
  402. backupExtension = "." + backupExtension;
  403. }
  404. if (!File.Exists(path)) {
  405. return;
  406. }
  407. string backupPath = path + backupExtension;
  408. try {
  409. if (File.Exists(backupPath)) {
  410. File.Move(backupPath, backupPath + ".temp");
  411. }
  412. File.Move(path, backupPath);
  413. if (File.Exists(backupPath + ".temp")) {
  414. File.Delete(backupPath + ".temp");
  415. }
  416. var backupEntry = GetFile(backupPath) ?? new SfbFileSystemEntry(backupPath, false, SfbFileSystemEntryType.File);
  417. AddEntry(backupEntry);
  418. }
  419. catch (Exception e) {
  420. Debug.LogException(e);
  421. }
  422. #endif
  423. }
  424. public void WriteBytesToDisk (string path, byte[] bytes) {
  425. path = GetNormalizedPath(path);
  426. var entry = GetFile(path) ?? new SfbFileSystemEntry(path, false, SfbFileSystemEntryType.File);
  427. entry.SetContents(bytes);
  428. entry.WriteContentsToDisk();
  429. }
  430. private bool IsUpdatingDirectoryContents;
  431. public void AsyncUpdateDirectoryContents (SfbFileSystemEntry fileSystemEntry) {
  432. if (IsFake) {
  433. fileSystemEntry.readContents = true;
  434. return;
  435. }
  436. #if !UNITY_WEBGL && !UNITY_WEBPLAYER
  437. if (fileSystemEntry.readContents || IsUpdatingDirectoryContents) {
  438. return;
  439. }
  440. if (fileSystemEntry.type == SfbFileSystemEntryType.Root) {
  441. ReadLogicalDrives();
  442. return;
  443. }
  444. thread.AsyncReadDirectoryContents(fileSystemEntry.path, AsyncRecieveDirectoryContents);
  445. IsUpdatingDirectoryContents = true;
  446. #endif
  447. }
  448. private void AsyncRecieveDirectoryContents (string path, SfbFileSystemEntry[] contents) {
  449. path = GetNormalizedPath(path);
  450. if (!entries.ContainsKey(path)) {
  451. Debug.Log("entry does not exist");
  452. return;
  453. }
  454. var directory = entries[path];
  455. foreach (var entry in contents) {
  456. if (entries.ContainsKey(entry.path)) {
  457. directory.AddChild(entries[entry.path]);
  458. continue;
  459. }
  460. directory.AddChild(entry);
  461. entries.Add(entry.path, entry);
  462. }
  463. directory.ReadLastWriteTime();
  464. directory.readContents = true;
  465. IsUpdatingDirectoryContents = false;
  466. }
  467. }
  468. }