/// <summary>
        /// The event handler for the add custom texture button clicked
        /// </summary>
        private async void AddCustomTextureButton_Click(object sender, RoutedEventArgs e)
        {
            var selectedItem = TextureMapComboBox.SelectedItem as ModComboBox;

            var mod = selectedItem.SelectedMod;

            byte[] modData;

            var includedMod = new IncludedMods
            {
                Name     = $"{Path.GetFileNameWithoutExtension(mod.fullPath)} ({((Category)ModListTreeView.SelectedItem).Name})",
                FullPath = mod.fullPath
            };

            var includedModsList = IncludedModsList.Items.Cast <IncludedMods>().ToList();

            var tex = new Tex(_gameDirectory);

            var ddsDirectory = new DirectoryInfo(CustomTextureTextBox.Text);

            if (selectedItem.TexTypePath.Type == XivTexType.ColorSet)
            {
                var mtrl = new Mtrl(_gameDirectory, XivDataFiles.GetXivDataFile(mod.datFile), GetLanguage());

                var xivMtrl = await mtrl.GetMtrlData(mod.data.modOffset, mod.fullPath, int.Parse(Settings.Default.DX_Version));

                modData = tex.DDStoMtrlData(xivMtrl, ddsDirectory, ((Category)ModListTreeView.SelectedItem).Item, GetLanguage());
            }
            else
            {
                var texData = await tex.GetTexData(selectedItem.TexTypePath);

                modData = await tex.DDStoTexData(texData, ((Category)ModListTreeView.SelectedItem).Item, ddsDirectory);
            }

            if (includedModsList.Any(item => item.Name.Equals(includedMod.Name)))
            {
                if (FlexibleMessageBox.Show(
                        string.Format(UIMessages.ExistingOption, includedMod.Name),
                        UIMessages.OverwriteTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
                    System.Windows.Forms.DialogResult.Yes)
                {
                    _selectedModOption.Mods[mod.fullPath].ModDataBytes = modData;
                }
            }
            else
            {
                IncludedModsList.Items.Add(includedMod);
                _selectedModOption.Mods.Add(mod.fullPath, new ModData
                {
                    Name         = mod.name,
                    Category     = mod.category,
                    FullPath     = mod.fullPath,
                    ModDataBytes = modData
                });
            }
        }
        public async Task SaveMulti()
        {
            // Get tokenized map info structs.
            // This will let us set them in the new Materials and
            // Detokenize them using the new paths.
            var mapInfos = _material.GetAllMapInfos(true);


            // Shader info likewise will be pumped into each new material.
            var shaderInfo = _material.GetShaderInfo();

            // Add new Materials for shared model items.
            var oldMaterialIdentifier = _material.GetMaterialIdentifier();

            // Ordering these by name ensures that we create textures for the new variants in the first
            // item alphabetically, just for consistency's sake.
            var sameModelItems = (await _item.GetSharedModelItems()).OrderBy(x => x.Name, new ItemNameComparer());

            var oldVariantString = "/v" + _material.GetVariant().ToString().PadLeft(4, '0') + '/';
            var modifiedVariants = new List <int>();


            var mtrlReplacementRegex       = "_" + oldMaterialIdentifier + ".mtrl";
            var mtrlReplacementRegexResult = "_" + _newMaterialIdentifier + ".mtrl";


            // Load and modify all the MTRLs.
            foreach (var item in sameModelItems)
            {
                // Resolve this item's material variant.
                // - This isn't always the same as the item model variant, for some reason.
                // - So it has to be resolved manually.
                var variantMtrlPath = "";
                var itemType        = ItemType.GetPrimaryItemType(_item);


                variantMtrlPath = (await _mtrl.GetMtrlPath(item, _material.GetRace(), oldMaterialIdentifier, itemType)).Folder;

                var match   = Regex.Match(variantMtrlPath, "/v([0-9]+)");
                var variant = 0;
                if (match.Success)
                {
                    variant = Int32.Parse(match.Groups[1].Value);
                }

                // Only modify each Variant once.
                if (modifiedVariants.Contains(variant))
                {
                    continue;
                }

                var     dxVersion = 11;
                XivMtrl itemXivMtrl;

                // Get mtrl path -- TODO: Need support here for offhand item materials.
                // But Offhand support is basically completely broken anyways, so this can wait.
                itemXivMtrl = await _mtrl.GetMtrlData(_item, _material.GetRace(), oldMaterialIdentifier, dxVersion);

                // Shift the MTRL to the new variant folder.
                itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, oldVariantString, "/v" + variant.ToString().PadLeft(4, '0') + "/");

                if (_mode == MaterialEditorMode.NewMulti)
                {
                    // Change the MTRL part identifier.
                    itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, mtrlReplacementRegex, mtrlReplacementRegexResult);
                }

                // Load the Shader Settings
                itemXivMtrl.SetShaderInfo(shaderInfo, true);

                // Loop our tokenized map infos and pump them back in
                // using the new modified material to detokenize them.
                foreach (var info in mapInfos)
                {
                    itemXivMtrl.SetMapInfo(info.Usage, (MapInfo)info.Clone());
                }

                // Write the new Material
                await _mtrl.ImportMtrl(itemXivMtrl, item, XivStrings.TexTools);

                modifiedVariants.Add(variant);
                _view.SaveStatusLabel.Content = "Updated " + modifiedVariants.Count + " Variants...";
            }
        }
