예제 #1
0
        private async Task <bool> AddFile(string file)
        {
            var _index = new Index(XivCache.GameInfo.GameDirectory);

            if (Path.GetExtension(file) != ".meta")
            {
                if (!(await _index.FileExists(file)))
                {
                    // File doesn't actually exist, can't be added.
                    return(false);
                }
            }

            var match = _suffixRegex.Match(file);

            if (match.Success && match.Groups[1].Value == "mdl")
            {
                ModelListBox.Items.Add(new StandardModpackFileSelect.FileEntry(file));
            }
            else if (match.Success && (match.Groups[1].Value == "mtrl" || match.Groups[1].Value == "avfx"))
            {
                MaterialListBox.Items.Add(new StandardModpackFileSelect.FileEntry(file));
            }
            else if (match.Success && (match.Groups[1].Value == "tex" || match.Groups[1].Value == "atex"))
            {
                TextureListBox.Items.Add(new StandardModpackFileSelect.FileEntry(file));
            }
            else
            {
                MetaListBox.Items.Add(new StandardModpackFileSelect.FileEntry(file));
            }
            return(true);
        }
예제 #2
0
        public SimpleModPackImporter(DirectoryInfo modPackDirectory, ModPackJson modPackJson, TextureViewModel textureViewModel, ModelViewModel modelViewModel, bool silent = false, bool messageInImport = false)
        {
            this.DataContext = this;

            InitializeComponent();

            JsonEntries     = new List <ModsJson>();
            SelectedEntries = new HashSet <int>();

            _modPackDirectory = modPackDirectory;
            _gameDirectory    = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory);
            _modding          = new Modding(_gameDirectory);
            _texToolsModPack  = new TTMP(new DirectoryInfo(Properties.Settings.Default.ModPack_Directory),
                                         XivStrings.TexTools);
            _messageInImport  = messageInImport;
            _textureViewModel = textureViewModel;
            _modelViewModel   = modelViewModel;

            var index = new Index(_gameDirectory);

            _indexLockStatus = index.IsIndexLocked(XivDataFile._0A_Exd);


            _packJson = modPackJson;
            _silent   = silent;

            ModListView.IsEnabled        = false;
            LockedStatusLabel.Foreground = Brushes.Black;
            LockedStatusLabel.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Left;
            LockedStatusLabel.Content = UIStrings.Loading;

            Task.Run(Initialize);
        }
예제 #3
0
        /// <summary>
        /// Toggles the mod on or off
        /// </summary>
        /// <param name="internalFilePath">The internal file path of the mod</param>
        /// <param name="enable">The status of the mod</param>
        public async Task <bool> ToggleModStatus(string internalFilePath, bool enable)
        {
            if (XivCache.GameInfo.UseLumina)
            {
                throw new Exception("TexTools mods cannot be altered in Lumina mode.");
            }

            var index = new Index(_gameDirectory);

            if (string.IsNullOrEmpty(internalFilePath))
            {
                throw new Exception("File Path missing, unable to toggle mod.");
            }
            var modList = GetModList();

            var modEntry = modList.Mods.FirstOrDefault(x => x.fullPath == internalFilePath);

            var result = await ToggleModUnsafe(enable, modEntry, false, true);

            if (!result)
            {
                return(result);
            }

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


            SaveModList(modList);

            return(result);
        }
예제 #4
0
        /// <summary>
        /// Toggles the mod on or off
        /// </summary>
        /// <param name="internalFilePath">The internal file path of the mod</param>
        /// <param name="enable">The status of the mod</param>
        public async Task ToggleModPackStatus(string modPackName, bool enable)
        {
            if (XivCache.GameInfo.UseLumina)
            {
                throw new Exception("TexTools mods cannot be altered in Lumina mode.");
            }

            var index = new Index(_gameDirectory);

            var        modList          = GetModList();
            var        modListDirectory = new DirectoryInfo(Path.Combine(_gameDirectory.Parent.Parent.FullName, XivStrings.ModlistFilePath));
            List <Mod> mods             = null;

            if (modPackName.Equals("Standalone (Non-ModPack)"))
            {
                mods = (from mod in modList.Mods
                        where mod.modPack == null
                        select mod).ToList();
            }
            else
            {
                mods = (from mod in modList.Mods
                        where mod.modPack != null && mod.modPack.name.Equals(modPackName)
                        select mod).ToList();
            }


            if (mods == null)
            {
                throw new Exception("Unable to find mods with given Mod Pack Name in modlist.");
            }

            await ToggleMods(enable, mods.Select(x => x.fullPath));
        }
예제 #5
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);
            }
        }
예제 #6
0
 /// <summary>
 /// Reads and parses Ex Header files.
 /// </summary>
 /// <param name="gameDirectory">The install directory for the game.</param>
 /// <param name="lang">The language in which to read the data.</param>
 public Ex(DirectoryInfo gameDirectory, XivLanguage lang)
 {
     _gameDirectory = gameDirectory;
     _langCode      = lang.GetLanguageCode();
     _index         = new Index(_gameDirectory);
     _dat           = new Dat(_gameDirectory);
 }
예제 #7
0
 public Tex(DirectoryInfo gameDirectory, XivDataFile dataFile)
 {
     _gameDirectory = gameDirectory;
     _index         = new Index(_gameDirectory);
     _dat           = new Dat(_gameDirectory);
     _dataFile      = dataFile;
 }
예제 #8
0
 /// <summary>
 /// Reads and parses Ex Header files, uses english as default language.
 /// </summary>
 /// <remarks>
 /// Used for ex files that do not have language data
 /// </remarks>
 /// <param name="gameDirectory">The install directory for the game.</param>
 public Ex(DirectoryInfo gameDirectory)
 {
     _gameDirectory = gameDirectory;
     _langCode      = XivLanguage.English.GetLanguageCode();
     _index         = new Index(_gameDirectory);
     _dat           = new Dat(_gameDirectory);
 }
