<|start_filename|>Scripts/Editor/PresetManagerStorage.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Presets;
using UnityEngine;

namespace BrunoMikoski.PresetManager
{
    public struct PresetData
    {
        public Preset Preset;
        public string[] TargetParameters;

        public PresetData(Preset preset, string[] targetParameters)
        {
            Preset = preset;
            TargetParameters = targetParameters;
        }
    }

    
    public class PresetManagerStorage : ScriptableObject
    {
        private const string DEFAULT_STORAGE_PATH = "Assets/PresetManager/PresetManager.asset";

        [SerializeField]
        private List<FolderToPresetData> foldersPresets = new List<FolderToPresetData>();
        public List<FolderToPresetData> FoldersPresets => foldersPresets;

        private static PresetManagerStorage instance;
        public static PresetManagerStorage Instance
        {
            get
            {
                GetOrCreateInstance();
                return instance;
            }
        }

        public static bool IsInstanceAvailable()
        {
            return Instance != null;
        }
        
        private static void GetOrCreateInstance()
        {
            if (instance != null)
                return;
            
            string[] avaialbleGUIDs = AssetDatabase.FindAssets("t:PresetManagerStorage");
            if (avaialbleGUIDs.Length == 0)
            {
                string directoryPath = Path.GetDirectoryName(Path.GetFullPath(DEFAULT_STORAGE_PATH));
                
                if (!Directory.Exists(directoryPath))
                    Directory.CreateDirectory(directoryPath);

                instance = CreateInstance<PresetManagerStorage>();

                AssetDatabase.CreateAsset(instance, DEFAULT_STORAGE_PATH);
                AssetDatabase.SaveAssets();
            }
            else
            {
                instance = AssetDatabase.LoadAssetAtPath<PresetManagerStorage>(
                    AssetDatabase.GUIDToAssetPath(avaialbleGUIDs[0]));

                ValidateInstances();
            }
        }

        private static void ValidateInstances()
        {
            for (int i = Instance.foldersPresets.Count - 1; i >= 0; i--)
            {
                FolderToPresetData instanceFoldersPreset = Instance.foldersPresets[i];
                if (!instanceFoldersPreset.IsValid)
                    Instance.foldersPresets.RemoveAt(i);
            }
        }

        public bool HasAnyPresetForFolder(string relativeFolderPath)
        {
            return TryGetPresetsForFolder(relativeFolderPath, out PresetData[] presets);
        }

        public bool TryGetPresetFolderPathFromFolder(string relativeFolderPath, AssetImporter assetImporter,
            out string ownerFolder)
        {
            ownerFolder = string.Empty;
            if (TryGetPresetsForFolder(relativeFolderPath, out PresetData[] presets))
            {
                for (int i = 0; i < presets.Length; i++)
                {
                    PresetData currentPreset = presets[i];
                    if (currentPreset.Preset.CanBeAppliedTo(assetImporter))
                    {
                        ownerFolder = relativeFolderPath;
                        return true;
                    }
                }
            }

            return false;
        }

        public bool TryGetAssetPresetFromFolder(string relativeFolderPath, AssetImporter assetImporter, out PresetData preset)
        {
            if (TryGetPresetsForFolder(relativeFolderPath, out PresetData[] presets))
            {
                for (int i = 0; i < presets.Length; i++)
                {
                    PresetData currentPreset = presets[i];
                    if (currentPreset.Preset.CanBeAppliedTo(assetImporter))
                    {
                        preset = currentPreset;
                        return true;
                    }
                }
            }

            preset = default;
            return false;
        }
        
