Exemple #1
0
        /// <summary>
        /// Gets the ATex data
        /// </summary>
        /// <param name="offset">The offset to the ATex file</param>
        /// <returns>An XivTex with all the texture data</returns>
        public XivTex GetATexData(int offset)
        {
            var dat      = new Dat(_gameDirectory);
            var atexData = dat.GetType2Data(offset, _dataFile);

            var xivTex = new XivTex();

            using (var br = new BinaryReader(new MemoryStream(atexData)))
            {
                var signature = br.ReadInt32();
                xivTex.TextureFormat = Dat.TextureTypeDictionary[br.ReadInt32()];
                xivTex.Width         = br.ReadInt16();
                xivTex.Height        = br.ReadInt16();

                br.ReadBytes(2);

                xivTex.MipMapCount = br.ReadInt16();

                br.ReadBytes(64);

                xivTex.TexData = br.ReadBytes(atexData.Length - 80);
            }

            return(xivTex);
        }
Exemple #2
0
        /// <summary>
        /// Applies a custom .rgsp file to the main Human.CMP file.
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="index"></param>
        /// <param name="modlist"></param>
        /// <returns></returns>
        internal static async Task ApplyRgspFile(string filePath, IndexFile index = null, ModList modlist = null)
        {
            var _dat     = new Dat(XivCache.GameInfo.GameDirectory);
            var rgspData = await _dat.GetType2Data(filePath, false, index, modlist);

            await ApplyRgspFile(rgspData, index, modlist);
        }
Exemple #3
0
        private static async Task <Dictionary <XivRace, Dictionary <ushort, ExtraSkeletonEntry> > > GetEstFile(EstType type, bool forceDefault = false)
        {
            var _dat = new Dat(_gameDirectory);
            var data = await _dat.GetType2Data(EstFiles[type], forceDefault);

            var count = BitConverter.ToUInt32(data, 0);

            Dictionary <XivRace, Dictionary <ushort, ExtraSkeletonEntry> > entries = new Dictionary <XivRace, Dictionary <ushort, ExtraSkeletonEntry> >();

            for (int i = 0; i < count; i++)
            {
                var entry = ExtraSkeletonEntry.Read(data, count, (uint)i);
                if (!entries.ContainsKey(entry.Race))
                {
                    entries.Add(entry.Race, new Dictionary <ushort, ExtraSkeletonEntry>());
                }

                if (!entries[entry.Race].ContainsKey(entry.SetId))
                {
                    // For whatever reason there is exactly one dupe in the game files, where a Lalafell M face has two identical entries.
                    // Doesn't seem to matter to SE, so shouldn't matter to us.
                    entries[entry.Race].Add(entry.SetId, entry);
                }
            }

            return(entries);
        }
Exemple #4
0
        /// <summary>
        /// Resolves the original skeleton path in the FFXIV file system and raw extracts it.
        /// </summary>
        /// <param name="fullMdlPath">Full path to the MDL.</param>
        /// <param name="internalSkelName">Internal skeleton name (for hair).  This can be resolved if missing, though it is slightly expensive to do so.</param>
        private static async Task <string> ExtractSkelb(string skelBPath)
        {
            var index    = new Index(XivCache.GameInfo.GameDirectory);
            var dat      = new Dat(XivCache.GameInfo.GameDirectory);
            var dataFile = IOUtil.GetDataFileFromPath(skelBPath);

            var offset = await index.GetDataOffset(skelBPath);

            if (offset == 0)
            {
                throw new Exception($"Could not find offset for {skelBPath}");
            }

            var sklbData = await dat.GetType2Data(offset, dataFile);

            using (var br = new BinaryReader(new MemoryStream(sklbData)))
            {
                br.BaseStream.Seek(0, SeekOrigin.Begin);

                var magic  = br.ReadInt32();
                var format = br.ReadInt32();

                br.ReadBytes(2);

                if (magic != 0x736B6C62)
                {
                    throw new FormatException();
                }

                var dataOffset = 0;

                switch (format)
                {
                case 0x31323030:
                    dataOffset = br.ReadInt16();
                    break;

                case 0x31333030:
                case 0x31333031:
                    br.ReadBytes(2);
                    dataOffset = br.ReadInt16();
                    break;

                default:
                    throw new Exception($"Unkown Data Format ({format})");
                }

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

                var havokData = br.ReadBytes(sklbData.Length - dataOffset);

                var skelName   = Path.GetFileNameWithoutExtension(skelBPath).Replace("skl_", "");
                var outputFile = skelName + ".sklb";

                var cwd        = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
                var parsedFile = Path.Combine(cwd, SkeletonsFolder, outputFile);
                File.WriteAllBytes(parsedFile, havokData);
                return(parsedFile);
            }
        }
        /// <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);
        }
        /// <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));
            }
        }
Exemple #7
0
        /// <summary>
        /// Reads and parses the ExHeader file
        /// </summary>
        /// <param name="exFile">The Ex file to use.</param>
        private void ReadExHeader(XivEx exFile)
        {
            OffsetTypeDict = new Dictionary <int, int>();
            PageList       = new List <int>();
            LanguageList   = new List <int>();

            var exdFolderHash = HashGenerator.GetHash("exd");
            var exdFileHash   = HashGenerator.GetHash(exFile + ExhExtension);

            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var offset = index.GetDataOffset(exdFolderHash, exdFileHash, XivDataFile._0A_Exd);

            if (offset == 0)
            {
                throw new Exception($"Could not find offest for exd/{exFile}{ExhExtension}");
            }

            var exhData = dat.GetType2Data(offset, XivDataFile._0A_Exd);

            // Big Endian Byte Order
            using (var br = new BinaryReaderBE(new MemoryStream(exhData)))
            {
                var signature      = br.ReadInt32();
                var version        = br.ReadInt16();
                var dataSetChunk   = br.ReadInt16();
                var dataSetCount   = br.ReadInt16();
                var pageTableCount = br.ReadInt16();
                var langTableCount = br.ReadInt16();
                var unknown        = br.ReadInt16();
                var unknown1       = br.ReadInt32();
                var entryCount     = br.ReadInt32();
                br.ReadBytes(8);

                for (var i = 0; i < dataSetCount; i++)
                {
                    var dataType   = br.ReadInt16();
                    var dataOffset = br.ReadInt16();

                    if (!OffsetTypeDict.ContainsKey(dataOffset))
                    {
                        OffsetTypeDict.Add(dataOffset, dataType);
                    }
                }

                for (var i = 0; i < pageTableCount; i++)
                {
                    var pageNumber = br.ReadInt32();
                    var pageSize   = br.ReadInt32();
                    PageList.Add(pageNumber);
                }

                for (var i = 0; i < langTableCount; i++)
                {
                    var langCode = br.ReadInt16();
                    LanguageList.Add(langCode);
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Retrieves an arbitrary selection of IMC entries based on their path::binaryoffset.
        /// </summary>
        /// <param name="pathsWithOffsets"></param>
        /// <returns></returns>
        public async Task <List <XivImc> > GetEntries(List <string> pathsWithOffsets)
        {
            var entries = new List <XivImc>();
            var index   = new Index(_gameDirectory);
            var dat     = new Dat(_gameDirectory);

            var lastPath  = "";
            int imcOffset = 0;

            byte[] imcByteData = new byte[0];

            foreach (var combinedPath in pathsWithOffsets)
            {
                var binaryMatch = _binaryOffsetRegex.Match(combinedPath);
                var pathMatch   = _pathOnlyRegex.Match(combinedPath);

                // Invalid format.
                if (!pathMatch.Success || !binaryMatch.Success)
                {
                    continue;
                }

                long   offset = Int64.Parse(binaryMatch.Groups[1].Value) / 8;
                string path   = pathMatch.Groups[1].Value;

                // Only reload this data if we need to.
                if (path != lastPath)
                {
                    imcOffset = await index.GetDataOffset(path);

                    imcByteData = await dat.GetType2Data(imcOffset, IOUtil.GetDataFileFromPath(path));
                }
                lastPath = path;

                // Offset would run us past the end of the file.
                const int entrySize = 6;
                if (offset > imcByteData.Length - entrySize)
                {
                    continue;
                }


                using (var br = new BinaryReader(new MemoryStream(imcByteData)))
                {
                    var subsetCount = br.ReadInt16();
                    var identifier  = (ImcType)br.ReadInt16();

                    br.BaseStream.Seek(offset, SeekOrigin.Begin);
                    entries.Add(new XivImc
                    {
                        Variant = br.ReadByte(),
                        Unknown = br.ReadByte(),
                        Mask    = br.ReadUInt16(),
                        Vfx     = br.ReadUInt16()
                    });
                }
            }
            return(entries);
        }
Exemple #9
0
        /// <summary>
        /// Gets the .atex paths that are within the .avfx file
        /// </summary>
        /// <param name="offset">The offset to the avfx file</param>
        /// <returns>A list of atex paths</returns>
        public async Task <List <string> > GetATexPaths(int offset)
        {
            var atexList = new List <string>();

            var dat = new Dat(_gameDirectory);

            var avfxData = await dat.GetType2Data(offset, _dataFile);

            await Task.Run(() =>
            {
                using (var br = new BinaryReader(new MemoryStream(avfxData)))
                {
                    var data = br.ReadInt32();

                    // Advance to the path data header
                    while (data != 5531000)
                    {
                        try
                        {
                            data = br.ReadInt32();
                        }
                        catch (EndOfStreamException e)
                        {
                            throw new System.Exception("VFX textures were detected but no texture paths could be found.\n" +
                                                       "VFX textures will not be accessible.");
                        }
                    }

                    // While the data is a path data header
                    while (data == 5531000)
                    {
                        var pathLength = br.ReadInt32();

                        atexList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathLength)).Replace("\0", ""));

                        try
                        {
                            while (br.PeekChar() != 120)
                            {
                                if (br.PeekChar() == -1)
                                {
                                    break;
                                }

                                br.ReadByte();
                            }

                            data = br.ReadInt32();
                        }
                        catch
                        {
                            break;
                        }
                    }
                }
            });

            return(atexList);
        }