예제 #9
0
        private async Task AddFile(FileEntry file, IItem item, byte[] rawData = null)
        {
            var dat = new Dat(_gameDirectory);
            var index = new Index(_gameDirectory);

            if (file == null || file.Path == null || _selectedModOption == null) return;

            var includedModsList = IncludedModsList.Items.Cast<FileEntry>().ToList();

            if (includedModsList.Any(f => f.Path.Equals(file.Path)))
            {
                if (FlexibleMessageBox.Show(
                        string.Format(UIMessages.ExistingOption, file.Name),
                        UIMessages.OverwriteTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question) !=
                    System.Windows.Forms.DialogResult.Yes)
                {
                    return;
                }
            }

            if (rawData == null)
            {
                var df = IOUtil.GetDataFileFromPath(file.Path);
                var offset = await index.GetDataOffset(file.Path);
                if (offset <= 0)
                {
                    FlexibleMessageBox.Show("Cannot include file, file offset invalid.",
                        UIMessages.ModDataReadErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                var size = await dat.GetCompressedFileSize(offset, df);
                rawData = dat.GetRawData(offset, df, size);

                if (rawData == null)
                {
                    FlexibleMessageBox.Show("Cannot include file, file offset invalid.",
                        UIMessages.ModDataReadErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
            }

            
            if(_selectedModOption.Mods.ContainsKey(file.Path))
            {
                _selectedModOption.Mods[file.Path].ModDataBytes = rawData;
            } else
            {
                IncludedModsList.Items.Add(file);
                var modData = new ModData
                {
                    Name = item.Name,
                    Category = item.SecondaryCategory,
                    FullPath = file.Path,
                    ModDataBytes = rawData,
                };
                _selectedModOption.Mods.Add(file.Path, modData);
            }

        }
예제 #10
0
        private async void ImportButton_Click(object sender, RoutedEventArgs e)
        {
            var path = PathBox.Text;

            if (String.IsNullOrWhiteSpace(path))
            {
                return;
            }

            var od     = new OpenFileDialog();
            var result = od.ShowDialog();

            if (result != System.Windows.Forms.DialogResult.OK)
            {
                return;
            }

            byte[] data = null;

            var _dat   = new Dat(XivCache.GameInfo.GameDirectory);
            var _index = new Index(XivCache.GameInfo.GameDirectory);

            if (DecompressedType2.IsChecked == true)
            {
                var temp = File.ReadAllBytes(od.FileName);
                data = await _dat.CreateType2Data(temp);
            }
            else
            {
                data = File.ReadAllBytes(od.FileName);
            }

            var type = BitConverter.ToInt32(data, 4);

            if (type < 2 || type > 4)
            {
                FlexibleMessageBox.Show("Invalid Data Type.\nGeneric binary files should be imported as decompressed type 2 Data.", "Data Type Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            try
            {
                var dummyItem = new XivGenericItemModel();
                dummyItem.Name = Path.GetFileName(path);
                dummyItem.SecondaryCategory = "Raw File Imports";
                await _dat.WriteModFile(data, path, XivStrings.TexTools, dummyItem);
            }
            catch (Exception Ex)
            {
                FlexibleMessageBox.Show("Unable to import file.\n\nError: " + Ex.Message, "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            FlexibleMessageBox.Show("File Imported Successfully.", "Import Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
            this.Close();
        }
예제 #11
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;
            }));
        }
예제 #12
0
        private async Task DoCopy()
        {
            var from = FromBox.Text;
            var to   = ToBox.Text;

            if (String.IsNullOrWhiteSpace(to) || String.IsNullOrWhiteSpace(to))
            {
                return;
            }

            var _dat   = new Dat(XivCache.GameInfo.GameDirectory);
            var _index = new Index(XivCache.GameInfo.GameDirectory);


            try
            {
                var exists = await _index.FileExists(from);

                if (!exists)
                {
                    throw new InvalidDataException("Source file does not exist.");
                }

                exists = await _index.FileExists(to);

                if (exists)
                {
                    var cancel = false;
                    Dispatcher.Invoke(() =>
                    {
                        var result = FlexibleMessageBox.Show("Destination file already exists.  Overwrite?", "Overwrite Confirmation", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Warning);
                        cancel     = result == System.Windows.Forms.DialogResult.No;
                    });

                    if (cancel)
                    {
                        return;
                    }
                }

                await _dat.CopyFile(from, to, XivStrings.TexTools, true);

                Dispatcher.Invoke(() =>
                {
                    FlexibleMessageBox.Show("File Copied Successfully.", "Copy Success", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
                    Close();
                });
            } catch (Exception ex)
            {
                FlexibleMessageBox.Show("File Copy Failed:\n" + ex.Message, "Copy Failure", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
            }
        }
예제 #13
0
        /// <summary>
        /// Checks to see whether the mod is currently enabled
        /// </summary>
        /// <param name="internalPath">The internal path of the file</param>
        /// <param name="dataFile">The data file to check in</param>
        /// <param name="indexCheck">Flag to determine whether to check the index file or just the modlist</param>
        /// <returns></returns>
        public async Task <XivModStatus> IsModEnabled(string internalPath, bool indexCheck)
        {
            if (!File.Exists(ModListDirectory.FullName))
            {
                return(XivModStatus.Original);
            }

            if (indexCheck)
            {
                var index = new Index(_gameDirectory);

                var modEntry = await TryGetModEntry(internalPath);

                if (modEntry == null)
                {
                    return(XivModStatus.Original);
                }

                var originalOffset = modEntry.data.originalOffset;
                var moddedOffset   = modEntry.data.modOffset;

                var offset = await index.GetDataOffset(
                    HashGenerator.GetHash(Path.GetDirectoryName(internalPath).Replace("\\", "/")),
                    HashGenerator.GetHash(Path.GetFileName(internalPath)),
                    XivDataFiles.GetXivDataFile(modEntry.datFile));

                if (offset.Equals(originalOffset))
                {
                    return(XivModStatus.Disabled);
                }

                if (offset.Equals(moddedOffset))
                {
                    return(XivModStatus.Enabled);
                }

                throw new Exception("Offset in Index does not match either original or modded offset in modlist.");
            }
            else
            {
                var modEntry = await TryGetModEntry(internalPath);

                if (modEntry == null)
                {
                    return(XivModStatus.Original);
                }

                return(modEntry.enabled ? XivModStatus.Enabled : XivModStatus.Disabled);
            }
        }
예제 #14
0
        /// <summary>
        /// Searches for housing items given a model ID
        /// </summary>
        /// <param name="modelID">The Model ID of the housing item</param>
        /// <param name="type">The type of housing item to search for</param>
        /// <returns>A list of Search Results</returns>
        public async Task <List <SearchResults> > SearchHousingByModelID(int modelID, XivItemType type)
        {
            var searchResultsList = new List <SearchResults>();
            var index             = new Index(_gameDirectory);
            var id = modelID.ToString().PadLeft(4, '0');

            var folder = "";

            if (type == XivItemType.furniture)
            {
                folder = $"bgcommon/hou/indoor/general/{id}/material";
            }

            if (await index.FolderExists(folder, XivDataFile._01_Bgcommon))
            {
                var searchResults = new SearchResults
                {
                    Body    = "-",
                    Slot    = XivStrings.Furniture_Indoor,
                    Variant = int.Parse(id)
                };

                searchResultsList.Add(searchResults);
            }

            folder = $"bgcommon/hou/outdoor/general/{id}/material";

            if (await index.FolderExists(folder, XivDataFile._01_Bgcommon))
            {
                var searchResults = new SearchResults
                {
                    Body    = "-",
                    Slot    = XivStrings.Furniture_Outdoor,
                    Variant = int.Parse(id)
                };

                searchResultsList.Add(searchResults);
            }

            searchResultsList.Sort();

            return(searchResultsList);
        }
예제 #15
0
        public Task BackupIndexFiles(DirectoryInfo backupsDirectory)
        {
            return(Task.Run(async() => {
                var indexFiles = new XivDataFile[] { XivDataFile._0A_Exd, XivDataFile._04_Chara, XivDataFile._06_Ui, XivDataFile._01_Bgcommon };
                var index = new Index(_gameDirectory);
                var modding = new Modding(_gameDirectory);

                if (index.IsIndexLocked(XivDataFile._0A_Exd))
                {
                    throw new Exception("Index files are in use by another process.");
                }

                try
                {
                    // Toggle off all mods
                    await modding.ToggleAllMods(false);
                }
                catch
                {
                    throw new Exception("Failed to disable mods.\n\n" +
                                        "Please check For problems by selecting Help -> Check For Problems");
                }


                var originalFiles = Directory.GetFiles(_gameDirectory.FullName);
                foreach (var originalFile in originalFiles)
                {
                    try
                    {
                        if (originalFile.Contains(".win32.index"))
                        {
                            File.Copy(originalFile, $"{backupsDirectory}/{Path.GetFileName(originalFile)}", true);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("Failed to copy index files.\n\n" + ex.Message);
                    }
                }
            }));
        }
예제 #16
0
        public async Task <List <TexTypePath> > GetAtexPaths(string vfxPath)
        {
            var index = new Index(_gameDirectory);
            var avfx  = new Avfx(_gameDirectory, _dataFile);

            var folder    = vfxPath.Substring(0, vfxPath.LastIndexOf("/"));
            var file      = Path.GetFileName(vfxPath);
            var vfxOffset = await index.GetDataOffset(HashGenerator.GetHash(folder), HashGenerator.GetHash(file), _dataFile);

            var atexTexTypePathList = new List <TexTypePath>();

            if (vfxOffset <= 0)
            {
                return(new List <TexTypePath>());
            }

            var aTexPaths = new List <string>();

            try
            {
                aTexPaths = await avfx.GetATexPaths(vfxOffset);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }

            foreach (var atexPath in aTexPaths)
            {
                var ttp = new TexTypePath
                {
                    DataFile = _dataFile,
                    Name     = "VFX: " + Path.GetFileNameWithoutExtension(atexPath),
                    Path     = atexPath
                };

                atexTexTypePathList.Add(ttp);
            }

            return(atexTexTypePathList);
        }
예제 #17
0
        public MainViewModel(MainWindow mainWindow)
        {
            _mainWindow = mainWindow;
            // This is actually synchronous and can just be called immediately...
            SetDirectories(true);

            _gameDirectory = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory);
            _index         = new Index(_gameDirectory);
            if (ProgressLabel == null)
            {
                ProgressLabel = "";
            }

            // And the rest of this can be pushed into a new thread.
            var task = Task.Run(Initialize);

            // Now we can wait on it.  But we have to thread-safety it.
            task.Wait();

            var exception = task.Exception;

            if (exception != null)
            {
                throw exception;
            }
            var result = task.Result;

            if (!result)
            {
                // We need to die NOW, and not risk any other functions possibly
                // f*****g with broken files.
                Process.GetCurrentProcess().Kill();
                return;
            }

            CacheTimer.Elapsed += UpdateDependencyQueueCount;
        }
예제 #18
0
        public async Task SaveFullImcInfo(FullImcInfo info, string path, string source, IItem referenceItem = null, IndexFile cachedIndexFile = null, ModList cachedModList = null)
        {
            if (info == null || info.TypeIdentifier != ImcType.Set && info.TypeIdentifier != ImcType.NonSet)
            {
                throw new InvalidDataException("Cannot save invalid IMC file.");
            }

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

            var data = new List <byte>();


            // 4 Header bytes.
            data.AddRange(BitConverter.GetBytes((short)info.SubsetCount));
            data.AddRange(BitConverter.GetBytes((short)info.TypeIdentifier));

            // The rest of this is easy, it's literally just post all the sets in order.
            foreach (var entry in info.DefaultSubset)
            {
                data.AddRange(entry.GetBytes(info.TypeIdentifier));
            }

            foreach (var set in info.SubsetList)
            {
                foreach (var entry in set)
                {
                    data.AddRange(entry.GetBytes(info.TypeIdentifier));
                }
            }

            // That's it.
            source ??= "Unknown";

            await dat.ImportType2Data(data.ToArray(), path, source, referenceItem, cachedIndexFile, cachedModList);
        }
예제 #19
0
        /// <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++;
                        }
                    }
                });
            }
        }