        public bool TryGetPresetsForFolder(string relativeFolderPath, out PresetData[] preset)
        {
            List<PresetData> presetsList = new List<PresetData>();
            string folderGUID = AssetDatabase.AssetPathToGUID(relativeFolderPath);
            for (int i = 0; i < foldersPresets.Count; i++)
            {
                FolderToPresetData folderToPresetReference = foldersPresets[i];
                if (string.Equals(folderToPresetReference.FolderGuid, folderGUID, StringComparison.Ordinal))
                {
                    string presetPath = AssetDatabase.GUIDToAssetPath(folderToPresetReference.PresetGuid);
                    Preset loadedPreset = AssetDatabase.LoadAssetAtPath<Preset>(presetPath);
                    if (loadedPreset != null)
                    {
                        presetsList.Add(new PresetData(loadedPreset,
                            folderToPresetReference.GetModifications(loadedPreset)));
                    }
                }
            }

            preset = presetsList.ToArray();
            return presetsList.Count > 0;
        }
        
        public void SetPresetForFolder(string relativeFolderPath, Preset preset)
        {
            if (TryGetFolderPresetIndex(relativeFolderPath, out int targetIndex))
            {
                foldersPresets[targetIndex].OverridePresetGUID(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(preset)));
            }
            else
            {
                foldersPresets.Add(new FolderToPresetData(AssetDatabase.AssetPathToGUID(relativeFolderPath),
                    AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(preset))));
            }

            EditorUtility.SetDirty(this);
        }

        private bool TryGetFolderPresetIndex(string relativeFolderPath, out int index)
        {
            string folderGUID = AssetDatabase.AssetPathToGUID(relativeFolderPath);
            for (int i = 0; i < foldersPresets.Count; i++)
            {
                FolderToPresetData folderToPresetReference = foldersPresets[i];
                if (string.Equals(folderToPresetReference.FolderGuid, folderGUID, StringComparison.Ordinal))
                {
                    index = i;
                    return true;
                }
            }

            index = -1;
            return false;
        }

        public void ClearPresetForFolder(string relativeFolderPath)
        {
            string folderPathGUID = AssetDatabase.AssetPathToGUID(relativeFolderPath);
            for (int i = 0; i < foldersPresets.Count; i++)
            {
                FolderToPresetData folderToPresetReference = foldersPresets[i];

                if (string.Equals(folderToPresetReference.FolderGuid, folderPathGUID, StringComparison.Ordinal))
                {
                    foldersPresets.RemoveAt(i);
                    EditorUtility.SetDirty(this);
                }
            }
        }

        public void ClearAllPresetForFolder(string relativeFolderPath)
        {
            string folderPathGUID = AssetDatabase.AssetPathToGUID(relativeFolderPath);
            for (int i = foldersPresets.Count - 1; i >= 0; i--)
            {
                FolderToPresetData folderToPresetReference = foldersPresets[i];

                if (string.Equals(folderToPresetReference.FolderGuid, folderPathGUID, StringComparison.Ordinal))
                {
                    foldersPresets.RemoveAt(i);
                }
            }

            EditorUtility.SetDirty(this);
        }
    }
}


<|start_filename|>Scripts/Editor/FolderToPresetData.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Presets;
using UnityEngine;

namespace BrunoMikoski.PresetManager
{
    [Serializable]
    public struct FolderToPresetData
    {
        [SerializeField]
        private string folderGUID;
        public string FolderGuid => folderGUID;

        [SerializeField]
        private string presetGUID;
        public string PresetGuid => presetGUID;

        public bool IsValid => !string.IsNullOrEmpty(folderGUID) && !string.IsNullOrEmpty(presetGUID);

        private List<string> ignoredProperties;

        public FolderToPresetData(string folderGUID, string presetGUID)
        {
            this.folderGUID = folderGUID;
            this.presetGUID = presetGUID;
            
            ignoredProperties = new List<string>();
            
        }

        public void OverridePresetGUID(string presetGUID)
        {
            this.presetGUID = presetGUID;
        }

        public bool IsSettingEnabled(string propertyModificationPropertyPath)
        {
            if (ignoredProperties == null)
                return true;
            
            for (int i = 0; i < ignoredProperties.Count; i++)
            {
                string ignoredProperty = ignoredProperties[i];
                if (string.Equals(ignoredProperty, propertyModificationPropertyPath, StringComparison.Ordinal))
                {
                    return false;
                }
            }

            return true;
        }

