예제 #1
0
        /// <summary>
        /// Retrieves the item metadata from a cached index setup (or raw)
        /// </summary>
        /// <param name="root"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static async Task <ItemMetadata> GetFromCachedIndex(XivDependencyRoot root, IndexFile index)
        {
            var _dat = new Dat(XivCache.GameInfo.GameDirectory);
            var df   = IOUtil.GetDataFileFromPath(root.Info.GetRootFile());

            long offset = 0;

            if (index != null)
            {
                offset = index.Get8xDataOffset(root.Info.GetRootFile());
            }

            ItemMetadata mData = null;

            if (offset == 0)
            {
                mData = await ItemMetadata.GetMetadata(root);
            }
            else
            {
                var data = await _dat.GetType2Data(offset, df);

                mData = await ItemMetadata.Deserialize(data);
            }
            return(mData);
        }
예제 #2
0
        internal static IItemModel FromDependencyRoot(XivDependencyRoot root, int imcSubset)
        {
            var item = new XivGear();

            item.ModelInfo           = new XivGearModelInfo();
            item.ModelInfo.PrimaryID = root.Info.PrimaryId;
            if (root.Info.SecondaryId != null)
            {
                item.ModelInfo.SecondaryID = (int)root.Info.SecondaryId;
            }
            item.ModelInfo.ImcSubsetID = imcSubset;

            item.Name            = root.Info.GetBaseFileName() + "_v" + imcSubset.ToString();
            item.PrimaryCategory = XivStrings.Gear;

            if (root.Info.PrimaryType == Enums.XivItemType.weapon)
            {
                ((XivGearModelInfo)item.ModelInfo).IsWeapon = true;
                item.SecondaryCategory = XivStrings.Main_Hand;
            }
            else
            {
                item.SecondaryCategory = Mdl.SlotAbbreviationDictionary.First(x => x.Value == root.Info.Slot).Key;
            }


            return(item);
        }
예제 #3
0
        internal static IItemModel FromDependencyRoot(XivDependencyRoot root)
        {
            var item = new XivCharacter();

            item.ModelInfo             = new XivModelInfo();
            item.ModelInfo.PrimaryID   = root.Info.PrimaryId;
            item.ModelInfo.SecondaryID = (int)root.Info.SecondaryId;
            item.PrimaryCategory       = XivStrings.Character;

            if (root.Info.Slot != null)
            {
                item.SecondaryCategory = Mdl.SlotAbbreviationDictionary.FirstOrDefault(x => x.Value == root.Info.Slot).Key;

                //var race = XivRaces.GetXivRace(root.Info.PrimaryId.ToString().PadLeft(4, '0')).GetDisplayName();
                item.Name = item.SecondaryCategory + " - " + root.Info.GetBaseFileName();
            }
            else
            {
                item.Name = root.Info.GetBaseFileName();
                item.SecondaryCategory = XivStrings.Body;
            }


            return(item);
        }
예제 #4
0
        /// <summary>
        /// Creates a new ItemMetaData entry from the constituent files around the FFXIV file system.
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        private static async Task <ItemMetadata> CreateFromRaw(XivDependencyRoot root, bool forceDefault = false)
        {
            var _eqp = new Eqp(XivCache.GameInfo.GameDirectory);
            var _imc = new Imc(XivCache.GameInfo.GameDirectory);

            // These functions generate the path::offset to each of our
            // contiguous metadata entries.
            var imcPaths = await root.GetImcEntryPaths();

            var ret = new ItemMetadata(root);

            if (imcPaths.Count > 0)
            {
                ret.ImcEntries = await _imc.GetEntries(imcPaths, forceDefault);
            }

            ret.EqpEntry = await _eqp.GetEqpEntry(root.Info, forceDefault);

            ret.EqdpEntries = await _eqp.GetEquipmentDeformationParameters(root.Info, forceDefault);

            ret.EstEntries = await Est.GetExtraSkeletonEntries(root, forceDefault);

            ret.GmpEntry = await _eqp.GetGimmickParameter(root, forceDefault);

            return(ret);
        }
