Пример #1
0
        /// <summary>
        /// Standard constructor.
        /// If disposeStreams is set to true, the binary streams will be disposed after use.
        /// </summary>
        public IndexFile(XivDataFile dataFile, BinaryReader index1Stream, BinaryReader index2Stream, bool disposeStreams = false)
        {
            DataFile = dataFile;

            ReadIndex1File(index1Stream);

            if (index2Stream != null)
            {
                ReadIndex2File(index2Stream);
                ReadOnlyMode = false;
            }
            else
            {
                ReadOnlyMode = true;
            }

            if (disposeStreams)
            {
                index1Stream.Dispose();
                if (index2Stream != null)
                {
                    index2Stream.Dispose();
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Gets the dat count within the index files.
        /// </summary>
        /// <param name="dataFile">The data file to update the index for.</param>
        public (int Index1, int Index2) GetIndexDatCount(XivDataFile dataFile)
        {
            int index1 = 0, index2 = 0;

            var indexPaths = new[]
            {
                Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}"),
                Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{Index2Extension}")
            };

            _semaphoreSlim.Wait();
            try
            {
                for (var i = 0; i < indexPaths.Length; i++)
                {
                    using (var br = new BinaryReader(File.OpenRead(indexPaths[i])))
                    {
                        br.BaseStream.Seek(1104, SeekOrigin.Begin);
                        if (i == 0)
                        {
                            index1 = br.ReadByte();
                        }
                        else
                        {
                            index2 = br.ReadByte();
                        }
                    }
                }
            } finally
            {
                _semaphoreSlim.Release();
            }

            return(index1, index2);
        }
Пример #3
0
        /// <summary>
        /// Gets all the folder hashes in a given folder path
        /// </summary>
        /// <param name="dataFile">The data file to look in</param>
        /// <returns>A list of all of the folder hashes</returns>
        public async Task <List <int> > GetAllFolderHashes(XivDataFile dataFile)
        {
            var folderHashList = new HashSet <int>();

            // These are the offsets to relevant data
            const int fileCountOffset = 1036;
            const int dataStartOffset = 2048;

            var indexPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");

            await Task.Run(() =>
            {
                using (var br = new BinaryReader(File.OpenRead(indexPath)))
                {
                    br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                    var totalFiles = br.ReadInt32();

                    br.BaseStream.Seek(dataStartOffset, SeekOrigin.Begin);
                    for (var i = 0; i < totalFiles; br.ReadBytes(4), i += 16)
                    {
                        br.ReadBytes(4);

                        var folderPathHash = br.ReadInt32();

                        folderHashList.Add(folderPathHash);

                        br.ReadBytes(4);
                    }
                }
            });

            return(folderHashList.ToList());
        }
Пример #4
0
        /// <summary>
        /// Determines whether the given folder path exists
        /// </summary>
        /// <param name="folderHash">The hashed folder</param>
        /// <param name="dataFile">The data file</param>
        /// <returns>True if it exists, False otherwise</returns>
        public async Task <bool> FolderExists(int folderHash, XivDataFile dataFile)
        {
            var indexPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");

            // These are the offsets to relevant data
            const int fileCountOffset = 1036;
            const int dataStartOffset = 2048;

            return(await Task.Run(() =>
            {
                using (var br = new BinaryReader(File.OpenRead(indexPath)))
                {
                    br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                    var numOfFiles = br.ReadInt32();

                    br.BaseStream.Seek(dataStartOffset, SeekOrigin.Begin);
                    for (var i = 0; i < numOfFiles; br.ReadBytes(4), i += 16)
                    {
                        var fileNameHash = br.ReadInt32();

                        var folderPathHash = br.ReadInt32();

                        if (folderPathHash == folderHash)
                        {
                            return true;
                        }

                        br.ReadBytes(4);
                    }
                }

                return false;
            }));
        }
Пример #5
0
        /// <summary>
        /// Gets the file dictionary for the data in the .dat file
        /// </summary>
        /// <param name="dataFile">The data file to look in</param>
        /// <returns>Dictionary containing (concatenated string of file+folder hashes, offset) </returns>
        public Task <Dictionary <string, int> > GetFileDictionary(XivDataFile dataFile)
        {
            return(Task.Run(() =>
            {
                var fileDictionary = new Dictionary <string, int>();
                var indexPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");

                // These are the offsets to relevant data
                const int fileCountOffset = 1036;
                const int dataStartOffset = 2048;

                using (var br = new BinaryReader(File.OpenRead(indexPath)))
                {
                    br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                    var fileCount = br.ReadInt32();

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

                    // loop through each file entry
                    for (var i = 0; i < fileCount; br.ReadBytes(4), i += 16)
                    {
                        var fileNameHash = br.ReadInt32();
                        var folderPathHash = br.ReadInt32();
                        var offset = br.ReadInt32() * 8;

                        fileDictionary.Add($"{fileNameHash}{folderPathHash}", offset);
                    }
                }

                return fileDictionary;
            }));
        }
Пример #6
0
 public Tex(DirectoryInfo gameDirectory, XivDataFile dataFile)
 {
     _gameDirectory = gameDirectory;
     _index         = new Index(_gameDirectory);
     _dat           = new Dat(_gameDirectory);
     _dataFile      = dataFile;
 }
Пример #7
0
        /// <summary>
        /// Update the dat count within the index files.
        /// </summary>
        /// <param name="dataFile">The data file to update the index for.</param>
        /// <param name="datNum">The dat number to update to.</param>
        public void UpdateIndexDatCount(XivDataFile dataFile, int datNum, bool alreadySemaphoreLocked = false)
        {
            var datCount = (byte)(datNum + 1);

            var indexPaths = new[]
            {
                Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}"),
                Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{Index2Extension}")
            };

            if (!alreadySemaphoreLocked)
            {
                _semaphoreSlim.Wait();
            }
            try
            {
                foreach (var indexPath in indexPaths)
                {
                    using (var bw = new BinaryWriter(File.OpenWrite(indexPath)))
                    {
                        bw.BaseStream.Seek(1104, SeekOrigin.Begin);
                        bw.Write(datCount);
                    }
                }
            }
            finally
            {
                if (!alreadySemaphoreLocked)
                {
                    _semaphoreSlim.Release();
                }
            }
        }
Пример #8
0
        /// <summary>
        /// Checks the dat counts in the index file
        /// </summary>
        /// <returns>Flag for problem found</returns>
        private bool CheckIndexDatCounts()
        {
            var problemFound = false;

            var filesToCheck = new XivDataFile[] { XivDataFile._0A_Exd, XivDataFile._01_Bgcommon, XivDataFile._04_Chara, XivDataFile._06_Ui };

            foreach (var file in filesToCheck)
            {
                AddText($"\t{file.GetDataFileName()} Index Files", textColor);

                var result = _problemChecker.CheckIndexDatCounts(file);

                if (result)
                {
                    _indexDatRepairList.Add(file);
                    AddText("\t\u2716\n", "Red");
                    problemFound = true;
                }
                else
                {
                    AddText("\t\u2714\n", "Green");
                }
            }

            return(problemFound);
        }
Пример #9
0
        /// <summary>
        /// Gets the dat count within the index files.
        /// </summary>
        /// <param name="dataFile">The data file to update the index for.</param>
        public (int Index1, int Index2) GetIndexDatCount(XivDataFile dataFile)
        {
            int index1 = 0, index2 = 0;

            var indexPaths = new[]
            {
                _gameDirectory + "\\" + dataFile.GetDataFileName() + IndexExtension,
                _gameDirectory + "\\" + dataFile.GetDataFileName() + Index2Extension
            };

            for (var i = 0; i < indexPaths.Length; i++)
            {
                using (var br = new BinaryReader(File.OpenRead(indexPaths[i])))
                {
                    br.BaseStream.Seek(1104, SeekOrigin.Begin);
                    if (i == 0)
                    {
                        index1 = br.ReadByte();
                    }
                    else
                    {
                        index2 = br.ReadByte();
                    }
                }
            }

            return(index1, index2);
        }
Пример #10
0
        /// <summary>
        /// Determines whether the given folder path exists
        /// </summary>
        /// <param name="folderHash">The hashed folder</param>
        /// <param name="dataFile">The data file</param>
        /// <returns>True if it exists, False otherwise</returns>
        public bool FolderExists(int folderHash, XivDataFile dataFile)
        {
            var indexPath = _gameDirectory + "\\" + dataFile.GetDataFileName() + IndexExtension;

            // These are the offsets to relevant data
            const int fileCountOffset = 1036;
            const int dataStartOffset = 2048;

            using (var br = new BinaryReader(File.OpenRead(indexPath)))
            {
                br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                var numOfFiles = br.ReadInt32();

                br.BaseStream.Seek(dataStartOffset, SeekOrigin.Begin);
                for (var i = 0; i < numOfFiles; br.ReadBytes(4), i += 16)
                {
                    var fileNameHash = br.ReadInt32();

                    var folderPathHash = br.ReadInt32();

                    if (folderPathHash == folderHash)
                    {
                        return(true);
                    }

                    br.ReadBytes(4);
                }
            }

            return(false);
        }
Пример #11
0
        /// <summary>
        /// Get all the hashed values of the files in a given folder
        /// </summary>
        /// <param name="hashedFolder">The hashed value of the folder path</param>
        /// <param name="dataFile">The data file to look in</param>
        /// <returns>A list containing the hashed values of the files in the given folder</returns>
        public List <int> GetAllHashedFilesInFolder(int hashedFolder, XivDataFile dataFile)
        {
            var fileHashesList = new List <int>();

            // These are the offsets to relevant data
            const int fileCountOffset = 1036;
            const int dataStartOffset = 2048;

            var indexPath = _gameDirectory + "\\" + dataFile.GetDataFileName() + IndexExtension;

            using (var br = new BinaryReader(File.OpenRead(indexPath)))
            {
                br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                var totalFiles = br.ReadInt32();

                br.BaseStream.Seek(dataStartOffset, SeekOrigin.Begin);
                for (var i = 0; i < totalFiles; br.ReadBytes(4), i += 16)
                {
                    var hashedFile = br.ReadInt32();

                    var folderPathHash = br.ReadInt32();

                    if (folderPathHash == hashedFolder)
                    {
                        fileHashesList.Add(hashedFile);
                    }

                    br.ReadBytes(4);
                }
            }

            return(fileHashesList);
        }
Пример #12
0
        /// <summary>
        /// Deletes a file descriptor/stub from the Index files.
        /// </summary>
        /// <param name="fullPath">Full internal file path to the file that should be deleted.</param>
        /// <param name="dataFile">Which data file to use</param>
        /// <returns></returns>
        public async Task <bool> DeleteFileDescriptor(string fullPath, XivDataFile dataFile, bool updateCache = true)
        {
            await UpdateDataOffset(0, fullPath, false, true);

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

                await ItemMetadata.RestoreDefaultMetadata(root);
            }

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

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

            return(true);
        }