Пример #3
0
        public async Task SaveMulti()
        {
            var _imc   = new Imc(XivCache.GameInfo.GameDirectory);
            var _index = new Index(XivCache.GameInfo.GameDirectory);

            // Get tokenized map info structs.
            // This will let us set them in the new Materials and
            // Detokenize them using the new paths.
            var mapInfos = _material.GetAllMapInfos(true);


            // Shader info likewise will be pumped into each new material.
            var shaderInfo = _material.GetShaderInfo();

            // Add new Materials for shared model items.
            var oldMaterialIdentifier = _material.GetMaterialIdentifier();
            var oldMtrlName           = Path.GetFileName(_material.MTRLPath);

            // Ordering these by name ensures that we create textures for the new variants in the first
            // item alphabetically, just for consistency's sake.
            var sameModelItems = (await _item.GetSharedModelItems()).OrderBy(x => x.Name, new ItemNameComparer());

            var oldVariantString = "/v" + _material.GetVariant().ToString().PadLeft(4, '0') + '/';
            var modifiedVariants = new List <int>();


            var mtrlReplacementRegex       = "_" + oldMaterialIdentifier + ".mtrl";
            var mtrlReplacementRegexResult = "_" + _newMaterialIdentifier + ".mtrl";

            if (_mode == MaterialEditorMode.NewRace)
            {
                mtrlReplacementRegexResult = mtrlReplacementRegex;
            }

            var newMtrlName = oldMtrlName.Replace(mtrlReplacementRegex, mtrlReplacementRegexResult);

            var root = _item.GetRootInfo();

            var imcEntries   = new List <XivImc>();
            var materialSets = new HashSet <byte>();

            try
            {
                var imcInfo = await _imc.GetFullImcInfo(_item);

                imcEntries   = imcInfo.GetAllEntries(root.Slot, true);
                materialSets = imcEntries.Select(x => x.MaterialSet).ToHashSet();
            } catch
            {
                // Item doesn't use IMC entries, and thus only has a single variant.
                materialSets.Clear();
                materialSets.Add(0);
            }


            // We need to save our non-existent base material once before we can continue.
            if (_mode == MaterialEditorMode.NewRace)
            {
                await _mtrl.ImportMtrl(_material, _item, XivStrings.TexTools);
            }

            var count = 0;

            var allItems = (await root.ToFullRoot().GetAllItems());

            var matNumToItems = new Dictionary <int, List <IItemModel> >();

            foreach (var i in allItems)
            {
                if (imcEntries.Count <= i.ModelInfo.ImcSubsetID)
                {
                    continue;
                }

                var matSet = imcEntries[i.ModelInfo.ImcSubsetID].MaterialSet;
                if (!matNumToItems.ContainsKey(matSet))
                {
                    matNumToItems.Add(matSet, new List <IItemModel>());
                }

                var saveItem = i;

                if (typeof(XivCharacter) == i.GetType())
                {
                    var temp = (XivCharacter)((XivCharacter)_item).Clone();
                    temp.Name = saveItem.SecondaryCategory;
                    saveItem  = temp;
                }

                matNumToItems[matSet].Add(saveItem);
            }

            var keys = matNumToItems.Keys.ToList();

            foreach (var key in keys)
            {
                var list = matNumToItems[key];
                matNumToItems[key] = list.OrderBy(x => x.Name, new ItemNameComparer()).ToList();
            }

            // Load and modify all the MTRLs.
            foreach (var materialSetId in materialSets)
            {
                var variantPath     = _mtrl.GetMtrlFolder(root, materialSetId);
                var oldMaterialPath = variantPath + "/" + oldMtrlName;
                var newMaterialPath = variantPath + "/" + newMtrlName;

                // Don't create materials for set 0.  (SE sets the material ID to 0 when that particular set-slot doesn't actually exist as an item)
                if (materialSetId == 0 && imcEntries.Count > 0)
                {
                    continue;
                }

                var     dxVersion = 11;
                XivMtrl itemXivMtrl;

                // Get mtrl path
                if (await _index.FileExists(oldMaterialPath))
                {
                    // Use the Material from this variant as a base?
                    itemXivMtrl = await _mtrl.GetMtrlData(_item, oldMaterialPath, dxVersion);
                }
                else
                {
                    itemXivMtrl = await _mtrl.GetMtrlData(_item, _material.MTRLPath, dxVersion);
                }

                // If we're an item that doesn't use IMC variants, make sure we don't accidentally move the material around.
                if (materialSetId != 0)
                {
                    // Shift the MTRL to the new variant folder.
                    itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, oldVariantString, "/v" + materialSetId.ToString().PadLeft(4, '0') + "/");
                }

                if (_mode == MaterialEditorMode.NewMulti)
                {
                    // Change the MTRL part identifier.
                    itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, mtrlReplacementRegex, mtrlReplacementRegexResult);
                }

                // Load the Shader Settings
                itemXivMtrl.SetShaderInfo(shaderInfo, true);

                // Loop our tokenized map infos and pump them back in
                // using the new modified material to detokenize them.
                foreach (var info in mapInfos)
                {
                    itemXivMtrl.SetMapInfo(info.Usage, (MapInfo)info.Clone());
                }


                IItem item;
                try
                {
                    item = matNumToItems[materialSetId].First();
                } catch
                {
                    item = (await XivCache.GetFirstRoot(itemXivMtrl.MTRLPath)).GetFirstItem();
                }

                count++;
                // Write the new Material
                await _mtrl.ImportMtrl(itemXivMtrl, item, XivStrings.TexTools);

                _view.SaveStatusLabel.Content = "Updated " + count + "/" + materialSets.Count + " Material Sets...";
            }
        }