        public void SetSettingEnabled(string propertyModificationPropertyPath, bool settingEnabled)
        {
            if (settingEnabled)
            {
                if (ignoredProperties == null)
                    return;
                
                ignoredProperties.Remove(propertyModificationPropertyPath);
            }
            else
            {
                if (ignoredProperties == null)
                    ignoredProperties = new List<string>();
                
                ignoredProperties.Add(propertyModificationPropertyPath);
            }
        }

        public string[] GetModifications(Preset preset)
        {
            List<string> finalItems = new List<string>();
            for (int i = 0; i < preset.PropertyModifications.Length; i++)
            {
                PropertyModification presetPropertyModification = preset.PropertyModifications[i];
                if(ignoredProperties != null && ignoredProperties.Contains(presetPropertyModification.propertyPath))
                    continue;
                
                finalItems.Add(presetPropertyModification.propertyPath);
            }

            return finalItems.ToArray();
        }
    }
}

<|start_filename|>package.json<|end_filename|>
{
  "name": "com.brunomikoski.presetmanager",
  "displayName": "Preset Manager",
  "version": "0.2.5",
  "unity": "2018.4",
  "description": "The missing folder based preset for assets.",
  "keywords": [
    "asset importer",
    "preset",
    "folder"
  ],
  "category": "editor extensions",
  "homepage": "https://github.com/brunomikoski/PresetManager",
  "repository": {
    "type": "git",
    "url": "https://github.com/brunomikoski/PresetManager.git"
  },
  "bugs": "https://github.com/brunomikoski/PresetManager/issues",
    "author": {
    "name": "<NAME>",
    "url": "https://www.brunomikoski.com"
  }
}


<|start_filename|>Scripts/Editor/FolderCustomEditor.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Presets;
using UnityEngine;

namespace BrunoMikoski.PresetManager
{
    [CustomEditor(typeof(DefaultAsset), true)]
    public class FolderCustomEditor : Editor
    {
        private bool isFolder;
        private string relativeFolderPath;
        private string absoluteFolderPath;
        private AssetImporter[] assetImportersType;
        private bool[] assetImportersTypeFoldout;
        private int selectedIndex  = -1;

        private void OnEnable()
        {
            if (!PresetManagerSettings.DisplayFolderInspector)
                return;
            
            relativeFolderPath = AssetDatabase.GetAssetPath(target);
            if(!relativeFolderPath.StartsWith("Assets"))
                return;
            
            absoluteFolderPath = PresetManagerUtils.RelativeToAbsolutePath(relativeFolderPath);
            isFolder = Directory.Exists(absoluteFolderPath);

            if (!isFolder)
                return;
            
            ReadFolder();
        }
        
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            if (!isFolder)
                return;

            if (assetImportersType == null || assetImportersType.Length == 0)
                return;
            
            bool wasGUIEnabled = GUI.enabled;
            
            GUI.enabled = true;
            DrawPresetManager();
            GUI.enabled = wasGUIEnabled;
        }

        private void DrawPresetManager()
        {
            using (new GUILayout.HorizontalScope())
            {
                GUILayout.Space(-8);
                
                EditorGUILayout.BeginVertical("Box");
                EditorGUILayout.LabelField("Assets Preset Manager", EditorStyles.toolbarDropDown);
                EditorGUI.indentLevel++;

                for (var i = 0; i < assetImportersType.Length; i++)
                {
                    AssetImporter assetImporter = assetImportersType[i];
                    assetImportersTypeFoldout[i] = EditorGUILayout.Foldout(assetImportersTypeFoldout[i],
                        assetImporter.GetType().Name, EditorStyles.foldout);

                    if (assetImportersTypeFoldout[i])
                    {
                        ShowOptionsForImporter(assetImporter);
                    }
                }
                EditorGUI.indentLevel--;
                DrawOptions();

                EditorGUILayout.EndVertical();
            }
        }