Exemple #10
0
        public static void ExportMsb(int slot, string gamePath, string outPath)
        {
            var index = new Index(new DirectoryInfo(gamePath));
            var dat   = new Dat(new DirectoryInfo(gamePath));

            var msbData = Task.Run(() => dat.GetType2Data($"sound/score/bgm_score_{slot:D3}.msb", true)).Result;

            ExportMsb(new MemoryStream(msbData), outPath);
        }
Exemple #11
0
        public static async Task <StainingTemplateFile> GetStainingTemplateFile(bool forceOriginal = false, IndexFile index = null, ModList modlist = null)
        {
            var _dat = new Dat(XivCache.GameInfo.GameDirectory);

            var data = await _dat.GetType2Data(GearStainingTemplatePath, forceOriginal, index, modlist);

            var ret = new StainingTemplateFile(data);

            return(ret);
        }
Exemple #12
0
        private static async Task <CharaMakeParameterSet> GetCharaMakeParameterSet(bool forceOriginal = false, IndexFile index = null, ModList modlist = null)
        {
            var _dat = new Dat(XivCache.GameInfo.GameDirectory);

            var data = await _dat.GetType2Data(HumanCmpPath, forceOriginal, index, modlist);

            var cmp = new CharaMakeParameterSet(data);


            return(cmp);
        }
Exemple #13
0
        /// <summary>
        /// Gets the relevant IMC information for a given item
        /// </summary>
        /// <param name="item">The item to get the version for</param>
        /// <param name="modelInfo">The model info of the item</param>
        /// <returns>The XivImc Data</returns>
        public XivImc GetImcInfo(IItemModel item, XivModelInfo modelInfo)
        {
            var xivImc = new XivImc();

            // These are the offsets to relevant data
            // These will need to be changed if data gets added or removed with a patch
            const int headerLength     = 4;
            const int variantLength    = 6;
            const int variantSetLength = 30;

            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var itemType = ItemType.GetItemType(item);
            var imcPath  = GetImcPath(modelInfo, itemType);

            var imcOffset = index.GetDataOffset(HashGenerator.GetHash(imcPath.Folder), HashGenerator.GetHash(imcPath.File), _dataFile);

            if (imcOffset == 0)
            {
                throw new Exception($"Could not find offest for {imcPath.Folder}/{imcPath.File}");
            }

            var imcData = dat.GetType2Data(imcOffset, _dataFile);

            using (var br = new BinaryReader(new MemoryStream(imcData)))
            {
                int variantOffset;

                if (itemType == XivItemType.weapon || itemType == XivItemType.monster)
                {
                    // weapons and monsters do not have variant sets
                    variantOffset = (modelInfo.Variant * variantLength) + headerLength;
                }
                else
                {
                    // Variant Sets contain 5 variants for each slot
                    // These can be Head, Body, Hands, Legs, Feet  or  Ears, Neck, Wrists, LRing, RRing
                    // This skips to the correct variant set, then to the correct slot within that set for the item
                    variantOffset = (modelInfo.Variant * variantSetLength) + (_slotOffsetDictionary[item.ItemCategory] * variantLength) + headerLength;
                }

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

                xivImc.Version = br.ReadByte();
                var unknown = br.ReadByte();
                xivImc.Mask = br.ReadUInt16();
                xivImc.Vfx  = br.ReadUInt16();
            }

            return(xivImc);
        }
Exemple #14
0
        /// <summary>
        /// Gets the .atex paths that are within the .avfx file
        /// </summary>
        /// <param name="offset">The offset to the avfx file</param>
        /// <returns>A list of atex paths</returns>
        public async Task <List <string> > GetATexPaths(int offset)
        {
            var atexList = new List <string>();

            var dat = new Dat(_gameDirectory);

            var avfxData = await dat.GetType2Data(offset, _dataFile);

            await Task.Run(() =>
            {
                using (var br = new BinaryReader(new MemoryStream(avfxData)))
                {
                    var data = br.ReadInt32();

                    // Advance to the path data header
                    while (data != 5531000)
                    {
                        data = br.ReadInt32();
                    }

                    // While the data is a path data header
                    while (data == 5531000)
                    {
                        var pathLength = br.ReadInt32();

                        atexList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathLength)).Replace("\0", ""));

                        try
                        {
                            while (br.PeekChar() != 120)
                            {
                                if (br.PeekChar() == -1)
                                {
                                    break;
                                }

                                br.ReadByte();
                            }

                            data = br.ReadInt32();
                        }
                        catch
                        {
                            break;
                        }
                    }
                }
            });

            return(atexList);
        }
