/// <summary>
        /// Fills the mod list tree view
        /// </summary>
        private void FillModListTreeView()
        {
            var modding = new Modding(_gameDirectory);
            var modList = modding.GetModList();

            var Categories = new ObservableCollection <Category>();

            if (modList == null)
            {
                return;
            }

            // Mods
            var mainCategories = new HashSet <string>();

            foreach (var modEntry in modList.Mods)
            {
                if (!modEntry.name.Equals(string.Empty))
                {
                    mainCategories.Add(modEntry.category);
                }
            }

            foreach (var mainCategory in mainCategories)
            {
                var category = new Category
                {
                    Name         = mainCategory,
                    Categories   = new ObservableCollection <Category>(),
                    CategoryList = new List <string>()
                };

                var modItems =
                    from mod in modList.Mods
                    where mod.category.Equals(mainCategory)
                    select mod;

                foreach (var modItem in modItems)
                {
                    if (category.CategoryList.Contains(modItem.name))
                    {
                        continue;
                    }

                    var categoryItem = new Category
                    {
                        Name           = modItem.name,
                        Item           = MakeItemModel(modItem),
                        ParentCategory = category
                    };

                    category.Categories.Add(categoryItem);
                    category.CategoryList.Add(modItem.name);
                }

                Categories.Add(category);
            }

            ModListTreeView.ItemsSource = Categories;
        }
Example #2
0
        /// <summary>
        /// Creates the simple mod pack data list
        /// </summary>
        /// <returns>Task</returns>
        private async Task MakeSimpleDataList()
        {
            DirectoryInfo modListDirectory = new DirectoryInfo(Path.Combine(_gameDirectory.Parent.Parent.FullName, XivStrings.ModlistFilePath));
            Modding       modding          = new Modding(_gameDirectory);

            this.ModList           = modding.GetModList();
            this.ParentsDictionary = XivCache.GetModListParents();


            List <SimpleModpackEntry> entries = new List <SimpleModpackEntry>();

            for (int i = 0; i < this.ModList.Mods.Count; i++)
            {
                SimpleModpackEntry entry = MakeEntry(i);
                if (entry == null)
                {
                    continue;
                }

                entries.Add(entry);
            }

            await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
            {
                foreach (SimpleModpackEntry entry in entries)
                {
                    this.Entries.Add(entry);
                }
            });
        }