예제 #20
0
        /// <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);
        }
        private async Task CreateBasic()
        {
            string modPackPath = Path.Combine(Properties.Settings.Default.ModPack_Directory, $"{ViewModel.Name}.ttmp2");

            if (File.Exists(modPackPath))
            {
                DialogResult overwriteDialogResult = FlexibleMessageBox.Show(new Wpf32Window(this), UIMessages.ModPackOverwriteMessage,
                                                                             UIMessages.OverwriteTitle, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
                if (overwriteDialogResult != System.Windows.Forms.DialogResult.Yes)
                {
                    return;
                }
            }

            TTMP texToolsModPack = new TTMP(new DirectoryInfo(Settings.Default.ModPack_Directory), XivStrings.TexTools);
            var  index           = new Index(XivCache.GameInfo.GameDirectory);
            var  dat             = new Dat(XivCache.GameInfo.GameDirectory);
            var  modding         = new Modding(XivCache.GameInfo.GameDirectory);
            var  ModList         = modding.GetModList();

            SimpleModPackData simpleModPackData = new SimpleModPackData
            {
                Name              = ViewModel.Name,
                Author            = ViewModel.Author,
                Version           = ViewModel.Version,
                Description       = ViewModel.Description,
                Url               = ViewModel.Url,
                SimpleModDataList = new List <SimpleModData>()
            };

            foreach (var entry in ViewModel.Entries)
            {
                foreach (var file in entry.AllFiles)
                {
                    var exists = await index.FileExists(file);

                    // This is a funny case where in order to create the modpack we actually have to write a default meta entry to the dats first.
                    // If we had the right functions we could just load and serialize the data, but we don't atm.
                    if (!exists && Path.GetExtension(file) == ".meta")
                    {
                        var meta = await ItemMetadata.GetMetadata(file);

                        await ItemMetadata.SaveMetadata(meta, XivStrings.TexTools);
                    }

                    var offset = await index.GetDataOffset(file);

                    var dataFile       = IOUtil.GetDataFileFromPath(file);
                    var compressedSize = await dat.GetCompressedFileSize(offset, dataFile);

                    var modEntry = ModList.Mods.FirstOrDefault(x => x.fullPath == file);
                    var modded   = modEntry != null && modEntry.enabled == true;


                    SimpleModData simpleData = new SimpleModData
                    {
                        Name      = entry.Item.Name,
                        Category  = entry.Item.SecondaryCategory,
                        FullPath  = file,
                        ModOffset = offset,
                        ModSize   = compressedSize,
                        IsDefault = !modded,
                        DatFile   = dataFile.GetDataFileName()
                    };

                    simpleModPackData.SimpleModDataList.Add(simpleData);
                }
            }



            try
            {
                await LockUi(UIStrings.Creating_Modpack, null, null);

                Progress <(int current, int total, string message)> progressIndicator = new Progress <(int current, int total, string message)>(ReportProgress);
                await texToolsModPack.CreateSimpleModPack(simpleModPackData, XivCache.GameInfo.GameDirectory, progressIndicator, true);

                FlexibleMessageBox.Show(new Wpf32Window(this), "Modpack Created Successfully.",
                                        "Modpack Created", MessageBoxButtons.OK, MessageBoxIcon.Information);
                await UnlockUi(this);

                DialogResult = true;
            }
            catch (Exception ex)
            {
                FlexibleMessageBox.Show(new Wpf32Window(this), "An Error occured while creating the modpack.\n\n" + ex.Message,
                                        "Modpack Creation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                await UnlockUi(this);
            }
        }
        private async Task CreateAdvanced()
        {
            string modPackPath = Path.Combine(Properties.Settings.Default.ModPack_Directory, $"{ViewModel.Name}.ttmp2");

            if (File.Exists(modPackPath))
            {
                DialogResult overwriteDialogResult = FlexibleMessageBox.Show(new Wpf32Window(this), UIMessages.ModPackOverwriteMessage,
                                                                             UIMessages.OverwriteTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (overwriteDialogResult != System.Windows.Forms.DialogResult.Yes)
                {
                    return;
                }
            }

            await LockUi(UIStrings.Creating_Modpack, null, null);

            try
            {
                TTMP texToolsModPack = new TTMP(new DirectoryInfo(Settings.Default.ModPack_Directory), XivStrings.TexTools);
                var  index           = new Index(XivCache.GameInfo.GameDirectory);
                var  dat             = new Dat(XivCache.GameInfo.GameDirectory);
                var  modding         = new Modding(XivCache.GameInfo.GameDirectory);
                var  ModList         = modding.GetModList();

                var wizardData = new ModPackData()
                {
                    Name         = ViewModel.Name,
                    Author       = ViewModel.Author,
                    Version      = ViewModel.Version,
                    Description  = ViewModel.Description,
                    Url          = ViewModel.Url,
                    ModPackPages = new List <ModPackData.ModPackPage>()
                };

                var page = new ModPackData.ModPackPage()
                {
                    PageIndex = 1,
                    ModGroups = new List <ModGroup>()
                };

                wizardData.ModPackPages.Add(page);


                foreach (var e in ViewModel.Entries)
                {
                    var item  = e.Item;
                    var files = e.AllFiles;

                    var group = new ModGroup()
                    {
                        GroupName     = item.Name,
                        SelectionType = "Multi",
                        OptionList    = new List <ModOption>()
                    };
                    page.ModGroups.Add(group);

                    var option = new ModOption
                    {
                        GroupName     = group.GroupName,
                        IsChecked     = true,
                        Name          = GetNiceLevelName(e.Level, true, true),
                        Description   = "Item: " + item.Name + "\nInclusion Level: " + GetNiceLevelName(e.Level) + "\nPrimary Files:" + e.MainFiles.Count + "\nTotal Files:" + e.AllFiles.Count,
                        SelectionType = "Multi",
                    };
                    group.OptionList.Add(option);

                    foreach (var file in e.AllFiles)
                    {
                        var exists = await index.FileExists(file);

                        // This is a funny case where in order to create the modpack we actually have to write a default meta entry to the dats first.
                        // If we had the right functions we could just load and serialize the data, but we don't atm.
                        if (!exists && Path.GetExtension(file) == ".meta")
                        {
                            var meta = await ItemMetadata.GetMetadata(file);

                            await ItemMetadata.SaveMetadata(meta, XivStrings.TexTools);
                        }

                        var offset = await index.GetDataOffset(file);

                        var dataFile       = IOUtil.GetDataFileFromPath(file);
                        var compressedSize = await dat.GetCompressedFileSize(offset, dataFile);

                        var modEntry = ModList.Mods.FirstOrDefault(x => x.fullPath == file);
                        var modded   = modEntry != null && modEntry.enabled == true;

                        var fData = new ModData
                        {
                            Name         = e.Item.Name,
                            Category     = e.Item.SecondaryCategory,
                            FullPath     = file,
                            IsDefault    = !modded,
                            ModDataBytes = dat.GetRawData(offset, dataFile, compressedSize)
                        };
                        option.Mods.Add(file, fData);
                    }
                }

                // Okay modpack is now created internally, just need to save it.
                var progressIndicator = new Progress <double>(ReportProgressAdv);
                await texToolsModPack.CreateWizardModPack(wizardData, progressIndicator, true);

                FlexibleMessageBox.Show(new Wpf32Window(this), "Modpack Created Successfully.",
                                        "Modpack Created", MessageBoxButtons.OK, MessageBoxIcon.Information);
                await UnlockUi(this);

                DialogResult = true;
            } catch (Exception ex)
            {
                FlexibleMessageBox.Show(new Wpf32Window(this), "An Error occured while creating the modpack.\n\n" + ex.Message,
                                        "Modpack Creation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                await UnlockUi(this);
            }
        }
예제 #23
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, IndexFile index = null, ModList modlist = null)
        {
            if (index == null)
            {
                var _index = new Index(_gameDirectory);
                index = await _index.GetIndexFile(IOUtil.GetDataFileFromPath(path), false, true);
            }
            var dat = new Dat(_gameDirectory);


            var imcOffset = index.Get8xDataOffset(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();
                        byte vfx = br.ReadByte();
                        byte anim = br.ReadByte();

                        imcData.DefaultSubset.Add(new XivImc
                        {
                            MaterialSet = variant,
                            Decal = unknown,
                            Mask = mask,
                            Vfx = variant,
                            Animation = anim
                        });

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

                            var newEntry = new XivImc
                            {
                                MaterialSet = variant,
                                Decal = unknown,
                                Mask = mask,
                                Vfx = vfx,
                                Animation = anim
                            };
                            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
                            {
                                MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                            },
                            new XivImc
                            {
                                MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                            },
                            new XivImc
                            {
                                MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                            },
                            new XivImc
                            {
                                MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                            },
                            new XivImc
                            {
                                MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                            },
                        };

                        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
                                {
                                    MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                                },
                                new XivImc
                                {
                                    MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                                },
                                new XivImc
                                {
                                    MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                                },
                                new XivImc
                                {
                                    MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                                },
                                new XivImc
                                {
                                    MaterialSet = br.ReadByte(), Decal = br.ReadByte(), Mask = br.ReadUInt16(), Vfx = br.ReadByte(), Animation = br.ReadByte()
                                },
                            };
                            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;
                }
            }));
        }