Пример #13
0
        /// <summary>
        /// Checks to see whether the index file is locked
        /// </summary>
        /// <param name="dataFile">The data file to check</param>
        /// <returns>True if locked</returns>
        public bool IsIndexLocked(XivDataFile dataFile)
        {
            var fileName = dataFile.GetDataFileName();

            var indexPath  = $"{_gameDirectory}\\{fileName}{IndexExtension}";
            var index2Path = $"{_gameDirectory}\\{fileName}{Index2Extension}";

            FileStream stream  = null;
            FileStream stream1 = null;

            try
            {
                stream  = File.Open(indexPath, FileMode.Open);
                stream1 = File.Open(index2Path, FileMode.Open);
            }
            catch (Exception e)
            {
                return(true);
            }
            finally
            {
                stream?.Close();
                stream1?.Close();
            }

            return(false);
        }
Пример #14
0
        /// <summary>
        /// Checks to see whether the index file is locked
        /// </summary>
        /// <param name="dataFile">The data file to check</param>
        /// <returns>True if locked</returns>
        public bool IsIndexLocked(XivDataFile dataFile)
        {
            var fileName = dataFile.GetDataFileName();
            var isLocked = false;

            var indexPath  = Path.Combine(_gameDirectory.FullName, $"{fileName}{IndexExtension}");
            var index2Path = Path.Combine(_gameDirectory.FullName, $"{fileName}{Index2Extension}");

            FileStream stream  = null;
            FileStream stream1 = null;

            try
            {
                stream  = File.Open(indexPath, FileMode.Open);
                stream1 = File.Open(index2Path, FileMode.Open);
            }
            catch (Exception e)
            {
                isLocked = true;
            }
            finally
            {
                stream?.Dispose();
                stream?.Close();
                stream1?.Dispose();
                stream1?.Close();
            }

            return(isLocked);
        }
