private static string DetectExtension(Stream input, Big.Entry entry) { var guess = new byte[64]; int read = 0; if (entry.CompressionScheme == Big.CompressionScheme.None) { if (entry.CompressedSize > 0) { input.Seek(entry.Offset, SeekOrigin.Begin); read = input.Read(guess, 0, (int)Math.Min( entry.CompressedSize, guess.Length)); } } else if (entry.CompressionScheme == Big.CompressionScheme.LZO1x) { input.Seek(entry.Offset, SeekOrigin.Begin); var compressedData = new byte[entry.CompressedSize]; if (input.Read(compressedData, 0, compressedData.Length) != compressedData.Length) { throw new EndOfStreamException(); } var uncompressedData = new byte[entry.UncompressedSize]; int uncompressedSize = (int)entry.UncompressedSize; var result = MiniLZO.LZO.DecompressSafe( compressedData, 0, (int)entry.CompressedSize, uncompressedData, 0, ref uncompressedSize); if (result != MiniLZO.ErrorCode.Success) { throw new InvalidOperationException("decompression error: " + result.ToString()); } else if (uncompressedSize != entry.UncompressedSize) { throw new InvalidOperationException("did not decompress correct amount of data"); } Array.Copy(uncompressedData, 0, guess, 0, Math.Min(guess.Length, uncompressedData.Length)); read = uncompressedData.Length; } else { throw new NotSupportedException(); } return(FileExtensions.Detect(guess, Math.Min(guess.Length, read))); }
private static void ExtractFile(Stream input, Big.Entry entry, Stream output) { if (entry.CompressionScheme == Big.CompressionScheme.None) { if (entry.CompressedSize > 0) { input.Seek(entry.Offset, SeekOrigin.Begin); output.WriteFromStream(input, entry.CompressedSize); } } else if (entry.CompressionScheme == Big.CompressionScheme.LZO1x) { if (entry.UncompressedSize > 0) { input.Seek(entry.Offset, SeekOrigin.Begin); var compressedData = new byte[entry.CompressedSize]; if (input.Read(compressedData, 0, compressedData.Length) != compressedData.Length) { throw new EndOfStreamException(); } var uncompressedData = new byte[entry.UncompressedSize]; int uncompressedSize = (int)entry.UncompressedSize; var result = MiniLZO.LZO.DecompressSafe( compressedData, 0, (int)entry.CompressedSize, uncompressedData, 0, ref uncompressedSize); if (result != 0) { throw new InvalidOperationException("decompression error: " + result.ToString()); } else if (uncompressedSize != entry.UncompressedSize) { throw new InvalidOperationException("did not decompress correct amount of data"); } output.Write(uncompressedData, 0, uncompressedData.Length); } } else { throw new NotSupportedException(); } }
public void Deserialize(Stream input) { var magic = input.ReadValueU32(Endian.Little); if (magic != 0x46415432) // FAT2 { throw new FormatException("not a big file"); } var endian = Endian.Little; var version = input.ReadValueU32(endian); if (version != 5) { throw new FormatException("unsupported big file version"); } input.ReadValueU32(endian); var indexCount = input.ReadValueU32(endian); this.Entries.Clear(); for (int i = 0; i < indexCount; i++) { var index = new Big.Entry(); index.Deserialize(input, endian); this.Entries.Add(index); } // There's a dword at the end of the file past the index entries, all observed // Far Cry 2 archives all have it as 0, I assume it's another table for something. if (input.ReadValueU32(endian) != 0) { throw new FormatException("unexpected value"); } }
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); } }