Пример #4
0
        /// <summary>
        /// Gets the materials for the model
        /// </summary>
        /// <returns>A dictionary containing the mesh number(key) and the associated texture data (value)</returns>
        public static Dictionary <int, ModelTextureData> GetMaterials(
            DirectoryInfo gameDirectory, IItemModel item, XivMdl mdlData, XivRace race)
        {
            var      textureDataDictionary = new Dictionary <int, ModelTextureData>();
            var      mtrlDictionary        = new Dictionary <int, XivMtrl>();
            var      mtrl                 = new Mtrl(gameDirectory, item.DataFile);
            var      mtrlFilePaths        = mdlData.PathData.MaterialList;
            var      hasColorChangeShader = false;
            Color?   customColor          = null;
            WinColor winColor;

            var materialNum = 0;

            foreach (var mtrlFilePath in mtrlFilePaths)
            {
                var mtrlItem = new XivGenericItemModel
                {
                    Category        = item.Category,
                    ItemCategory    = item.ItemCategory,
                    ItemSubCategory = item.ItemSubCategory,
                    ModelInfo       = new XivModelInfo
                    {
                        Body      = item.ModelInfo.Body,
                        ModelID   = item.ModelInfo.ModelID,
                        ModelType = item.ModelInfo.ModelType,
                        Variant   = item.ModelInfo.Variant
                    },
                    Name = item.Name
                };

                var modelID  = mtrlItem.ModelInfo.ModelID;
                var bodyID   = mtrlItem.ModelInfo.Body;
                var filePath = mtrlFilePath;

                if (!filePath.Contains("hou") && mtrlFilePath.Count(x => x == '/') > 1)
                {
                    filePath = mtrlFilePath.Substring(mtrlFilePath.LastIndexOf("/"));
                }

                var typeChar = $"{mtrlFilePath[4]}{mtrlFilePath[9]}";

                var raceString = "";
                switch (typeChar)
                {
                // Character Body
                case "cb":
                    var body = mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4);
                    raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4);
                    race       = XivRaces.GetXivRace(raceString);

                    if (!raceString.Equals("0901") && !raceString.Equals("1001") && !raceString.Equals("1101"))
                    {
                        var gender = 0;
                        if (int.Parse(raceString.Substring(0, 2)) % 2 == 0)
                        {
                            gender = 1;
                        }

                        var settingsRace = GetSettingsRace(gender);

                        race = settingsRace.Race;

                        filePath = mtrlFilePath.Replace(raceString, race.GetRaceCode()).Replace(body, settingsRace.BodyID);

                        body = settingsRace.BodyID;
                    }


                    mtrlItem = new XivGenericItemModel
                    {
                        Category     = XivStrings.Character,
                        ItemCategory = XivStrings.Body,
                        Name         = XivStrings.Body,
                        ModelInfo    = new XivModelInfo
                        {
                            Body = int.Parse(body)
                        }
                    };

                    winColor    = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Skin_Color);
                    customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A);

                    break;

                // Face
                case "cf":
                    bodyID     = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("f") + 1, 4));
                    raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4);
                    race       = XivRaces.GetXivRace(raceString);

                    mtrlItem = new XivGenericItemModel
                    {
                        Category     = XivStrings.Character,
                        ItemCategory = XivStrings.Face,
                        Name         = XivStrings.Face,
                        ModelInfo    = new XivModelInfo
                        {
                            Body = bodyID
                        }
                    };

                    break;

                // Hair
                case "ch":
                    bodyID     = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("h") + 1, 4));
                    raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4);
                    race       = XivRaces.GetXivRace(raceString);

                    mtrlItem = new XivGenericItemModel
                    {
                        Category     = XivStrings.Character,
                        ItemCategory = XivStrings.Hair,
                        Name         = XivStrings.Hair,
                        ModelInfo    = new XivModelInfo
                        {
                            Body = bodyID
                        }
                    };

                    winColor    = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Hair_Color);
                    customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A);

                    break;

                // Tail
                case "ct":
                    var tempPath = mtrlFilePath.Substring(4);
                    bodyID     = int.Parse(tempPath.Substring(tempPath.IndexOf("t") + 1, 4));
                    raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4);
                    race       = XivRaces.GetXivRace(raceString);

                    mtrlItem = new XivGenericItemModel
                    {
                        Category     = XivStrings.Character,
                        ItemCategory = XivStrings.Tail,
                        Name         = XivStrings.Tail,
                        ModelInfo    = new XivModelInfo
                        {
                            Body = bodyID
                        }
                    };

                    winColor    = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Hair_Color);
                    customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A);

                    break;

                // Equipment
                case "ce":
                    modelID    = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("e") + 1, 4));
                    raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4);
                    race       = XivRaces.GetXivRace(raceString);

                    mtrlItem.ModelInfo.ModelID = modelID;
                    break;

                // Accessory
                case "ca":
                    modelID    = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("a") + 1, 4));
                    raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4);
                    race       = XivRaces.GetXivRace(raceString);

                    mtrlItem.ModelInfo.ModelID = modelID;
                    break;

                // Weapon
                case "wb":
                    modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("w") + 1, 4));
                    bodyID  = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4));
                    mtrlItem.ModelInfo.ModelID = modelID;
                    mtrlItem.ModelInfo.Body    = bodyID;
                    break;

                // Monster
                case "mb":
                    modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("_m") + 2, 4));
                    bodyID  = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4));
                    mtrlItem.ModelInfo.ModelID = modelID;
                    mtrlItem.ModelInfo.Body    = bodyID;
                    break;

                // DemiHuman
                case "de":
                    modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("d") + 1, 4));
                    bodyID  = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("e") + 1, 4));
                    mtrlItem.ModelInfo.ModelID = modelID;
                    mtrlItem.ModelInfo.Body    = bodyID;
                    break;

                default:
                    break;
                }

                var dxVersion = int.Parse(Settings.Default.DX_Version);

                var     mtrlFile = filePath.Remove(0, 1);
                XivMtrl mtrlData;
                try
                {
                    mtrlData = mtrl.GetMtrlData(mtrlItem, race, mtrlFile, dxVersion);
                }
                catch (Exception)
                {
                    if (mtrlItem.ModelInfo.ModelID == item.ModelInfo.ModelID)
                    {
                        throw;
                    }

                    // Fall back to material data from the primary model.
                    mtrlData = mtrl.GetMtrlData(item, race, mtrlFile, dxVersion);
                }


                if (mtrlData.Shader.Contains("colorchange"))
                {
                    hasColorChangeShader = true;
                }

                mtrlDictionary.Add(materialNum, mtrlData);

                materialNum++;
            }

            foreach (var xivMtrl in mtrlDictionary)
            {
                var modelTexture = new ModelTexture(gameDirectory, xivMtrl.Value);

                if (hasColorChangeShader)
                {
                    var modelMaps = modelTexture.GetModelMaps(null, true);

                    textureDataDictionary.Add(xivMtrl.Key, modelMaps);
                }
                else
                {
                    if (item.ItemCategory.Equals(XivStrings.Face))
                    {
                        var path = xivMtrl.Value.MTRLPath;

                        if (path.Contains("_iri_"))
                        {
                            winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Iris_Color);
                        }
                        else if (path.Contains("_etc_"))
                        {
                            winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Etc_Color);
                        }
                        else
                        {
                            winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Skin_Color);
                        }

                        customColor = new Color(winColor.R, winColor.G, winColor.B, winColor.A);
                    }

                    var modelMaps = modelTexture.GetModelMaps(customColor);

                    textureDataDictionary.Add(xivMtrl.Key, modelMaps);
                }
            }

            return(textureDataDictionary);
        }