Пример #15
0
        /// <summary>
        /// Checks the index for the number of dats the game will attempt to read
        /// </summary>
        /// <returns>True if there is a problem, False otherwise</returns>
        public Task <bool> CheckForLargeDats(XivDataFile dataFile)
        {
            return(Task.Run(() =>
            {
                var largestDatNum = _dat.GetLargestDatNumber(dataFile) + 1;

                var fileSizeList = new List <long>();

                for (var i = 0; i < largestDatNum; i++)
                {
                    var fileInfo = new FileInfo(Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}.win32.dat{i}"));

                    try
                    {
                        fileSizeList.Add(fileInfo.Length);
                    }
                    catch
                    {
                        return true;
                    }
                }

                if (largestDatNum > 8 || fileSizeList.FindAll(x => x.Equals(2048)).Count > 1)
                {
                    return true;
                }

                return false;
            }));
        }
Пример #16
0
        /// <summary>
        /// Gets the description from the enum value, in this case the File Name
        /// </summary>
        /// <param name="value">The enum value</param>
        /// <returns>The File Name</returns>
        public static string GetDataFileName(this XivDataFile value)
        {
            var field     = value.GetType().GetField(value.ToString());
            var attribute = (XivDataFileDescriptionAttribute[])field.GetCustomAttributes(typeof(XivDataFileDescriptionAttribute), false);

            return(attribute.Length > 0 ? attribute[0].CatNumber : value.ToString());
        }