예제 #5
0
        /// <summary>
        /// Gets the metadata file for a given root.
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        public static async Task <ItemMetadata> GetMetadata(XivDependencyRoot root, bool forceDefault = false)
        {
            if (root == null)
            {
                return(null);
            }

            Mod mod      = null;
            var filePath = root.Info.GetRootFile();

            if (!forceDefault)
            {
                var _modding = new Modding(XivCache.GameInfo.GameDirectory);
                mod = await _modding.TryGetModEntry(filePath);
            }

            if (mod != null && mod.enabled)
            {
                var _dat = new Dat(XivCache.GameInfo.GameDirectory);
                // We have modded metadata stored in the .meta file in the DAT we can use.
                var data = await _dat.GetType2Data(filePath, false);

                // Run it through the binary deserializer and we're good.
                //return await Deserialize(data);
                return(await CreateFromRaw(root, forceDefault));
            }
            else
            {
                // This is the fun part where we get to pull the Metadata from all the disparate files around the FFXIV File System.
                return(await CreateFromRaw(root, forceDefault));
            }
        }
예제 #6
0
 public static bool UsesImc(XivDependencyRoot root)
 {
     if (root == null)
     {
         return(false);
     }
     return(UsesImc(root.Info));
 }
예제 #7
0
        /// <summary>
        /// Deserializes the binary EQP data into a EQP entry.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static EquipmentParameter DeserializeEqpData(byte[] data, XivDependencyRoot root, uint dataVersion)
        {
            // This one's relatively simple for the moment.
            // Though managing this data may grow more complex if SE decides to add more
            // bytes into the EQP entries at some point in the future.
            var ret = new EquipmentParameter(root.Info.Slot, data);

            return(ret);
        }
예제 #8
0
        public static EstType GetEstType(XivDependencyRoot root)
        {
            if (root == null)
            {
                return(EstType.Invalid);
            }

            return(GetEstType(root.Info));
        }
예제 #9
0
 private static async Task <GimmickParameter> DeserializeGmpData(byte[] data, XivDependencyRoot root, uint dataVersion)
 {
     if (dataVersion == 1)
     {
         // Version 1 didn't have GMP data, so include the default GMP data.
         var _eqp = new Eqp(XivCache.GameInfo.GameDirectory);
         return(await _eqp.GetGimmickParameter(root, true));
     }
     // 5 Bytes to parse, ezpz lemon sqzy
     return(new GimmickParameter(data));
 }
예제 #10
0
        private static string UpdateFileName(XivDependencyRoot Source, XivDependencyRoot Destination, string path)
        {
            var file = Path.GetFileName(path);

            if (Destination.Info.PrimaryType == XivItemType.human && Destination.Info.SecondaryType == XivItemType.hair && Path.GetExtension(path) == ".mtrl")
            {
                var hairRoot = Mtrl.GetHairMaterialRoot(Destination.Info);

                // Force replace the root information to the correct one for this target hair.
                var raceReplace = new Regex("^mt_c[0-9]{4}h[0-9]{4}");
                file = raceReplace.Replace(file, "mt_c" + hairRoot.PrimaryId.ToString().PadLeft(4, '0') + "h" + hairRoot.SecondaryId.ToString().PadLeft(4, '0'));

                // Jam in a suffix into the MTRL to make it unique/non-colliding.
                var initialPartRex = new Regex("^(mt_c[0-9]{4}h[0-9]{4})(?:_c[0-9]{4})?(.+)$");
                var m = initialPartRex.Match(file);

                // ???
                if (!m.Success)
                {
                    return(file);
                }

                file = m.Groups[1].Value + "_c" + Destination.Info.PrimaryId.ToString().PadLeft(4, '0') + m.Groups[2].Value;
                return(file);
            }

            var rex   = new Regex("[a-z][0-9]{4}([a-z][0-9]{4})");
            var match = rex.Match(file);

            if (!match.Success)
            {
                return(file);
            }

            if (Source.Info.SecondaryType == null)
            {
                // Equipment/Accessory items. Only replace the back half of the file names.
                var srcString = match.Groups[1].Value;
                var dstString = Destination.Info.GetBaseFileName(false);

                file = file.Replace(srcString, dstString);
            }
            else
            {
                // Replace the entire root chunk for roots that have two identifiers.
                var srcString = match.Groups[0].Value;
                var dstString = Destination.Info.GetBaseFileName(false);

                file = file.Replace(srcString, dstString);
            }

            return(file);
        }