Exemple #15
0
        /// <summary>
        /// Gets the texture paths from the uld file
        /// </summary>
        /// <returns>List of texture paths from the uld file</returns>
        public List <string> GetTexFromUld()
        {
            var hashedFolder = HashGenerator.GetHash("ui/uld");
            var index        = new Index(_gameDirectory);
            var dat          = new Dat(_gameDirectory);

            var uldStringList = new HashSet <string>();
            var uldOffsetList = index.GetAllFileOffsetsInFolder(hashedFolder, XivDataFile._06_Ui);

            foreach (var offset in uldOffsetList)
            {
                var uldData = dat.GetType2Data(offset, XivDataFile._06_Ui);

                using (var br = new BinaryReader(new MemoryStream(uldData)))
                {
                    var signature = br.ReadInt32();

                    if (signature != 1751411829)
                    {
                        continue;
                    }

                    br.ReadBytes(56);

                    int pathCount = br.ReadByte();

                    br.ReadBytes(7);

                    for (var i = 0; i < pathCount; i++)
                    {
                        br.ReadBytes(4);
                        var path = Encoding.UTF8.GetString(br.ReadBytes(48)).Replace("\0", "");

                        if (path.Length <= 2 || !path.Contains("uld"))
                        {
                            continue;
                        }

                        var uldPath = path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal) + 4);
                        uldStringList.Add(uldPath);
                    }
                }
            }

            return(uldStringList.ToList());
        }
        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);
        }
        /// <summary>
        /// Gets additional assets when the original asset file contains asset file paths within it
        /// </summary>
        /// <param name="assets">The current asset object</param>
        private async Task GetAdditionalAssets(HousingAssets assets)
        {
            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            foreach (var additionalAsset in assets.AdditionalAssetList.ToList())
            {
                var assetFolder = Path.GetDirectoryName(additionalAsset).Replace("\\", "/");
                var assetFile   = Path.GetFileName(additionalAsset);

                var assetOffset = await index.GetDataOffset(HashGenerator.GetHash(assetFolder), HashGenerator.GetHash(assetFile), XivDataFile._01_Bgcommon);

                var assetData = await dat.GetType2Data(assetOffset, XivDataFile._01_Bgcommon);

                await Task.Run(() =>
                {
                    using (var br = new BinaryReader(new MemoryStream(assetData)))
                    {
                        br.BaseStream.Seek(20, SeekOrigin.Begin);

                        var skip = br.ReadInt32() + 20;

                        br.BaseStream.Seek(skip + 4, SeekOrigin.Begin);

                        var stringsOffset = br.ReadInt32();

                        br.BaseStream.Seek(skip + stringsOffset, SeekOrigin.Begin);

                        var pathCounts = 0;

                        while (true)
                        {
                            // Because we don't know the length of the string, we read the data until we reach a 0 value
                            // That 0 value is the space between strings
                            byte a;
                            var pathName = new List <byte>();
                            while ((a = br.ReadByte()) != 0)
                            {
                                if (a == 0xFF)
                                {
                                    break;
                                }

                                pathName.Add(a);
                            }

                            if (a == 0xFF)
                            {
                                break;
                            }

                            // Read the string from the byte array and remove null terminators
                            var path = Encoding.ASCII.GetString(pathName.ToArray()).Replace("\0", "");

                            if (path.Equals(string.Empty))
                            {
                                continue;
                            }

                            // Add the attribute to the list
                            if (pathCounts == 0)
                            {
                                assets.Shared = path;
                            }
                            else if (pathCounts == 1)
                            {
                                assets.BaseFileName = path;
                            }
                            else
                            {
                                if (path.Contains(".mdl"))
                                {
                                    assets.MdlList.Add(path);
                                }
                                else if (path.Contains(".sgb"))
                                {
                                    assets.AdditionalAssetList.Add(path);
                                }
                                else if (!path.Contains("."))
                                {
                                    assets.BaseFolder = path;
                                }
                                else
                                {
                                    assets.OthersList.Add(path);
                                }
                            }

                            pathCounts++;
                        }
                    }
                });
            }
        }
        /// <summary>
        /// Gets the assets for furniture
        /// </summary>
        /// <param name="modelID">The model id to get the assets for</param>
        /// <returns>A HousingAssets object containing the asset info</returns>
        private async Task <HousingAssets> GetFurnitureAssets(int modelID, string category)
        {
            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var id = modelID.ToString().PadLeft(4, '0');

            var assetFolder = "";
            var assetFile   = "";

            if (category.Equals(XivStrings.Furniture_Indoor))
            {
                assetFolder = $"bgcommon/hou/indoor/general/{id}/asset";
                assetFile   = $"fun_b0_m{id}.sgb";
            }
            else if (category.Equals(XivStrings.Furniture_Outdoor))
            {
                assetFolder = $"bgcommon/hou/outdoor/general/{id}/asset";
                assetFile   = $"gar_b0_m{id}.sgb";
            }

            var assetOffset = await index.GetDataOffset(HashGenerator.GetHash(assetFolder), HashGenerator.GetHash(assetFile),
                                                        XivDataFile._01_Bgcommon);

            var assetData = await dat.GetType2Data(assetOffset, XivDataFile._01_Bgcommon);

            var housingAssets = new HousingAssets();

            await Task.Run(() =>
            {
                using (var br = new BinaryReader(new MemoryStream(assetData)))
                {
                    br.BaseStream.Seek(20, SeekOrigin.Begin);

                    var skip = br.ReadInt32() + 20;

                    br.BaseStream.Seek(skip + 4, SeekOrigin.Begin);

                    var stringsOffset = br.ReadInt32();

                    br.BaseStream.Seek(skip + stringsOffset, SeekOrigin.Begin);

                    var pathCounts = 0;

                    while (true)
                    {
                        // Because we don't know the length of the string, we read the data until we reach a 0 value
                        // That 0 value is the space between strings
                        byte a;
                        var pathName = new List <byte>();
                        while ((a = br.ReadByte()) != 0)
                        {
                            if (a == 0xFF)
                            {
                                break;
                            }

                            pathName.Add(a);
                        }

                        if (a == 0xFF)
                        {
                            break;
                        }

                        // Read the string from the byte array and remove null terminators
                        var path = Encoding.ASCII.GetString(pathName.ToArray()).Replace("\0", "");

                        if (path.Equals(string.Empty))
                        {
                            continue;
                        }

                        // Add the attribute to the list
                        if (pathCounts == 0)
                        {
                            housingAssets.Shared = path;
                        }
                        else if (pathCounts == 1)
                        {
                            housingAssets.BaseFileName = path;
                        }
                        else
                        {
                            if (path.Contains(".mdl"))
                            {
                                housingAssets.MdlList.Add(path);
                            }
                            else if (path.Contains(".sgb"))
                            {
                                housingAssets.AdditionalAssetList.Add(path);
                            }
                            else if (!path.Contains("."))
                            {
                                housingAssets.BaseFolder = path;
                            }
                            else
                            {
                                housingAssets.OthersList.Add(path);
                            }
                        }

                        pathCounts++;
                    }
                }
            });

            if (housingAssets.AdditionalAssetList.Count > 0)
            {
                await GetAdditionalAssets(housingAssets);
            }


            return(housingAssets);
        }
