/// <summary>
        /// Restores the original SE metadata for this root.
        /// This should only be called by Index.DeleteFileDescriptor().
        /// </summary>
        public static async Task RestoreDefaultMetadata(XivDependencyRoot root, IndexFile index = null, ModList modlist = null)
        {
            var original = await ItemMetadata.CreateFromRaw(root, true);

            await ApplyMetadata(original, index, modlist);
        }
        /// <summary>
        /// Applies multiple metadata mods simultaneously for performance gains.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="index"></param>
        /// <param name="modlist"></param>
        /// <returns></returns>
        internal static async Task ApplyMetadataBatched(List <ItemMetadata> data, IndexFile index, ModList modlist, bool save = true)
        {
            if (data == null || data.Count == 0)
            {
                return;
            }

            var _eqp     = new Eqp(XivCache.GameInfo.GameDirectory);
            var _modding = new Modding(XivCache.GameInfo.GameDirectory);
            var _index   = new Index(XivCache.GameInfo.GameDirectory);

            var dummyItem = new XivGenericItemModel();

            dummyItem.Name = Constants.InternalModSourceName;
            dummyItem.SecondaryCategory = Constants.InternalModSourceName;

            Dictionary <XivRace, List <(uint PrimaryId, string Slot, EquipmentDeformationParameter Entry)> > eqdpEntries = new Dictionary <XivRace, List <(uint PrimaryId, string Slot, EquipmentDeformationParameter Entry)> >();
            Dictionary <Est.EstType, List <ExtraSkeletonEntry> > estEntries = new Dictionary <Est.EstType, List <ExtraSkeletonEntry> >();
            List <(uint PrimaryId, EquipmentParameter EqpData)>  eqpEntries = new List <(uint PrimaryId, EquipmentParameter EqpData)>();
            List <(uint PrimaryId, GimmickParameter GmpData)>    gmpEntries = new List <(uint PrimaryId, GimmickParameter GmpData)>();

            foreach (var meta in data)
            {
                // Construct the parameter collections for each function call.
                foreach (var kv in meta.EqdpEntries)
                {
                    if (!eqdpEntries.ContainsKey(kv.Key))
                    {
                        eqdpEntries.Add(kv.Key, new List <(uint PrimaryId, string Slot, EquipmentDeformationParameter Entry)>());
                    }

                    eqdpEntries[kv.Key].Add(((uint)meta.Root.Info.PrimaryId, meta.Root.Info.Slot, kv.Value));
                }

                var estType = Est.GetEstType(meta.Root);
                foreach (var kv in meta.EstEntries)
                {
                    if (!estEntries.ContainsKey(estType))
                    {
                        estEntries.Add(estType, new List <ExtraSkeletonEntry>());
                    }

                    estEntries[estType].Add(kv.Value);
                }

                if (meta.EqpEntry != null)
                {
                    eqpEntries.Add(((uint)meta.Root.Info.PrimaryId, meta.EqpEntry));
                }

                if (meta.GmpEntry != null)
                {
                    gmpEntries.Add(((uint)meta.Root.Info.PrimaryId, meta.GmpEntry));
                }
            }


            if (index.DataFile == XivDataFile._04_Chara)
            {
                // Batch install functions for these three.
                await _eqp.SaveEqpEntries(eqpEntries, dummyItem, index, modlist);

                await _eqp.SaveEqdpEntries(eqdpEntries, dummyItem, index, modlist);

                await _eqp.SaveGmpEntries(gmpEntries, dummyItem, index, modlist);

                // The EST function already does batch applications by nature of how it works,
                // so just call it once for each of the four EST types represented.
                foreach (var kv in estEntries)
                {
                    await Est.SaveExtraSkeletonEntries(kv.Key, kv.Value, dummyItem, index, modlist);
                }
            }


            // IMC Files don't really overlap that often, so it's
            // not a significant loss generally to just write them individually.
            foreach (var meta in data)
            {
                if (meta.ImcEntries.Count > 0)
                {
                    var _imc    = new Imc(XivCache.GameInfo.GameDirectory);
                    var imcPath = meta.Root.GetRawImcFilePath();
                    await _imc.SaveEntries(imcPath, meta.Root.Info.Slot, meta.ImcEntries, null, index, modlist);
                }
            }

            if (save)
            {
                await _index.SaveIndexFile(index);

                await _modding.SaveModListAsync(modlist);
            }
        }
        /// <summary>
        /// Applies this Metadata object to the FFXIV file system.
        /// This should only called by Dat.WriteToDat() / RestoreDefaultMetadata()
        /// </summary>
        internal static async Task ApplyMetadata(ItemMetadata meta, IndexFile index = null, ModList modlist = null)
        {
            var _eqp     = new Eqp(XivCache.GameInfo.GameDirectory);
            var _modding = new Modding(XivCache.GameInfo.GameDirectory);
            var _index   = new Index(XivCache.GameInfo.GameDirectory);
            var df       = IOUtil.GetDataFileFromPath(meta.Root.Info.GetRootFile());

            var dummyItem = new XivGenericItemModel();

            dummyItem.Name = Constants.InternalModSourceName;
            dummyItem.SecondaryCategory = Constants.InternalModSourceName;


            // Beep boop
            bool doSave = false;

            if (index == null)
            {
                doSave = true;
                index  = await _index.GetIndexFile(df);

                modlist = await _modding.GetModListAsync();
            }


            if (meta.ImcEntries.Count > 0)
            {
                var _imc    = new Imc(XivCache.GameInfo.GameDirectory);
                var imcPath = meta.Root.GetRawImcFilePath();
                await _imc.SaveEntries(imcPath, meta.Root.Info.Slot, meta.ImcEntries, dummyItem, index, modlist);
            }

            // Applying EQP data via set 0 is not allowed, as it is a special set hard-coded to use Set 1's data.
            if (meta.EqpEntry != null && !(meta.Root.Info.PrimaryType == Items.Enums.XivItemType.equipment && meta.Root.Info.PrimaryId == 0))
            {
                await _eqp.SaveEqpEntry(meta.Root.Info.PrimaryId, meta.EqpEntry, dummyItem, index, modlist);
            }

            if (meta.EqdpEntries.Count > 0)
            {
                await _eqp.SaveEqdpEntries((uint)meta.Root.Info.PrimaryId, meta.Root.Info.Slot, meta.EqdpEntries, dummyItem, index, modlist);
            }

            if (meta.EstEntries.Count > 0)
            {
                var type    = Est.GetEstType(meta.Root);
                var entries = meta.EstEntries.Values.ToList();
                await Est.SaveExtraSkeletonEntries(type, entries, dummyItem, index, modlist);
            }

            if (meta.GmpEntry != null)
            {
                await _eqp.SaveGimmickParameter(meta.Root.Info.PrimaryId, meta.GmpEntry, dummyItem, index, modlist);
            }

            if (doSave)
            {
                await _index.SaveIndexFile(index);

                await _modding.SaveModListAsync(modlist);
            }
        }
        /// <summary>
        /// Saves this metadata file to the FFXIV file system.
        /// </summary>
        /// <param name="meta"></param>
        /// <returns></returns>
        public static async Task SaveMetadata(ItemMetadata meta, string source, IndexFile index = null, ModList modlist = null)
        {
            var _dat     = new Dat(XivCache.GameInfo.GameDirectory);
            var _modding = new Modding(XivCache.GameInfo.GameDirectory);

            var path = meta.Root.Info.GetRootFile();
            var item = meta.Root.GetFirstItem();

            await _dat.ImportType2Data(await Serialize(meta), path, source, item, index, modlist);
        }