Пример #17
0
        public bool ValidateIndexFiles()
        {
            bool keepGoing = true;

            if (MainClass._gameDirectory != null)
            {
                string modlistPath = Path.Combine(MainClass._gameDirectory.FullName, "XivMods.json");
                if (!File.Exists(modlistPath))
                {
                    ProblemChecker problemChecker = new ProblemChecker(MainClass._indexDirectory);
                    var            filesToCheck   = new XivDataFile[] { XivDataFile._0A_Exd, XivDataFile._01_Bgcommon, XivDataFile._04_Chara, XivDataFile._06_Ui };
                    bool           modifiedIndex  = false;
                    foreach (var file in filesToCheck)
                    {
                        var datCountCheck = problemChecker.CheckIndexDatCounts(file);
                        datCountCheck.Wait();
                        if (datCountCheck.Result)
                        {
                            modifiedIndex = true;
                            break;
                        }
                    }
                    if (modifiedIndex)
                    {
                        main.PrintMessage("HERE BE DRAGONS\nPreviously modified game files found\nUse the originally used tool to start over, or reinstall the game before using this tool", 2);
                        keepGoing = PromptContinuation();
                    }
                }
                else
                {
                    var    modData           = JsonConvert.DeserializeObject <ModList>(File.ReadAllText(modlistPath));
                    bool   unsupportedSource = false;
                    string unknownSource     = "";

                    //List of acceptable mod sources
                    //FFXIV_Modding_Tool is used by this tool
                    //FilesAddedByTexTools is hardcoded in the framework and is used in certain situations
                    //BLANK seems to be caused by a framework bug as well, so we allow it
                    List <string> acceptedSourcesList = new List <string> {
                        "FFXIV_Modding_Tool", "FilesAddedByTexTools", "_INTERNAL_", ""
                    };
                    foreach (Mod mod in modData.Mods)
                    {
                        if (!acceptedSourcesList.Contains(mod.source))
                        {
                            unknownSource     = mod.source;
                            unsupportedSource = true;
                            break;
                        }
                    }
                    if (unsupportedSource)
                    {
                        main.PrintMessage($"Found a mod applied by an unknown application, game stability cannot be guaranteed: {unknownSource}", 3);
                        keepGoing = PromptContinuation();
                    }
                }
            }
            return(keepGoing);
        }