        private void DrawOptions()
        {
            EditorGUILayout.BeginHorizontal("Box");
            bool hasAnyPresetForFolder = PresetManagerUtils.HasAnyPresetForFolder(relativeFolderPath);
            EditorGUI.BeginDisabledGroup(!hasAnyPresetForFolder);
            if (GUILayout.Button("Apply on current", EditorStyles.toolbarButton))
            {
                PresetManagerUtils.ApplyPresetsToFolder(relativeFolderPath);
            }
            
            if (GUILayout.Button("Delete Folder Settings", EditorStyles.toolbarButton))
            {
                PresetManagerUtils.ClearAllPresetsForFolder(relativeFolderPath);
                selectedIndex = 0;
            }
            EditorGUI.EndDisabledGroup();

            
            EditorGUILayout.EndHorizontal();
        }

        private void ShowOptionsForImporter(AssetImporter assetImporter)
        {
            Preset[] presets = PresetManagerUtils.GetAvailablePresetsForAssetImporter(assetImporter);
            string[] presetsNames = GetNamesFromList(presets, true);

            if (selectedIndex == -1)
            {
                if (PresetManagerUtils.TryGetAssetPresetFromFolder(relativeFolderPath, assetImporter, out PresetData appliedPreset))
                    selectedIndex = Array.IndexOf(presets, appliedPreset.Preset) + 1;
                else
                    selectedIndex = 0;
            }

            if (selectedIndex == 0)
            {
                if (PresetManagerUtils.TryToGetParentPresetSettings(relativeFolderPath, assetImporter,
                    out string parentRelativePath))
                {
                    EditorGUI.BeginDisabledGroup(true);
                    EditorGUILayout.ObjectField("Inheriting settings from:",
                        AssetDatabase.LoadAssetAtPath<DefaultAsset>(parentRelativePath), typeof(DefaultAsset), false);
                    EditorGUI.EndDisabledGroup();
                }
            }

            EditorGUI.BeginChangeCheck();

            using (new GUILayout.HorizontalScope())
            {
                GUILayout.Space(20);
                selectedIndex = GUILayout.SelectionGrid(selectedIndex, presetsNames, 1, EditorStyles.radioButton);
            }


            if (EditorGUI.EndChangeCheck())
            {
                if (selectedIndex > 0)
                {
                    PresetManagerUtils.SetPresetForFolder(relativeFolderPath, presets[selectedIndex - 1]);
                }
                else
                {
                    PresetManagerUtils.ClearPresetForFolder(relativeFolderPath);
                }
            }
        }    

        private string[] GetNamesFromList(Preset[] presets, bool includeNone)
        {
            List<string> options = new List<string>();
            if (includeNone)
            {
                options.Add("None");
            }
            for (var i = 0; i < presets.Length; i++)
            {
                Preset preset = presets[i];
                options.Add(preset.name);
            }

            return options.ToArray();
        }

        private void ReadFolder()
        {
            string[] files = GetAllFiles(absoluteFolderPath);

            HashSet<AssetImporter> assetImporters = new HashSet<AssetImporter>();
            HashSet<Type> assetImportersTypes = new HashSet<Type>();
            
            for (var i = 0; i < files.Length; i++)
            {
                string absoluteFilePath = files[i];
                if (absoluteFilePath.EndsWith(".meta"))
                    continue;
                string relativeFilePath = PresetManagerUtils.AbsoluteToRelativePath(absoluteFilePath);

                AssetImporter assetImporter = AssetImporter.GetAtPath(relativeFilePath);

                if (assetImporter == null)
                    continue;
                
                if (!PresetManagerUtils.HasPresetFor(assetImporter))
                    continue;
                
                if(assetImportersTypes.Contains(assetImporter.GetType()))
                    continue;
                
                assetImporters.Add(assetImporter);
                assetImportersTypes.Add(assetImporter.GetType());
            }

            assetImportersType = assetImporters.ToArray();
            if (assetImportersTypeFoldout == null || assetImportersTypeFoldout.Length != assetImportersType.Length)
                assetImportersTypeFoldout = new bool[assetImportersType.Length];
        }