예제 #11
0
        internal static IItemModel FromDependencyRoot(XivDependencyRoot root, int imcSubset)
        {
            var item = new XivGenericItemModel();

            item.ModelInfo             = new XivModelInfo();
            item.ModelInfo.ImcSubsetID = imcSubset;
            item.ModelInfo.PrimaryID   = root.Info.PrimaryId;
            item.ModelInfo.SecondaryID = (int)root.Info.SecondaryId;
            item.Name            = root.Info.GetBaseFileName() + "_v" + imcSubset.ToString();
            item.PrimaryCategory = XivStrings.Gear;

            return(item);
        }
예제 #12
0
        /// <summary>
        /// Deserializes the binary IMC data into a list of IMC entries.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static List <XivImc> DeserializeImcData(byte[] data, XivDependencyRoot root, uint dataVersion)
        {
            const int ImcSubEntrySize = 6;
            var       entries         = data.Length / ImcSubEntrySize;

            List <XivImc> ret = new List <XivImc>();

            for (int i = 0; i < entries; i++)
            {
                var entryData = data.Skip(i * ImcSubEntrySize).Take(ImcSubEntrySize).ToArray();
                ret.Add(Imc.DeserializeEntry(entryData));
            }

            return(ret);
        }
예제 #13
0
        internal static string UpdatePath(XivDependencyRoot Source, XivDependencyRoot Destination, string path)
        {
            // For common path items, copy them to our own personal mimic of the path.
            if (path.StartsWith(CommonPath))
            {
                var len         = CommonPath.Length;
                var afterCommon = path.Substring(len);
                path = Destination.Info.GetRootFolder() + "common/" + afterCommon;
                return(path);
            }

            // Things that live in the common folder get to stay there/don't get copied.

            var file   = UpdateFileName(Source, Destination, path);
            var folder = UpdateFolder(Source, Destination, path);

            return(folder + "/" + file);
        }
        private bool IsSupported(XivDependencyRoot fullRoot)
        {
            if (fullRoot == null)
            {
                return(false);
            }

            var root = fullRoot.Info;

            if (root == null)
            {
                return(false);
            }
            if (root.PrimaryType == XivItemType.monster)
            {
                return(false);
            }
            if (root.PrimaryType == XivItemType.demihuman)
            {
                return(false);
            }
            if (root.PrimaryType == XivItemType.indoor)
            {
                return(false);
            }
            if (root.PrimaryType == XivItemType.outdoor)
            {
                return(false);
            }

            if (root.PrimaryType == XivItemType.human)
            {
                if (root.SecondaryType == XivItemType.body)
                {
                    return(false);
                }
                if (root.SecondaryType == XivItemType.face)
                {
                    return(false);
                }
            }

            return(true);
        }
예제 #15
0
        internal static IItemModel FromDependencyRoot(XivDependencyRoot root)
        {
            var item = new XivFurniture();

            item.ModelInfo           = new XivModelInfo();
            item.ModelInfo.PrimaryID = root.Info.PrimaryId;
            item.Name            = root.Info.GetBaseFileName();
            item.PrimaryCategory = XivStrings.Housing;
            if (root.Info.PrimaryType == Enums.XivItemType.indoor)
            {
                item.SecondaryCategory = XivStrings.Furniture_Indoor;
            }
            else
            {
                item.SecondaryCategory = XivStrings.Furniture_Outdoor;
            }

            return(item);
        }
        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;
        }
예제 #17
0
        public static bool IsSupported(XivDependencyRoot root)
        {
            if (root.Info.PrimaryType == XivItemType.weapon)
            {
                return(true);
            }
            if (root.Info.PrimaryType == XivItemType.equipment)
            {
                return(true);
            }
            if (root.Info.PrimaryType == XivItemType.accessory)
            {
                return(true);
            }
            if (root.Info.PrimaryType == XivItemType.human && root.Info.SecondaryType == XivItemType.hair)
            {
                return(true);
            }

            return(false);
        }