Пример #18
0
        /// <summary>
        /// Gets the offset for the data in the .dat file
        /// </summary>
        /// <param name="hashedFolder">The hashed value of the folder path</param>
        /// <param name="hashedFile">The hashed value of the file name</param>
        /// <param name="dataFile">The data file to look in</param>
        /// <returns>The offset to the data</returns>
        public Task <int> GetDataOffset(int hashedFolder, int hashedFile, XivDataFile dataFile)
        {
            return(Task.Run(async() =>
            {
                var indexPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");
                var offset = 0;

                // These are the offsets to relevant data
                const int fileCountOffset = 1036;
                const int dataStartOffset = 2048;

                await _semaphoreSlim.WaitAsync();

                try
                {
                    using (var br = new BinaryReader(File.OpenRead(indexPath)))
                    {
                        br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                        var fileCount = br.ReadInt32();

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

                        // loop through each file entry
                        for (var i = 0; i < fileCount; br.ReadBytes(4), i += 16)
                        {
                            var fileNameHash = br.ReadInt32();

                            // check if the provided file name hash matches the current file name hash
                            if (fileNameHash == hashedFile)
                            {
                                var folderPathHash = br.ReadInt32();

                                // check if the provided folder path hash matches the current folder path hash
                                if (folderPathHash == hashedFolder)
                                {
                                    // this is the entry we are looking for, get the offset and break out of the loop
                                    offset = br.ReadInt32() * 8;
                                    break;
                                }

                                br.ReadBytes(4);
                            }
                            else
                            {
                                br.ReadBytes(8);
                            }
                        }
                    }
                }
                finally
                {
                    _semaphoreSlim.Release();
                }

                return offset;
            }));
        }
Пример #19
0
        /// <summary>
        /// Repairs the dat count in the index files
        /// </summary>
        public Task RepairIndexDatCounts(XivDataFile dataFile)
        {
            return(Task.Run(() =>
            {
                var largestDatNum = _dat.GetLargestDatNumber(dataFile);

                _index.UpdateIndexDatCount(dataFile, largestDatNum);
            }));
        }
Пример #20
0
        /// <summary>
        /// Repairs the dat count in the index files
        /// </summary>
        public void RepairIndexDatCounts(XivDataFile dataFile)
        {
            var index = new Index(_gameDirectory);
            var dat   = new Dat(_gameDirectory);

            var largestDatNum = dat.GetLargestDatNumber(dataFile);

            index.UpdateIndexDatCount(dataFile, largestDatNum);
        }
Пример #21
0
        /// <summary>
        /// Retrieves all of the offsets for an arbitrary list of files within the same data file, via their Index2 entries.
        /// </summary>
        /// <param name="dataFile"></param>
        /// <returns></returns>
        private async Task <Dictionary <string, long> > GetDataOffsetsIndex2(XivDataFile dataFile, Dictionary <uint, string> fileHashes)
        {
            var ret = new Dictionary <string, long>();

            return(await Task.Run(async() =>
            {
                var index2Path = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{Index2Extension}");


                var SegmentHeaders = new int[4];
                var SegmentOffsets = new int[4];
                var SegmentSizes = new int[4];

                // Segment header offsets
                SegmentHeaders[0] = 1028;                   // Files
                SegmentHeaders[1] = 1028 + (72 * 1) + 4;    // Unknown
                SegmentHeaders[2] = 1028 + (72 * 2) + 4;    // Unknown
                SegmentHeaders[3] = 1028 + (72 * 3) + 4;    // Folders


                await _semaphoreSlim.WaitAsync();
                try
                {
                    // Might as well grab the whole thing since we're doing a full scan.
                    byte[] originalIndex = File.ReadAllBytes(index2Path);

                    // Get all the segment header data
                    for (int i = 0; i < SegmentHeaders.Length; i++)
                    {
                        SegmentOffsets[i] = BitConverter.ToInt32(originalIndex, SegmentHeaders[i] + 4);
                        SegmentSizes[i] = BitConverter.ToInt32(originalIndex, SegmentHeaders[i] + 8);
                    }

                    int fileCount = SegmentSizes[0] / 8;

                    for (int i = 0; i < fileCount; i++)
                    {
                        int position = SegmentOffsets[0] + (i * 8);
                        uint iFullPathHash = BitConverter.ToUInt32(originalIndex, position);
                        uint iOffset = BitConverter.ToUInt32(originalIndex, position + 4);

                        // Index 2 is just in hash order, so find the spot where we fit in.
                        if (fileHashes.ContainsKey(iFullPathHash))
                        {
                            long offset = (long)iOffset;
                            ret.Add(fileHashes[iFullPathHash], offset * 8);
                        }
                    }
                }
                finally
                {
                    _semaphoreSlim.Release();
                }
                return ret;
            }));
        }
