private async Task <HashSet <string> > GetChildrenRecursive(string file)
        {
            var files = new HashSet <string>();

            files.Add(file);

            var baseChildren = await XivCache.GetChildFiles(file);

            if (baseChildren == null || baseChildren.Count == 0)
            {
                // No children, just us.
                return(files);
            }
            else
            {
                // We have child files.
                foreach (var child in baseChildren)
                {
                    // Recursively get their children.
                    var children = await GetChildrenRecursive(child);

                    foreach (var subchild in children)
                    {
                        // Add the results to the list.
                        files.Add(subchild);
                    }
                }
            }
            return(files);
        }
Example #2
0
        /// <summary>
        /// Deletes a file descriptor/stub from the Index files.
        /// </summary>
        /// <param name="fullPath">Full internal file path to the file that should be deleted.</param>
        /// <param name="dataFile">Which data file to use</param>
        /// <returns></returns>
        public async Task <bool> DeleteFileDescriptor(string fullPath, XivDataFile dataFile, bool updateCache = true)
        {
            await UpdateDataOffset(0, fullPath, false, true);

            // This is a metadata entry being deleted, we'll need to restore the metadata entries back to default.
            if (fullPath.EndsWith(".meta"))
            {
                var root = await XivCache.GetFirstRoot(fullPath);

                await ItemMetadata.RestoreDefaultMetadata(root);
            }

            if (fullPath.EndsWith(".rgsp"))
            {
                await CMP.RestoreDefaultScaling(fullPath);
            }

            if (updateCache)
            {
                // Queue us for updating, *after* updating the associated metadata files.
                XivCache.QueueDependencyUpdate(fullPath);
            }

            return(true);
        }
Example #3
0
        private async Task AddWithChildren(string file, IItem item, byte[] rawData = null)
        {
            var children = new HashSet<string>();
            if (Path.GetExtension(file) == ".meta")
            {
                // If we're the root file, use the proper get all function
                // which will throw in the AVFX/ATEX stuff as well.
                var root = await XivCache.GetFirstRoot(file);
                if(root != null)
                {
                    var files = await root.GetAllFiles();
                    children = files.ToHashSet();
                }
            } else
            {
                children = await XivCache.GetChildrenRecursive(file);
            }


            foreach (var child in children)
            {
                var fe = new FileEntry() { Name = MakeFriendlyFileName(child), Path = child };
                if (child == file && rawData != null)
                {
                    await AddFile(fe, item, rawData);
                } else 
                {
                    await AddFile(fe, item);
                }
            }
        }
Example #4
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);
                }
            });
        }
        public void UpdateDependencyQueueCount(object sender, System.Timers.ElapsedEventArgs e)
        {
            var count = XivCache.GetDependencyQueueLength();

            if (count > 0)
            {
                _mainWindow.ShowStatusMessage("Queue Length: " + count + "");
            }
        }
Example #6
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);
                }
            });
        }