예제 #18
0
        private static string UpdateFolder(XivDependencyRoot Source, XivDependencyRoot Destination, string path)
        {
            if (Destination.Info.PrimaryType == XivItemType.human && Destination.Info.SecondaryType == XivItemType.hair && Path.GetExtension(path) == ".mtrl")
            {
                var hairRoot = Mtrl.GetHairMaterialRoot(Destination.Info);

                // Force the race code to the appropriate one.
                var raceReplace = new Regex("/c[0-9]{4}");
                path = raceReplace.Replace(path, "/c" + hairRoot.PrimaryId.ToString().PadLeft(4, '0'));

                var hairReplace = new Regex("/h[0-9]{4}");
                path = hairReplace.Replace(path, "/h" + hairRoot.SecondaryId.ToString().PadLeft(4, '0'));

                // Hairs between 115 and 200 have forced material path sharing enabled.
                path = Path.GetDirectoryName(path);
                path = path.Replace('\\', '/');
                return(path);
            }

            // So first off, just copy anything from the old root folder to the new one.
            var match = RemoveRootPathRegex.Match(path);

            if (match.Success)
            {
                // The item existed in an old root path, so we can just clone the same post-root path into the new root folder.
                var afterRootPath = match.Groups[1].Value;
                path = Destination.Info.GetRootFolder() + afterRootPath;
                path = Path.GetDirectoryName(path);
                path = path.Replace('\\', '/');
                return(path);
            }

            // Okay, stuff at this point didn't actually exist in any root path, and didn't exist in the common path either.
            // Just copy this crap into our root folder.

            // The only way we can really get here is if some mod author created textures in a totally arbitrary path.
            path = Path.GetDirectoryName(Destination.Info.GetRootFolder());
            path = path.Replace('\\', '/');
            return(path);
        }
예제 #19
0
        internal static IItemModel FromDependencyRoot(XivDependencyRoot root, int imcSubset)
        {
            var item = new XivMount();
            var mi   = new XivMonsterModelInfo();

            mi.ModelType   = root.Info.PrimaryType;
            mi.PrimaryID   = root.Info.PrimaryId;
            mi.SecondaryID = (int)root.Info.SecondaryId;
            mi.ImcSubsetID = imcSubset;

            item.ModelInfo         = mi;
            item.Name              = root.Info.GetBaseFileName() + "_v" + imcSubset.ToString();
            item.PrimaryCategory   = XivStrings.Companions;
            item.SecondaryCategory = XivStrings.Mounts;

            if (root.Info.Slot != null)
            {
                item.TertiaryCategory = Mdl.SlotAbbreviationDictionary.First(x => x.Value == root.Info.Slot).Key;
            }

            return(item);
        }
        private bool DestinationOk(XivDependencyRoot fullRoot)
        {
            if (fullRoot == null)
            {
                return(false);
            }
            if (Source == null)
            {
                return(false);
            }

            var root = fullRoot.Info;
            var src  = Source.Info;

            if (root.PrimaryType != src.PrimaryType)
            {
                return(false);
            }
            if (root.SecondaryType != src.SecondaryType)
            {
                return(false);
            }
            if (root.Slot != src.Slot)
            {
                return(false);
            }

            if (root.PrimaryType == XivItemType.human)
            {
                if (root.PrimaryId != src.PrimaryId)
                {
                    return(false);
                }
            }

            return(true);
        }
예제 #21
0
        public async Task <bool> SetRoot(XivDependencyRoot root, int defaultVariant = 0)
        {
            _root = root;
            if (_root == null)
            {
                return(false);
            }

            if (root.Info.PrimaryType == XivItemType.human)
            {
                if (_lastNumber > 0)
                {
                    if (_lastNumber != (int)root.Info.SecondaryId)
                    {
                        // Change root to the new "number" root.
                        var nRoot = new XivDependencyRootInfo()
                        {
                            PrimaryId     = _root.Info.PrimaryId,
                            PrimaryType   = _root.Info.PrimaryType,
                            SecondaryId   = _lastNumber,
                            SecondaryType = _root.Info.SecondaryType,
                            Slot          = _root.Info.Slot
                        };
                        _root = nRoot.ToFullRoot();
                    }
                }
                if (_root.Info.SecondaryId > 0)
                {
                    _lastNumber = (int)_root.Info.SecondaryId;
                }
            }

            SetLabel.Content  = XivItemTypes.GetSystemPrefix(root.Info.PrimaryType) + root.Info.PrimaryId.ToString().PadLeft(4, '0');
            SlotLabel.Content = Mdl.SlotAbbreviationDictionary.FirstOrDefault(x => x.Value == _root.Info.Slot).Key + "(" + _root.Info.Slot + ")";

            var items = await _root.GetAllItems();

            ItemNameBox.Text = "[" + items.Count + "] " + items[0].Name;

            var _modding = new Modding(XivCache.GameInfo.GameDirectory);
            var path     = _root.Info.GetRootFile();
            var mod      = await _modding.TryGetModEntry(path);

            if (mod == null)
            {
                ToggleButton.IsEnabled = false;
                ToggleButton.Content   = "Enable";
            }
            else
            {
                ToggleButton.IsEnabled = true;
                if (mod.enabled)
                {
                    ToggleButton.Content = "Disable";
                }
                else
                {
                    ToggleButton.Content = "Enable";
                }
            }

            return(await _vm.SetRoot(_root, defaultVariant));
        }