예제 #24
0
        /// <summary>
        /// Performs post-patch modlist corrections and validation, prompting user also to generate backups after a successful completion.
        /// </summary>
        /// <returns></returns>
        public async Task DoPostPatchCleanup()
        {
            FlexibleMessageBox.Show(_mainWindow.Win32Window, UIMessages.PatchDetectedMessage, "Post Patch Cleanup Starting", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
            MainWindow.MakeHighlander();

            var resetLumina = false;

            await _mainWindow.LockUi("Performing Post-Patch Maintenence", "This may take a few minutes if you have many mods installed.", this);

            var gi = XivCache.GameInfo;

            if (XivCache.GameInfo.UseLumina)
            {
                resetLumina = true;
                XivCache.SetGameInfo(gi.GameDirectory, gi.GameLanguage, gi.DxMode, false, false, gi.LuminaDirectory, gi.UseLumina);
            }

            var workerStatus = XivCache.CacheWorkerEnabled;

            if (workerStatus)
            {
                // Stop the cache worker if it's running.
                XivCache.CacheWorkerEnabled = false;
            }
            try
            {
                var modding = new Modding(_gameDirectory);
                var _index  = new Index(_gameDirectory);
                var _dat    = new Dat(_gameDirectory);

                var validTypes = new List <int>()
                {
                    2, 3, 4
                };

                // We have to do a few things here.
                // 1.  Save a list of what mods were enabled.
                // 2.  Go through and validate everything that says it is enabled actually is enabled, or mark it as disabled and update its original index offset if it is not.
                // 3.  Prompt the user for either a full disable and backup creation, or a restore to normal state (re-enable anything that was enabled before but is not now)
                var modList = modding.GetModList();

                Dictionary <XivDataFile, IndexFile> indexFiles = new Dictionary <XivDataFile, IndexFile>();


                // Cache our currently enabled stuff.
                List <Mod> enabledMods = modList.Mods.Where(x => x.enabled == true).ToList();
                var        toRemove    = new List <Mod>();

                foreach (var mod in modList.Mods)
                {
                    if (!String.IsNullOrEmpty(mod.fullPath))
                    {
                        var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                        if (!indexFiles.ContainsKey(df))
                        {
                            indexFiles[df] = await _index.GetIndexFile(df);
                        }

                        var index1Value       = indexFiles[df].Get8xDataOffset(mod.fullPath);
                        var index2Value       = indexFiles[df].Get8xDataOffsetIndex2(mod.fullPath);
                        var oldOriginalOffset = mod.data.originalOffset;
                        var modOffset         = mod.data.modOffset;


                        // In any event where an offset does not match either of our saved offsets, we must assume this is a new
                        // default file offset for post-patch.
                        if (index1Value != oldOriginalOffset && index1Value != modOffset && index1Value != 0)
                        {
                            // Index 1 value is our new base offset.
                            var type = _dat.GetFileType(index1Value, df);

                            // Make sure the file it's trying to point to is actually valid.
                            if (validTypes.Contains(type))
                            {
                                mod.data.originalOffset = index1Value;
                                mod.enabled             = false;
                            }
                            else
                            {
                                // Oh dear.  The new index is f****d.  Is the old Index Ok?
                                type = _dat.GetFileType(oldOriginalOffset, df);

                                if (validTypes.Contains(type) && oldOriginalOffset != 0)
                                {
                                    // Old index is fine, so keep using that.

                                    // But mark the index value as invalid, so that we stomp on the index value after this.
                                    index1Value = -1;
                                    mod.enabled = false;
                                }
                                else
                                {
                                    // Okay... Maybe the new Index2 Value?
                                    if (index2Value != 0)
                                    {
                                        type = _dat.GetFileType(index2Value, df);
                                        if (validTypes.Contains(type))
                                        {
                                            // Set the index 1 value to invalid so that the if later down the chain stomps the index1 value.
                                            index1Value = -1;

                                            mod.data.originalOffset = index2Value;
                                            mod.enabled             = false;
                                        }
                                        else
                                        {
                                            // We be f****d.
                                            throw new Exception("Unable to determine working original offset for file:" + mod.fullPath);
                                        }
                                    }
                                    else
                                    {
                                        // We be f****d.
                                        throw new Exception("Unable to determine working original offset for file:" + mod.fullPath);
                                    }
                                }
                            }
                        }
                        else if (index2Value != oldOriginalOffset && index2Value != modOffset && index2Value != 0)
                        {
                            // Our Index 1 was normal, but our Index 2 is changed to an unknown value.
                            // If the index 2 points to a valid file, we must assume that this new file
                            // is our new base data offset.

                            var type = _dat.GetFileType(index2Value, df);

                            if (validTypes.Contains(type) && index2Value != 0)
                            {
                                mod.data.originalOffset = index2Value;
                                mod.enabled             = false;
                            }
                            else
                            {
                                // Oh dear.  The new index is f****d.  Is the old Index Ok?
                                type = _dat.GetFileType(oldOriginalOffset, df);

                                if (validTypes.Contains(type) && oldOriginalOffset != 0)
                                {
                                    // Old index is fine, so keep using that, but set the index2 value to invalid to ensure we
                                    // stomp on the current broken index value.
                                    index2Value = -1;
                                }
                                else
                                {
                                    // We be f****d.
                                    throw new Exception("Unable to determine working original offset for file:" + mod.fullPath);
                                }
                            }
                        }

                        // Indexes don't match.  This can occur if SE adds something to index2 that didn't exist in index2 before.
                        if (index1Value != index2Value && index2Value != 0)
                        {
                            // We should never actually get to this state for file-addition mods.  If we do, uh.. I guess correct the indexes and yolo?
                            // ( Only way we get here is if SE added a new file at the same name as a file the user had created via modding, in which case, it's technically no longer a file addition mod )
                            indexFiles[df].SetDataOffset(mod.fullPath, mod.data.originalOffset);
                            index1Value = mod.data.originalOffset;
                            index2Value = mod.data.originalOffset;

                            mod.enabled = false;
                        }

                        // Set it to the corrected state.
                        if (index1Value == mod.data.modOffset)
                        {
                            mod.enabled = true;
                        }
                        else
                        {
                            mod.enabled = false;
                        }

                        // Perform a basic file type check on our results.
                        var fileType         = _dat.GetFileType(mod.data.modOffset, IOUtil.GetDataFileFromPath(mod.fullPath));
                        var originalFileType = _dat.GetFileType(mod.data.modOffset, IOUtil.GetDataFileFromPath(mod.fullPath));

                        if (!validTypes.Contains(fileType) || mod.data.modOffset == 0)
                        {
                            // Mod data is busted.  Fun.
                            toRemove.Add(mod);
                        }

                        if ((!validTypes.Contains(originalFileType)) || mod.data.originalOffset == 0)
                        {
                            if (mod.IsCustomFile())
                            {
                                // Okay, in this case this is recoverable as the mod is a custom addition anyways, so we can just delete it.
                                if (!toRemove.Contains(mod))
                                {
                                    toRemove.Add(mod);
                                }
                            }
                            else
                            {
                                // Update ended up with us unable to find a working offset.  Double fun.
                                throw new Exception("Unable to determine working offset for file:" + mod.fullPath);
                            }
                        }
                    }

                    // Okay, this mod is now represented in the modlist in it's actual post-patch index state.
                    var datNum = (int)((mod.data.modOffset / 8) & 0x0F) / 2;
                    var dat    = XivDataFiles.GetXivDataFile(mod.datFile);

                    var originalDats = await _dat.GetUnmoddedDatList(dat);

                    var datPath = $"{dat.GetDataFileName()}{Dat.DatExtension}{datNum}";

                    // Test for SE Dat file rollover.
                    if (originalDats.Contains(datPath))
                    {
                        // Shit.  This means that the dat file where this mod lived got eaten by SE.  We have to destroy the modlist entry at this point.
                        toRemove.Add(mod);
                    }
                }

                // Save any index changes we made.
                foreach (var dkv in indexFiles)
                {
                    await _index.SaveIndexFile(dkv.Value);
                }

                // The modlist is now saved in its current index-represented post patch state.
                modding.SaveModList(modList);

                // We now need to clear out any mods that are irreparably f****d, and clear out all of our
                // internal data files so we can rebuild them later.

                var internalFiles = modList.Mods.Where(x => x.IsInternal());
                toRemove.AddRange(internalFiles);

                if (toRemove.Count > 0)
                {
                    var removedString = "";

                    // Soft-Disable all metadata mods, since we're going to purge their internal file entries.
                    var metadata = modList.Mods.Where(x => x.fullPath.EndsWith(".meta") || x.fullPath.EndsWith(".rgsp"));
                    foreach (var mod in metadata)
                    {
                        var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                        await modding.ToggleModUnsafe(false, mod, true, false, indexFiles[df], modList);
                    }

                    foreach (var mod in toRemove)
                    {
                        if (mod.data.modOffset == 0 || mod.data.originalOffset == 0)
                        {
                            if (mod.data.originalOffset == 0 && mod.enabled)
                            {
                                // This is awkward.  We have a mod whose data got bashed, but has no valid original offset to restore.
                                // So the indexes here are f****d if we do, f****d if we don't.
                                throw new Exception("Patch-Broken file has no valid index to restore.  Clean Index Restoration required.");
                            }

                            modList.Mods.Remove(mod);
                            enabledMods.Remove(mod);
                            removedString += mod.fullPath + "\n";
                        }
                        else
                        {
                            if (mod.enabled)
                            {
                                var df = IOUtil.GetDataFileFromPath(mod.fullPath);
                                await modding.ToggleModUnsafe(false, mod, true, false, indexFiles[df], modList);
                            }

                            modList.Mods.Remove(mod);

                            // Since we're deleting this entry entirely, we can't leave it in the other cached list either to get re-enabled later.
                            enabledMods.Remove(mod);

                            if (!mod.IsInternal())
                            {
                                removedString += mod.fullPath + "\n";
                            }
                        }
                    }

                    // Save the index files and modlist again now that we've removed all the invalid entries.
                    foreach (var dkv in indexFiles)
                    {
                        await _index.SaveIndexFile(dkv.Value);
                    }
                    modding.SaveModList(modList);

                    // Show the user a message if we purged any real files.
                    if (toRemove.Any(x => !String.IsNullOrEmpty(x.fullPath) && !x.IsInternal()))
                    {
                        var text = String.Format(UIMessages.PatchDestroyedFiles, removedString);

                        FlexibleMessageBox.Show(_mainWindow.Win32Window, text, "Destroyed Files Notification", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
                    }
                }

                // Always create clean index backups after this process is completed.

                _mainWindow.LockProgress.Report("Disabling Mods...");
                await modding.ToggleAllMods(false);

                _mainWindow.LockProgress.Report("Creating Index Backups...");
                var           pc = new ProblemChecker(_gameDirectory);
                DirectoryInfo backupDir;
                try
                {
                    Directory.CreateDirectory(Settings.Default.Backup_Directory);
                    backupDir = new DirectoryInfo(Settings.Default.Backup_Directory);
                }
                catch
                {
                    throw new Exception("Unable to create index backups.\nThe Index Backup directory is invalid or inaccessible: " + Settings.Default.Backup_Directory);
                }

                await pc.BackupIndexFiles(backupDir);

                // Now restore the modlist enable/disable state back to how the user had it before.
                _mainWindow.LockProgress.Report("Re-Enabling mods...");

                // Re-enable things.
                await modding.ToggleMods(true, enabledMods.Select(x => x.fullPath));

                FlexibleMessageBox.Show(_mainWindow.Win32Window, UIMessages.PostPatchComplete, "Post-Patch Process Complete", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
            }
            catch (Exception Ex)
            {
                // Show the user the error, then let them go about their business of fixing things.
                FlexibleMessageBox.Show(_mainWindow.Win32Window, String.Format(UIMessages.PostPatchError, Ex.Message), "Post-Patch Failure", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
            }
            finally
            {
                if (resetLumina)
                {
                    // Reset lumina mode back to on if we disabled it to perform update checks.
                    XivCache.SetGameInfo(gi.GameDirectory, gi.GameLanguage, gi.DxMode, true, false, gi.LuminaDirectory, true);
                }

                XivCache.CacheWorkerEnabled = workerStatus;
                await _mainWindow.UnlockUi(this);
            }
        }
        private async void RunChecks()
        {
            var index = new Index(_gameDirectory);

            IProgress <(int current, int total)> progress = new Progress <(int current, int total)>((update) =>
            {
                ProgressBar.Value     = (((float)update.current / (float)update.total) * 100);
                ProgressLabel.Content = $"{update.current} / {update.total}";
            });

            AddText($"{UIStrings.ProblemCheck_Initialize}\n\n", textColor);

            AddText($"{UIStrings.ProblemCheck_IndexDat}\n", secondaryTextColor);

            if (await CheckIndexDatCounts())
            {
                AddText($"\n{UIStrings.ProblemCheck_ErrorsFound}\n", secondaryTextColor);
                if (!index.IsIndexLocked(XivDataFile._0A_Exd))
                {
                    await FixIndexDatCounts();

                    AddText($"{UIStrings.ProblemCheck_RepairComplete}\n", "Green");
                    await CheckIndexDatCounts();
                }
                else
                {
                    AddText($"\n{UIStrings.ProblemCheck_IndexLocked} \n", "Red");
                }
            }

            AddText($"\n{UIStrings.ProblemCheck_IndexBackups}\n", secondaryTextColor);
            await CheckBackups();

            AddText($"\n{UIStrings.ProblemCheck_DatSize}\n", secondaryTextColor);
            await CheckDatSizes();

            try
            {
                AddText($"\n{UIStrings.ProblemCheck_ModList}\n", secondaryTextColor);
                await CheckMods(progress);
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Loading Canceled\n\n{ex.Message}");
            }


            try
            {
                AddText($"\n{UIStrings.ProblemCheck_LoD}\n", secondaryTextColor);
                cfpTextBox.ScrollToEnd();
                await CheckDXSettings();
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Loading Canceled\n\n{ex.Message}");
            }

            ProgressBar.Value     = 0;
            ProgressLabel.Content = UIStrings.Done;
        }
예제 #26
0
        /// <summary>
        /// Copies the entirety of a given root to a new root.
        /// </summary>
        /// <param name="Source">Original Root to copy from.</param>
        /// <param name="Destination">Destination root to copy to.</param>
        /// <param name="ApplicationSource">Application to list as the source for the resulting mod entries.</param>
        /// <returns>Returns a Dictionary of all the file conversion</returns>
        public static async Task <Dictionary <string, string> > CloneRoot(XivDependencyRoot Source, XivDependencyRoot Destination, string ApplicationSource, int singleVariant = -1, string saveDirectory = null, IProgress <string> ProgressReporter = null, IndexFile index = null, ModList modlist = null, ModPack modPack = null)
        {
            if (!IsSupported(Source) || !IsSupported(Destination))
            {
                throw new InvalidDataException("Cannot clone unsupported root.");
            }

            if (XivCache.GameInfo.UseLumina)
            {
                // Using the root cloner with Lumina import mode enabled is unstable/not safe.
                // ( The state of the game files in the TT system may not match the state of the
                // Lumina mod setup, and the modlist/etc. can get bashed as this function directly
                // modifies the modlist during the cloning process. )
                throw new Exception("Item Conversion cannot be used with Lumina import mode.");
            }


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

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

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

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

                    modlist = await _modding.GetModListAsync();
                }


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


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

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


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

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

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

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

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

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

                // Time to start editing things.

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

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

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

                    newMetadata.EqpEntry = set1Metadata.EqpEntry;

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

                    var tmdl = TTModel.FromRaw(xmdl);

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

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

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

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

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

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

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

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


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

                        xivMtrl.MTRLPath = dst;

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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



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

                            string existentCopy = null;

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

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

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

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

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

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

                        mods.Add(mod);
                    }
                }

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

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

                    await _modding.SaveModListAsync(modlist);
                }

                XivCache.QueueDependencyUpdate(allFiles.ToList());

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

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

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

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



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

                // Return the final file conversion listing.
                var ret  = newModelPaths.Union(newMaterialPaths).Union(newAvfxPaths).Union(newTexturePaths);
                var dict = ret.ToDictionary(x => x.Key, x => x.Value);
                dict.Add(Source.Info.GetRootFile(), Destination.Info.GetRootFile());
                return(dict);
            } finally
            {
                XivCache.CacheWorkerEnabled = workerStatus;
            }
        }
        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;
            }
        }