Пример #5
0
        /// <summary>
        /// Copies the entirety of a given root to a new root.
        /// </summary>
        /// <param name="Source">Original Root to copy from.</param>
        /// <param name="Destination">Destination root to copy to.</param>
        /// <param name="ApplicationSource">Application to list as the source for the resulting mod entries.</param>
        /// <returns>Returns a Dictionary of all the file conversion</returns>
        public static async Task <Dictionary <string, string> > CloneRoot(XivDependencyRoot Source, XivDependencyRoot Destination, string ApplicationSource, int singleVariant = -1, string saveDirectory = null, IProgress <string> ProgressReporter = null, IndexFile index = null, ModList modlist = null, ModPack modPack = null)
        {
            if (!IsSupported(Source) || !IsSupported(Destination))
            {
                throw new InvalidDataException("Cannot clone unsupported root.");
            }


            if (ProgressReporter != null)
            {
                ProgressReporter.Report("Stopping Cache Worker...");
            }
            var workerStatus = XivCache.CacheWorkerEnabled;

            XivCache.CacheWorkerEnabled = false;
            try
            {
                var df = IOUtil.GetDataFileFromPath(Source.ToString());

                var _imc     = new Imc(XivCache.GameInfo.GameDirectory);
                var _mdl     = new Mdl(XivCache.GameInfo.GameDirectory, df);
                var _dat     = new Dat(XivCache.GameInfo.GameDirectory);
                var _index   = new Index(XivCache.GameInfo.GameDirectory);
                var _mtrl    = new Mtrl(XivCache.GameInfo.GameDirectory);
                var _modding = new Modding(XivCache.GameInfo.GameDirectory);

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

                    modlist = await _modding.GetModListAsync();
                }


                bool locked = _index.IsIndexLocked(df);
                if (locked)
                {
                    throw new Exception("Game files currently in use.");
                }


                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Analyzing items and variants...");
                }

                // First, try to get everything, to ensure it's all valid.
                ItemMetadata originalMetadata = await GetCachedMetadata(index, modlist, Source, df, _dat);


                var originalModelPaths = await Source.GetModelFiles(index, modlist);

                var originalMaterialPaths = await Source.GetMaterialFiles(-1, index, modlist);

                var originalTexturePaths = await Source.GetTextureFiles(-1, index, modlist);

                var originalVfxPaths = new HashSet <string>();
                if (Imc.UsesImc(Source))
                {
                    var avfxSets = originalMetadata.ImcEntries.Select(x => x.Vfx).Distinct();
                    foreach (var avfx in avfxSets)
                    {
                        var avfxStuff = await ATex.GetVfxPath(Source.Info, avfx);

                        if (String.IsNullOrEmpty(avfxStuff.Folder) || String.IsNullOrEmpty(avfxStuff.File))
                        {
                            continue;
                        }

                        var path = avfxStuff.Folder + "/" + avfxStuff.File;
                        if (index.FileExists(path))
                        {
                            originalVfxPaths.Add(path);
                        }
                    }
                }

                // Time to start editing things.

                // First, get a new, clean copy of the metadata, pointed at the new root.
                var newMetadata = await GetCachedMetadata(index, modlist, Source, df, _dat);

                newMetadata.Root = Destination.Info.ToFullRoot();
                ItemMetadata originalDestinationMetadata = null;
                try
                {
                    originalDestinationMetadata = await GetCachedMetadata(index, modlist, Destination, df, _dat);
                } catch
                {
                    originalDestinationMetadata = new ItemMetadata(Destination);
                }

                // Set 0 needs special handling.
                if (Source.Info.PrimaryType == XivItemType.equipment && Source.Info.PrimaryId == 0)
                {
                    var set1Root     = new XivDependencyRoot(Source.Info.PrimaryType, 1, null, null, Source.Info.Slot);
                    var set1Metadata = await GetCachedMetadata(index, modlist, set1Root, df, _dat);

                    newMetadata.EqpEntry = set1Metadata.EqpEntry;

                    if (Source.Info.Slot == "met")
                    {
                        newMetadata.GmpEntry = set1Metadata.GmpEntry;
                    }
                }
                else if (Destination.Info.PrimaryType == XivItemType.equipment && Destination.Info.PrimaryId == 0)
                {
                    newMetadata.EqpEntry = null;
                    newMetadata.GmpEntry = null;
                }


                // Now figure out the path names for all of our new paths.
                // These dictionarys map Old Path => New Path
                Dictionary <string, string> newModelPaths        = new Dictionary <string, string>();
                Dictionary <string, string> newMaterialPaths     = new Dictionary <string, string>();
                Dictionary <string, string> newMaterialFileNames = new Dictionary <string, string>();
                Dictionary <string, string> newTexturePaths      = new Dictionary <string, string>();
                Dictionary <string, string> newAvfxPaths         = new Dictionary <string, string>();

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Calculating files to copy...");
                }

                // For each path, replace any instances of our primary and secondary types.
                foreach (var path in originalModelPaths)
                {
                    newModelPaths.Add(path, UpdatePath(Source, Destination, path));
                }

                foreach (var path in originalMaterialPaths)
                {
                    var nPath = UpdatePath(Source, Destination, path);
                    newMaterialPaths.Add(path, nPath);
                    var fName = Path.GetFileName(path);

                    if (!newMaterialFileNames.ContainsKey(fName))
                    {
                        newMaterialFileNames.Add(fName, Path.GetFileName(nPath));
                    }
                }

                foreach (var path in originalTexturePaths)
                {
                    newTexturePaths.Add(path, UpdatePath(Source, Destination, path));
                }

                foreach (var path in originalVfxPaths)
                {
                    newAvfxPaths.Add(path, UpdatePath(Source, Destination, path));
                }

                var destItem = Destination.GetFirstItem();
                var srcItem  = (await Source.GetAllItems(singleVariant))[0];
                var iCat     = destItem.SecondaryCategory;
                var iName    = destItem.Name;


                var files = newModelPaths.Select(x => x.Value).Union(
                    newMaterialPaths.Select(x => x.Value)).Union(
                    newAvfxPaths.Select(x => x.Value)).Union(
                    newTexturePaths.Select(x => x.Value));
                var allFiles = new HashSet <string>();
                foreach (var f in files)
                {
                    allFiles.Add(f);
                }

                allFiles.Add(Destination.Info.GetRootFile());

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Getting modlist...");
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Removing existing modifications to destination root...");
                }

                if (Destination != Source)
                {
                    var dPath   = Destination.Info.GetRootFolder();
                    var allMods = modlist.Mods.ToList();
                    foreach (var mod in allMods)
                    {
                        if (mod.fullPath.StartsWith(dPath) && !mod.IsInternal())
                        {
                            if (Destination.Info.SecondaryType != null || Destination.Info.Slot == null)
                            {
                                // If this is a slotless root, purge everything.
                                await _modding.DeleteMod(mod.fullPath, false, index, modlist);
                            }
                            else if (allFiles.Contains(mod.fullPath) || mod.fullPath.Contains(Destination.Info.GetBaseFileName(true)))
                            {
                                // Otherwise, only purge the files we're replacing, and anything else that
                                // contains our slot name.
                                await _modding.DeleteMod(mod.fullPath, false, index, modlist);
                            }
                        }
                    }
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Copying models...");
                }

                // Load, Edit, and resave the model files.
                foreach (var kv in newModelPaths)
                {
                    var src    = kv.Key;
                    var dst    = kv.Value;
                    var offset = index.Get8xDataOffset(src);
                    var xmdl   = await _mdl.GetRawMdlData(src, false, offset);

                    var tmdl = TTModel.FromRaw(xmdl);

                    if (xmdl == null || tmdl == null)
                    {
                        continue;
                    }

                    tmdl.Source  = dst;
                    xmdl.MdlPath = dst;

                    // Replace any material references as needed.
                    foreach (var m in tmdl.MeshGroups)
                    {
                        foreach (var matKv in newMaterialFileNames)
                        {
                            m.Material = m.Material.Replace(matKv.Key, matKv.Value);
                        }
                    }

                    // Save new Model.
                    var bytes = await _mdl.MakeNewMdlFile(tmdl, xmdl, null);

                    await _dat.WriteModFile(bytes, dst, ApplicationSource, destItem, index, modlist);
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Copying textures...");
                }

                // Raw Copy all Texture files to the new destinations to avoid having the MTRL save functions auto-generate blank textures.
                foreach (var kv in newTexturePaths)
                {
                    var src = kv.Key;
                    var dst = kv.Value;

                    await _dat.CopyFile(src, dst, ApplicationSource, true, destItem, index, modlist);
                }


                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Copying materials...");
                }
                HashSet <string> CopiedMaterials = new HashSet <string>();
                // Load every Material file and edit the texture references to the new texture paths.
                foreach (var kv in newMaterialPaths)
                {
                    var src = kv.Key;
                    var dst = kv.Value;
                    try
                    {
                        var offset = index.Get8xDataOffset(src);
                        if (offset == 0)
                        {
                            continue;
                        }
                        var xivMtrl = await _mtrl.GetMtrlData(offset, src, 11);

                        xivMtrl.MTRLPath = dst;

                        for (int i = 0; i < xivMtrl.TexturePathList.Count; i++)
                        {
                            foreach (var tkv in newTexturePaths)
                            {
                                xivMtrl.TexturePathList[i] = xivMtrl.TexturePathList[i].Replace(tkv.Key, tkv.Value);
                            }
                        }

                        await _mtrl.ImportMtrl(xivMtrl, destItem, ApplicationSource, index, modlist);

                        CopiedMaterials.Add(dst);
                    }
                    catch (Exception ex)
                    {
                        // Let functions later handle this mtrl then.
                    }
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Copying VFX...");
                }
                // Copy VFX files.
                foreach (var kv in newAvfxPaths)
                {
                    var src = kv.Key;
                    var dst = kv.Value;

                    await _dat.CopyFile(src, dst, ApplicationSource, true, destItem, index, modlist);
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Creating missing variants...");
                }
                // Check to see if we need to add any variants
                var cloneNum = newMetadata.ImcEntries.Count >= 2 ? 1 : 0;
                while (originalDestinationMetadata.ImcEntries.Count > newMetadata.ImcEntries.Count)
                {
                    // Clone Variant 1 into the variants we are missing.
                    newMetadata.ImcEntries.Add((XivImc)newMetadata.ImcEntries[cloneNum].Clone());
                }


                if (singleVariant >= 0)
                {
                    if (ProgressReporter != null)
                    {
                        ProgressReporter.Report("Setting single-variant data...");
                    }

                    if (singleVariant < newMetadata.ImcEntries.Count)
                    {
                        var v = newMetadata.ImcEntries[singleVariant];

                        for (int i = 0; i < newMetadata.ImcEntries.Count; i++)
                        {
                            newMetadata.ImcEntries[i] = (XivImc)v.Clone();
                        }
                    }
                }

                // Update Skeleton references to be for the correct set Id.
                var setId = Destination.Info.SecondaryId == null ? (ushort)Destination.Info.PrimaryId : (ushort)Destination.Info.SecondaryId;
                foreach (var entry in newMetadata.EstEntries)
                {
                    entry.Value.SetId = setId;
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Copying metdata...");
                }

                // Poke through the variants and adjust any that point to null Material Sets to instead use a valid one.
                if (newMetadata.ImcEntries.Count > 0 && originalMetadata.ImcEntries.Count > 0)
                {
                    var valid = newMetadata.ImcEntries.FirstOrDefault(x => x.MaterialSet != 0).MaterialSet;
                    if (valid <= 0)
                    {
                        valid = originalMetadata.ImcEntries.FirstOrDefault(x => x.MaterialSet != 0).MaterialSet;
                    }

                    for (int i = 0; i < newMetadata.ImcEntries.Count; i++)
                    {
                        var entry = newMetadata.ImcEntries[i];
                        if (entry.MaterialSet == 0)
                        {
                            entry.MaterialSet = valid;
                        }
                    }
                }

                await ItemMetadata.SaveMetadata(newMetadata, ApplicationSource, index, modlist);

                // Save the new Metadata file via the batch function so that it's only written to the memory cache for now.
                await ItemMetadata.ApplyMetadataBatched(new List <ItemMetadata>() { newMetadata }, index, modlist, false);



                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Filling in missing material sets...");
                }
                // Validate all variants/material sets for valid materials, and copy materials as needed to fix.
                if (Imc.UsesImc(Destination))
                {
                    var mSets = newMetadata.ImcEntries.Select(x => x.MaterialSet).Distinct();
                    foreach (var mSetId in mSets)
                    {
                        var path = Destination.Info.GetRootFolder() + "material/v" + mSetId.ToString().PadLeft(4, '0') + "/";
                        foreach (var mkv in newMaterialFileNames)
                        {
                            // See if the material was copied over.
                            var destPath = path + mkv.Value;
                            if (CopiedMaterials.Contains(destPath))
                            {
                                continue;
                            }

                            string existentCopy = null;

                            // If not, find a material where one *was* copied over.
                            foreach (var mSetId2 in mSets)
                            {
                                var p2 = Destination.Info.GetRootFolder() + "material/v" + mSetId2.ToString().PadLeft(4, '0') + "/";
                                foreach (var cmat2 in CopiedMaterials)
                                {
                                    if (cmat2 == p2 + mkv.Value)
                                    {
                                        existentCopy = cmat2;
                                        break;
                                    }
                                }
                            }

                            // Shouldn't ever actually hit this, but if we do, nothing to be done about it.
                            if (existentCopy == null)
                            {
                                continue;
                            }

                            // Copy the material over.
                            await _dat.CopyFile(existentCopy, destPath, ApplicationSource, true, destItem, index, modlist);
                        }
                    }
                }

                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Updating modlist...");
                }

                if (modPack == null)
                {
                    modPack = new ModPack()
                    {
                        author = "System", name = "Item Copy - " + srcItem.Name + " to " + iName, url = "", version = "1.0"
                    };
                }

                List <Mod> mods = new List <Mod>();
                foreach (var mod in modlist.Mods)
                {
                    if (allFiles.Contains(mod.fullPath))
                    {
                        // Ensure all of our modified files are attributed correctly.
                        mod.name     = iName;
                        mod.category = iCat;
                        mod.source   = ApplicationSource;
                        mod.modPack  = modPack;

                        mods.Add(mod);
                    }
                }

                if (!modlist.ModPacks.Any(x => x.name == modPack.name))
                {
                    modlist.ModPacks.Add(modPack);
                }

                if (doSave)
                {
                    // Save everything.
                    await _index.SaveIndexFile(index);

                    await _modding.SaveModListAsync(modlist);
                }

                XivCache.QueueDependencyUpdate(allFiles.ToList());

                if (saveDirectory != null)
                {
                    ProgressReporter.Report("Creating TTMP File...");
                    var desc = "Item Converter Modpack - " + srcItem.Name + " -> " + iName + "\nCreated at: " + DateTime.Now.ToString();
                    // Time to save the modlist to file.
                    var dir   = new DirectoryInfo(saveDirectory);
                    var _ttmp = new TTMP(dir, ApplicationSource);
                    var smpd  = new SimpleModPackData()
                    {
                        Author            = modPack.author,
                        Description       = desc,
                        Url               = modPack.url,
                        Version           = new Version(1, 0, 0),
                        Name              = modPack.name,
                        SimpleModDataList = new List <SimpleModData>()
                    };

                    foreach (var mod in mods)
                    {
                        var size = await _dat.GetCompressedFileSize(mod.data.modOffset, df);

                        var smd = new SimpleModData()
                        {
                            Name      = iName,
                            FullPath  = mod.fullPath,
                            DatFile   = df.GetDataFileName(),
                            Category  = iCat,
                            IsDefault = false,
                            ModSize   = size,
                            ModOffset = mod.data.modOffset
                        };
                        smpd.SimpleModDataList.Add(smd);
                    }

                    await _ttmp.CreateSimpleModPack(smpd, XivCache.GameInfo.GameDirectory, null, true);
                }



                if (ProgressReporter != null)
                {
                    ProgressReporter.Report("Root copy complete.");
                }

                // Return the final file conversion listing.
                var ret  = newModelPaths.Union(newMaterialPaths).Union(newAvfxPaths).Union(newTexturePaths);
                var dict = ret.ToDictionary(x => x.Key, x => x.Value);
                dict.Add(Source.Info.GetRootFile(), Destination.Info.GetRootFile());
                return(dict);
            } finally
            {
                XivCache.CacheWorkerEnabled = workerStatus;
            }
        }