예제 #22
0
 public ItemMetadata(XivDependencyRoot root)
 {
     Root = root;
 }
예제 #23
0
        private static async Task <ItemMetadata> GetCachedMetadata(IndexFile index, ModList modlist, XivDependencyRoot root, XivDataFile df, Dat _dat)
        {
            var          originalMetadataOffset = index.Get8xDataOffset(root.Info.GetRootFile());
            ItemMetadata originalMetadata       = null;

            if (originalMetadataOffset == 0)
            {
                originalMetadata = await ItemMetadata.GetMetadata(root);
            }
            else
            {
                var data = await _dat.GetType2Data(originalMetadataOffset, df);

                originalMetadata = await ItemMetadata.Deserialize(data);
            }
            return(originalMetadata);
        }
예제 #24
0
        public static async Task <Dictionary <XivRace, ExtraSkeletonEntry> > GetExtraSkeletonEntries(XivDependencyRoot root, bool forceDefault = false)
        {
            if (root == null)
            {
                return(new Dictionary <XivRace, ExtraSkeletonEntry>());
            }

            return(await GetExtraSkeletonEntries(root.Info, forceDefault));
        }
        /// <summary>
        /// Sets the given dependency root for display.
        /// Automatically resolves to the first item of the root if none were given.
        ///
        /// Returns false if the root or metadata is invalid.
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        public async Task <bool> SetRoot(XivDependencyRoot root, int startingVariant = 0)
        {
            _metadata = await ItemMetadata.GetMetadata(root);

            _original = await ItemMetadata.GetMetadata(root);

            if (_metadata == null || !_metadata.AnyMetadata)
            {
                _view.SaveButton.IsEnabled         = false;
                _view.NexSlotButton.IsEnabled      = false;
                _view.PreviousSlotButton.IsEnabled = false;
                return(false);
            }

            _view.SaveButton.IsEnabled = true;

            if (root.Info.Slot == null)
            {
                _view.NexSlotButton.IsEnabled      = false;
                _view.PreviousSlotButton.IsEnabled = false;
            }
            else
            {
                _view.NexSlotButton.IsEnabled      = true;
                _view.PreviousSlotButton.IsEnabled = true;
            }

            if (_metadata.ImcEntries.Count > 0)
            {
                _view.ImcView.Visibility = System.Windows.Visibility.Visible;
                await _view.ImcView.SetMetadata(_metadata, startingVariant);
            }
            else
            {
                _view.ImcView.Visibility = System.Windows.Visibility.Collapsed;
            }

            if (_metadata.EqpEntry != null)
            {
                _view.EqpView.Visibility = System.Windows.Visibility.Visible;
                await _view.EqpView.SetMetadata(_metadata);
            }
            else
            {
                _view.EqpView.Visibility = System.Windows.Visibility.Collapsed;
            }

            if (_metadata.EqdpEntries.Count > 0)
            {
                _view.EqdpView.Visibility = System.Windows.Visibility.Visible;
                await _view.EqdpView.SetMetadata(_metadata);
            }
            else
            {
                _view.EqdpView.Visibility = System.Windows.Visibility.Collapsed;
            }

            if (_metadata.EstEntries.Count > 0)
            {
                _view.SkeletonView.Visibility = System.Windows.Visibility.Visible;
                await _view.SkeletonView.SetMetadata(_metadata);
            }
            else
            {
                _view.SkeletonView.Visibility = System.Windows.Visibility.Collapsed;
            }

            if (_metadata.GmpEntry != null)
            {
                _view.VisorView.Visibility = System.Windows.Visibility.Visible;
                await _view.VisorView.SetMetadata(_metadata);
            }
            else
            {
                _view.VisorView.Visibility = System.Windows.Visibility.Collapsed;
            }

            return(_metadata != null);
        }
 private void SourceSelected(object sender, IItem e)
 {
     Source     = e.GetRoot();
     SourceItem = e;
     SetState(ItemConverterState.DestinationSelect);
 }