Example #7
0
        private void TreeRefreshRequested(object sender, EventArgs e)
        {
            IProgress <(int current, string category)> progress = new Progress <(int current, string category)>((prog) =>
            {
                if (prog.category == "Done")
                {
                    ProgressBarVisible   = Visibility.Collapsed;
                    ProgressLabelVisible = Visibility.Collapsed;
                }
                else
                {
                    ProgressValue = prog.current;
                    ProgressLabel = $"Loading {prog.category} List";
                }
            });

            _mainWindow.ItemSearchTextBox.IsEnabled = false;

            var gameDirectory = new DirectoryInfo(Settings.Default.FFXIV_Directory);
            var lang          = XivLanguages.GetXivLanguage(Settings.Default.Application_Language);

            try
            {
                // Settings are valid, application is updated, initialize the
                // Cache once so it can test if it needs to be updated as well.
                var _cache = new XivCache(gameDirectory, lang);
                FillTree(progress).GetAwaiter().OnCompleted(OnTreeRefreshCompleted);
            }
            catch (Exception ex)
            {
                // Revert to English when there were errors while loading the item tree/cache
                // and the game language was set to Chinese or Korean (they have separate clients)
                if (lang == XivLanguage.Chinese || lang == XivLanguage.Korean)
                {
                    if (FlexibleMessageBox.Show(UIMessages.LanguageError,
                                                UIMessages.LanguageErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error) ==
                        DialogResult.OK)
                    {
                        Properties.Settings.Default.Application_Language = "en";
                        Properties.Settings.Default.Save();

                        System.Windows.Forms.Application.Restart();
                        System.Windows.Application.Current.Shutdown();
                    }
                }
                else
                {
                    // Make this error not totally silent at least.
                    FlexibleMessageBox.Show("An error occurred while trying to populate the item list.",
                                            "Item list Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        private async Task CheckCache()
        {
            var gameDirectory = new DirectoryInfo(Settings.Default.FFXIV_Directory);
            var lang          = XivLanguages.GetXivLanguage(Settings.Default.Application_Language);
            await Task.Run(async() =>
            {
                var _cache = new XivCache(gameDirectory, lang);
                _cache.RebuildCache();
            });

            AddText("\tCache Rebuilt Successfully.", textColor);
            AddText("\t\u2714\n", "Green");
        }
Example #9
0
        /// <summary>
        /// Retrieves the base racial or body skeleton for a given model file, parsing it from the base
        /// game files to generate it, if necessary.
        /// </summary>
        /// <param name="fullMdlPath"></param>
        /// <returns></returns>
        public static async Task <string> GetBaseSkeletonFile(string fullMdlPath)
        {
            var root = await XivCache.GetFirstRoot(fullMdlPath);

            var race = XivRace.All_Races;

            if (root.Info.PrimaryType == XivItemType.human || root.Info.PrimaryType == XivItemType.equipment || root.Info.PrimaryType == XivItemType.accessory)
            {
                race = XivRaces.GetXivRace(fullMdlPath.Substring(1, 4));
            }

            return(await GetBaseSkeletonFile(root.Info, race));
        }
Example #10
0
 bool CheckGameDirectory()
 {
     if (MainClass._indexDirectory == null)
     {
         string configGameDirectory = config.ReadConfig("GameDirectory");
         MainClass._gameDirectory  = new DirectoryInfo(Path.Combine(configGameDirectory, "game"));
         MainClass._indexDirectory = new DirectoryInfo(Path.Combine(configGameDirectory, "game", "sqpack", "ffxiv"));
     }
     if (MainClass._indexDirectory == null || !validation.ValidateDirectory(MainClass._indexDirectory, "GameDirectory"))
     {
         main.PrintMessage("Invalid game directory", 2);
         return(false);
     }
     XivCache.SetGameInfo(MainClass._indexDirectory, XivLanguage.English);
     return(true);
 }
Example #11
0
        /// <summary>
        /// Retrieves the Ex skeleton file for a given model file, parsing it from the base
        /// game files to generate it, if necessary.
        /// </summary>
        /// <param name="fullMdlPath"></param>
        /// <returns></returns>
        public static async Task <string> GetExtraSkeletonFile(string fullMdlPath)
        {
            var root = await XivCache.GetFirstRoot(fullMdlPath);

            var estType = Est.GetEstType(root);

            if (estType == Est.EstType.Invalid)
            {
                return(null);
            }

            // This is a hair/hat/face/body model at this point so this is a safe pull.
            var race = XivRaces.GetXivRace(fullMdlPath.Substring(1, 4));

            return(await GetExtraSkeletonFile(root.Info, race));
        }
        private static async Task RunInternal(string inputFilePath)
        {
            try
            {
                await MainWin.LockUi("Exporting Anamnesis Character", "....", LockObj);

                MainWin.LockProgress.Report("Loading chara file...");

                name = Path.GetFileNameWithoutExtension(inputFilePath);

                // read chara file
                string json = File.ReadAllText(inputFilePath);
                character = JsonConvert.DeserializeObject <CharacterFile>(json);
                XivRace race = character.GetXivRace();

                // load all items we can
                MainWin.LockProgress.Report("Loading items...");
                allItems = await XivCache.GetFullItemList();

                // Body
                await Export("Face", GetFaceModel(race, character.Head, character.Eyebrows, character.Eyes, character.Nose, character.Jaw, character.Mouth), race);
                await Export("EarsTail", GetEarsTailModel(race, character.TailEarsType), race);
                await Export("Hair", GetHairModel(race, character.Hair), race);
                await Export("Head", GetItemModel(character.HeadGear, "Head"), race);
                await Export("Body", GetItemModel(character.Body, "Body"), race);
                await Export("Hands", GetItemModel(character.Hands, "Hands"), race);
                await Export("Legs", GetItemModel(character.Legs, "Legs"), race);
                await Export("Feet", GetItemModel(character.Feet, "Feet"), race);
                await Export("Earring", GetItemModel(character.Ears, "Earring"), race);
                await Export("Neck", GetItemModel(character.Neck, "Neck"), race);
                await Export("Wrists", GetItemModel(character.Wrists, "Wrists"), race);
                await Export("L Ring", GetItemModel(character.LeftRing, "Rings"), race);
                await Export("R ring", GetItemModel(character.RightRing, "Rings"), race);
                await Export("Weapon Main", GetWeaponModel(character.MainHand, true), race);
                await Export("Weapon Off", GetWeaponModel(character.OffHand, false), race);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error running export");
            }
            finally
            {
                await MainWin.UnlockUi(LockObj);
            }
        }
        private async Task AsyncInit()
        {
            // Get this model's root.
            _root = await XivCache.GetFirstRoot(_oldModel.Source);

            if (_root != null && _root.Info.PrimaryType != XivItemType.indoor && _root.Info.PrimaryType != XivItemType.outdoor)
            {
                // Get all the materials in this root, and add them to the selectable list.

                var materials = await _root.GetMaterialFiles();

                foreach (var m in materials)
                {
                    var mName = Path.GetFileName(m);
                    mName = "/" + mName;
                    RootMaterials.Add(mName);
                }

                RootMaterials = RootMaterials.OrderBy(x => x).ToHashSet();
            }

            UpdateModelSizeWarning();
            UpdateMaterialsList();

            _view.MeshNumberBox.SelectionChanged       += MeshNumberBox_SelectionChanged;
            _view.PartNumberBox.SelectionChanged       += PartNumberBox_SelectionChanged;
            _view.MaterialSelectorBox.SelectionChanged += MaterialSelectorBox_SelectionChanged;
            _view.MaterialPathTextBox.KeyDown          += MaterialPathTextBox_KeyDown;
            _view.MaterialPathTextBox.LostFocus        += MaterialPathTextBox_LostFocus;

            _view.ShapesListBox.SelectionChanged     += ShapesListBox_SelectionChanged;
            _view.AttributesListBox.SelectionChanged += AttributesListBox_SelectionChanged;

            _view.RemoveShapeButton.Click     += RemoveShapeButton_Click;
            _view.RemoveAttributeButton.Click += RemoveAttributeButton_Click;

            _view.AddAttributeBox.SelectionChanged += AddAttributeBox_SelectionChanged;
            _view.AddAttributeTextBox.KeyDown      += AddAttributeTextBox_KeyDown;

            _view.ScaleComboBox.SelectionChanged += ScaleComboBox_SelectionChanged;

            _view.MeshNumberBox.SelectedIndex = 0;
        }
Example #14
0
        static void Main(string[] args)
        {
            ReadConfig();

            var lang = XivLanguage.English;

            _gameDir = new DirectoryInfo(_gamePath);
            XivCache.SetGameInfo(_gameDir, XivLanguage.English);

            _ttObj      = new Obj(_gameDir);
            _gear       = new Gear(_gameDir, lang);
            _companions = new Companions(_gameDir, lang);
            _housing    = new Housing(_gameDir, lang);

            BatchExport();

            Console.WriteLine("Done");
            Console.ReadKey();
        }
Example #15
0
        private async Task LoadFiles()
        {
            var parentFiles = _entry.MainFiles;
            var files       = new SortedSet <string>();

            if (_entry.Level == XivDependencyLevel.Root)
            {
                var root = await XivCache.GetFirstRoot(_entry.MainFiles[0]);

                files = await root.GetAllFiles();
            }
            else
            {
                foreach (var file in parentFiles)
                {
                    var children = await XivCache.GetChildrenRecursive(file);

                    foreach (var child in children)
                    {
                        files.Add(child);
                    }
                }
            }


            _entry.AllFiles.Clear();
            foreach (var file in files)
            {
                var exists = await AddFile(file);

                if (exists)
                {
                    _entry.AllFiles.Add(file);
                }
            }

            UpdateCounts();

            ConfirmButton.IsEnabled = true;
        }
Example #16
0
 bool CheckGameDirectory()
 {
     if (MainClass._indexDirectory == null)
     {
         string configGameDirectory = config.ReadConfig("GameDirectory");
         MainClass._gameDirectory  = new DirectoryInfo(Path.Combine(configGameDirectory, "game"));
         MainClass._indexDirectory = new DirectoryInfo(Path.Combine(configGameDirectory, "game", "sqpack", "ffxiv"));
     }
     if (MainClass._indexDirectory == null || !validation.ValidateDirectory(MainClass._indexDirectory, "GameDirectory"))
     {
         main.PrintMessage("Invalid game directory", 2);
         return(false);
     }
     if (!validation.ValidateCache())
     {
         File.Delete(Path.Combine(MainClass._gameDirectory.FullName, "mod_cache.db"));
         File.Delete(Path.Combine(MainClass._gameDirectory.FullName, "item_sets.db"));
     }
     XivCache.SetGameInfo(MainClass._indexDirectory, XivLanguage.English);
     XivCache.CacheWorkerEnabled = false;
     return(true);
 }
        /// <summary>
        /// Initializes the Cache and loads the item tree for the first time when done.
        /// </summary>
        /// <returns></returns>
        private async Task InitializeCache()
        {
            var gameDir = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory);
            var lang    = XivLanguages.GetXivLanguage(Properties.Settings.Default.Application_Language);

            await LockUi(UIStrings.Updating_Cache, UIStrings.Updating_Cache_Message, this);

            // Kick this in a new thread because the cache call will lock up the one it's on if it has to do a rebuild.
            await Task.Run(async() =>
            {
                bool cacheOK = true;
                try
                {
                    // If the cache needs to be rebuilt, this will synchronously block until it is done.
                    XivCache.SetGameInfo(gameDir, lang);
                } catch (Exception ex)
                {
                    cacheOK = false;
                    FlexibleMessageBox.Show("An error occurred while attempting to rebuild the cache.\n" + ex.Message, "Cache Rebuild Error.", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                }

                await Dispatcher.Invoke(async() =>
                {
                    await UnlockUi();

                    if (cacheOK)
                    {
                        await RefreshTree();
                    }

                    if (InitialLoadComplete != null)
                    {
                        InitialLoadComplete.Invoke(this, null);
                    }
                });
            });
        }
Example #18
0
        private async void CopyButton_Click(object sender, RoutedEventArgs e)
        {
            var to   = ToBox.Text;
            var from = FromBox.Text;

            try
            {
                if (string.IsNullOrWhiteSpace(to) || string.IsNullOrWhiteSpace(from))
                {
                    return;
                }
                to   = to.Trim().ToLower();
                from = from.Trim().ToLower();

                if (!to.EndsWith(".mdl") || !from.EndsWith(".mdl"))
                {
                    return;
                }

                var toRoot = await XivCache.GetFirstRoot(to);

                var fromRoot = await XivCache.GetFirstRoot(from);

                var df = IOUtil.GetDataFileFromPath(to);

                var _mdl = new Mdl(XivCache.GameInfo.GameDirectory, df);

                await _mdl.CopyModel(from, to, XivStrings.TexTools, true);

                FlexibleMessageBox.Show("Model Copied Successfully.", "Model Copy Confirmation", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
                Close();
            }
            catch (Exception ex)
            {
                FlexibleMessageBox.Show("Model Copied Failed.\n\nError: " + ex.Message, "Model Copy Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
            }
        }
Example #19
0
 /// <summary>
 /// Gets the list of all Housing Items
 /// </summary>
 /// <returns>A list of XivFurniture objects containing housing items</returns>
 public async Task <List <XivFurniture> > GetFurnitureList(string substring = null)
 {
     return(await XivCache.GetCachedFurnitureList(substring));
 }
Example #20
0
        public async Task <List <XivUi> > GetUIList()
        {
            var cache = new XivCache(_gameDirectory, _xivLanguage);

            return(await cache.GetCachedUiList());
        }
        /// <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);
            }
        }
        /// <summary>
        /// Builds the set tree from XivCache.GetAllRoots().
        /// </summary>
        private void BuildSetTree()
        {
            // First we must generate all the dependency root nodes.
            var roots               = XivCache.GetAllRootsDictionary();
            var primaryTypeGroups   = new Dictionary <XivItemType, ItemTreeElement>();
            var primaryIdGroups     = new Dictionary <XivItemType, Dictionary <int, ItemTreeElement> >();
            var secondaryTypeGroups = new Dictionary <XivItemType, Dictionary <int, Dictionary <XivItemType, ItemTreeElement> > >();
            var secondaryIdGroups   = new Dictionary <XivItemType, Dictionary <int, Dictionary <XivItemType, Dictionary <int, ItemTreeElement> > > >();

            // This giant for loop monstrosity builds the actual root nodes based on the dictionaries returned by XivCache.GetAllRoots()
            foreach (var kvPrimaryType in roots)
            {
                var primaryType = kvPrimaryType.Key;

                // Create the new node.
                primaryTypeGroups.Add(primaryType, new ItemTreeElement(null, null, XivItemTypes.NiceNames[primaryType], true));

                // Add us to parent.
                SetElements.Add(primaryTypeGroups[primaryType]);

                // Ensure the other lists have our primary type reference.
                primaryIdGroups.Add(primaryType, new Dictionary <int, ItemTreeElement>());
                secondaryTypeGroups.Add(primaryType, new Dictionary <int, Dictionary <XivItemType, ItemTreeElement> >());
                secondaryIdGroups.Add(primaryType, new Dictionary <int, Dictionary <XivItemType, Dictionary <int, ItemTreeElement> > >());

                foreach (var kvPrimaryId in kvPrimaryType.Value)
                {
                    var primaryId = kvPrimaryId.Key;

                    // Create the new node.
                    primaryIdGroups[primaryType].Add(primaryId, new ItemTreeElement(null, primaryTypeGroups[primaryType], XivItemTypes.GetSystemPrefix(primaryType) + primaryId.ToString().PadLeft(4, '0'), true));

                    // Add us to parent.
                    primaryTypeGroups[primaryType].Children.Add(primaryIdGroups[primaryType][primaryId]);

                    // Ensure the other lists have our primary id reference.
                    secondaryTypeGroups[primaryType].Add(primaryId, new Dictionary <XivItemType, ItemTreeElement>());
                    secondaryIdGroups[primaryType].Add(primaryId, new Dictionary <XivItemType, Dictionary <int, ItemTreeElement> >());

                    foreach (var kvSecondaryType in kvPrimaryId.Value)
                    {
                        var secondaryType = kvSecondaryType.Key;

                        if (secondaryType != XivItemType.none)
                        {
                            // Create the new node.
                            secondaryTypeGroups[primaryType][primaryId].Add(secondaryType, new ItemTreeElement(null, primaryIdGroups[primaryType][primaryId], XivItemTypes.NiceNames[secondaryType], true));

                            // Add us to parent.
                            primaryIdGroups[primaryType][primaryId].Children.Add(secondaryTypeGroups[primaryType][primaryId][secondaryType]);

                            // Ensure the other lists have our secondary type reference.
                            secondaryIdGroups[primaryType][primaryId].Add(secondaryType, new Dictionary <int, ItemTreeElement>());
                        }

                        foreach (var kvSecondaryId in kvSecondaryType.Value)
                        {
                            var secondaryId = kvSecondaryId.Key;

                            if (secondaryType != XivItemType.none)
                            {
                                // Create the new node.
                                secondaryIdGroups[primaryType][primaryId][secondaryType].Add(secondaryId, new ItemTreeElement(null, secondaryTypeGroups[primaryType][primaryId][secondaryType], XivItemTypes.GetSystemPrefix(secondaryType) + secondaryId.ToString().PadLeft(4, '0'), true));

                                // Add us to parent.
                                secondaryTypeGroups[primaryType][primaryId][secondaryType].Children.Add(secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId]);
                            }

                            foreach (var kvSlot in kvSecondaryId.Value)
                            {
                                var root = kvSlot.Value;


                                var slotName = Mdl.SlotAbbreviationDictionary.FirstOrDefault(x => x.Value == root.Slot).Key;

                                if (secondaryType != XivItemType.none)
                                {
                                    // This root has no slots, just list the parent as the root element.
                                    if (String.IsNullOrWhiteSpace(slotName))
                                    {
                                        DependencyRootNodes.Add(root.ToString(), secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId]);
                                        break;
                                    }

                                    // Create the new node.
                                    var elem = new ItemTreeElement(null, secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId], slotName);

                                    // Add us to parent.
                                    secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId].Children.Add(elem);

                                    // Save us to the primary listing so the items can list themselves under us.
                                    DependencyRootNodes.Add(root.ToString(), elem);
                                }
                                else
                                {
                                    // This root has no slots, just list the parent as the root element.
                                    if (String.IsNullOrWhiteSpace(slotName))
                                    {
                                        DependencyRootNodes.Add(root.ToString(), primaryIdGroups[primaryType][primaryId]);
                                        break;
                                    }

                                    // Create the new node.
                                    var elem = new ItemTreeElement(null, primaryIdGroups[primaryType][primaryId], slotName);

                                    // Add us to parent.
                                    primaryIdGroups[primaryType][primaryId].Children.Add(elem);

                                    // Save us to the primary listing so the items can list themselves under us.
                                    DependencyRootNodes.Add(root.ToString(), elem);
                                }
                            }
                        }
                    }
                }
            }
        }
        public Task PerformStartOver(DirectoryInfo backupsDirectory, IProgress <string> progress = null, XivLanguage language = XivLanguage.None)
        {
            return(Task.Run(async() =>
            {
                progress?.Report("Deleting mods...");

                var modding = new Modding(_gameDirectory);
                var backupsRestored = false;

                try
                {
                    // Try to restore the index entries to their original values by deleting any files added by TexTools
                    // and setting mods to disabled
                    await modding.DeleteAllFilesAddedByTexTools();
                    await modding.ToggleAllMods(false);
                    progress?.Report("Restoring index file backups...");
                }
                catch
                {
                    // If an exception occurred due to a corrupted modlist which couldn't be deserealized restore the backup index
                    // files by force
                    backupsRestored = await RestoreBackups(backupsDirectory);

                    if (!backupsRestored)
                    {
                        throw new Exception("Start Over Failed: Index backups missing/outdated.");
                    }
                }
                finally
                {
                    // If no exception occured, restore the backups anyway just to be safe but don't throw an exception if it fails
                    // due to outdated or missing backups since setting back the original index values should be enough hopefully
                    if (!backupsRestored)
                    {
                        backupsRestored = await RestoreBackups(backupsDirectory);

                        // If backups were not restored that means they were missing/outdated so try to make new backups now
                        if (!backupsRestored)
                        {
                            try
                            {
                                await BackupIndexFiles(backupsDirectory);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Start Over Failed: Failed to update outdated backups.\n\n" + ex.Message);
                            }
                        }
                    }

                    progress?.Report("Deleting modded dat files...");

                    var dat = new Dat(_gameDirectory);

                    // Delete modded dat files
                    foreach (var xivDataFile in (XivDataFile[])Enum.GetValues(typeof(XivDataFile)))
                    {
                        var datFiles = await dat.GetModdedDatList(xivDataFile);

                        foreach (var datFile in datFiles)
                        {
                            File.Delete(datFile);
                        }

                        if (datFiles.Count > 0)
                        {
                            await RepairIndexDatCounts(xivDataFile);
                        }
                    }

                    progress?.Report("Cleaning up mod list...");

                    var modListDirectory = new DirectoryInfo(Path.Combine(_gameDirectory.Parent.Parent.FullName, XivStrings.ModlistFilePath));

                    // Delete mod list
                    File.Delete(modListDirectory.FullName);

                    modding.CreateModlist();

                    progress?.Report("Rebuilding Cache...");

                    await Task.Run(async() =>
                    {
                        var _cache = new XivCache(_gameDirectory, language);
                        _cache.RebuildCache();
                    });
                }
            }));
        }
