public static void Main(string[] args) { bool showHelp = false; bool verbose = false; bool compress = false; var options = new OptionSet() { { "v|verbose", "be verbose", v => verbose = v != null }, { "c|compress", "compress data with LZO1x", v => compress = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extras; try { extras = options.Parse(args); } catch (OptionException e) { Console.Write("{0}: ", GetExecutableName()); Console.WriteLine(e.Message); Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName()); return; } if (extras.Count < 1 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ output_fat input_directory+", GetExecutableName()); Console.WriteLine("Pack files from input directories into a Encapsulated Resource File."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPaths = new List <string>(); string fatPath, datPath; if (extras.Count == 1) { inputPaths.Add(extras[0]); fatPath = Path.ChangeExtension(extras[0], ".fat"); datPath = Path.ChangeExtension(extras[0], ".dat"); } else { fatPath = extras[0]; if (Path.GetExtension(fatPath) != ".fat") { datPath = fatPath; fatPath = Path.ChangeExtension(datPath, ".fat"); } else { datPath = Path.ChangeExtension(fatPath, ".dat"); } inputPaths.AddRange(extras.Skip(1)); } var paths = new SortedDictionary <uint, string>(); if (verbose == true) { Console.WriteLine("Finding files..."); } foreach (var relPath in inputPaths) { string inputPath = Path.GetFullPath(relPath); if (inputPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == true) { inputPath = inputPath.Substring(0, inputPath.Length - 1); } foreach (string path in Directory.GetFiles(inputPath, "*", SearchOption.AllDirectories)) { string fullPath = Path.GetFullPath(path); string partPath = fullPath.Substring(inputPath.Length + 1).ToLowerInvariant(); uint hash = 0xFFFFFFFFu; if (partPath.ToUpper().StartsWith("__UNKNOWN") == true) { string partName; partName = Path.GetFileNameWithoutExtension(partPath); if (partName.Length > 8) { partName = partName.Substring(0, 8); } hash = uint.Parse( partName, System.Globalization.NumberStyles.AllowHexSpecifier); } else { hash = partPath.HashFileNameCRC32(); } if (paths.ContainsKey(hash) == true) { //if (verbose == true) { Console.WriteLine("Ignoring {0} duplicate.", partPath); } continue; } paths[hash] = fullPath; //Console.WriteLine(fullPath); } } var big = new BigFile(); using (var output = File.Create(datPath)) { foreach (var value in paths) { var hash = value.Key; var path = value.Value; if (verbose == true) { Console.WriteLine(path); } var entry = new Big.Entry(); entry.NameHash = hash; entry.Offset = output.Position; using (var input = File.OpenRead(path)) { if (compress == false) { entry.CompressionScheme = Big.CompressionScheme.None; entry.UncompressedSize = 0; entry.CompressedSize = (uint)input.Length; output.WriteFromStream(input, input.Length); } else { var uncompressedData = new byte[input.Length]; var uncompressedSize = (uint)uncompressedData.Length; input.Read(uncompressedData, 0, uncompressedData.Length); var compressedData = new byte[ uncompressedData.Length + (uncompressedData.Length / 16) + 64 + 3]; var compressedSize = (uint)compressedData.Length; var result = LZO1x.Compress( uncompressedData, uncompressedSize, compressedData, ref compressedSize); if (result != 0) { throw new InvalidOperationException("compression error " + result.ToString()); } if (compressedSize < uncompressedSize) { entry.CompressionScheme = Big.CompressionScheme.LZO1x; entry.UncompressedSize = uncompressedSize; entry.CompressedSize = compressedSize; output.Write(compressedData, 0, (int)compressedSize); } else { input.Seek(0, SeekOrigin.Begin); entry.CompressionScheme = Big.CompressionScheme.None; entry.UncompressedSize = 0; entry.CompressedSize = (uint)input.Length; output.WriteFromStream(input, input.Length); } } output.Seek(output.Position.Align(16), SeekOrigin.Begin); } big.Entries.Add(entry); } } using (var output = File.Create(fatPath)) { big.Serialize(output); } }
public static void Main(string[] args) { bool showHelp = false; bool overwriteFiles = false; bool verbose = false; var options = new OptionSet() { { "v|verbose", "be verbose", v => verbose = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extras; try { extras = options.Parse(args); } catch (OptionException e) { Console.Write("{0}: ", GetExecutableName()); Console.WriteLine(e.Message); Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName()); return; } if (extras.Count < 1 || extras.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_fat [output_dir]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string inputPath = extras[0]; string outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null) + ".fc2map"; var map = new MapFile(); map.Info = new Map.Info(); map.Snapshot = new Map.Snapshot(); map.Data = new Map.Data(); map.Data.Unknown2 = new Map.Snapshot(); map.Archive = new Map.Archive(); using (var input = File.OpenRead(Path.Combine(inputPath, "map.xml"))) { var doc = new XPathDocument(input); var nav = doc.CreateNavigator(); var info = nav.SelectSingleNode("/map/info"); map.Info.Name = info.SelectSingleNode("name").Value; map.Info.Creator = info.SelectSingleNode("creator").Value; map.Info.Author = info.SelectSingleNode("author").Value; map.Info.Size = (Map.Size)Enum.Parse(typeof(Map.Size), info.SelectSingleNode("size").Value); map.Info.Players = (Map.Players)Enum.Parse(typeof(Map.Players), info.SelectSingleNode("players").Value); map.Info.Unknown2 = uint.Parse(info.SelectSingleNode("unknown2").Value, CultureInfo.InvariantCulture); map.Info.Unknown3 = uint.Parse(info.SelectSingleNode("unknown3").Value, CultureInfo.InvariantCulture); map.Info.Unknown4 = uint.Parse(info.SelectSingleNode("unknown4").Value, CultureInfo.InvariantCulture); map.Info.Unknown5 = ulong.Parse(info.SelectSingleNode("unknown5").Value, CultureInfo.InvariantCulture); map.Info.Unknown7 = ulong.Parse(info.SelectSingleNode("unknown7").Value, CultureInfo.InvariantCulture); map.Info.Unknown10 = ulong.Parse(info.SelectSingleNode("unknown10").Value, CultureInfo.InvariantCulture); using (var reader = new XmlTextReader(new StringReader(info.SelectSingleNode("unknown11").OuterXml))) { reader.MoveToContent(); map.Info.Unknown11 = new byte[0]; int read = 0; do { Array.Resize(ref map.Info.Unknown11, map.Info.Unknown11.Length + 4096); read += reader.ReadBinHex(map.Info.Unknown11, read, 4096); }while (reader.EOF == false); Array.Resize(ref map.Info.Unknown11, read); } using (var reader = new XmlTextReader(new StringReader(info.SelectSingleNode("unknown12").OuterXml))) { reader.MoveToContent(); map.Info.Unknown12 = new byte[0]; int read = 0; do { Array.Resize(ref map.Info.Unknown12, map.Info.Unknown12.Length + 4096); read += reader.ReadBinHex(map.Info.Unknown12, read, 4096); }while (reader.EOF == false); Array.Resize(ref map.Info.Unknown12, read); } map.Info.Unknown15 = uint.Parse(info.SelectSingleNode("unknown15").Value, CultureInfo.InvariantCulture); var snapshot = nav.SelectSingleNode("/map/snapshot"); map.Snapshot.Width = uint.Parse(snapshot.SelectSingleNode("width").Value, CultureInfo.InvariantCulture); map.Snapshot.Height = uint.Parse(snapshot.SelectSingleNode("height").Value, CultureInfo.InvariantCulture); map.Snapshot.BytesPerPixel = uint.Parse(snapshot.SelectSingleNode("bpp").Value, CultureInfo.InvariantCulture); map.Snapshot.Unknown4 = uint.Parse(snapshot.SelectSingleNode("unknown4").Value, CultureInfo.InvariantCulture); var data = nav.SelectSingleNode("/map/data"); map.Data.Unknown1 = data.SelectSingleNode("unknown1").Value; } using (var input = File.OpenRead(Path.Combine(inputPath, "snapshot.bin"))) { map.Snapshot.Data = new byte[input.Length]; input.Read(map.Snapshot.Data, 0, map.Snapshot.Data.Length); } var paths = new SortedDictionary <uint, string>(); if (verbose == true) { Console.WriteLine("Finding files..."); } var dataPath = Path.Combine(inputPath, "archive"); dataPath = Path.GetFullPath(dataPath); if (dataPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == true) { dataPath = dataPath.Substring(0, dataPath.Length - 1); } foreach (string path in Directory.GetFiles(dataPath, "*", SearchOption.AllDirectories)) { string fullPath = Path.GetFullPath(path); string partPath = fullPath.Substring(dataPath.Length + 1).ToLowerInvariant(); uint hash = 0xFFFFFFFFu; if (partPath.ToUpper().StartsWith("__UNKNOWN") == true) { string partName; partName = Path.GetFileNameWithoutExtension(partPath); if (partName.Length > 8) { partName = partName.Substring(0, 8); } hash = uint.Parse( partName, System.Globalization.NumberStyles.AllowHexSpecifier); } else { hash = partPath.HashFileNameCRC32(); } if (paths.ContainsKey(hash) == true) { continue; } paths[hash] = fullPath; } var big = new BigFile(); using (var output = new MemoryStream()) { foreach (var value in paths) { var hash = value.Key; var path = value.Value; if (verbose == true) { Console.WriteLine(path); } var entry = new Big.Entry(); entry.NameHash = hash; entry.Offset = output.Position; using (var input = File.OpenRead(path)) { entry.CompressionScheme = Big.CompressionScheme.None; entry.UncompressedSize = 0; entry.CompressedSize = (uint)input.Length; output.WriteFromStream(input, input.Length); } big.Entries.Add(entry); } map.Archive.DAT = MakeCompressedData(output); } using (var output = new MemoryStream()) { big.Serialize(output); map.Archive.FAT = MakeCompressedData(output); } using (var output = new MemoryStream()) { var settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; settings.IndentChars = "\t"; settings.Encoding = Encoding.ASCII; using (var writer = XmlWriter.Create(output, settings)) { writer.WriteStartDocument(); writer.WriteStartElement("FatInfo"); foreach (var entry in big.Entries.OrderBy(e => e.NameHash)) { writer.WriteStartElement("File"); writer.WriteAttributeString("Path", entry.NameHash.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("Crc", entry.NameHash.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("FileTime", "0"); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteEndDocument(); } output.Position = 0; map.Archive.XML = MakeCompressedData(output); } using (var output = File.Create(outputPath)) { map.Serialize(output); } }
private static void Main(string[] args) { bool showHelp = false; bool verbose = false; bool compress = false; int packageVersion = 9; Big.Platform packagePlatform = Big.Platform.PC; var options = new OptionSet() { { "v|verbose", "be verbose", v => verbose = v != null }, { "c|compress", "compress data with LZO1x", v => compress = v != null }, { "pv|package-version", "package version (default 9)", v => packageVersion = ParsePackageVersion(v) }, { "pp|package-platform", "package platform (default PC)", v => packagePlatform = ParsePackagePlatform(v) }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extras; try { extras = options.Parse(args); } catch (OptionException e) { Console.Write("{0}: ", GetExecutableName()); Console.WriteLine(e.Message); Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName()); return; } if (extras.Count < 1 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ output_fat input_directory+", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Pack files from input directories into a Big File (FAT/DAT pair)."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPaths = new List <string>(); string fatPath, datPath; if (extras.Count == 1) { inputPaths.Add(extras[0]); fatPath = Path.ChangeExtension(extras[0], ".fat"); datPath = Path.ChangeExtension(extras[0], ".dat"); } else { fatPath = extras[0]; if (Path.GetExtension(fatPath) != ".fat") { datPath = fatPath; fatPath = Path.ChangeExtension(datPath, ".fat"); } else { datPath = Path.ChangeExtension(fatPath, ".dat"); } inputPaths.AddRange(extras.Skip(1)); } var pendingEntries = new SortedDictionary <ulong, PendingEntry>(); if (verbose == true) { Console.WriteLine("Finding files..."); } foreach (var relativePath in inputPaths) { string inputPath = Path.GetFullPath(relativePath); if (inputPath.EndsWith(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture)) == true) { inputPath = inputPath.Substring(0, inputPath.Length - 1); } foreach (string path in Directory.GetFiles(inputPath, "*", SearchOption.AllDirectories)) { PendingEntry pendingEntry; string fullPath = Path.GetFullPath(path); string partPath = fullPath.Substring(inputPath.Length + 1).ToLowerInvariant(); pendingEntry.FullPath = fullPath; pendingEntry.PartPath = partPath; var pieces = partPath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); int index = 0; if (index >= pieces.Length) { continue; } if (pieces[index].ToUpperInvariant() == "__SUBFAT") { Console.WriteLine("Sorry, packing of subfats is not currently implemented."); return; } if (index >= pieces.Length) { continue; } if (pieces[index].ToUpperInvariant() == "__UNKNOWN") { var partName = Path.GetFileNameWithoutExtension(partPath); if (string.IsNullOrEmpty(partName) == true) { continue; } if (partName.Length > 16) { partName = partName.Substring(0, 16); } pendingEntry.Name = null; pendingEntry.NameHash = ulong.Parse(partName, NumberStyles.AllowHexSpecifier); } else { pendingEntry.Name = string.Join("\\", pieces.Skip(index).ToArray()).ToLowerInvariant(); pendingEntry.NameHash = CRC64.Hash(pendingEntry.Name); } if (pendingEntries.ContainsKey(pendingEntry.NameHash) == true) { Console.WriteLine("Ignoring duplicate of {0:X}: {1}", pendingEntry.NameHash, partPath); if (verbose == true) { Console.WriteLine(" Previously added from: {0}", pendingEntries[pendingEntry.NameHash].PartPath); } continue; } pendingEntries[pendingEntry.NameHash] = pendingEntry; } } var fat = new BigFile { Version = packageVersion, Platform = packagePlatform }; // reasonable default? // need to figure out what this value actually does if (packagePlatform == Big.Platform.PC) { fat.Unknown74 = 3; } else if (packagePlatform == Big.Platform.PS3 || packagePlatform == Big.Platform.X360) { fat.Unknown74 = 4; } else { throw new InvalidOperationException(); } using (var output = File.Create(datPath)) { long current = 0; long total = pendingEntries.Count; var padding = total.ToString(CultureInfo.InvariantCulture).Length; foreach (var pendingEntry in pendingEntries.Select(kv => kv.Value)) { current++; if (verbose == true) { Console.WriteLine("[{0}/{1}] {2}", current.ToString(CultureInfo.InvariantCulture).PadLeft(padding), total, pendingEntry.PartPath); } var entry = new Big.Entry(); entry.NameHash = pendingEntry.NameHash; entry.Offset = output.Position; using (var input = File.OpenRead(pendingEntry.FullPath)) { EntryCompression.Compress(fat.Platform, ref entry, input, compress, output); output.Seek(output.Position.Align(16), SeekOrigin.Begin); } fat.Entries.Add(entry); } } using (var output = File.Create(fatPath)) { fat.Serialize(output); } }
public static void Main(string[] args) { bool showHelp = false; bool verbose = false; OptionSet options = new OptionSet() { { "v|verbose", "be verbose (list files)", v => verbose = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extras; try { extras = options.Parse(args); } catch (OptionException e) { Console.Write("{0}: ", GetExecutableName()); Console.WriteLine(e.Message); Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName()); return; } if (extras.Count < 1 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ output_big input_directory+", GetExecutableName()); Console.WriteLine("Pack files from input directories into a Big File."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPaths = new List <string>(); string outputPath; if (extras.Count == 1) { inputPaths.Add(extras[0]); outputPath = Path.ChangeExtension(extras[0], ".viv"); } else { outputPath = extras[0]; inputPaths.AddRange(extras.Skip(1)); } var paths = new SortedDictionary <uint, string>(); if (verbose == true) { Console.WriteLine("Finding files..."); } foreach (var relPath in inputPaths) { string inputPath = Path.GetFullPath(relPath); if (inputPath.EndsWith(Path.DirectorySeparatorChar.ToString()) == true) { inputPath = inputPath.Substring(0, inputPath.Length - 1); } foreach (string path in Directory.GetFiles(inputPath, "*", SearchOption.AllDirectories)) { string fullPath = Path.GetFullPath(path); string partPath = fullPath.Substring(inputPath.Length + 1).ToLowerInvariant(); uint hash = 0xFFFFFFFF; if (partPath.ToUpper().StartsWith("__UNKNOWN") == true) { string partName; partName = Path.GetFileNameWithoutExtension(partPath); if (partName.Length > 8) { partName = partName.Substring(0, 8); } hash = uint.Parse( partName, System.Globalization.NumberStyles.AllowHexSpecifier); } else { hash = partPath.ToLowerInvariant().HashFileName(); } if (paths.ContainsKey(hash) == true) { Console.WriteLine("Ignoring {0} duplicate.", partPath); continue; } paths[hash] = fullPath; } } using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { var big = new BigFile(); if (verbose == true) { Console.WriteLine("Adding files..."); } // write a dummy header output.Seek(0, SeekOrigin.Begin); big.Entries.Clear(); foreach (var kvp in paths) { big.Entries.Add(new BigFile.Entry() { Name = 0, Offset = 0, Size = 0, }); } big.Serialize(output); output.Seek(output.Position.Align(2048), SeekOrigin.Begin); long baseOffset = output.Position; // write file data big.Entries.Clear(); if (verbose == true) { Console.WriteLine("Writing to disk..."); } foreach (var kvp in paths) { if (verbose == true) { Console.WriteLine(kvp.Value); } using (var input = File.Open(kvp.Value, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { output.Seek(baseOffset, SeekOrigin.Begin); uint size = (uint)input.Length.Align(2048); big.Entries.Add(new BigFile.Entry() { Name = kvp.Key, Offset = (uint)output.Position, Size = size, }); output.WriteFromStream(input, input.Length); baseOffset += size; } } // write filled header output.Seek(0, SeekOrigin.Begin); big.TotalFileSize = (uint)output.Length; big.Serialize(output); } }
public static void Main(string[] args) { bool showHelp = false; bool extractUnknowns = true; bool extractFiles = true; bool extractSubFats = true; bool unpackSubFats = false; string filterPattern = null; bool overwriteFiles = false; bool verbose = false; var options = new OptionSet() { { "o|overwrite", "overwrite existing files", v => overwriteFiles = v != null }, { "nf|no-files", "don't extract files", v => extractFiles = v == null }, { "nu|no-unknowns", "don't extract unknown files", v => extractUnknowns = v == null }, { "ns|no-subfats", "don't extract subfats", v => extractSubFats = v == null }, { "us|unpack-subfats", "unpack files from subfats", v => unpackSubFats = v != null }, { "f|filter=", "only extract files using pattern", v => filterPattern = v }, { "v|verbose", "be verbose", v => verbose = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extras; try { extras = options.Parse(args); } catch (OptionException e) { Console.Write("{0}: ", GetExecutableName()); Console.WriteLine(e.Message); Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName()); return; } if (extras.Count < 1 || extras.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_fat [output_dir]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Unpack files from a Big File (FAT/DAT pair)."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } string fatPath = extras[0]; string outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(fatPath, null) + "_unpack"; string datPath; Regex filter = null; if (string.IsNullOrEmpty(filterPattern) == false) { filter = new Regex(filterPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); } if (Path.GetExtension(fatPath) == ".dat") { datPath = fatPath; fatPath = Path.ChangeExtension(fatPath, ".fat"); } else { datPath = Path.ChangeExtension(fatPath, ".dat"); } if (verbose == true) { Console.WriteLine("Loading project..."); } var manager = ProjectData.Manager.Load(); if (manager.ActiveProject == null) { Console.WriteLine("Warning: no active project loaded."); } if (verbose == true) { Console.WriteLine("Reading FAT..."); } BigFile fat; using (var input = File.OpenRead(fatPath)) { fat = new BigFile(); fat.Deserialize(input); } var hashes = manager.LoadListsFileNames(fat.Version); var subFatHashes = manager.LoadListsSubFatNames(fat.Version); using (var input = File.OpenRead(datPath)) { if (extractFiles == true) { Big.Entry[] entries; if (extractSubFats == true && unpackSubFats == true) { entries = fat.Entries.Concat(fat.SubFats.SelectMany(sf => sf.Entries)) .OrderBy(e => e.Offset) .ToArray(); } else { entries = fat.Entries.OrderBy(e => e.Offset).ToArray(); } if (entries.Length > 0) { if (verbose == true) { Console.WriteLine("Unpacking files..."); } long current = 0; long total = entries.Length; var padding = total.ToString(CultureInfo.InvariantCulture).Length; var duplicates = new Dictionary <ulong, int>(); foreach (var entry in entries) { current++; if (subFatHashes.Contains(entry.NameHash) == true) { continue; } string entryName; if (GetEntryName(input, fat, entry, hashes, extractUnknowns, out entryName) == false) { continue; } if (duplicates.ContainsKey(entry.NameHash) == true) { var number = duplicates[entry.NameHash]++; var e = Path.GetExtension(entryName); var nn = Path.ChangeExtension( Path.ChangeExtension(entryName, null) + "__DUPLICATE_" + number.ToString(CultureInfo.InvariantCulture), e); entryName = Path.Combine("__DUPLICATE", nn); } else { duplicates[entry.NameHash] = 0; } if (filter != null && filter.IsMatch(entryName) == false) { continue; } var entryPath = Path.Combine(outputPath, entryName); if (overwriteFiles == false && File.Exists(entryPath) == true) { continue; } if (verbose == true) { Console.WriteLine("[{0}/{1}] {2}", current.ToString(CultureInfo.InvariantCulture).PadLeft(padding), total, entryName); } input.Seek(entry.Offset, SeekOrigin.Begin); var entryParent = Path.GetDirectoryName(entryPath); if (string.IsNullOrEmpty(entryParent) == false) { Directory.CreateDirectory(entryParent); } using (var output = File.Create(entryPath)) { EntryDecompression.Decompress(entry, input, output); } } } } if (extractSubFats == true && unpackSubFats == false && fat.SubFats.Count > 0) { if (verbose == true) { Console.WriteLine("Unpacking subfats..."); } var subFatsFromFat = fat.SubFats.ToList(); long current = 0; long total = subFatsFromFat.Count; var padding = total.ToString(CultureInfo.InvariantCulture).Length; foreach (var headerEntry in fat.Entries.Where(e => subFatHashes.Contains(e.NameHash) == true)) { current++; var subFat = new SubFatFile(); using (var temp = new MemoryStream()) { EntryDecompression.Decompress(headerEntry, input, temp); temp.Position = 0; subFat.Deserialize(temp, fat); } var matchingSubFats = subFatsFromFat .Where(sf => subFat.Entries.SequenceEqual(sf.Entries)) .ToArray(); if (matchingSubFats.Length == 0) { continue; } if (matchingSubFats.Length > 1) { throw new InvalidOperationException(); } var entryName = subFatHashes[headerEntry.NameHash]; entryName = FilterEntryName(entryName); var entryHeaderPath = Path.Combine(outputPath, "__SUBFAT", entryName); if (overwriteFiles == false && File.Exists(entryHeaderPath) == true) { continue; } if (verbose == true) { Console.WriteLine("[{0}/{1}] {2}", current.ToString(CultureInfo.InvariantCulture).PadLeft(padding), total, entryName); } var entryParent = Path.GetDirectoryName(entryHeaderPath); if (string.IsNullOrEmpty(entryParent) == false) { Directory.CreateDirectory(entryParent); } var entryDataPath = Path.ChangeExtension(entryHeaderPath, ".dat"); var rebuiltFat = new BigFile { Version = fat.Version, Platform = fat.Platform, Unknown74 = fat.Unknown74 }; using (var output = File.Create(entryDataPath)) { var rebuiltEntries = new List <Big.Entry>(); foreach (var entry in subFat.Entries.OrderBy(e => e.Offset)) { var rebuiltEntry = new Big.Entry { NameHash = entry.NameHash, UncompressedSize = entry.UncompressedSize, CompressedSize = entry.CompressedSize, Offset = output.Position, CompressionScheme = entry.CompressionScheme }; input.Seek(entry.Offset, SeekOrigin.Begin); output.WriteFromStream(input, entry.CompressedSize); output.Seek(output.Position.Align(16), SeekOrigin.Begin); rebuiltEntries.Add(rebuiltEntry); } rebuiltFat.Entries.AddRange(rebuiltEntries.OrderBy(e => e.NameHash)); } using (var output = File.Create(entryHeaderPath)) { rebuiltFat.Serialize(output); } foreach (var matchingSubFat in matchingSubFats) { subFatsFromFat.Remove(matchingSubFat); } } if (subFatsFromFat.Count > 0) { Console.WriteLine("Warning: could not identify {0} subfats", subFatsFromFat.Count); } } } }