예제 #28
0
 public Gear(DirectoryInfo gameDirectory, XivLanguage xivLanguage)
 {
     _gameDirectory = gameDirectory;
     _xivLanguage   = xivLanguage;
     _index         = new Index(_gameDirectory);
 }
        private async Task LoadItems()
        {
            List <string> children = new List <string>();
            var           root     = _item.GetRoot();

            if (root != null)
            {
                if (_level == XivDependencyLevel.Model)
                {
                    children = await root.GetModelFiles();
                }
                else if (_level == XivDependencyLevel.Material)
                {
                    var imc = new Imc(XivCache.GameInfo.GameDirectory);
                    try
                    {
                        var entry = await imc.GetImcInfo((IItemModel)_item);

                        children = await root.GetMaterialFiles(entry.MaterialSet);
                    } catch
                    {
                        if (root.Info.SecondaryType == XivItemType.hair ||
                            root.Info.SecondaryType == XivItemType.tail ||
                            (root.Info.PrimaryType == XivItemType.human && root.Info.SecondaryType == XivItemType.body))
                        {
                            // These types don't have IMC entries, but have a material variant number.
                            // Kind of weird, but whatever.
                            children = await root.GetMaterialFiles(1);
                        }
                        else
                        {
                            children = await root.GetMaterialFiles(0);
                        }
                    }
                }
                else if (_level == XivDependencyLevel.Texture)
                {
                    try
                    {
                        var imc   = new Imc(XivCache.GameInfo.GameDirectory);
                        var entry = await imc.GetImcInfo((IItemModel)_item);

                        children = await root.GetTextureFiles(entry.MaterialSet);
                    }
                    catch
                    {
                        if (root.Info.SecondaryType == XivItemType.hair ||
                            root.Info.SecondaryType == XivItemType.tail ||
                            (root.Info.PrimaryType == XivItemType.human && root.Info.SecondaryType == XivItemType.body))
                        {
                            // These types don't have IMC entries, but have a material variant number.
                            // Kind of weird, but whatever.
                            children = await root.GetTextureFiles(1);
                        }
                        else
                        {
                            children = await root.GetTextureFiles(0);
                        }
                    }
                }
                else
                {
                    // Invalid or root, nothing listed.
                }
            }

            var index = new Index(XivCache.GameInfo.GameDirectory);

            foreach (var file in children)
            {
                var exists = await index.FileExists(file);

                if (!exists)
                {
                    continue;
                }

                Files.Add(new FileEntry(file));
            }
        }