Example #24
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...";
            }
        }
Example #25
0
        public Task PerformStartOver(DirectoryInfo backupsDirectory, IProgress <string> progress = null, XivLanguage language = XivLanguage.None)
        {
            return(Task.Run(async() =>
            {
                var modding = new Modding(_gameDirectory);
                var backupsRestored = false;

                // Stop the cache worker since we're blowing up the entire index file and db anyways.
                // The cache rebuild will start it up again after the cache is rebuilt.
                XivCache.CacheWorkerEnabled = false;

                try
                {
                    // Try restoring the indexes FIRST.
                    backupsRestored = await RestoreBackups(backupsDirectory);
                    progress?.Report("Restoring index file backups...");

                    if (!backupsRestored)
                    {
                        throw new Exception("Start Over Failed: Index backups missing/outdated.");
                    }
                }
                catch (Exception ex)
                {
                    try
                    {
                        // If the index restore failed, try just disabling.
                        await modding.DeleteAllFilesAddedByTexTools();
                        await modding.ToggleAllMods(false);
                        progress?.Report("Index restore failed, attempting to delete all mods instead...");
                    } catch
                    {
                        throw new Exception("Start Over Failed: Index Backups Invalid and Unable to Disable all mods.");
                    }
                }
                finally
                {
                    progress?.Report("Deleting modded dat files...");

                    var dat = new Dat(_gameDirectory);

                    // Delete modded dat files
                    foreach (var xivDataFile in (XivDataFile[])Enum.GetValues(typeof(XivDataFile)))
                    {
                        var datFiles = await dat.GetModdedDatList(xivDataFile);

                        foreach (var datFile in datFiles)
                        {
                            File.Delete(datFile);
                        }

                        if (datFiles.Count > 0)
                        {
                            await RepairIndexDatCounts(xivDataFile);
                        }
                    }

                    progress?.Report("Cleaning up mod list...");

                    var modListDirectory = new DirectoryInfo(Path.Combine(_gameDirectory.Parent.Parent.FullName, XivStrings.ModlistFilePath));

                    // Delete mod list
                    File.Delete(modListDirectory.FullName);

                    modding.CreateModlist();

                    progress?.Report("Rebuilding Cache...");

                    await Task.Run(async() =>
                    {
                        XivCache.RebuildCache();
                    });
                }
            }));
        }