Exemple #19
0
        /// <summary>
        /// Gets the full IMC information for a given item
        /// </summary>
        /// <param name="item"></param>
        /// <param name="useSecondary">Determines if the SecondaryModelInfo should be used instead.(XivGear only)</param>
        /// <returns>The ImcData data</returns>
        public async Task <FullImcInfo> GetFullImcInfo(string path)
        {
            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);


            var imcOffset = await index.GetDataOffset(path);

            if (imcOffset == 0)
            {
                throw new InvalidDataException($"Could not find offset for {path}");
            }

            var imcByteData = await dat.GetType2Data(imcOffset, IOUtil.GetDataFileFromPath(path));

            return(await Task.Run(() =>
            {
                using (var br = new BinaryReader(new MemoryStream(imcByteData)))
                {
                    var subsetCount = br.ReadInt16();
                    var identifier = br.ReadInt16();
                    var imcData = new FullImcInfo()
                    {
                        TypeIdentifier = (ImcType)identifier,
                        DefaultSubset = new List <XivImc>(),
                        SubsetList = new List <List <XivImc> >(subsetCount)
                    };

                    //weapons and monsters do not have variant sets
                    if (imcData.TypeIdentifier == ImcType.NonSet)
                    {
                        // This type uses the first short for both Variant and VFX.
                        byte variant = br.ReadByte();
                        byte unknown = br.ReadByte();
                        ushort mask = br.ReadUInt16();
                        ushort vfx = br.ReadUInt16();

                        imcData.DefaultSubset.Add(new XivImc
                        {
                            Variant = variant,
                            Unknown = unknown,
                            Mask = mask,
                            Vfx = variant
                        });

                        for (var i = 0; i < subsetCount; i++)
                        {
                            variant = br.ReadByte();
                            unknown = br.ReadByte();
                            mask = br.ReadUInt16();
                            vfx = br.ReadUInt16();

                            var newEntry = new XivImc
                            {
                                Variant = variant,
                                Unknown = unknown,
                                Mask = mask,
                                Vfx = vfx
                            };
                            var subset = new List <XivImc>()
                            {
                                newEntry
                            };
                            imcData.SubsetList.Add(subset);
                        }
                    }
                    else if (imcData.TypeIdentifier == ImcType.Set)
                    {
                        // Identifier used by Equipment.
                        imcData.DefaultSubset = new List <XivImc>()
                        {
                            new XivImc
                            {
                                Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            new XivImc
                            {
                                Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            new XivImc
                            {
                                Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            new XivImc
                            {
                                Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            new XivImc
                            {
                                Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                        };

                        for (var i = 0; i < subsetCount; i++)
                        {
                            // gets the data for each slot in the current variant set
                            var imcGear = new List <XivImc>()
                            {
                                new XivImc
                                {
                                    Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                new XivImc
                                {
                                    Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                new XivImc
                                {
                                    Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                new XivImc
                                {
                                    Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                new XivImc
                                {
                                    Variant = br.ReadByte(), Unknown = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                            };
                            imcData.SubsetList.Add(imcGear);
                        }
                    }
                    else
                    {
                        throw new NotSupportedException("Unknown IMC Type Identifier. (Please report this item in the TexTools Discord #bug_reports channel.)");
                    }

                    return imcData;
                }
            }));
        }
Exemple #20
0
        /// <summary>
        /// Gets the MTRL data for the given item
        /// </summary>
        /// <remarks>
        /// It requires a race (The default is usually <see cref="XivRace.Hyur_Midlander_Male"/>)
        /// It also requires an mtrl part <see cref="GearInfo.GetPartList(IItemModel, XivRace)"/> (default is 'a')
        /// </remarks>
        /// <param name="itemModel">Item that contains model data</param>
        /// <param name="race">The race for the requested data</param>
        /// <param name="part">The Mtrl part </param>
        /// <returns></returns>
        public XivMtrl GetMtrlData(IItemModel itemModel, XivRace race, char part)
        {
            var index    = new Index(_gameDirectory);
            var dat      = new Dat(_gameDirectory);
            var itemType = ItemType.GetItemType(itemModel);

            // Get mtrl path
            var mtrlPath = GetMtrlPath(itemModel, race, part, itemType);

            // Get mtrl offset
            var mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlPath.Folder), HashGenerator.GetHash(mtrlPath.File),
                                                 _dataFile);

            if (mtrlOffset == 0)
            {
                throw new Exception($"Could not find offest for {mtrlPath.Folder}/{mtrlPath.File}");
            }

            // Get uncompressed mtrl data
            var mtrlData = dat.GetType2Data(mtrlOffset, _dataFile);

            XivMtrl xivMtrl;

            using (var br = new BinaryReader(new MemoryStream(mtrlData)))
            {
                xivMtrl = new XivMtrl
                {
                    Signature            = br.ReadInt32(),
                    FileSize             = br.ReadInt16(),
                    ColorSetDataSize     = br.ReadInt16(),
                    MaterialDataSize     = br.ReadInt16(),
                    TexturePathsDataSize = br.ReadByte(),
                    Unknown             = br.ReadByte(),
                    TextureCount        = br.ReadByte(),
                    MapCount            = br.ReadByte(),
                    ColorSetCount       = br.ReadByte(),
                    Unknown1            = br.ReadByte(),
                    TextureTypePathList = new List <TexTypePath>(),
                    MTRLPath            = $"{mtrlPath.Folder}/{mtrlPath.File}"
                };

                var pathSizeList = new List <int>();

                // get the texture path offsets
                xivMtrl.TexturePathOffsetList = new List <int>(xivMtrl.TextureCount);
                for (var i = 0; i < xivMtrl.TextureCount; i++)
                {
                    xivMtrl.TexturePathOffsetList.Add(br.ReadInt16());
                    br.ReadBytes(2);

                    // add the size of the paths
                    if (i > 0)
                    {
                        pathSizeList.Add(xivMtrl.TexturePathOffsetList[i] - xivMtrl.TexturePathOffsetList[i - 1]);
                    }
                }

                // get the map path offsets
                xivMtrl.MapPathOffsetList = new List <int>(xivMtrl.MapCount);
                for (var i = 0; i < xivMtrl.MapCount; i++)
                {
                    xivMtrl.MapPathOffsetList.Add(br.ReadInt16());
                    br.ReadBytes(2);

                    // add the size of the paths
                    if (i > 0)
                    {
                        pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.MapPathOffsetList[i - 1]);
                    }
                    else
                    {
                        pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.TexturePathOffsetList[xivMtrl.TextureCount - 1]);
                    }
                }

                // get the color set offsets
                xivMtrl.ColorSetPathOffsetList = new List <int>(xivMtrl.ColorSetCount);
                for (var i = 0; i < xivMtrl.ColorSetCount; i++)
                {
                    xivMtrl.ColorSetPathOffsetList.Add(br.ReadInt16());
                    br.ReadBytes(2);

                    // add the size of the paths
                    if (i > 0)
                    {
                        pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.ColorSetPathOffsetList[i - 1]);
                    }
                    else
                    {
                        pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.MapPathOffsetList[xivMtrl.MapCount - 1]);
                    }
                }

                pathSizeList.Add(xivMtrl.TexturePathsDataSize - xivMtrl.ColorSetPathOffsetList[xivMtrl.ColorSetCount - 1]);

                var count = 0;

                // get the texture path strings
                xivMtrl.TexturePathList = new List <string>(xivMtrl.TextureCount);
                for (var i = 0; i < xivMtrl.TextureCount; i++)
                {
                    xivMtrl.TexturePathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", ""));
                    count++;
                }

                // add the textures to the TextureTypePathList
                xivMtrl.TextureTypePathList.AddRange(GetTexNames(xivMtrl.TexturePathList, _dataFile));

                // get the map path strings
                xivMtrl.MapPathList = new List <string>(xivMtrl.MapCount);
                for (var i = 0; i < xivMtrl.MapCount; i++)
                {
                    xivMtrl.MapPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", ""));
                    count++;
                }

                // get the color set path strings
                xivMtrl.ColorSetPathList = new List <string>(xivMtrl.ColorSetCount);
                for (var i = 0; i < xivMtrl.ColorSetCount; i++)
                {
                    xivMtrl.ColorSetPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", ""));
                    count++;
                }

                // If the mtrl file contains a color set, add it to the TextureTypePathList
                if (xivMtrl.ColorSetDataSize > 0)
                {
                    var ttp = new TexTypePath
                    {
                        Path     = mtrlPath.Folder + "/" + mtrlPath.File,
                        Type     = XivTexType.ColorSet,
                        DataFile = _dataFile
                    };
                    xivMtrl.TextureTypePathList.Add(ttp);
                }

                var shaderPathSize = xivMtrl.MaterialDataSize - xivMtrl.TexturePathsDataSize;

                xivMtrl.Shader = Encoding.UTF8.GetString(br.ReadBytes(shaderPathSize)).Replace("\0", "");

                xivMtrl.Unknown2 = br.ReadInt32();

                xivMtrl.ColorSetData = new List <Half>();
                for (var i = 0; i < xivMtrl.ColorSetDataSize / 2; i++)
                {
                    xivMtrl.ColorSetData.Add(new Half(br.ReadUInt16()));
                }

                xivMtrl.AdditionalDataSize = br.ReadInt16();

                xivMtrl.DataStruct1Count = br.ReadInt16();

                xivMtrl.DataStruct2Count = br.ReadInt16();

                xivMtrl.ParameterStructCount = br.ReadInt16();

                xivMtrl.ShaderNumber = br.ReadInt16();

                xivMtrl.Unknown3 = br.ReadInt16();

                xivMtrl.DataStruct1List = new List <DataStruct1>(xivMtrl.DataStruct1Count);
                for (var i = 0; i < xivMtrl.DataStruct1Count; i++)
                {
                    xivMtrl.DataStruct1List.Add(new DataStruct1 {
                        ID = br.ReadUInt32(), Unknown1 = br.ReadUInt32()
                    });
                }

                xivMtrl.DataStruct2List = new List <DataStruct2>(xivMtrl.DataStruct2Count);
                for (var i = 0; i < xivMtrl.DataStruct2Count; i++)
                {
                    xivMtrl.DataStruct2List.Add(new DataStruct2 {
                        ID = br.ReadUInt32(), Offset = br.ReadInt16(), Size = br.ReadInt16()
                    });
                }

                xivMtrl.ParameterStructList = new List <ParameterStruct>(xivMtrl.ParameterStructCount);
                for (var i = 0; i < xivMtrl.ParameterStructCount; i++)
                {
                    xivMtrl.ParameterStructList.Add(new ParameterStruct {
                        ID = br.ReadUInt32(), Unknown1 = br.ReadInt16(), Unknown2 = br.ReadInt16(), TextureIndex = br.ReadUInt32()
                    });
                }

                xivMtrl.AdditionalData = br.ReadBytes(xivMtrl.AdditionalDataSize);
            }

            return(xivMtrl);
        }
Exemple #21
0
        /// <summary>
        /// Resolves the original skeleton path in the FFXIV file system and raw extracts it.
        /// </summary>
        /// <param name="fullMdlPath">Full path to the MDL.</param>
        /// <param name="internalSkelName">Internal skeleton name (for hair).  This can be resolved if missing, though it is slightly expensive to do so.</param>
        private async Task <string> ExtractSkelb(string fullMdlPath, string internalSkelName = null)
        {
            var index    = new Index(_gameDirectory);
            var dat      = new Dat(_gameDirectory);
            var dataFile = IOUtil.GetDataFileFromPath(fullMdlPath);

            var fileName   = Path.GetFileNameWithoutExtension(fullMdlPath);
            var skelFolder = "";
            var skelFile   = "";
            var slotAbr    = "";

            if (IsNonhuman(fullMdlPath))
            {
                // Weapons / Monsters / Demihumans are simple enough cases, we just have to use different formatting strings.
                if (IsWeapon(fullMdlPath))
                {
                    skelFolder = string.Format(XivStrings.WeapSkelFolder, fileName.Substring(1, 4), "0001");
                    skelFile   = string.Format(XivStrings.WeapSkelFile, fileName.Substring(1, 4), "0001");
                }
                else if (IsMonster(fullMdlPath))
                {
                    skelFolder = string.Format(XivStrings.MonsterSkelFolder, fileName.Substring(1, 4), "0001");
                    skelFile   = string.Format(XivStrings.MonsterSkelFile, fileName.Substring(1, 4), "0001");
                }
                else if (IsDemihuman(fullMdlPath))
                {
                    skelFolder = string.Format(XivStrings.DemiSkelFolder, fileName.Substring(1, 4), "0001");
                    skelFile   = string.Format(XivStrings.DemiSkelFile, fileName.Substring(1, 4), "0001");
                }
            }
            else if (IsHair(fullMdlPath))
            {
                // Hair we have to potentially scrape the MDL file to check for EX bones to scrape those EX bones for their skeleton name.
                // Pretty ugly, but you do what you gotta do.
                if (internalSkelName == null)
                {
                    internalSkelName = await GetInternalSkelName(fullMdlPath);
                }

                // First arg is the constant string to format based on.  Second arg is Race #.
                skelFolder = string.Format(XivStrings.EquipSkelFolder, fileName.Substring(1, 4), "hair", internalSkelName, "");
                skelFile   = string.Format(XivStrings.EquipSkelFile, fileName.Substring(1, 4), internalSkelName, "");
            }
            else
            {
                // This is some jank in order to determine what id/slotAbr to use.
                var slotRegex     = new Regex("_([a-z]{3})$");
                var match         = slotRegex.Match(fileName);
                var normalSlotAbr = match.Groups[1].Value;
                var category      = Mdl.SlotAbbreviationDictionary.First(x => x.Value == normalSlotAbr).Key;
                slotAbr = SlotAbbreviationDictionary[category];

                var id = fileName.Substring(6, 4);

                // Most subtypes just use body 0001 always, but not *all* of them.
                // This weird construct is to check for those exceptions.
                if (slotAbr.Equals("base"))
                {
                    id = "0001";
                }
                skelFolder = string.Format(XivStrings.EquipSkelFolder, fileName.Substring(1, 4), slotAbr, slotAbr[0], id);
                skelFile   = string.Format(XivStrings.EquipSkelFile, fileName.Substring(1, 4), slotAbr[0], id);
            }

            // Continue only if the skeleton file exists
            if (!await index.FileExists(HashGenerator.GetHash(skelFile), HashGenerator.GetHash(skelFolder), dataFile))
            {
                // Sometimes for face skeletons id 0001 does not exist but 0002 does
                if (IsFace(fullMdlPath))
                {
                    skelFolder = string.Format(XivStrings.EquipSkelFolder, fileName.Substring(1, 4), slotAbr, slotAbr[0], "0002");
                    skelFile   = string.Format(XivStrings.EquipSkelFile, fileName.Substring(1, 4), slotAbr[0], "0002");

                    if (!await index.FileExists(HashGenerator.GetHash(skelFile), HashGenerator.GetHash(skelFolder),
                                                dataFile))
                    {
                        return(null);
                    }
                }
                else
                {
                    return(null);
                }
            }

            var offset = await index.GetDataOffset(HashGenerator.GetHash(skelFolder), HashGenerator.GetHash(skelFile), dataFile);

            if (offset == 0)
            {
                throw new Exception($"Could not find offset for {skelFolder}/{skelFile}");
            }

            var sklbData = await dat.GetType2Data(offset, dataFile);

            using (var br = new BinaryReader(new MemoryStream(sklbData)))
            {
                br.BaseStream.Seek(0, SeekOrigin.Begin);

                var magic  = br.ReadInt32();
                var format = br.ReadInt32();

                br.ReadBytes(2);

                if (magic != 0x736B6C62)
                {
                    throw new FormatException();
                }

                var dataOffset = 0;

                switch (format)
                {
                case 0x31323030:
                    dataOffset = br.ReadInt16();
                    break;

                case 0x31333030:
                case 0x31333031:
                    br.ReadBytes(2);
                    dataOffset = br.ReadInt16();
                    break;

                default:
                    throw new Exception($"Unkown Data Format ({format})");
                }

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

                var havokData = br.ReadBytes(sklbData.Length - dataOffset);

                var cwd        = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
                var outputPath = cwd + "/Skeletons/" + internalSkelName + ".sklb";
                File.WriteAllBytes(outputPath, havokData);
                return(outputPath);
            }
        }
Exemple #22
0
        /// <summary>
        /// Gets the skeleton sklb file
        /// </summary>
        /// <param name="modelName">The name of the model</param>
        /// <param name="category">The items category</param>
        private void GetSkeleton(string modelName, string category)
        {
            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var skelFolder = "";
            var skelFile   = "";

            if (modelName[0].Equals('w'))
            {
                skelFolder = string.Format(XivStrings.WeapSkelFolder, modelName.Substring(1, 4), "0001");
                skelFile   = string.Format(XivStrings.WeapSkelFile, modelName.Substring(1, 4), "0001");
            }
            else if (modelName[0].Equals('m'))
            {
                skelFolder = string.Format(XivStrings.MonsterSkelFolder, modelName.Substring(1, 4), "0001");
                skelFile   = string.Format(XivStrings.MonsterSkelFile, modelName.Substring(1, 4), "0001");
            }
            else if (modelName[0].Equals('d'))
            {
                skelFolder = string.Format(XivStrings.DemiSkelFolder, modelName.Substring(1, 4), "0001");
                skelFile   = string.Format(XivStrings.DemiSkelFile, modelName.Substring(1, 4), "0001");
            }
            else
            {
                var abr = SlotAbbreviationDictionary[category];

                var id = modelName.Substring(6, 4);

                if (abr.Equals("base"))
                {
                    id = "0001";
                }

                skelFolder = string.Format(XivStrings.EquipSkelFolder, modelName.Substring(1, 4), abr, abr[0], id);
                skelFile   = string.Format(XivStrings.EquipSkelFile, modelName.Substring(1, 4), abr[0], id);
            }

            // Continue only if the skeleton file exists
            if (!index.FileExists(HashGenerator.GetHash(skelFile), HashGenerator.GetHash(skelFolder), _dataFile)
                )
            {
                return;
            }

            var offset = index.GetDataOffset(HashGenerator.GetHash(skelFolder), HashGenerator.GetHash(skelFile), _dataFile);

            if (offset == 0)
            {
                throw new Exception($"Could not find offest for {skelFolder}/{skelFile}");
            }

            var sklbData = dat.GetType2Data(offset, _dataFile);

            using (var br = new BinaryReader(new MemoryStream(sklbData)))
            {
                br.BaseStream.Seek(0, SeekOrigin.Begin);

                var magic  = br.ReadInt32();
                var format = br.ReadInt32();

                br.ReadBytes(2);

                if (magic != 0x736B6C62)
                {
                    throw new FormatException();
                }

                var dataOffset = 0;

                switch (format)
                {
                case 0x31323030:
                    dataOffset = br.ReadInt16();
                    break;

                case 0x31333030:
                case 0x31333031:
                    br.ReadBytes(2);
                    dataOffset = br.ReadInt16();
                    break;

                default:
                    throw new Exception($"Unkown Data Format ({format})");
                }

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

                var havokData = br.ReadBytes(sklbData.Length - dataOffset);

                var mName = modelName.Substring(0, 5);

                if (category.Equals(XivStrings.Head))
                {
                    mName = modelName.Substring(5, 5);
                }

                File.WriteAllBytes(Directory.GetCurrentDirectory() + "/Skeletons/" + mName + ".sklb", havokData);
            }
        }
        /// <summary>
        /// Performs the most low-level mod enable/disable functions, without saving the modlist,
        /// ergo this should only be called by functions which will handle saving the modlist after
        /// they're done performing all modlist operations.
        ///
        /// If the Index and modlist are provided, the actions are only applied to those cached entries, rather
        /// than to the live files.
        /// </summary>
        /// <param name="enable"></param>
        /// <param name="mod"></param>
        /// <returns></returns>
        public async Task <bool> ToggleModUnsafe(bool enable, Mod mod, bool includeInternal, bool updateCache, IndexFile cachedIndex = null, ModList cachedModlist = null)
        {
            if (mod == null)
            {
                return(false);
            }
            if (string.IsNullOrEmpty(mod.name))
            {
                return(false);
            }
            if (string.IsNullOrEmpty(mod.fullPath))
            {
                return(false);
            }

            if (mod.data.originalOffset <= 0 && !enable)
            {
                throw new Exception("Cannot disable mod with invalid original offset.");
            }

            if (enable && mod.data.modOffset <= 0)
            {
                throw new Exception("Cannot enable mod with invalid mod offset.");
            }

            if (mod.IsInternal() && !includeInternal)
            {
                // Don't allow toggling internal mods unless we were specifically told to.
                return(false);
            }

            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            // Added file.
            if (enable)
            {
                if (cachedIndex != null)
                {
                    cachedIndex.SetDataOffset(mod.fullPath, mod.data.modOffset);
                }
                else
                {
                    await index.UpdateDataOffset(mod.data.modOffset, mod.fullPath, false);
                }
                mod.enabled = true;

                if (cachedIndex == null)
                {
                    // Check if we're re-enabling a metadata mod.
                    var ext = Path.GetExtension(mod.fullPath);
                    if (ext == ".meta")
                    {
                        var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                        // Retreive the uncompressed meta entry we just enabled.
                        var data = await dat.GetType2Data(mod.data.modOffset, df);

                        var meta = await ItemMetadata.Deserialize(data);

                        meta.Validate(mod.fullPath);

                        // And write that metadata to the actual constituent files.
                        await ItemMetadata.ApplyMetadata(meta, cachedIndex, cachedModlist);
                    }
                    else if (ext == ".rgsp")
                    {
                        await CMP.ApplyRgspFile(mod.fullPath, cachedIndex, cachedModlist);
                    }
                }
            }
            else if (!enable)
            {
                if (mod.IsCustomFile())
                {
                    // Delete file descriptor handles removing metadata as needed on its own.
                    if (cachedIndex != null)
                    {
                        cachedIndex.SetDataOffset(mod.fullPath, 0);
                    }
                    else
                    {
                        await index.DeleteFileDescriptor(mod.fullPath, IOUtil.GetDataFileFromPath(mod.fullPath), false);
                    }
                }
                else
                {
                    if (cachedIndex != null)
                    {
                        cachedIndex.SetDataOffset(mod.fullPath, mod.data.originalOffset);
                    }
                    else
                    {
                        await index.UpdateDataOffset(mod.data.originalOffset, mod.fullPath, false);
                    }
                }
                mod.enabled = false;
            }

            if (updateCache)
            {
                XivCache.QueueDependencyUpdate(mod.fullPath);
            }

            return(true);
        }
Exemple #24
0
        /// <summary>
        /// Gets the texture paths from the uld file
        /// </summary>
        /// <returns>List of texture paths from the uld file</returns>
        public List <string> GetTexFromUld()
        {
            var hashedFolder = HashGenerator.GetHash("ui/uld");
            var index        = new Index(_gameDirectory);
            var dat          = new Dat(_gameDirectory);

            var uldStringList = new HashSet <string>();
            var uldOffsetList = index.GetAllFileOffsetsInFolder(hashedFolder, XivDataFile._06_Ui);

            foreach (var offset in uldOffsetList)
            {
                byte[] uldData;
                try
                {
                    uldData = dat.GetType2Data(offset, XivDataFile._06_Ui);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Error at offset: {offset}");
                    Debug.WriteLine($"Message: {ex.Message}");
                    continue;
                }

                if (uldData.Length < 10)
                {
                    continue;
                }

                using (var br = new BinaryReader(new MemoryStream(uldData)))
                {
                    var signature = br.ReadInt32();

                    if (signature != 1751411829)
                    {
                        continue;
                    }

                    br.ReadBytes(56);

                    int pathCount = br.ReadByte();

                    br.ReadBytes(7);

                    for (var i = 0; i < pathCount; i++)
                    {
                        var pathNum = br.ReadInt32();
                        while (pathNum != i + 1)
                        {
                            pathNum = br.ReadInt32();
                        }

                        var path = Encoding.UTF8.GetString(br.ReadBytes(48)).Replace("\0", "");
                        path = new string(path.Where(c => !char.IsControl(c)).ToArray());

                        if (path.Length <= 2 || !path.Contains("uld"))
                        {
                            continue;
                        }

                        var uldPath = path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal) + 4);

                        uldStringList.Add(uldPath);
                    }
                }
            }

            return(uldStringList.ToList());
        }
Exemple #25
0
        /// <summary>
        /// Gets the MTRL data for the given offset and path
        /// </summary>
        /// <param name="mtrlOffset">The offset to the mtrl in the dat file</param>
        /// <param name="mtrlPath">The full internal game path for the mtrl</param>
        /// <returns>XivMtrl containing all the mtrl data</returns>
        public XivMtrl GetMtrlData(int mtrlOffset, string mtrlPath, int dxVersion)
        {
            var dat   = new Dat(_gameDirectory);
            var index = new Index(_gameDirectory);

            // Get uncompressed mtrl data
            var mtrlData = dat.GetType2Data(mtrlOffset, _dataFile);

            XivMtrl xivMtrl;

            using (var br = new BinaryReader(new MemoryStream(mtrlData)))
            {
                xivMtrl = new XivMtrl
                {
                    Signature            = br.ReadInt32(),
                    FileSize             = br.ReadInt16(),
                    ColorSetDataSize     = br.ReadUInt16(),
                    MaterialDataSize     = br.ReadUInt16(),
                    TexturePathsDataSize = br.ReadUInt16(),
                    TextureCount         = br.ReadByte(),
                    MapCount             = br.ReadByte(),
                    ColorSetCount        = br.ReadByte(),
                    UnknownDataSize      = br.ReadByte(),
                    TextureTypePathList  = new List <TexTypePath>(),
                    MTRLPath             = mtrlPath
                };

                var pathSizeList = new List <int>();

                // get the texture path offsets
                xivMtrl.TexturePathOffsetList  = new List <int>(xivMtrl.TextureCount);
                xivMtrl.TexturePathUnknownList = new List <short>(xivMtrl.TextureCount);
                for (var i = 0; i < xivMtrl.TextureCount; i++)
                {
                    xivMtrl.TexturePathOffsetList.Add(br.ReadInt16());
                    xivMtrl.TexturePathUnknownList.Add(br.ReadInt16());

                    // add the size of the paths
                    if (i > 0)
                    {
                        pathSizeList.Add(xivMtrl.TexturePathOffsetList[i] - xivMtrl.TexturePathOffsetList[i - 1]);
                    }
                }

                // get the map path offsets
                xivMtrl.MapPathOffsetList  = new List <int>(xivMtrl.MapCount);
                xivMtrl.MapPathUnknownList = new List <short>(xivMtrl.MapCount);
                for (var i = 0; i < xivMtrl.MapCount; i++)
                {
                    xivMtrl.MapPathOffsetList.Add(br.ReadInt16());
                    xivMtrl.MapPathUnknownList.Add(br.ReadInt16());

                    // add the size of the paths
                    if (i > 0)
                    {
                        pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.MapPathOffsetList[i - 1]);
                    }
                    else
                    {
                        pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.TexturePathOffsetList[xivMtrl.TextureCount - 1]);
                    }
                }

                // get the color set offsets
                xivMtrl.ColorSetPathOffsetList  = new List <int>(xivMtrl.ColorSetCount);
                xivMtrl.ColorSetPathUnknownList = new List <short>(xivMtrl.ColorSetCount);
                for (var i = 0; i < xivMtrl.ColorSetCount; i++)
                {
                    xivMtrl.ColorSetPathOffsetList.Add(br.ReadInt16());
                    xivMtrl.ColorSetPathUnknownList.Add(br.ReadInt16());

                    // add the size of the paths
                    if (i > 0)
                    {
                        pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.ColorSetPathOffsetList[i - 1]);
                    }
                    else
                    {
                        pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.MapPathOffsetList[xivMtrl.MapCount - 1]);
                    }
                }

                pathSizeList.Add(xivMtrl.TexturePathsDataSize - xivMtrl.ColorSetPathOffsetList[xivMtrl.ColorSetCount - 1]);

                var count = 0;

                // get the texture path strings
                xivMtrl.TexturePathList = new List <string>(xivMtrl.TextureCount);
                for (var i = 0; i < xivMtrl.TextureCount; i++)
                {
                    var texturePath  = Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "");
                    var dx11FileName = Path.GetFileName(texturePath).Insert(0, "--");

                    if (index.FileExists(HashGenerator.GetHash(dx11FileName),
                                         HashGenerator.GetHash(Path.GetDirectoryName(texturePath).Replace("\\", "/")), _dataFile))
                    {
                        texturePath = texturePath.Insert(texturePath.LastIndexOf("/") + 1, "--");
                    }

                    xivMtrl.TexturePathList.Add(texturePath);
                    count++;
                }

                // add the textures to the TextureTypePathList
                xivMtrl.TextureTypePathList.AddRange(GetTexNames(xivMtrl.TexturePathList, _dataFile));

                // get the map path strings
                xivMtrl.MapPathList = new List <string>(xivMtrl.MapCount);
                for (var i = 0; i < xivMtrl.MapCount; i++)
                {
                    xivMtrl.MapPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", ""));
                    count++;
                }

                // get the color set path strings
                xivMtrl.ColorSetPathList = new List <string>(xivMtrl.ColorSetCount);
                for (var i = 0; i < xivMtrl.ColorSetCount; i++)
                {
                    xivMtrl.ColorSetPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", ""));
                    count++;
                }

                // If the mtrl file contains a color set, add it to the TextureTypePathList
                if (xivMtrl.ColorSetDataSize > 0)
                {
                    var ttp = new TexTypePath
                    {
                        Path     = mtrlPath,
                        Type     = XivTexType.ColorSet,
                        DataFile = _dataFile
                    };
                    xivMtrl.TextureTypePathList.Add(ttp);
                }

                var shaderPathSize = xivMtrl.MaterialDataSize - xivMtrl.TexturePathsDataSize;

                xivMtrl.Shader = Encoding.UTF8.GetString(br.ReadBytes(shaderPathSize)).Replace("\0", "");

                xivMtrl.Unknown2 = br.ReadBytes(xivMtrl.UnknownDataSize);

                if (xivMtrl.ColorSetDataSize > 0)
                {
                    // Color Data is always 512 (6 x 14 = 64 x 8bpp = 512)
                    var colorDataSize = 512;

                    xivMtrl.ColorSetData = new List <Half>();
                    for (var i = 0; i < colorDataSize / 2; i++)
                    {
                        xivMtrl.ColorSetData.Add(new Half(br.ReadUInt16()));
                    }

                    // If the color set is 544 in length, it has an extra 32 bytes at the end
                    if (xivMtrl.ColorSetDataSize == 544)
                    {
                        xivMtrl.ColorSetExtraData = br.ReadBytes(32);
                    }
                }

                xivMtrl.AdditionalDataSize = br.ReadUInt16();

                xivMtrl.DataStruct1Count = br.ReadUInt16();

                xivMtrl.DataStruct2Count = br.ReadUInt16();

                xivMtrl.ParameterStructCount = br.ReadUInt16();

                xivMtrl.ShaderNumber = br.ReadUInt16();

                xivMtrl.Unknown3 = br.ReadUInt16();

                xivMtrl.DataStruct1List = new List <DataStruct1>(xivMtrl.DataStruct1Count);
                for (var i = 0; i < xivMtrl.DataStruct1Count; i++)
                {
                    xivMtrl.DataStruct1List.Add(new DataStruct1 {
                        ID = br.ReadUInt32(), Unknown1 = br.ReadUInt32()
                    });
                }

                xivMtrl.DataStruct2List = new List <DataStruct2>(xivMtrl.DataStruct2Count);
                for (var i = 0; i < xivMtrl.DataStruct2Count; i++)
                {
                    xivMtrl.DataStruct2List.Add(new DataStruct2 {
                        ID = br.ReadUInt32(), Offset = br.ReadInt16(), Size = br.ReadInt16()
                    });
                }

                xivMtrl.ParameterStructList = new List <ParameterStruct>(xivMtrl.ParameterStructCount);
                for (var i = 0; i < xivMtrl.ParameterStructCount; i++)
                {
                    xivMtrl.ParameterStructList.Add(new ParameterStruct {
                        ID = br.ReadUInt32(), Unknown1 = br.ReadInt16(), Unknown2 = br.ReadInt16(), TextureIndex = br.ReadUInt32()
                    });
                }

                xivMtrl.AdditionalData = br.ReadBytes(xivMtrl.AdditionalDataSize);
            }

            return(xivMtrl);
        }
