CodeSmile AssetDatabase 1.10
Unity's AssetDatabase in enjoyable, consistent, concise, convenient, comprehensible, safe, documented form.
Loading...
Searching...
No Matches
Asset.File.cs
1// Copyright (C) 2021-2024 Steffen Itterheim
2// Refer to included LICENSE file for terms and conditions.
3
4using System;
5using System.Collections.Generic;
6using System.Diagnostics.CodeAnalysis;
7using System.Linq;
8using System.Text;
9using UnityEditor;
10// UnityEngine is imported for the GUID type, which is declared in UnityEditor up to Unity
11// 6000.3 and in UnityEngine from Unity 6000.4 on (neither namespace declared it in both on any of
12// the nine editors tested), and for the EntityId type that replaces the integer instance ID
13// overloads from Unity 6000.3 on.
14using UnityEngine;
15using Object = UnityEngine.Object;
16
17namespace CodeSmileEditor
18{
19 // This file contains asset 'file operations' related method groups, in that order:
20 // Create
21 // Save
22 // Import
23 // Load
24 // Find
25 // Copy
26 // Move
27 // Rename
28 // Open
29 // Delete
30 // Trash
31
32 public sealed partial class Asset
33 {
37 public static class File
38 {
39 private static List<String> s_PathsNotDeleted = new();
40
50 [ExcludeFromCodeCoverage] // trivial
51 public static IList<String> PathsNotDeleted => s_PathsNotDeleted;
52
82 public static void BatchEditing([NotNull] Action massAssetFileEditAction)
83 {
84 try
85 {
86 StartAssetEditing();
87
88 massAssetFileEditAction?.Invoke();
89 }
90 finally
91 {
92 StopAssetEditing();
93 }
94 }
95
108 public static Object Create([NotNull] Byte[] contents, [NotNull] Path path) => CreateInternal(contents, path);
109
123 public static Object CreateAsNew([NotNull] Byte[] contents, [NotNull] Path path) => CreateInternal(contents, path.UniqueFilePath);
124
137 public static Object Create([NotNull] String contents, [NotNull] Path path) => CreateInternal(contents, path);
138
152 public static Object CreateAsNew([NotNull] String contents, [NotNull] Path path) => CreateInternal(contents, path.UniqueFilePath);
153
168 public static Object Create([NotNull] Object instance, [NotNull] Path path) => CreateInternal(instance, path);
169
184 public static Object CreateAsNew([NotNull] Object instance, [NotNull] Path path) => CreateInternal(instance, path.UniqueFilePath);
185
204 public static T CreateOrLoad<T>([NotNull] Path path, [NotNull] Func<T> getInstance) where T : Object =>
205 LoadOrCreate(path, getInstance);
206
219 public static void Save([NotNull] Object asset) => SaveInternal(asset);
220
239 public static void ForceSave([NotNull] Object asset) => SaveInternal(asset, true);
240
250 public static void Save(GUID guid)
251 {
252 ThrowIf.NotAnAssetGuid(guid);
253
254 AssetDatabase.SaveAssetIfDirty(guid);
255 }
256
276 public static void Import([NotNull] Path path, ImportAssetOptions options = ImportAssetOptions.Default)
277 {
278 ThrowIf.ArgumentIsNull(path, nameof(path));
279 ThrowIf.DoesNotExistInFileSystem(path);
280
281 AssetDatabase.ImportAsset(path, options);
282 }
283
308 public static T ImportAndLoad<T>([NotNull] Path path,
309 ImportAssetOptions options = ImportAssetOptions.Default)
310 where T : Object => Load<T>(path);
311
325 public static void
326 Import([NotNull] IEnumerable<Path> paths, ImportAssetOptions options = ImportAssetOptions.Default) =>
327 Import(Path.ToStrings(paths.ToArray()), options);
328
342 public static void
343 Import([NotNull] IEnumerable<string> paths, ImportAssetOptions options = ImportAssetOptions.Default) => BatchEditing(
344 () =>
345 {
346 foreach (var path in paths)
347 AssetDatabase.ImportAsset(path, options);
348 });
349
371 public static T Load<T>([NotNull] Path path) where T : Object
372 {
373 ThrowIf.ArgumentIsNull(path, nameof(path));
374
375 ImportIfNotImported(path);
376 return AssetDatabase.LoadAssetAtPath<T>(path);
377 }
378
393 public static T LoadOrCreate<T>([NotNull] Path path, [NotNull] Func<T> getInstance) where T : Object
394 {
395 if (path.ExistsInFileSystem == false)
396 return Create(getInstance.Invoke(), path) as T;
397
398 return ImportAndLoad<T>(path);
399 }
400
416 public static T LoadMain<T>([NotNull] Path path) where T : Object
417 {
418 ThrowIf.ArgumentIsNull(path, nameof(path));
419 ThrowIf.DoesNotExistInFileSystem(path);
420
421 ImportIfNotImported(path);
422 return AssetDatabase.LoadMainAssetAtPath(path) as T;
423 }
424
440 public static T LoadMain<T>(GUID guid) where T : Object
441 {
442 ThrowIf.NotAnAssetGuid(guid);
443
444 var path = Path.Get(guid);
445 ImportIfNotImported(path);
446 return LoadMain<T>(path);
447 }
448
463 [ExcludeFromCodeCoverage] // simple relay
464 public static AssetDatabaseLoadOperation LoadAsync([NotNull] Path path, Int64 localFileId)
465 {
466 return AssetDatabase.LoadObjectAsync(path, localFileId);
467 }
468
488 [ExcludeFromCodeCoverage] // simple relay
489 public static String[] Find([NotNull] String filter, String[] searchInFolders = null) => searchInFolders == null
490 ? AssetDatabase.FindAssets(filter)
491 : AssetDatabase.FindAssets(filter, searchInFolders);
492
513 [ExcludeFromCodeCoverage] // simple relay
514 public static GUID[] FindGuids([NotNull] String filter, String[] searchInFolders = null) =>
515 Find(filter, searchInFolders).Select(guid => new GUID(guid)).ToArray();
516
536 [ExcludeFromCodeCoverage] // simple relay
537 public static Path[] FindPaths([NotNull] String filter, String[] searchInFolders = null) =>
538 Find(filter, searchInFolders).Select(guid => Path.Get(new GUID(guid))).ToArray();
539
559 [ExcludeFromCodeCoverage] // simple relay
560 public static Path[] FindPaths([NotNull] String filter, Path[] searchInFolders = null) =>
561 FindPaths(filter, Path.ToStrings(searchInFolders));
562
580 public static Boolean Copy([NotNull] Path sourcePath, [NotNull] Path destinationPath) =>
581 CopyInternal(sourcePath, destinationPath, true);
582
601 public static Boolean CopyAsNew([NotNull] Path sourcePath, [NotNull] Path destinationPath) =>
602 CopyInternal(sourcePath, destinationPath.UniqueFilePath, false);
603
623 public static Boolean CanMove([NotNull] Path sourcePath, [NotNull] Path destinationPath)
624 {
625 if (sourcePath == null || destinationPath == null)
626 return false;
627
628 return Succeeded(AssetDatabase.ValidateMoveAsset(sourcePath, destinationPath));
629 }
630
649 public static Boolean Move([NotNull] Path sourcePath, [NotNull] Path destinationPath)
650 {
651 if (sourcePath == null || destinationPath == null)
652 return false;
653
654 destinationPath.CreateFolders();
655 return Succeeded(AssetDatabase.MoveAsset(sourcePath, destinationPath));
656 }
657
675 public static Boolean Rename([NotNull] Path path, String newFileName) => String.IsNullOrEmpty(newFileName) == false &&
676 Succeeded(AssetDatabase.RenameAsset(path, newFileName));
677
689 [ExcludeFromCodeCoverage] // simple relay
690 public static Boolean CanOpenInEditor([NotNull] Object instance) =>
691#if UNITY_6000_3_OR_NEWER
692 AssetDatabase.CanOpenAssetInEditor(instance.GetEntityId());
693#else
694 CanOpenInEditor(instance.GetInstanceID());
695#endif
696
697#if UNITY_6000_4_OR_NEWER
716 [ExcludeFromCodeCoverage] // simple relay
717 public static Boolean CanOpenInEditor(EntityId entityId) => AssetDatabase.CanOpenAssetInEditor(entityId);
718#else
730 [ExcludeFromCodeCoverage] // simple relay
731 public static Boolean CanOpenInEditor(Int32 instanceId) =>
732#if UNITY_6000_3_OR_NEWER
733 AssetDatabase.CanOpenAssetInEditor((EntityId)instanceId);
734#else
735 AssetDatabase.CanOpenAssetInEditor(instanceId);
736#endif
737#endif
738
752 [ExcludeFromCodeCoverage] // cannot be tested
753 public static void OpenExternal([NotNull] Object asset, Int32 lineNumber = -1, Int32 columnNumber = -1) =>
754 AssetDatabase.OpenAsset(asset, lineNumber, columnNumber);
755
756#if UNITY_6000_4_OR_NEWER
777 [ExcludeFromCodeCoverage] // cannot be tested
778 public static void OpenExternal(EntityId entityId, Int32 lineNumber = -1, Int32 columnNumber = -1) =>
779 AssetDatabase.OpenAsset(entityId, lineNumber, columnNumber);
780#else
794 [ExcludeFromCodeCoverage] // cannot be tested
795 public static void OpenExternal(Int32 instanceId, Int32 lineNumber = -1, Int32 columnNumber = -1) =>
796#if UNITY_6000_3_OR_NEWER
797 AssetDatabase.OpenAsset((EntityId)instanceId, lineNumber, columnNumber);
798#else
799 AssetDatabase.OpenAsset(instanceId, lineNumber, columnNumber);
800#endif
801#endif
802
816 [ExcludeFromCodeCoverage] // cannot be tested
817 public static void OpenExternal([NotNull] Path path, Int32 lineNumber = -1, Int32 columnNumber = -1) =>
818 OpenExternal(Load<Object>(path), lineNumber, columnNumber);
819
831 public static Boolean Delete([NotNull] Path path) =>
832 // path.Exists prevents Unity from spitting out an unnecessary warning message
833 path != null && path.Exists && AssetDatabase.DeleteAsset(path);
834
846 public static Boolean Delete([NotNull] Object asset) => Delete(Path.Get(asset));
847
861 public static Boolean Delete([NotNull] IEnumerable<Path> paths) => Delete(Path.ToStrings(paths));
862
876 public static Boolean Delete([NotNull] IEnumerable<String> paths) =>
877 AssetDatabase.DeleteAssets(paths.ToArray(), s_PathsNotDeleted = new List<String>());
878
894 public static Boolean Trash([NotNull] Path path) => path != null && AssetDatabase.MoveAssetToTrash(path);
895
911 public static Boolean Trash([NotNull] Object asset) => Trash(Path.Get(asset));
912
927 public static Boolean Trash([NotNull] IEnumerable<Path> paths) => Trash(Path.ToStrings(paths));
928
943 public static Boolean Trash([NotNull] IEnumerable<String> paths) =>
944 AssetDatabase.MoveAssetsToTrash(paths.ToArray(), s_PathsNotDeleted = new List<String>());
945
955 public static Type GetMainType([NotNull] Path path) => AssetDatabase.GetMainAssetTypeAtPath(path);
956
970 [ExcludeFromCodeCoverage] // simple relay
971 public static Type GetMainType(GUID guid)
972 {
973#if UNITY_2023_2_OR_NEWER // below 2023.2 the type is obtained from the path the GUID resolves to
974 return AssetDatabase.GetMainAssetTypeFromGUID(guid);
975#else
976 return GetMainType(Path.Get(guid));
977#endif
978 }
979
990 [ExcludeFromCodeCoverage] // simple relay
991 public static Type GetSubType([NotNull] Path path, Int64 fileId) => AssetDatabase.GetTypeFromPathAndFileID(path, fileId);
992
1007
1008 // ValueTuple makes doxygen accept it as documented, see: https://github.com/doxygen/doxygen/issues/9618
1009 public static ValueTuple<GUID, Int64> GetGuidAndFileId([NotNull] Object asset)
1010 {
1011 if (asset == null)
1012 return (new GUID(), 0L);
1013
1014 // explicit variable + assign because TryGetGUIDAndLocalFileIdentifier has both an Int64 and an
1015 // Int32 overload on 2022.3.62f3 and only the Int64 form on 6000.6.0f1 and 6000.7.0a6; the
1016 // declared type selects the Int64 one
1017 var localId = Int64.MaxValue;
1018 return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out localId)
1019 ? (new GUID(guid), localId)
1020 : (new GUID(), 0L);
1021 }
1022
1034 public static GUID GetGuid([NotNull] Object asset)
1035 {
1036 if (asset == null)
1037 return new GUID();
1038
1039 // explicit variable + assign because TryGetGUIDAndLocalFileIdentifier has both an Int64 and an
1040 // Int32 overload on 2022.3.62f3 and only the Int64 form on 6000.6.0f1 and 6000.7.0a6; the
1041 // declared type selects the Int64 one
1042 var localId = Int64.MaxValue;
1043 return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out localId)
1044 ? new GUID(guid)
1045 : new GUID();
1046 }
1047
1059 public static Int64 GetFileId([NotNull] Object asset)
1060 {
1061 if (asset == null)
1062 return 0L;
1063
1064 // explicit variable + assign because TryGetGUIDAndLocalFileIdentifier has both an Int64 and an
1065 // Int32 overload on 2022.3.62f3 and only the Int64 form on 6000.6.0f1 and 6000.7.0a6; the
1066 // declared type selects the Int64 one
1067 var localId = Int64.MaxValue;
1068 return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var _, out localId) ? localId : 0L;
1069 }
1070
1071 // Internal on purpose: use Asset.File.BatchEditing(Action) instead
1072 [ExcludeFromCodeCoverage] // untestable
1073 internal static void StartAssetEditing() => AssetDatabase.StartAssetEditing();
1074
1075 // Internal on purpose: use Asset.File.BatchEditing(Action) instead
1076 [ExcludeFromCodeCoverage] // untestable
1077 internal static void StopAssetEditing() => AssetDatabase.StopAssetEditing();
1078
1079 internal static Object CreateInternal([NotNull] Byte[] bytes, [NotNull] Path path)
1080 {
1081 ThrowIf.ArgumentIsNull(bytes, nameof(bytes));
1082 ThrowIf.ArgumentIsNull(path, nameof(path));
1083
1084 path.CreateFolders();
1085 System.IO.File.WriteAllBytes(path, bytes);
1086 return ImportAndLoad<Object>(path);
1087 }
1088
1089 internal static Object CreateInternal([NotNull] String contents, [NotNull] Path path)
1090 {
1091 ThrowIf.ArgumentIsNull(contents, nameof(contents));
1092 ThrowIf.ArgumentIsNull(path, nameof(path));
1093
1094 path.CreateFolders();
1095 System.IO.File.WriteAllText(path, contents, Encoding.UTF8); // string assets ought to be UTF8
1096 return ImportAndLoad<Object>(path);
1097 }
1098
1099 internal static Object CreateInternal([NotNull] Object instance, [NotNull] Path path)
1100 {
1101 ThrowIf.ArgumentIsNull(instance, nameof(instance));
1102 ThrowIf.ArgumentIsNull(path, nameof(path));
1103
1104 path.CreateFolders();
1105 AssetDatabase.CreateAsset(instance, path);
1106 return instance;
1107 }
1108
1109 internal static Boolean CopyInternal([NotNull] Path sourcePath, [NotNull] Path destinationPath,
1110 Boolean overwriteExisting)
1111 {
1112 ThrowIf.ArgumentIsNull(sourcePath, nameof(sourcePath));
1113 ThrowIf.ArgumentIsNull(destinationPath, nameof(destinationPath));
1114 ThrowIf.AssetPathNotInDatabase(sourcePath);
1115 ThrowIf.SourceAndDestPathAreEqual(sourcePath, destinationPath);
1116
1117 destinationPath.CreateFolders();
1118
1119 var success = AssetDatabase.CopyAsset(sourcePath, destinationPath);
1120 SetLastErrorMessage(success ? String.Empty : $"failed to copy {sourcePath} to {destinationPath}");
1121 return success;
1122 }
1123
1124 private static void SaveInternal([NotNull] Object asset, Boolean forceSave = false)
1125 {
1126 ThrowIf.ArgumentIsNull(asset, nameof(asset));
1127 ThrowIf.NotInDatabase(asset);
1128
1129 if (forceSave)
1130 EditorUtility.SetDirty(asset);
1131
1132 AssetDatabase.SaveAssetIfDirty(asset);
1133 }
1134
1135 private static void ImportIfNotImported([NotNull] Path path,
1136 ImportAssetOptions options = ImportAssetOptions.Default)
1137 {
1138 // Not in database but on disk? => Import path
1139 // Cannot determine if existing file has been updated though.
1140 if (path.Exists == false && path.ExistsInFileSystem)
1141 Import(path, options);
1142 }
1143 }
1144 }
1145}
static IList< String > PathsNotDeleted
The paths that failed to be deleted or trashed. Is an empty list if no failure occured on the last ca...
Definition Asset.File.cs:51
static void Save([NotNull] Object asset)
Saves the object to disk if it is dirty.
static ValueTuple< GUID, Int64 > GetGuidAndFileId([NotNull] Object asset)
static void Save(GUID guid)
Saves any changes to the asset to disk, by GUID.
static Boolean Delete([NotNull] IEnumerable< Path > paths)
Tries to delete multiple files/folders.
static void OpenExternal([NotNull] Path path, Int32 lineNumber=-1, Int32 columnNumber=-1)
Opens the asset in the application associated with the file's extension.
static AssetDatabaseLoadOperation LoadAsync([NotNull] Path path, Int64 localFileId)
Loads an object and its dependencies asynchronously.
static Int64 GetFileId([NotNull] Object asset)
Returns the local FileID of the object.
static Boolean CanMove([NotNull] Path sourcePath, [NotNull] Path destinationPath)
Tests if an asset can be moved to destination without moving the asset.
static Type GetMainType([NotNull] Path path)
static Boolean Rename([NotNull] Path path, String newFileName)
Renames an asset's file or folder name.
static Boolean Delete([NotNull] IEnumerable< String > paths)
Tries to delete multiple files/folders.
static Boolean CanOpenInEditor([NotNull] Object instance)
Returns true if the given object can be opened (edited) by the Unity editor.
static Path[] FindPaths([NotNull] String filter, Path[] searchInFolders=null)
Finds asset paths by the given filter criteria.
static T ImportAndLoad< T >([NotNull] Path path, ImportAssetOptions options=ImportAssetOptions.Default)
Imports a file at a given path that was created or modified 'externally', then loads and returns the ...
static Object Create([NotNull] String contents, [NotNull] Path path)
Writes the string to disk, then imports and loads the asset. Overwrites any existing file.
static Boolean Delete([NotNull] Path path)
Deletes an asset file or folder.
static Boolean Trash([NotNull] IEnumerable< String > paths)
Tries to move multiple files/folders to the OS trash.
static T CreateOrLoad< T >([NotNull] Path path, [NotNull] Func< T > getInstance)
Loads or creates an asset at path.
static Boolean Trash([NotNull] IEnumerable< Path > paths)
Tries to move multiple files/folders to the OS trash.
static Object Create([NotNull] Byte[] contents, [NotNull] Path path)
Writes the byte array to disk, then imports and loads the asset. Overwrites any existing file.
static Boolean Copy([NotNull] Path sourcePath, [NotNull] Path destinationPath)
Copies an asset from source to destination path. Overwrites any existing assets.
static GUID[] FindGuids([NotNull] String filter, String[] searchInFolders=null)
Finds asset GUIDs by the given filter criteria.
static void OpenExternal(EntityId entityId, Int32 lineNumber=-1, Int32 columnNumber=-1)
Opens the asset in the application associated with the file's extension.
static Object CreateAsNew([NotNull] String contents, [NotNull] Path path)
Writes the string to disk, then imports and loads the asset. Generates a unique file name if an asset...
static Boolean CopyAsNew([NotNull] Path sourcePath, [NotNull] Path destinationPath)
Copies an asset from source to destination path. Generates a unique file name if an asset already exi...
static Boolean Trash([NotNull] Path path)
Moves an asset file or folder to the OS trash.
static Object CreateAsNew([NotNull] Object instance, [NotNull] Path path)
Writes the object to disk. Generates a unique file name if an asset exists at the path.
static Type GetMainType(GUID guid)
Returns the type of the main asset for the GUID.
static GUID GetGuid([NotNull] Object asset)
Returns the GUID of an object. Returns an empty GUID if the object is null or not an asset.
static T LoadOrCreate< T >([NotNull] Path path, [NotNull] Func< T > getInstance)
Loads an asset at path or creates the asset if needed.
static String[] Find([NotNull] String filter, String[] searchInFolders=null)
Finds asset GUIDs by the given filter criteria.
static Boolean Move([NotNull] Path sourcePath, [NotNull] Path destinationPath)
Moves an asset file to destination path.
static void OpenExternal([NotNull] Object asset, Int32 lineNumber=-1, Int32 columnNumber=-1)
Opens the asset in the application associated with the file's extension.
static Boolean Trash([NotNull] Object asset)
Moves an asset file or folder to the OS trash.
static void BatchEditing([NotNull] Action massAssetFileEditAction)
Batch multiple asset file operations to improve execution speed.
Definition Asset.File.cs:82
static Type GetSubType([NotNull] Path path, Int64 fileId)
Gets the type of a sub asset by the main asset's path and the local file ID of the sub-asset.
static Boolean Delete([NotNull] Object asset)
Deletes an asset file or folder.
static Object CreateAsNew([NotNull] Byte[] contents, [NotNull] Path path)
Writes the byte array to disk, then imports and loads the asset. Generates a unique file name if an a...
static Object Create([NotNull] Object instance, [NotNull] Path path)
Writes the object to disk. Overwrites any existing file.
static void Import([NotNull] Path path, ImportAssetOptions options=ImportAssetOptions.Default)
Imports a file at a given path that was created or modified 'externally'. Externally refers to any me...
static Path[] FindPaths([NotNull] String filter, String[] searchInFolders=null)
Finds asset paths by the given filter criteria.
static Boolean CanOpenInEditor(EntityId entityId)
Returns true if the given object can be opened (edited) by the Unity editor.
static void ForceSave([NotNull] Object asset)
Forces the object to be saved to disk. Marks the object as dirty and then calls CodeSmileEditor....
Groups file related operations.
Definition Asset.File.cs:38
static String[] ToStrings([NotNull] IEnumerable< Path > paths)
Converts an IEnumerable collection of Path instances to a string array.
static Path Get([NotNull] Object asset)
Gets the relative path of an asset.
Represents a relative path to an asset file or folder, typically under 'Assets' or 'Packages'.
Definition Asset.Path.cs:25
T Load< T >()
Loads a (sub) object from the asset identified by type.
Boolean CanOpenInEditor()
Returns true if the asset can be opened (edited) by the Unity Editor itself.
Replacement implementation for Unity's massive AssetDatabase class with a cleaner interface and more ...