Example #26
0
        /// <summary>
        /// Creates texture data ready to be imported into the DATs from an external file.
        /// If format is not specified, either the incoming file's DDS format is used (DDS files),
        /// or the existing internal file's DDS format is used.
        /// </summary>
        /// <param name="internalPath"></param>
        /// <param name="externalPath"></param>
        /// <param name="texFormat"></param>
        /// <returns></returns>
        public async Task <byte[]> MakeTexData(string internalPath, string externalPath, XivTexFormat texFormat = XivTexFormat.INVALID)
        {
            // Ensure file exists.
            if (!File.Exists(externalPath))
            {
                throw new IOException($"Could not find file: {externalPath}");
            }

            var root = await XivCache.GetFirstRoot(internalPath);

            bool isDds = Path.GetExtension(externalPath).ToLower() == ".dds";

            var ddsContainer = new DDSContainer();

            try
            {
                // If no format was specified...
                if (texFormat == XivTexFormat.INVALID)
                {
                    if (isDds)
                    {
                        // If we're importing a DDS file, get the format from the incoming DDS file
                        using (var fs = new FileStream(externalPath, FileMode.Open))
                        {
                            using (var sr = new BinaryReader(fs))
                            {
                                texFormat = GetDDSTexFormat(sr);
                            }
                        }
                    }
                    else
                    {
                        // Otherwise use the current internal format.
                        var xivt = await _dat.GetType4Data(internalPath, false);

                        texFormat = xivt.TextureFormat;
                    }
                }

                // Check if the texture being imported has been imported before
                CompressionFormat compressionFormat = CompressionFormat.BGRA;

                switch (texFormat)
                {
                case XivTexFormat.DXT1:
                    compressionFormat = CompressionFormat.BC1a;
                    break;

                case XivTexFormat.DXT5:
                    compressionFormat = CompressionFormat.BC3;
                    break;

                case XivTexFormat.A8R8G8B8:
                    compressionFormat = CompressionFormat.BGRA;
                    break;

                default:
                    if (!isDds)
                    {
                        throw new Exception($"Format {texFormat} is not currently supported for BMP import\n\nPlease use the DDS import option instead.");
                    }
                    break;
                }

                if (!isDds)
                {
                    using (var surface = Surface.LoadFromFile(externalPath))
                    {
                        if (surface == null)
                        {
                            throw new FormatException($"Unsupported texture format");
                        }

                        surface.FlipVertically();

                        var maxMipCount = 1;
                        if (root != null)
                        {
                            // For things that have real roots (things that have actual models/aren't UI textures), we always want mipMaps, even if the existing texture only has one.
                            // (Ex. The Default Mat-Add textures)
                            maxMipCount = -1;
                        }

                        using (var compressor = new Compressor())
                        {
                            // UI/Paintings only have a single mipmap and will crash if more are generated, for everything else generate max levels
                            compressor.Input.SetMipmapGeneration(true, maxMipCount);
                            compressor.Input.SetData(surface);
                            compressor.Compression.Format = compressionFormat;
                            compressor.Compression.SetBGRAPixelFormat();

                            compressor.Process(out ddsContainer);
                        }
                    }
                }

                // If we're not a DDS, write the DDS to file temporarily.
                var ddsFilePath = externalPath;
                if (!isDds)
                {
                    var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".dds");
                    ddsContainer.Write(tempFile, DDSFlags.None);
                    ddsFilePath = tempFile;
                }

                using (var br = new BinaryReader(File.OpenRead(ddsFilePath)))
                {
                    br.BaseStream.Seek(12, SeekOrigin.Begin);

                    var newHeight = br.ReadInt32();
                    var newWidth  = br.ReadInt32();
                    br.ReadBytes(8);
                    var newMipCount = br.ReadInt32();

                    if (newHeight % 2 != 0 || newWidth % 2 != 0)
                    {
                        throw new Exception("Resolution must be a multiple of 2");
                    }

                    br.BaseStream.Seek(80, SeekOrigin.Begin);

                    var textureFlags = br.ReadInt32();
                    var texType      = br.ReadInt32();

                    var uncompressedLength = (int)new FileInfo(ddsFilePath).Length - 128;
                    var newTex             = new List <byte>();

                    if (!internalPath.Contains(".atex"))
                    {
                        var DDSInfo = await DDS.ReadDDS(br, texFormat, newWidth, newHeight, newMipCount);

                        newTex.AddRange(_dat.MakeType4DatHeader(texFormat, DDSInfo.mipPartOffsets, DDSInfo.mipPartCounts, (int)uncompressedLength, newMipCount, newWidth, newHeight));
                        newTex.AddRange(MakeTextureInfoHeader(texFormat, newWidth, newHeight, newMipCount));
                        newTex.AddRange(DDSInfo.compressedDDS);

                        return(newTex.ToArray());
                    }
                    else
                    {
                        br.BaseStream.Seek(128, SeekOrigin.Begin);
                        newTex.AddRange(MakeTextureInfoHeader(texFormat, newWidth, newHeight, newMipCount));
                        newTex.AddRange(br.ReadBytes((int)uncompressedLength));
                        var data = await _dat.CreateType2Data(newTex.ToArray());

                        return(data);
                    }
                }
            }
            finally
            {
                ddsContainer.Dispose();
            }
        }