예제 #27
0
        /// <summary>
        /// Deserializes the binary EQDP data into a dictionary of EQDP entries.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static Dictionary <XivRace, EquipmentDeformationParameter> DeserializeEqdpData(byte[] data, XivDependencyRoot root, uint dataVersion)
        {
            const int eqdpEntrySize = 5;
            var       entries       = data.Length / eqdpEntrySize;

            var ret = new Dictionary <XivRace, EquipmentDeformationParameter>();

            var read = 0;

            using (var reader = new BinaryReader(new MemoryStream(data)))
            {
                while (read < entries)
                {
                    var raceCode = reader.ReadInt32();
                    var race     = XivRaces.GetXivRace(raceCode.ToString().PadLeft(4, '0'));

                    var eqpByte = reader.ReadByte();
                    var entry   = EquipmentDeformationParameter.FromByte(eqpByte);

                    ret.Add(race, entry);

                    read++;
                }
            }

            // Catch for cases where for some reason the EQP doesn't have all races,
            // for example, SE adding more races in the future, and we're
            // reading old metadata entries.
            foreach (var race in Eqp.PlayableRaces)
            {
                if (!ret.ContainsKey(race))
                {
                    ret.Add(race, new EquipmentDeformationParameter());
                }
            }

            return(ret);
        }
 private void DestinationSelected(object sender, IItem e)
 {
     Destination     = e.GetRoot();
     DestinationItem = e;
     SetState(ItemConverterState.Confirmation);
 }
예제 #29
0
        /// <summary>
        /// Copies the entirety of a given root to a new root.
        /// </summary>
        /// <param name="Source">Original Root to copy from.</param>
        /// <param name="Destination">Destination root to copy to.</param>
        /// <param name="ApplicationSource">Application to list as the source for the resulting mod entries.</param>
        /// <returns>Returns a Dictionary of all the file conversion</returns>
        public static async Task <Dictionary <string, string> > CloneRoot(XivDependencyRoot Source, XivDependencyRoot Destination, string ApplicationSource, int singleVariant = -1, string saveDirectory = null, IProgress <string> ProgressReporter = null, IndexFile index = null, ModList modlist = null, ModPack modPack = null)
        {
            if (!IsSupported(Source) || !IsSupported(Destination))
            {
                throw new InvalidDataException("Cannot clone unsupported root.");
            }


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

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

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

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

                    modlist = await _modding.GetModListAsync();
                }


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


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

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


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

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

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

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

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

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

                // Time to start editing things.

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

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

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

                    newMetadata.EqpEntry = set1Metadata.EqpEntry;

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

                    var tmdl = TTModel.FromRaw(xmdl);

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

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

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

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

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

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

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

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


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

                        xivMtrl.MTRLPath = dst;

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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



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

                            string existentCopy = null;

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

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

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

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

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

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

                        mods.Add(mod);
                    }
                }

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

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

                    await _modding.SaveModListAsync(modlist);
                }

                XivCache.QueueDependencyUpdate(allFiles.ToList());

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

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

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

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



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

                // Return the final file conversion listing.
                var ret  = newModelPaths.Union(newMaterialPaths).Union(newAvfxPaths).Union(newTexturePaths);
                var dict = ret.ToDictionary(x => x.Key, x => x.Value);
                dict.Add(Source.Info.GetRootFile(), Destination.Info.GetRootFile());
                return(dict);
            } finally
            {
                XivCache.CacheWorkerEnabled = workerStatus;
            }
        }
예제 #30
0
        private static async Task <Dictionary <XivRace, ExtraSkeletonEntry> > DeserializeEstData(byte[] data, XivDependencyRoot root, uint dataVersion)
        {
            if (dataVersion == 1)
            {
                // Version 1 didn't include EST data, so just get the defaults.
                return(await Est.GetExtraSkeletonEntries(root));
            }


            // 6 Bytes per entry.
            var count = data.Length / 6;
            var ret   = new Dictionary <XivRace, ExtraSkeletonEntry>(count);

            for (int i = 0; i < count; i++)
            {
                var offset   = i * 6;
                var raceCode = BitConverter.ToUInt16(data, offset);
                var setId    = BitConverter.ToUInt16(data, offset + 2);
                var skelId   = BitConverter.ToUInt16(data, offset + 4);

                var race = XivRaces.GetXivRace(raceCode);

                ret.Add(race, new ExtraSkeletonEntry(race, setId, skelId));
            }

            return(ret);
        }