Пример #22
0
        public bool ValidateBackups()
        {
            main.PrintMessage("Checking backups before proceeding...");
            bool keepGoing    = true;
            bool problemFound = false;

            if (MainClass._backupDirectory == null)
            {
                main.PrintMessage($"No backup directory specified, can't check the status of backups.\nYou are strongly recommended to add a backup directory in {Path.Combine(MainClass._projectconfDirectory.FullName, "config.cfg")} and running the 'backup' command before proceeding", 2);
                problemFound = true;
            }
            else if (MainClass._gameDirectory == null)
            {
                main.PrintMessage("No game directory specified, can't check if backups are up to date", 2);
                problemFound = true;
            }
            else
            {
                var            filesToCheck   = new XivDataFile[] { XivDataFile._01_Bgcommon, XivDataFile._04_Chara, XivDataFile._06_Ui };
                ProblemChecker problemChecker = new ProblemChecker(MainClass._indexDirectory);
                foreach (var file in filesToCheck)
                {
                    if (!File.Exists(Path.Combine(MainClass._backupDirectory.FullName, $"{file.GetDataFileName()}.win32.index")))
                    {
                        main.PrintMessage($"One or more index files could not be found in {MainClass._backupDirectory.FullName}. Creating new ones or downloading them from the TexTools discord is recommended", 2);
                        problemFound = true;
                        break;
                    }
                    var outdatedBackupsCheck = problemChecker.CheckForOutdatedBackups(file, MainClass._backupDirectory);
                    outdatedBackupsCheck.Wait();
                    if (!outdatedBackupsCheck.Result)
                    {
                        main.PrintMessage($"One or more index files are out of date in {MainClass._backupDirectory.FullName}. Recreating or downloading them from the TexTools discord is recommended", 2);
                        problemFound = true;
                        break;
                    }
                }
            }
            if (problemFound)
            {
                if (PromptContinuation("Would you like to back up now?", true))
                {
                    main.BackupIndexes();
                    keepGoing = true;
                }
                else
                {
                    keepGoing = PromptContinuation();
                }
            }
            else
            {
                main.PrintMessage("All backups present and up to date", 1);
            }
            return(keepGoing);
        }
Пример #23
0
        /// <summary>
        /// Updates the .index files offset for a given item.
        /// </summary>
        /// <param name="offset">The new offset to be used.</param>
        /// <param name="fullPath">The internal path of the file whos offset is to be updated.</param>
        /// <param name="dataFile">The data file to update the index for</param>
        /// <returns>The offset which was replaced.</returns>
        public async Task <int> UpdateIndex(long offset, string fullPath, XivDataFile dataFile)
        {
            fullPath = fullPath.Replace("\\", "/");
            var folderHash =
                HashGenerator.GetHash(fullPath.Substring(0, fullPath.LastIndexOf("/", StringComparison.Ordinal)));
            var fileHash  = HashGenerator.GetHash(Path.GetFileName(fullPath));
            var oldOffset = 0;

            // These are the offsets to relevant data
            const int fileCountOffset = 1036;
            const int dataStartOffset = 2048;

            var indexPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");

            await Task.Run(() =>
            {
                using (var index = File.Open(indexPath, FileMode.Open))
                {
                    using (var br = new BinaryReader(index))
                    {
                        using (var bw = new BinaryWriter(index))
                        {
                            br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                            var numOfFiles = br.ReadInt32();

                            br.BaseStream.Seek(dataStartOffset, SeekOrigin.Begin);
                            for (var i = 0; i < numOfFiles; br.ReadBytes(4), i += 16)
                            {
                                var fileNameHash = br.ReadInt32();

                                if (fileNameHash == fileHash)
                                {
                                    var folderPathHash = br.ReadInt32();

                                    if (folderPathHash == folderHash)
                                    {
                                        oldOffset = br.ReadInt32();
                                        bw.BaseStream.Seek(br.BaseStream.Position - 4, SeekOrigin.Begin);
                                        bw.Write(offset / 8);
                                        break;
                                    }

                                    br.ReadBytes(4);
                                }
                                else
                                {
                                    br.ReadBytes(8);
                                }
                            }
                        }
                    }
                }
            });

            return(oldOffset);
        }
Пример #24
0
        /// <summary>
        /// Gets all the file offsets in a given folder path
        /// </summary>
        /// <param name="hashedFolder">The hashed value of the folder path</param>
        /// <param name="dataFile">The data file to look in</param>
        /// <returns>A list of all of the offsets in the given folder</returns>
        public async Task <List <long> > GetAllFileOffsetsInFolder(int hashedFolder, XivDataFile dataFile)
        {
            var index = await GetIndexFile(dataFile, false, true);

            var entries = index.GetEntriesInFolder((uint)hashedFolder);

            var hashes = entries.Select(x => ((long)x.RawOffset) * 8L);

            return(hashes.ToList());
        }