Exemple #26
0
        /// <summary>
        /// Reads and parses the ExData file
        /// </summary>
        /// <remarks>
        /// This reads the data at each index of the exd file
        /// It then places the data in a dictionary with format [index, raw data]
        /// </remarks>
        /// <param name="exFile"></param>
        /// <returns>A dictionary containing the Index and Raw Data of the ex file</returns>
        public Dictionary <int, byte[]> ReadExData(XivEx exFile)
        {
            var exdOffsetList     = new List <int>();
            var exdDataDictionary = new Dictionary <int, byte[]>();

            ReadExHeader(exFile);

            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var language = "_" + _langCode;

            // Some Ex files are universal and do not have a language code
            if (LanguageList.Count <= 1)
            {
                language = "";
            }

            // Each page is a new exd file
            // A good example is item_[page]_[language].exd
            // item_0_en.exd, item_500_en.exd, item_1000_en.exd, etc.
            foreach (var page in PageList)
            {
                var exdFile = exFile + "_" + page + language + ExdExtension;

                var exdFolderHash = HashGenerator.GetHash("exd");
                var exdFileHash   = HashGenerator.GetHash(exdFile);

                exdOffsetList.Add(index.GetDataOffset(exdFolderHash, exdFileHash, XivDataFile._0A_Exd));
            }

            foreach (var offset in exdOffsetList)
            {
                var exData = dat.GetType2Data(offset, XivDataFile._0A_Exd);

                // Big Endian Byte Order
                using (var br = new BinaryReaderBE(new MemoryStream(exData)))
                {
                    br.ReadBytes(8);
                    var offsetTableSize = br.ReadInt32();

                    for (var i = 0; i < offsetTableSize; i += 8)
                    {
                        br.BaseStream.Seek(i + 32, SeekOrigin.Begin);

                        var entryNum    = br.ReadInt32();
                        var entryOffset = br.ReadInt32();

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

                        var entrySize = br.ReadInt32();
                        br.ReadBytes(2);


                        exdDataDictionary.Add(entryNum, br.ReadBytes(entrySize));
                    }
                }
            }

            return(exdDataDictionary);
        }
