Example #1
0
        public async Task <XivTex> GetTexData(string path)
        {
            var folder = Path.GetDirectoryName(path);

            folder = folder.Replace("\\", "/");
            var file = Path.GetFileName(path);

            long offset = 0;

            var hashedfolder = 0;
            var hashedfile   = 0;

            hashedfolder = HashGenerator.GetHash(folder);
            hashedfile   = HashGenerator.GetHash(file);
            var df = IOUtil.GetDataFileFromPath(path);

            offset = await _index.GetDataOffset(hashedfolder, hashedfile, df);

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

            XivTex xivTex;

            try
            {
                if (path.Contains(".atex"))
                {
                    var atex = new ATex(_gameDirectory, df);
                    xivTex = await atex.GetATexData(offset);
                }
                else
                {
                    xivTex = await _dat.GetType4Data(offset, df);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"There was an error reading texture data at offset {offset}");
            }

            var ttp = new TexTypePath();

            ttp.DataFile = df;
            ttp.Name     = Path.GetFileName(path);
            ttp.Type     = XivTexType.Other;
            ttp.Path     = path;
            xivTex.TextureTypeAndPath = ttp;

            return(xivTex);
        }
Example #2
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);
            }
        }
Example #3
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);
            }

        }
Example #4
0
        /// <summary>
        /// Gets the original or modded data for type 4 files based on the path specified.
        /// </summary>
        /// <remarks>
        /// Type 4 files are used for Textures
        /// </remarks>
        /// <param name="internalPath">The internal file path of the item</param>
        /// <param name="forceOriginal">Flag used to get original game data</param>
        /// <param name="xivTex">The XivTex container to fill</param>
        public void GetType4Data(string internalPath, bool forceOriginal, XivTex xivTex)
        {
            var index = new Index(_gameDirectory);

            ModInfo modInfo   = null;
            var     inModList = false;

            var dataFile = GetDataFileFromPath(internalPath);

            if (forceOriginal)
            {
                // Checks if the item being imported already exists in the modlist
                using (var streamReader = new StreamReader(_modListDirectory.FullName))
                {
                    string line;
                    while ((line = streamReader.ReadLine()) != null)
                    {
                        modInfo = JsonConvert.DeserializeObject <ModInfo>(line);
                        if (modInfo.fullPath.Equals(internalPath))
                        {
                            inModList = true;
                            break;
                        }
                    }
                }

                // If the file exists in the modlist, get the data from the original data
                if (inModList)
                {
                    GetType4Data(modInfo.originalOffset, dataFile, xivTex);
                    return;
                }
            }

            // If it doesn't exist in the modlist(the item is not modded) or force original is false,
            // grab the data directly from them index file.

            var folder = Path.GetDirectoryName(internalPath);

            folder = folder.Replace("\\", "/");
            var file = Path.GetFileName(internalPath);

            var offset = index.GetDataOffset(HashGenerator.GetHash(folder), HashGenerator.GetHash(file),
                                             dataFile);

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

            GetType4Data(offset, dataFile, xivTex);
        }
Example #5
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);
            }
        }
Example #6
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);
        }
Example #7
0
        /// <summary>
        /// Reads and parses the ExHeader file
        /// </summary>
        /// <param name="exFile">The Ex file to use.</param>
        private async Task ReadExHeader(XivEx exFile)
        {
            OffsetTypeDict = new Dictionary <int, int>();
            PageList       = new List <int>();
            LanguageList   = new List <int>();

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

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

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

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

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

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

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

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

                    for (var i = 0; i < langTableCount; i++)
                    {
                        var langCode = br.ReadInt16();
                        if (langCode != 0)
                        {
                            LanguageList.Add(langCode);
                        }
                    }
                }
            });
        }
        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;
            }
        }
Example #9
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++;
                        }
                    }
                });
            }
        }
Example #10
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);
            }
        }