Пример #25
0
        /// <summary>
        /// Gets the largest dat number for a given data file.
        /// </summary>
        /// <param name="dataFile">The data file to check.</param>
        /// <returns>The largest dat number for the given data file.</returns>
        private int GetLargestDatNumber(XivDataFile dataFile)
        {
            var allFiles = Directory.GetFiles(_gameDirectory.FullName);

            var dataFiles = from file in allFiles where file.Contains(dataFile.GetDataFileName()) && file.Contains(".dat") select file;

            var max = dataFiles.Select(file => int.Parse(file.Substring(file.Length - 1))).Concat(new[] { 0 }).Max();

            return(max);
        }
Пример #26
0
        /// <summary>
        /// Get all the hashed values of the files in a given folder
        /// </summary>
        /// <param name="hashedFolder">The hashed value of the folder path</param>
        /// <param name="dataFile">The data file to look in</param>
        /// <returns>A list containing the hashed values of the files in the given folder</returns>
        public async Task <List <int> > GetAllHashedFilesInFolder(int hashedFolder, XivDataFile dataFile)
        {
            var index = await GetIndexFile(dataFile, false, true);

            var entries = index.GetEntriesInFolder((uint)hashedFolder);

            var hashes = entries.Select(x => (int)x.FileNameHash);

            return(hashes.ToList());
        }
Пример #27
0
        /// <summary>
        /// Creates an Index File object from the game index files.
        /// </summary>
        /// <param name="dataFile"></param>
        /// <returns></returns>
        public async Task <IndexFile> GetIndexFile(XivDataFile dataFile, bool alreadySemaphoreLocked = false, bool allowReadOnly = false)
        {
            var index1Path = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");
            var index2Path = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{Index2Extension}");

            if (!alreadySemaphoreLocked)
            {
                await _semaphoreSlim.WaitAsync();
            }
            try {
                IndexFile index;

                if (!allowReadOnly)
                {
                    // If we're getting a writeable index, we need to get a fresh copy to avoid polluting the cache.
                    using (var index1Stream = new BinaryReader(File.OpenRead(index1Path)))
                    {
                        using (var index2Stream = new BinaryReader(File.OpenRead(index2Path)))
                        {
                            index = new IndexFile(dataFile, index1Stream, index2Stream);
                        }
                    }
                    return(index);
                }
                else
                {
                    var lastTime     = File.GetLastWriteTimeUtc(index1Path).Ticks;
                    var creationTime = File.GetCreationTimeUtc(index1Path).Ticks;

                    // If we don't have the file cached or the write time doesn't match exactly.
                    if (!_ReadOnlyIndexLastModifiedTime.ContainsKey(dataFile) || lastTime != _ReadOnlyIndexLastModifiedTime[dataFile] || lastTime == creationTime || lastTime == 0)
                    {
                        using (var index1Stream = new BinaryReader(File.OpenRead(index1Path)))
                        {
                            index = new IndexFile(dataFile, index1Stream, null);
                        }

                        _ReadOnlyIndexLastModifiedTime[dataFile] = lastTime;
                        _CachedReadOnlyIndexFiles[dataFile]      = index;
                        return(index);
                    }
                    else
                    {
                        return(_CachedReadOnlyIndexFiles[dataFile]);
                    }
                }
            }
            finally
            {
                if (!alreadySemaphoreLocked)
                {
                    _semaphoreSlim.Release();
                }
            }
        }