Exemple #27
0
        /// <summary>
        /// Gets the full IMC information for a given item
        /// </summary>
        /// <param name="item"></param>
        /// <param name="modelInfo"></param>
        /// <returns>The ImcData data</returns>
        public async Task <ImcData> GetFullImcInfo(IItemModel item, XivModelInfo modelInfo)
        {
            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var itemType = ItemType.GetItemType(item);
            var imcPath  = GetImcPath(modelInfo, itemType);

            var imcOffset = await index.GetDataOffset(HashGenerator.GetHash(imcPath.Folder), HashGenerator.GetHash(imcPath.File), _dataFile);

            if (imcOffset == 0)
            {
                throw new Exception($"Could not find offset for {imcPath.Folder}/{imcPath.File}");
            }

            var imcByteData = await dat.GetType2Data(imcOffset, _dataFile);

            return(await Task.Run(() =>
            {
                using (var br = new BinaryReader(new MemoryStream(imcByteData)))
                {
                    var imcData = new ImcData()
                    {
                        VariantCount = br.ReadInt16(),
                        Unknown = br.ReadInt16(),
                        GearVariantList = new List <VariantSet>()
                    };

                    //weapons and monsters do not have variant sets
                    if (itemType == XivItemType.weapon || itemType == XivItemType.monster)
                    {
                        imcData.OtherVariantList = new List <XivImc>();

                        imcData.DefaultVariant = new XivImc
                        {
                            Version = br.ReadUInt16(),
                            Mask = br.ReadUInt16(),
                            Vfx = br.ReadUInt16()
                        };

                        for (var i = 0; i < imcData.VariantCount; i++)
                        {
                            imcData.OtherVariantList.Add(new XivImc
                            {
                                Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            });
                        }
                    }
                    else
                    {
                        imcData.GearVariantList = new List <VariantSet>();

                        imcData.DefaultVariantSet = new VariantSet
                        {
                            Slot1 = new XivImc
                            {
                                Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            Slot2 = new XivImc
                            {
                                Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            Slot3 = new XivImc
                            {
                                Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            Slot4 = new XivImc
                            {
                                Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                            Slot5 = new XivImc
                            {
                                Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                            },
                        };

                        for (var i = 0; i < imcData.VariantCount; i++)
                        {
                            // gets the data for each slot in the current variant set
                            var imcGear = new VariantSet
                            {
                                Slot1 = new XivImc
                                {
                                    Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                Slot2 = new XivImc
                                {
                                    Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                Slot3 = new XivImc
                                {
                                    Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                Slot4 = new XivImc
                                {
                                    Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                                Slot5 = new XivImc
                                {
                                    Version = br.ReadUInt16(), Mask = br.ReadUInt16(), Vfx = br.ReadUInt16()
                                },
                            };

                            imcData.GearVariantList.Add(imcGear);
                        }
                    }

                    return imcData;
                }
            }));
        }
Exemple #28
0
        private async void ExtractButton_Click(object sender, RoutedEventArgs e)
        {
            if (String.IsNullOrWhiteSpace(FromBox.Text))
            {
                return;
            }

            var path   = FromBox.Text;
            var ext    = Path.GetExtension(path);
            var _dat   = new Dat(XivCache.GameInfo.GameDirectory);
            var _index = new Index(XivCache.GameInfo.GameDirectory);

            byte[] data = null;

            var sd = new SaveFileDialog();

            if (ext.Length > 0)
            {
                ext = ext.Substring(1);

                sd.Filter = $"{ext.ToUpper()} Files (*.{ext})|*.{ext}";
            }

            sd.FileName = Path.GetFileName(path);

            sd.RestoreDirectory = true;
            if (sd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
            {
                return;
            }

            try
            {
                var offset = await _index.GetDataOffset(path);

                var df = IOUtil.GetDataFileFromPath(path);
                if (offset <= 0)
                {
                    FlexibleMessageBox.Show("File does not exist.\n\nFile: " + path, "File Not Found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                var type = _dat.GetFileType(offset, df);
                if (type < 2 || type > 4)
                {
                    throw new InvalidDataException("Invalid or Unknown Data Type.");
                }

                var size = await _dat.GetCompressedFileSize(offset, df);

                if (type == 2)
                {
                    if (DecompressType2Box.IsChecked == true)
                    {
                        data = await _dat.GetType2Data(offset, df);
                    }
                    else
                    {
                        data = _dat.GetRawData(offset, df, size);
                    }
                }
                if (type == 3)
                {
                    data = _dat.GetRawData(offset, df, size);
                }
                if (type == 4)
                {
                    data = _dat.GetRawData(offset, df, size);
                }


                using (var stream = new BinaryWriter(sd.OpenFile()))
                {
                    stream.Write(data);
                }

                FlexibleMessageBox.Show("Raw file extracted successfully to path:\n" + sd.FileName, "Extraction Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
                this.Close();
            } catch (Exception Ex)
            {
                FlexibleMessageBox.Show("Unable to decompress or read file:\n" + path + "\n\nError: " + Ex.Message, "File Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }
Exemple #29
0
 /// <summary>
 /// Gets the raw equipment parameter file.
 /// </summary>
 /// <returns></returns>
 private async Task <byte[]> LoadEquipmentParameterFile()
 {
     return(await _dat.GetType2Data(EquipmentParameterFile, false));
 }