예제 #30
0
        /// <summary>
        /// Saves a set of IMC entries to file.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="entries"></param>
        /// <returns></returns>
        internal async Task SaveEntries(string path, string slot, List <XivImc> entries, IItem referenceItem = null, IndexFile cachedIndexFile = null, ModList cachedModList = null)
        {
            var dat   = new Dat(_gameDirectory);
            var index = new Index(_gameDirectory);

            var exists = await index.FileExists(path);

            FullImcInfo info;

            if (exists)
            {
                info = await GetFullImcInfo(path, cachedIndexFile, cachedModList);
            }
            else
            {
                var ri = XivDependencyGraph.ExtractRootInfo(path);
                if (ri.SecondaryType == null)
                {
                    info = new FullImcInfo()
                    {
                        DefaultSubset = new List <XivImc>()
                        {
                            new XivImc(), new XivImc(), new XivImc(), new XivImc(), new XivImc()
                        },
                        SubsetList     = new List <List <XivImc> >(),
                        TypeIdentifier = ImcType.Set
                    };
                }
                else
                {
                    info = new FullImcInfo()
                    {
                        DefaultSubset = new List <XivImc>()
                        {
                            new XivImc()
                        },
                        SubsetList     = new List <List <XivImc> >(),
                        TypeIdentifier = ImcType.NonSet
                    };
                }
            }


            for (int i = 0; i < entries.Count; i++)
            {
                XivImc e;
                if (i >= info.SubsetCount + 1)
                {
                    e = new XivImc();
                }
                else
                {
                    e = info.GetEntry(i, slot);
                }
                e.Mask        = entries[i].Mask;
                e.Decal       = entries[i].Decal;
                e.Vfx         = entries[i].Vfx;
                e.Animation   = entries[i].Animation;
                e.MaterialSet = entries[i].MaterialSet;

                if (i >= info.SubsetCount + 1)
                {
                    info.SetEntry(e, i, slot, true);
                }
            }

            // Save the modified info.
            await SaveFullImcInfo(info, path, Constants.InternalModSourceName, referenceItem, cachedIndexFile, cachedModList);
        }