Example #27
0
        private async void AnyTextChanged(object sender, TextChangedEventArgs e)
        {
            var to   = ToBox.Text;
            var from = FromBox.Text;

            if (string.IsNullOrWhiteSpace(to) || string.IsNullOrWhiteSpace(from))
            {
                CopyButton.IsEnabled = false;
                return;
            }

            to   = to.Trim().ToLower();
            from = from.Trim().ToLower();

            if (!to.EndsWith(".mdl") || !from.EndsWith(".mdl"))
            {
                MaterialCopyNotice.Text       = "--";
                MaterialCopyNotice.Foreground = Brushes.Black;

                RaceChangeNotice.Text       = "--";
                RaceChangeNotice.Foreground = Brushes.Black;

                CopyButton.IsEnabled = false;
                return;
            }

            try
            {
                var df  = IOUtil.GetDataFileFromPath(to);
                var df2 = IOUtil.GetDataFileFromPath(from);

                if (df != df2)
                {
                    MaterialCopyNotice.Text       = "Source and target files must exist within the same data file.";
                    MaterialCopyNotice.Foreground = Brushes.Red;

                    RaceChangeNotice.Text       = "--";
                    RaceChangeNotice.Foreground = Brushes.Black;

                    CopyButton.IsEnabled = false;
                    return;
                }
            } catch
            {
                MaterialCopyNotice.Text       = "At least one file path is not a valid internal FFXIV file path.";
                MaterialCopyNotice.Foreground = Brushes.Red;

                RaceChangeNotice.Text       = "--";
                RaceChangeNotice.Foreground = Brushes.Black;

                CopyButton.IsEnabled = false;
                return;
            }

            CopyButton.IsEnabled = true;
            var toRoot = await XivCache.GetFirstRoot(to);

            var fromRoot = await XivCache.GetFirstRoot(from);

            if (toRoot == null || fromRoot == null)
            {
                MaterialCopyNotice.Text       = "Unknown File Root - Materials and textures will not be copied.";
                MaterialCopyNotice.Foreground = Brushes.DarkGoldenrod;

                RaceChangeNotice.Text       = "Unknown File Root - Model will not be racially adjusted.";
                RaceChangeNotice.Foreground = Brushes.DarkGoldenrod;
                return;
            }
            else
            {
                MaterialCopyNotice.Text       = "Materials and textures will be copied to destination root folder.";
                MaterialCopyNotice.Foreground = Brushes.Green;
            }

            var raceRegex = new Regex("c([0-9]{4})");

            var toMatch   = raceRegex.Match(to);
            var fromMatch = raceRegex.Match(from);

            if (!toMatch.Success || !fromMatch.Success)
            {
                RaceChangeNotice.Text       = "Model is not racial - Model will not be racially adjusted.";
                RaceChangeNotice.Foreground = Brushes.Black;
                return;
            }

            var toRace   = XivRaces.GetXivRace(toMatch.Groups[1].Value);
            var fromRace = XivRaces.GetXivRace(fromMatch.Groups[1].Value);

            if (toRace == fromRace)
            {
                RaceChangeNotice.Text       = "Model races are identical - Model will not be racially adjusted.";
                RaceChangeNotice.Foreground = Brushes.Black;
                return;
            }


            RaceChangeNotice.Text       = "Model will be adjusted from " + fromRace.GetDisplayName() + " to " + toRace.GetDisplayName() + ".";
            RaceChangeNotice.Foreground = Brushes.Green;
        }
        private async Task <List <IItem> > BuildCategoryTree()
        {
            foreach (var kv in _categoryStructure)
            {
                // Make the top level node.
                var e = new ItemTreeElement(null, null, kv.Key);

                foreach (var secondary in kv.Value)
                {
                    var e2 = new ItemTreeElement(e, null, secondary);
                    e.Children.Add(e2);
                }
                CategoryElements.Add(e);
            }

            var gameDir  = XivCache.GameInfo.GameDirectory;
            var language = XivCache.GameInfo.GameLanguage;

            var items = await XivCache.GetFullItemList();

            foreach (var item in items)
            {
                // Find what node we should be attached to.
                ItemTreeElement catParent = null;
                var             topLevel  = CategoryElements.FirstOrDefault(x => x.DisplayName == item.PrimaryCategory);
                if (topLevel == null)
                {
                    topLevel = new ItemTreeElement(null, null, item.PrimaryCategory);
                    CategoryElements.Add(topLevel);
                }

                var secondLevel = topLevel.Children.FirstOrDefault(x => x.DisplayName == item.SecondaryCategory);
                if (secondLevel == null)
                {
                    if (item.SecondaryCategory == item.Name)
                    {
                        // These are a special snowflake case.
                        secondLevel = topLevel;
                    }
                    else
                    {
                        secondLevel = new ItemTreeElement(topLevel, null, item.SecondaryCategory);
                        topLevel.Children.Add(secondLevel);
                    }
                }

                catParent = secondLevel;

                ItemTreeElement setParent = null;
                // Try and see if we have a valid root parent to attach to in the sets tree.
                try
                {
                    var type = item.GetType();
                    // Perf.  Much faster to just not test those types at all, as we know they won't resolve.
                    if (type != typeof(XivUi))
                    {
                        var itemRoot = item.GetRootInfo();
                        if (itemRoot.PrimaryType != XivItemType.unknown)
                        {
                            var st = itemRoot.ToString();
                            if (DependencyRootNodes.ContainsKey(st))
                            {
                                setParent = DependencyRootNodes[st];
                            }
                        }
                    }
                } catch (Exception ex)
                {
                    throw;
                }

                var e2 = new ItemTreeElement(catParent, setParent, item);
                if (catParent != null)
                {
                    catParent.Children.Add(e2);
                }
                if (setParent != null)
                {
                    setParent.Children.Add(e2);
                }
            }
            return(items);
        }