Пример #28
0
        /// <summary>
        /// Gets the data for type 2 files.
        /// </summary>
        /// <remarks>
        /// Type 2 files vary in content.
        /// </remarks>
        /// <param name="offset">The offset where the data is located.</param>
        /// <param name="dataFile">The data file that contains the data.</param>
        /// <returns>Byte array containing the decompressed type 2 data.</returns>
        public byte[] GetType2Data(int offset, XivDataFile dataFile)
        {
            var type2Bytes = new List <byte>();

            // This formula is used to obtain the dat number in which the offset is located
            var datNum = ((offset / 8) & 0x0F) / 2;

            var datPath = _gameDirectory + "\\" + dataFile.GetDataFileName() + DatExtension + datNum;

            offset = OffsetCorrection(datNum, offset);

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

                var headerLength = br.ReadInt32();

                br.ReadBytes(16);

                var dataBlockCount = br.ReadInt32();

                for (var i = 0; i < dataBlockCount; i++)
                {
                    br.BaseStream.Seek(offset + (24 + (8 * i)), SeekOrigin.Begin);

                    var dataBlockOffset = br.ReadInt32();

                    br.BaseStream.Seek(offset + headerLength + dataBlockOffset, SeekOrigin.Begin);

                    br.ReadBytes(8);

                    var compressedSize   = br.ReadInt32();
                    var uncompressedSize = br.ReadInt32();

                    // When the compressed size of a data block shows 32000, it is uncompressed.
                    if (compressedSize == 32000)
                    {
                        type2Bytes.AddRange(br.ReadBytes(uncompressedSize));
                    }
                    else
                    {
                        var compressedData = br.ReadBytes(compressedSize);

                        var decompressedData = IOUtil.Decompressor(compressedData, uncompressedSize);

                        type2Bytes.AddRange(decompressedData);
                    }
                }
            }

            return(type2Bytes.ToArray());
        }
Пример #29
0
        public Task <bool> RestoreBackups(DirectoryInfo backupsDirectory)
        {
            return(Task.Run(async() =>
            {
                var backupFiles = Directory.GetFiles(backupsDirectory.FullName);
                var filesToCheck = new XivDataFile[] { XivDataFile._0A_Exd, XivDataFile._04_Chara, XivDataFile._06_Ui, XivDataFile._01_Bgcommon };
                var outdated = false;

                foreach (var xivDataFile in filesToCheck)
                {
                    var backupFile = new DirectoryInfo($"{backupsDirectory.FullName}\\{xivDataFile.GetDataFileName()}.win32.index");

                    if (!File.Exists(backupFile.FullName))
                    {
                        continue;
                    }

                    try
                    {
                        var outdatedCheck = await CheckForOutdatedBackups(xivDataFile, backupsDirectory);

                        if (!outdatedCheck)
                        {
                            outdated = true;
                        }
                    }
                    catch {
                        // If the outdated check errored out, we likely have completely broken internal dat files.
                        // ( Either deleted or 0 byte files ), so replacing them with *anything* is an improvement.
                    }
                }

                var _index = new Index(_gameDirectory);
                // Make sure backups exist and are up to date unless called with forceRestore true
                if (backupFiles.Length != 0 && !outdated)
                {
                    // Copy backups to ffxiv folder
                    foreach (var backupFile in backupFiles)
                    {
                        if (backupFile.Contains(".win32.index"))
                        {
                            File.Copy(backupFile, $"{_gameDirectory}/{Path.GetFileName(backupFile)}", true);
                        }
                    }

                    // Update all the index counts to be safe, in case the user's index backups were generated when some mod dats existed.
                    _index.UpdateAllIndexDatCounts();
                    return true;
                }
                return false;
            }));
        }
Пример #30
0
        /// <summary>
        /// Retrieves all of the offsets for an arbitrary list of files within the same data file.
        /// </summary>
        /// <param name="dataFile"></param>
        /// <returns></returns>
        private async Task <Dictionary <string, long> > GetDataOffsets(XivDataFile dataFile, Dictionary <int, Dictionary <int, string> > FolderFiles)
        {
            var ret = new Dictionary <string, long>();

            return(await Task.Run(() =>
            {
                var indexPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{IndexExtension}");

                // These are the offsets to relevant data
                const int fileCountOffset = 1036;
                const int dataStartOffset = 2048;

                int count = 0;
                _semaphoreSlim.Wait();
                try
                {
                    using (var br = new BinaryReader(File.OpenRead(indexPath)))
                    {
                        br.BaseStream.Seek(fileCountOffset, SeekOrigin.Begin);
                        var fileCount = br.ReadInt32();

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

                        // loop through each file entry
                        for (var i = 0; i < fileCount; i += 16)
                        {
                            var fileNameHash = br.ReadInt32();
                            var folderPathHash = br.ReadInt32();
                            long offset = br.ReadUInt32();
                            var unused = br.ReadInt32();

                            if (FolderFiles.ContainsKey(folderPathHash))
                            {
                                if (FolderFiles[folderPathHash].ContainsKey(fileNameHash))
                                {
                                    count++;
                                    ret.Add(FolderFiles[folderPathHash][fileNameHash], offset * 8);
                                }
                            }
                        }
                    }
                }
                finally
                {
                    _semaphoreSlim.Release();
                }

                return ret;
            }));
        }