        private string[] GetAllFiles(string absoluteFolderPath)
        {
            List<string> filesPath = new List<string>();
            int level = 0;
            SearchForAllFiles(absoluteFolderPath, ref filesPath, ref level);
            return filesPath.ToArray();
        }

        private void SearchForAllFiles(string directoryAbsolutePath, ref List<string> filesPath, ref int level)
        {
            filesPath.AddRange(Directory.GetFiles(directoryAbsolutePath));

            string[] folderPaths = Directory.GetDirectories(directoryAbsolutePath);
            for (int i = 0; i < folderPaths.Length; i++)
            {
                string folderPath = folderPaths[i];
                string absoluteToRelativePath = PresetManagerUtils.AbsoluteToRelativePath(folderPath);

                if (level > PresetManagerSettings.MaximumDirectorySearch)
                    continue;

                if (!PresetManagerUtils.HasAnyPresetForFolder(absoluteToRelativePath))
                {
                    level++;
                    SearchForAllFiles(folderPath, ref filesPath, ref level);
                }
            }
        }
    }
}


<|start_filename|>Scripts/Editor/PresetManagerSettings.cs<|end_filename|>
﻿using UnityEditor;

namespace BrunoMikoski.PresetManager
{
    public class PresetManagerSettings
    {
        public static bool DisplayFolderInspector
        {
            get => EditorPrefs.GetBool(nameof(DisplayFolderInspector), true);
            set => EditorPrefs.SetBool(nameof(DisplayFolderInspector), value);
        }

        public static int MaximumDirectorySearch
        {
            get => EditorPrefs.GetInt(nameof(MaximumDirectorySearch), 3);
            set => EditorPrefs.SetInt(nameof(MaximumDirectorySearch), value);
        }

        //I refuse to use this terrible Settings Provider thingy from unity, so until the new preferences package
        //is available, this warning will happen :( 
        //https://docs.unity3d.com/2018.3/Documentation/ScriptReference/SettingsProvider.html

        [PreferenceItem("Preset Manager")]
        public static void PreferencesGUI()
        {
            EditorGUI.BeginChangeCheck();
            bool displayFolderInspector = EditorGUILayout.Toggle("Show Folder Inspector", DisplayFolderInspector);
            int maximumDirectorySearch =
                EditorGUILayout.IntField("Maximum Recursive Search Levels", MaximumDirectorySearch);

            if (EditorGUI.EndChangeCheck())
            {
                DisplayFolderInspector = displayFolderInspector;
                MaximumDirectorySearch = maximumDirectorySearch;
            }
        }
        
    }
}

<|start_filename|>Scripts/Editor/PresetAssetPostProcessor.cs<|end_filename|>
﻿using System.IO;
using UnityEditor;

namespace BrunoMikoski.PresetManager
{
    public class PresetAssetPostProcessor : AssetPostprocessor
    {
        private void OnPreprocessAsset()
        {
            if (!assetImporter.importSettingsMissing)
                return;

            if (IsPresetAsset(assetImporter.assetPath))
            {
                PresetManagerUtils.ProjectPresetsChanged();
                return;
            }
            
            string path = Path.GetDirectoryName(assetPath);
            if (string.IsNullOrEmpty(path))
                return;
            
            PresetManagerUtils.ApplySettingsToAsset(path, assetImporter);
        }

        static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets,
            string[] movedFromAssetPaths)
        {
            if (!PresetManagerStorage.IsInstanceAvailable())
                return;

            if (!IsPresetAsset(importedAssets) && !IsPresetAsset(deletedAssets)) 
                return;
            
            PresetManagerUtils.ProjectPresetsChanged();
        }

        private static bool IsPresetAsset(params string[] assetsPath)
        {
            for (int i = 0; i < assetsPath.Length; i++)
            {
                string assetPath = assetsPath[i];

                if (assetPath.EndsWith(".preset"))
                    return true;
            }

            return false;
        }
    }
}