Example #29
0
 public async Task <List <XivGear> > GetGearList(string substring = null)
 {
     return(await XivCache.GetCachedGearList(substring));
 }
        /// <summary>
        /// This should only really be called directly if the control was created with DeferLoading set to true.
        /// </summary>
        /// <returns></returns>
        public async Task LoadItems()
        {
            if (_READY)
            {
                SearchTimer.Stop();
                SearchTimer.Dispose();
                ClearSelection();
            }

            if (LockUiFunction != null)
            {
                await LockUiFunction(UIStrings.Loading_Items, null, this);
            }


            // Pump us into another thread so the UI stays nice and fresh.
            await Task.Run(async() =>
            {
                CategoryElements    = new ObservableCollection <ItemTreeElement>();
                SetElements         = new ObservableCollection <ItemTreeElement>();
                DependencyRootNodes = new Dictionary <string, ItemTreeElement>();

                try
                {
                    // Gotta build set tree first, so the items from the item list can latch onto the nodes there.
                    BuildSetTree();
                    await BuildCategoryTree();
                }
                catch (Exception ex)
                {
                    FlexibleMessageBox.Show("An error occurred while loading the item list.\n" + ex.Message, "Item List Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning);
                    return;
                }


                var toAdd = new List <(ItemTreeElement parent, ItemTreeElement child)>();
                foreach (var kv in DependencyRootNodes)
                {
                    // This dependency root had no EXD-Items associated with it.
                    // Gotta make a generic item for it.
                    if (kv.Value.Children.Count == 0)
                    {
                        // See if we can actually turn this root into a fully fledged item.
                        try
                        {
                            var root = await XivCache.GetFirstRoot(kv.Key);
                            if (root != null)
                            {
                                // If we can, add it into the list.
                                var item = root.ToRawItem();
                                var e    = new ItemTreeElement(null, kv.Value, item);
                                toAdd.Add((kv.Value, e));
                            }
                            else
                            {
                                var e = new ItemTreeElement(null, kv.Value, "[Unsupported]");
                                toAdd.Add((kv.Value, e));
                            }
                        }
                        catch (Exception ex)
                        {
                            throw;
                        }
                    }
                }

                // Loop back through to add the new items, so we're not affecting the previous iteration.
                foreach (var tup in toAdd)
                {
                    tup.parent.Children.Add(tup.child);
                }
            });

            var view = (CollectionView)CollectionViewSource.GetDefaultView(CategoryElements);

            view.Filter = SearchFilter;


            view        = (CollectionView)CollectionViewSource.GetDefaultView(SetElements);
            view.Filter = SearchFilter;

            SearchTimer          = new Timer(300);
            SearchTimer.Elapsed += Search;

            CategoryTree.ItemsSource = CategoryElements;
            SetTree.ItemsSource      = SetElements;

            _READY = true;

            Search(this, null);

            if (UnlockUiFunction != null)
            {
                await UnlockUiFunction(this);
            }

            if (ItemsLoaded != null)
            {
                ItemsLoaded.Invoke(this, null);
            }
        }