/// <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; }
/// <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); } }); }
/// <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); } }