Example #3
0
        /// <summary>
        /// Creates the simple mod pack data list
        /// </summary>
        /// <returns>Task</returns>
        private async Task MakeSimpleDataList()
        {
            DirectoryInfo modListDirectory = new DirectoryInfo(Path.Combine(_gameDirectory.Parent.Parent.FullName, XivStrings.ModlistFilePath));
            Modding       modding          = new Modding(_gameDirectory);

            this.ModList = modding.GetModList();

            // Don't show or list internal mods at all in this menu.
            this.ModList.Mods.RemoveAll(x => x.IsInternal());

            // Rip through the mod list and get the correct raw compressed sizes for all the mods.
            var _dat = new Dat(XivCache.GameInfo.GameDirectory);

            foreach (var mod in ModList.Mods)
            {
                var compressedSize = mod.data.modSize;
                try
                {
                    compressedSize = await _dat.GetCompressedFileSize(mod.data.modOffset, IOUtil.GetDataFileFromPath(mod.fullPath));

                    mod.data.modSize = compressedSize;
                }
                catch
                {
                    // If the calculation failed, just use the original size I guess?
                    // The main way this happens though is if the data is broken, so maybe we should error?
                    // Though there's possibly filetypes from other framework applications in here that we don't know how to measure?
                }
            }

            this.ParentsDictionary = XivCache.GetModListParents();


            List <SimpleModpackEntry> entries = new List <SimpleModpackEntry>();

            for (int i = 0; i < this.ModList.Mods.Count; i++)
            {
                SimpleModpackEntry entry = MakeEntry(i);
                if (entry == null)
                {
                    continue;
                }

                entries.Add(entry);
            }

            await System.Windows.Application.Current.Dispatcher.InvokeAsync(() =>
            {
                foreach (SimpleModpackEntry entry in entries)
                {
                    this.Entries.Add(entry);
                }
            });
        }
        public BackupModPackCreator()
        {
            InitializeComponent();

            _gameDirectory = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory);
            var modding = new Modding(_gameDirectory);

            _modList = modding.GetModList();

            DataContext             = new BackupModpackViewModel();
            ModpackList.ItemsSource = new List <BackupModpackItemEntry>();
            ModPackName.Text        = string.Format("Backup_{0}", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));

            // Manually add an entry for the mods that don't belong to a modpack
            ((List <BackupModpackItemEntry>)ModpackList.ItemsSource).Add(new BackupModpackItemEntry(UIStrings.Standalone_Non_ModPack));

            foreach (var modpack in _modList.ModPacks)
            {
                var entry = new BackupModpackItemEntry(modpack.name);
                ((List <BackupModpackItemEntry>)ModpackList.ItemsSource).Add(entry);
            }

            ModpackList.SelectedIndex = 0;
        }
        private async Task CreateBasic()
        {
            string modPackPath = Path.Combine(Properties.Settings.Default.ModPack_Directory, $"{ViewModel.Name}.ttmp2");

            if (File.Exists(modPackPath))
            {
                DialogResult overwriteDialogResult = FlexibleMessageBox.Show(new Wpf32Window(this), UIMessages.ModPackOverwriteMessage,
                                                                             UIMessages.OverwriteTitle, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                if (overwriteDialogResult != System.Windows.Forms.DialogResult.Yes)
                {
                    return;
                }
            }

            TTMP texToolsModPack = new TTMP(new DirectoryInfo(Settings.Default.ModPack_Directory), XivStrings.TexTools);
            var  index           = new Index(XivCache.GameInfo.GameDirectory);
            var  dat             = new Dat(XivCache.GameInfo.GameDirectory);
            var  modding         = new Modding(XivCache.GameInfo.GameDirectory);
            var  ModList         = modding.GetModList();

            SimpleModPackData simpleModPackData = new SimpleModPackData
            {
                Name              = ViewModel.Name,
                Author            = ViewModel.Author,
                Version           = ViewModel.Version,
                Description       = ViewModel.Description,
                Url               = ViewModel.Url,
                SimpleModDataList = new List <SimpleModData>()
            };

            foreach (var entry in ViewModel.Entries)
            {
                foreach (var file in entry.AllFiles)
                {
                    var exists = await index.FileExists(file);

                    // This is a funny case where in order to create the modpack we actually have to write a default meta entry to the dats first.
                    // If we had the right functions we could just load and serialize the data, but we don't atm.
                    if (!exists && Path.GetExtension(file) == ".meta")
                    {
                        var meta = await ItemMetadata.GetMetadata(file);

                        await ItemMetadata.SaveMetadata(meta, XivStrings.TexTools);
                    }

                    var offset = await index.GetDataOffset(file);

                    var dataFile       = IOUtil.GetDataFileFromPath(file);
                    var compressedSize = await dat.GetCompressedFileSize(offset, dataFile);

                    var modEntry = ModList.Mods.FirstOrDefault(x => x.fullPath == file);
                    var modded   = modEntry != null && modEntry.enabled == true;


                    SimpleModData simpleData = new SimpleModData
                    {
                        Name      = entry.Item.Name,
                        Category  = entry.Item.SecondaryCategory,
                        FullPath  = file,
                        ModOffset = offset,
                        ModSize   = compressedSize,
                        IsDefault = !modded,
                        DatFile   = dataFile.GetDataFileName()
                    };

                    simpleModPackData.SimpleModDataList.Add(simpleData);
                }
            }



            try
            {
                await LockUi(UIStrings.Creating_Modpack, null, null);

                Progress <(int current, int total, string message)> progressIndicator = new Progress <(int current, int total, string message)>(ReportProgress);
                await texToolsModPack.CreateSimpleModPack(simpleModPackData, XivCache.GameInfo.GameDirectory, progressIndicator, true);

                FlexibleMessageBox.Show(new Wpf32Window(this), "Modpack Created Successfully.",
                                        "Modpack Created", MessageBoxButtons.OK, MessageBoxIcon.Information);
                await UnlockUi(this);

                DialogResult = true;
            }
            catch (Exception ex)
            {
                FlexibleMessageBox.Show(new Wpf32Window(this), "An Error occured while creating the modpack.\n\n" + ex.Message,
                                        "Modpack Creation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                await UnlockUi(this);
            }
        }
        private async Task CreateAdvanced()
        {
            string modPackPath = Path.Combine(Properties.Settings.Default.ModPack_Directory, $"{ViewModel.Name}.ttmp2");

            if (File.Exists(modPackPath))
            {
                DialogResult overwriteDialogResult = FlexibleMessageBox.Show(new Wpf32Window(this), UIMessages.ModPackOverwriteMessage,
                                                                             UIMessages.OverwriteTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (overwriteDialogResult != System.Windows.Forms.DialogResult.Yes)
                {
                    return;
                }
            }

            await LockUi(UIStrings.Creating_Modpack, null, null);

            try
            {
                TTMP texToolsModPack = new TTMP(new DirectoryInfo(Settings.Default.ModPack_Directory), XivStrings.TexTools);
                var  index           = new Index(XivCache.GameInfo.GameDirectory);
                var  dat             = new Dat(XivCache.GameInfo.GameDirectory);
                var  modding         = new Modding(XivCache.GameInfo.GameDirectory);
                var  ModList         = modding.GetModList();

                var wizardData = new ModPackData()
                {
                    Name         = ViewModel.Name,
                    Author       = ViewModel.Author,
                    Version      = ViewModel.Version,
                    Description  = ViewModel.Description,
                    Url          = ViewModel.Url,
                    ModPackPages = new List <ModPackData.ModPackPage>()
                };

                var page = new ModPackData.ModPackPage()
                {
                    PageIndex = 1,
                    ModGroups = new List <ModGroup>()
                };

                wizardData.ModPackPages.Add(page);


                foreach (var e in ViewModel.Entries)
                {
                    var item  = e.Item;
                    var files = e.AllFiles;

                    var group = new ModGroup()
                    {
                        GroupName     = item.Name,
                        SelectionType = "Multi",
                        OptionList    = new List <ModOption>()
                    };
                    page.ModGroups.Add(group);

                    var option = new ModOption
                    {
                        GroupName     = group.GroupName,
                        IsChecked     = true,
                        Name          = GetNiceLevelName(e.Level, true, true),
                        Description   = "Item: " + item.Name + "\nInclusion Level: " + GetNiceLevelName(e.Level) + "\nPrimary Files:" + e.MainFiles.Count + "\nTotal Files:" + e.AllFiles.Count,
                        SelectionType = "Multi",
                    };
                    group.OptionList.Add(option);

                    foreach (var file in e.AllFiles)
                    {
                        var exists = await index.FileExists(file);

                        // This is a funny case where in order to create the modpack we actually have to write a default meta entry to the dats first.
                        // If we had the right functions we could just load and serialize the data, but we don't atm.
                        if (!exists && Path.GetExtension(file) == ".meta")
                        {
                            var meta = await ItemMetadata.GetMetadata(file);

                            await ItemMetadata.SaveMetadata(meta, XivStrings.TexTools);
                        }

                        var offset = await index.GetDataOffset(file);

                        var dataFile       = IOUtil.GetDataFileFromPath(file);
                        var compressedSize = await dat.GetCompressedFileSize(offset, dataFile);

                        var modEntry = ModList.Mods.FirstOrDefault(x => x.fullPath == file);
                        var modded   = modEntry != null && modEntry.enabled == true;

                        var fData = new ModData
                        {
                            Name         = e.Item.Name,
                            Category     = e.Item.SecondaryCategory,
                            FullPath     = file,
                            IsDefault    = !modded,
                            ModDataBytes = dat.GetRawData(offset, dataFile, compressedSize)
                        };
                        option.Mods.Add(file, fData);
                    }
                }

                // Okay modpack is now created internally, just need to save it.
                var progressIndicator = new Progress <double>(ReportProgressAdv);
                await texToolsModPack.CreateWizardModPack(wizardData, progressIndicator, true);

                FlexibleMessageBox.Show(new Wpf32Window(this), "Modpack Created Successfully.",
                                        "Modpack Created", MessageBoxButtons.OK, MessageBoxIcon.Information);
                await UnlockUi(this);

                DialogResult = true;
            } catch (Exception ex)
            {
                FlexibleMessageBox.Show(new Wpf32Window(this), "An Error occured while creating the modpack.\n\n" + ex.Message,
                                        "Modpack Creation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                await UnlockUi(this);
            }
        }
        /// <summary>
        /// The event handler for the tree view item selection changed
        /// </summary>
        private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs <object> e)
        {
            var selectedItem = e.NewValue as Category;

            if (selectedItem == null || selectedItem.Item == null)
            {
                return;
            }

            TextureMapComboBox.Items.Clear();
            ModelTypeComboBox.Items.Clear();
            MaterialComboBox.Items.Clear();
            CustomTextureTextBox.Text = string.Empty;


            var modding = new Modding(_gameDirectory);
            var modList = modding.GetModList();

            var modItems =
                from mod in modList.Mods
                where mod.name.Equals(selectedItem.Name)
                select mod;

            foreach (var modItem in modItems)
            {
                var itemPath = modItem.fullPath;
                var modCB    = new ModComboBox();
                var ttp      = new TexTypePath
                {
                    Path     = itemPath,
                    DataFile = XivDataFiles.GetXivDataFile(modItem.datFile)
                };

                // Textures
                if (itemPath.Contains("_d."))
                {
                    modCB.Name        = $"{XivTexType.Diffuse} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Diffuse;
                }
                else if (itemPath.Contains("_n."))
                {
                    modCB.Name        = $"{XivTexType.Normal} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Normal;
                }
                else if (itemPath.Contains("_s."))
                {
                    modCB.Name        = $"{XivTexType.Specular} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Specular;
                }
                else if (itemPath.Contains("_m."))
                {
                    modCB.Name        = $"{XivTexType.Multi} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Multi;
                }
                else if (itemPath.Contains("material"))
                {
                    modCB.Name        = $"{XivTexType.ColorSet} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.ColorSet;
                }
                else if (itemPath.Contains("decal"))
                {
                    modCB.Name        = $"{XivTexType.Mask} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Mask;
                }
                else if (itemPath.Contains("vfx"))
                {
                    modCB.Name        = $"{XivTexType.Vfx} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Vfx;
                }
                else if (itemPath.Contains("ui/"))
                {
                    if (itemPath.Contains("icon"))
                    {
                        modCB.Name        = $"{XivTexType.Icon} ({Path.GetFileNameWithoutExtension(itemPath)})";
                        modCB.SelectedMod = modItem;
                        ttp.Type          = XivTexType.Icon;
                    }
                    else if (itemPath.Contains("map"))
                    {
                        modCB.Name        = $"{XivTexType.Map} ({Path.GetFileNameWithoutExtension(itemPath)})";
                        modCB.SelectedMod = modItem;
                        ttp.Type          = XivTexType.Map;
                    }
                    else
                    {
                        modCB.Name        = $"UI ({Path.GetFileNameWithoutExtension(itemPath)})";
                        modCB.SelectedMod = modItem;
                        ttp.Type          = XivTexType.Other;
                    }
                }
                else if (itemPath.Contains(".tex"))
                {
                    modCB.Name        = $"{XivTexType.Other} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    modCB.SelectedMod = modItem;
                    ttp.Type          = XivTexType.Other;
                }

                if (modCB.Name != null)
                {
                    modCB.TexTypePath = ttp;
                    TextureMapComboBox.Items.Add(modCB);
                }

                // Models
                if (itemPath.Contains(".mdl"))
                {
                    //esrinzou for Repair program crash when selecting [character/Body] item
                    //modCB.Name = $"{((IItemModel)selectedItem.Item).ModelInfo.ModelID} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    //esrinzou begin
                    if (((IItemModel)selectedItem.Item).ModelInfo == null)
                    {
                        modCB.Name = $"{((IItemModel)selectedItem.Item).Name} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    }
                    else
                    {
                        var modelId = ((IItemModel)selectedItem.Item).ModelInfo.PrimaryID;

                        if (selectedItem.Item.PrimaryCategory.Equals(XivStrings.Character))
                        {
                            var item = selectedItem.Item;

                            if (item.Name.Equals(XivStrings.Body))
                            {
                                modelId = int.Parse(
                                    itemPath.Substring(itemPath.IndexOf("/body", StringComparison.Ordinal) + 7, 4));
                            }
                            else if (item.Name.Equals(XivStrings.Hair))
                            {
                                modelId = int.Parse(
                                    itemPath.Substring(itemPath.IndexOf("/hair", StringComparison.Ordinal) + 7, 4));
                            }
                            else if (item.Name.Equals(XivStrings.Face))
                            {
                                modelId = int.Parse(
                                    itemPath.Substring(itemPath.IndexOf("/face", StringComparison.Ordinal) + 7, 4));
                            }
                            else if (item.Name.Equals(XivStrings.Tail))
                            {
                                modelId = int.Parse(
                                    itemPath.Substring(itemPath.IndexOf("/tail", StringComparison.Ordinal) + 7, 4));
                            }
                        }

                        modCB.Name = $"{modelId} ({Path.GetFileNameWithoutExtension(itemPath)})";
                    }
                    //esrinzou end
                    modCB.SelectedMod = modItem;
                    modCB.TexTypePath = null;

                    ModelTypeComboBox.Items.Add(modCB);
                }

                // Material
                if (itemPath.Contains(".mtrl"))
                {
                    var materialModCB = new ModComboBox
                    {
                        Name        = $"Material ({Path.GetFileNameWithoutExtension(itemPath)})",
                        SelectedMod = modItem,
                        TexTypePath = null
                    };

                    MaterialComboBox.Items.Add(materialModCB);
                    MaterialTabItem.IsEnabled = true;
                }
            }

            if (TextureMapComboBox.Items.Count > 0)
            {
                AddCurrentTextureButton.IsEnabled = true;
                GetCustomTextureButton.IsEnabled  = true;
                CustomTextureTextBox.IsEnabled    = true;
                AddCustomTextureButton.IsEnabled  = false;
                TextureMapComboBox.SelectedIndex  = 0;
                NoTextureModsLabel.Content        = string.Empty;
            }
            else
            {
                AddCurrentTextureButton.IsEnabled = false;
                GetCustomTextureButton.IsEnabled  = false;
                CustomTextureTextBox.IsEnabled    = false;
                AddCustomTextureButton.IsEnabled  = false;
                NoTextureModsLabel.Content        = UIStrings.No_Texture_Mods;
            }

            if (ModelTypeComboBox.Items.Count > 0)
            {
                AddCurrentModelButton.IsEnabled = true;
                AdvOptionsButton.IsEnabled      = true;
                ModelTypeComboBox.SelectedIndex = 0;
                NoModelModsLabel.Content        = string.Empty;
            }
            else
            {
                AddCurrentModelButton.IsEnabled = false;
                AdvOptionsButton.IsEnabled      = false;
                NoModelModsLabel.Content        = UIStrings.No_3D_Mods;
            }

            if (MaterialComboBox.Items.Count > 0)
            {
                AddCurrentMaterialButton.IsEnabled = true;
                MaterialComboBox.SelectedIndex     = 0;
                NoMaterialsModsLabel.Content       = string.Empty;
            }
            else
            {
                AddCurrentMaterialButton.IsEnabled = false;
                NoMaterialsModsLabel.Content       = UIStrings.No_Material_Mods;
            }

            SelectModGroup.IsEnabled = true;
        }
        /// <summary>
        /// Performs post-patch modlist corrections and validation, prompting user also to generate backups after a successful completion.
        /// </summary>
        /// <returns></returns>
        public async Task DoPostPatchCleanup()
        {
            FlexibleMessageBox.Show(_mainWindow.Win32Window, UIMessages.PatchDetectedMessage, "Post Patch Cleanup Starting", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
            MainWindow.MakeHighlander();

            var resetLumina = false;

            await _mainWindow.LockUi("Performing Post-Patch Maintenence", "This may take a few minutes if you have many mods installed.", this);

            var gi = XivCache.GameInfo;

            if (XivCache.GameInfo.UseLumina)
            {
                resetLumina = true;
                XivCache.SetGameInfo(gi.GameDirectory, gi.GameLanguage, gi.DxMode, false, false, gi.LuminaDirectory, gi.UseLumina);
            }

            var workerStatus = XivCache.CacheWorkerEnabled;

            if (workerStatus)
            {
                // Stop the cache worker if it's running.
                XivCache.CacheWorkerEnabled = false;
            }
            try
            {
                var modding = new Modding(_gameDirectory);
                var _index  = new Index(_gameDirectory);
                var _dat    = new Dat(_gameDirectory);

                var validTypes = new List <int>()
                {
                    2, 3, 4
                };

                // We have to do a few things here.
                // 1.  Save a list of what mods were enabled.
                // 2.  Go through and validate everything that says it is enabled actually is enabled, or mark it as disabled and update its original index offset if it is not.
                // 3.  Prompt the user for either a full disable and backup creation, or a restore to normal state (re-enable anything that was enabled before but is not now)
                var modList = modding.GetModList();

                Dictionary <XivDataFile, IndexFile> indexFiles = new Dictionary <XivDataFile, IndexFile>();


                // Cache our currently enabled stuff.
                List <Mod> enabledMods = modList.Mods.Where(x => x.enabled == true).ToList();
                var        toRemove    = new List <Mod>();

                foreach (var mod in modList.Mods)
                {
                    if (!String.IsNullOrEmpty(mod.fullPath))
                    {
                        var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                        if (!indexFiles.ContainsKey(df))
                        {
                            indexFiles[df] = await _index.GetIndexFile(df);
                        }

                        var index1Value       = indexFiles[df].Get8xDataOffset(mod.fullPath);
                        var index2Value       = indexFiles[df].Get8xDataOffsetIndex2(mod.fullPath);
                        var oldOriginalOffset = mod.data.originalOffset;
                        var modOffset         = mod.data.modOffset;


                        // In any event where an offset does not match either of our saved offsets, we must assume this is a new
                        // default file offset for post-patch.
                        if (index1Value != oldOriginalOffset && index1Value != modOffset && index1Value != 0)
                        {
                            // Index 1 value is our new base offset.
                            var type = _dat.GetFileType(index1Value, df);

                            // Make sure the file it's trying to point to is actually valid.
                            if (validTypes.Contains(type))
                            {
                                mod.data.originalOffset = index1Value;
                                mod.enabled             = false;
                            }
                            else
                            {
                                // Oh dear.  The new index is f****d.  Is the old Index Ok?
                                type = _dat.GetFileType(oldOriginalOffset, df);

                                if (validTypes.Contains(type) && oldOriginalOffset != 0)
                                {
                                    // Old index is fine, so keep using that.

                                    // But mark the index value as invalid, so that we stomp on the index value after this.
                                    index1Value = -1;
                                    mod.enabled = false;
                                }
                                else
                                {
                                    // Okay... Maybe the new Index2 Value?
                                    if (index2Value != 0)
                                    {
                                        type = _dat.GetFileType(index2Value, df);
                                        if (validTypes.Contains(type))
                                        {
                                            // Set the index 1 value to invalid so that the if later down the chain stomps the index1 value.
                                            index1Value = -1;

                                            mod.data.originalOffset = index2Value;
                                            mod.enabled             = false;
                                        }
                                        else
                                        {
                                            // We be f****d.
                                            throw new Exception("Unable to determine working original offset for file:" + mod.fullPath);
                                        }
                                    }
                                    else
                                    {
                                        // We be f****d.
                                        throw new Exception("Unable to determine working original offset for file:" + mod.fullPath);
                                    }
                                }
                            }
                        }
                        else if (index2Value != oldOriginalOffset && index2Value != modOffset && index2Value != 0)
                        {
                            // Our Index 1 was normal, but our Index 2 is changed to an unknown value.
                            // If the index 2 points to a valid file, we must assume that this new file
                            // is our new base data offset.

                            var type = _dat.GetFileType(index2Value, df);

                            if (validTypes.Contains(type) && index2Value != 0)
                            {
                                mod.data.originalOffset = index2Value;
                                mod.enabled             = false;
                            }
                            else
                            {
                                // Oh dear.  The new index is f****d.  Is the old Index Ok?
                                type = _dat.GetFileType(oldOriginalOffset, df);

                                if (validTypes.Contains(type) && oldOriginalOffset != 0)
                                {
                                    // Old index is fine, so keep using that, but set the index2 value to invalid to ensure we
                                    // stomp on the current broken index value.
                                    index2Value = -1;
                                }
                                else
                                {
                                    // We be f****d.
                                    throw new Exception("Unable to determine working original offset for file:" + mod.fullPath);
                                }
                            }
                        }

                        // Indexes don't match.  This can occur if SE adds something to index2 that didn't exist in index2 before.
                        if (index1Value != index2Value && index2Value != 0)
                        {
                            // We should never actually get to this state for file-addition mods.  If we do, uh.. I guess correct the indexes and yolo?
                            // ( Only way we get here is if SE added a new file at the same name as a file the user had created via modding, in which case, it's technically no longer a file addition mod )
                            indexFiles[df].SetDataOffset(mod.fullPath, mod.data.originalOffset);
                            index1Value = mod.data.originalOffset;
                            index2Value = mod.data.originalOffset;

                            mod.enabled = false;
                        }

                        // Set it to the corrected state.
                        if (index1Value == mod.data.modOffset)
                        {
                            mod.enabled = true;
                        }
                        else
                        {
                            mod.enabled = false;
                        }

                        // Perform a basic file type check on our results.
                        var fileType         = _dat.GetFileType(mod.data.modOffset, IOUtil.GetDataFileFromPath(mod.fullPath));
                        var originalFileType = _dat.GetFileType(mod.data.modOffset, IOUtil.GetDataFileFromPath(mod.fullPath));

                        if (!validTypes.Contains(fileType) || mod.data.modOffset == 0)
                        {
                            // Mod data is busted.  Fun.
                            toRemove.Add(mod);
                        }

                        if ((!validTypes.Contains(originalFileType)) || mod.data.originalOffset == 0)
                        {
                            if (mod.IsCustomFile())
                            {
                                // Okay, in this case this is recoverable as the mod is a custom addition anyways, so we can just delete it.
                                if (!toRemove.Contains(mod))
                                {
                                    toRemove.Add(mod);
                                }
                            }
                            else
                            {
                                // Update ended up with us unable to find a working offset.  Double fun.
                                throw new Exception("Unable to determine working offset for file:" + mod.fullPath);
                            }
                        }
                    }

                    // Okay, this mod is now represented in the modlist in it's actual post-patch index state.
                    var datNum = (int)((mod.data.modOffset / 8) & 0x0F) / 2;
                    var dat    = XivDataFiles.GetXivDataFile(mod.datFile);

                    var originalDats = await _dat.GetUnmoddedDatList(dat);

                    var datPath = $"{dat.GetDataFileName()}{Dat.DatExtension}{datNum}";

                    // Test for SE Dat file rollover.
                    if (originalDats.Contains(datPath))
                    {
                        // Shit.  This means that the dat file where this mod lived got eaten by SE.  We have to destroy the modlist entry at this point.
                        toRemove.Add(mod);
                    }
                }

                // Save any index changes we made.
                foreach (var dkv in indexFiles)
                {
                    await _index.SaveIndexFile(dkv.Value);
                }

                // The modlist is now saved in its current index-represented post patch state.
                modding.SaveModList(modList);

                // We now need to clear out any mods that are irreparably f****d, and clear out all of our
                // internal data files so we can rebuild them later.

                var internalFiles = modList.Mods.Where(x => x.IsInternal());
                toRemove.AddRange(internalFiles);

                if (toRemove.Count > 0)
                {
                    var removedString = "";

                    // Soft-Disable all metadata mods, since we're going to purge their internal file entries.
                    var metadata = modList.Mods.Where(x => x.fullPath.EndsWith(".meta") || x.fullPath.EndsWith(".rgsp"));
                    foreach (var mod in metadata)
                    {
                        var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                        await modding.ToggleModUnsafe(false, mod, true, false, indexFiles[df], modList);
                    }

                    foreach (var mod in toRemove)
                    {
                        if (mod.data.modOffset == 0 || mod.data.originalOffset == 0)
                        {
                            if (mod.data.originalOffset == 0 && mod.enabled)
                            {
                                // This is awkward.  We have a mod whose data got bashed, but has no valid original offset to restore.
                                // So the indexes here are f****d if we do, f****d if we don't.
                                throw new Exception("Patch-Broken file has no valid index to restore.  Clean Index Restoration required.");
                            }

                            modList.Mods.Remove(mod);
                            enabledMods.Remove(mod);
                            removedString += mod.fullPath + "\n";
                        }
                        else
                        {
                            if (mod.enabled)
                            {
                                var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                                await modding.ToggleModUnsafe(false, mod, true, false, indexFiles[df], modList);
                            }

                            modList.Mods.Remove(mod);

                            // Since we're deleting this entry entirely, we can't leave it in the other cached list either to get re-enabled later.
                            enabledMods.Remove(mod);

                            if (!mod.IsInternal())
                            {
                                removedString += mod.fullPath + "\n";
                            }
                        }
                    }

                    // Save the index files and modlist again now that we've removed all the invalid entries.
                    foreach (var dkv in indexFiles)
                    {
                        await _index.SaveIndexFile(dkv.Value);
                    }
                    modding.SaveModList(modList);

                    // Show the user a message if we purged any real files.
                    if (toRemove.Any(x => !String.IsNullOrEmpty(x.fullPath) && !x.IsInternal()))
                    {
                        var text = String.Format(UIMessages.PatchDestroyedFiles, removedString);

                        FlexibleMessageBox.Show(_mainWindow.Win32Window, text, "Destroyed Files Notification", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
                    }
                }

                // Always create clean index backups after this process is completed.

                _mainWindow.LockProgress.Report("Disabling Mods...");
                await modding.ToggleAllMods(false);

                _mainWindow.LockProgress.Report("Creating Index Backups...");
                var           pc = new ProblemChecker(_gameDirectory);
                DirectoryInfo backupDir;
                try
                {
                    Directory.CreateDirectory(Settings.Default.Backup_Directory);
                    backupDir = new DirectoryInfo(Settings.Default.Backup_Directory);
                }
                catch
                {
                    throw new Exception("Unable to create index backups.\nThe Index Backup directory is invalid or inaccessible: " + Settings.Default.Backup_Directory);
                }

                await pc.BackupIndexFiles(backupDir);

                // Now restore the modlist enable/disable state back to how the user had it before.
                _mainWindow.LockProgress.Report("Re-Enabling mods...");

                // Re-enable things.
                await modding.ToggleMods(true, enabledMods.Select(x => x.fullPath));

                FlexibleMessageBox.Show(_mainWindow.Win32Window, UIMessages.PostPatchComplete, "Post-Patch Process Complete", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
            }
            catch (Exception Ex)
            {
                // Show the user the error, then let them go about their business of fixing things.
                FlexibleMessageBox.Show(_mainWindow.Win32Window, String.Format(UIMessages.PostPatchError, Ex.Message), "Post-Patch Failure", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
            }
            finally
            {
                if (resetLumina)
                {
                    // Reset lumina mode back to on if we disabled it to perform update checks.
                    XivCache.SetGameInfo(gi.GameDirectory, gi.GameLanguage, gi.DxMode, true, false, gi.LuminaDirectory, true);
                }

                XivCache.CacheWorkerEnabled = workerStatus;
                await _mainWindow.UnlockUi(this);
            }
        }
        private async Task CreateModpack()
        {
            TTMP texToolsModPack = new TTMP(new DirectoryInfo(Settings.Default.ModPack_Directory), XivStrings.TexTools);
            var  index           = new Index(XivCache.GameInfo.GameDirectory);
            var  dat             = new Dat(XivCache.GameInfo.GameDirectory);
            var  modding         = new Modding(XivCache.GameInfo.GameDirectory);
            var  ModList         = modding.GetModList();

            SimpleModPackData simpleModPackData = new SimpleModPackData
            {
                Name              = ViewModel.Name,
                Author            = ViewModel.Author,
                Version           = ViewModel.Version,
                Description       = ViewModel.Description,
                Url               = ViewModel.Url,
                SimpleModDataList = new List <SimpleModData>()
            };

            foreach (var entry in ViewModel.Entries)
            {
                foreach (var file in entry.AllFiles)
                {
                    var offset = await index.GetDataOffset(file);

                    var dataFile       = IOUtil.GetDataFileFromPath(file);
                    var compressedSize = await dat.GetCompressedFileSize(offset, dataFile);

                    var modEntry = ModList.Mods.FirstOrDefault(x => x.fullPath == file);
                    var modded   = modEntry != null && modEntry.enabled == true;


                    SimpleModData simpleData = new SimpleModData
                    {
                        Name      = entry.Item.Name,
                        Category  = entry.Item.SecondaryCategory,
                        FullPath  = file,
                        ModOffset = offset,
                        ModSize   = compressedSize,
                        IsDefault = !modded,
                        DatFile   = dataFile.GetDataFileName()
                    };

                    simpleModPackData.SimpleModDataList.Add(simpleData);
                }
            }


            string modPackPath = Path.Combine(Properties.Settings.Default.ModPack_Directory, $"{simpleModPackData.Name}.ttmp2");

            if (File.Exists(modPackPath))
            {
                DialogResult overwriteDialogResult = FlexibleMessageBox.Show(new Wpf32Window(this), UIMessages.ModPackOverwriteMessage,
                                                                             UIMessages.OverwriteTitle, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                if (overwriteDialogResult == System.Windows.Forms.DialogResult.Cancel)
                {
                    return;
                }
            }

            try
            {
                await LockUi(UIStrings.Creating_Modpack, null, null);

                Progress <(int current, int total, string message)> progressIndicator = new Progress <(int current, int total, string message)>(ReportProgress);
                await texToolsModPack.CreateSimpleModPack(simpleModPackData, XivCache.GameInfo.GameDirectory, progressIndicator, true);
                await UnlockUi(this);

                DialogResult = true;
            }
            catch (Exception ex)
            {
                FlexibleMessageBox.Show(new Wpf32Window(this), "An Error occured while creating the modpack.\n\n" + ex.Message,
                                        "Modpack Creation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                await UnlockUi(this);
            }
        }