internal static void SanityCheckEntry(Big.Entry entry, Big.Target platform) { if (entry.CompressionScheme == Big.CompressionScheme.None) { if (platform != Big.Target.Xbox360 && entry.UncompressedSize != 0) { throw new FormatException("got entry with no compression with a non-zero uncompressed size"); } } else if (entry.CompressionScheme == Big.CompressionScheme.LZO1x || entry.CompressionScheme == Big.CompressionScheme.Zlib) { if (entry.CompressedSize == 0 && entry.UncompressedSize > 0) { throw new FormatException( "got entry with compression with a zero compressed size and a non-zero uncompressed size"); } } else if (entry.CompressionScheme == Big.CompressionScheme.Xbox) { if (entry.CompressedSize == 0 && entry.UncompressedSize > 0) { throw new FormatException( "got entry with compression with a zero compressed size and a non-zero uncompressed size"); } } else { throw new FormatException("got entry with unsupported compression scheme"); } }
private static bool GetEntryName(Stream input, BigFile fat, Big.Entry entry, ProjectData.HashList <uint> hashes, bool extractUnknowns, bool onlyUnknowns, out string entryName) { entryName = hashes[entry.NameHash]; if (entryName == null) { if (extractUnknowns == false) { return(false); } string type; string extension; { 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 { using (var temp = new MemoryStream()) { EntryDecompression.Decompress(entry, input, temp); temp.Position = 0; read = temp.Read(guess, 0, (int)Math.Min(temp.Length, guess.Length)); } } var tuple = FileDetection.Detect(guess, Math.Min(guess.Length, read)); type = tuple != null ? tuple.Item1 : "unknown"; extension = tuple != null ? tuple.Item2 : null; } entryName = entry.NameHash.ToString("X8"); if (string.IsNullOrEmpty(extension) == false) { entryName = Path.ChangeExtension(entryName, "." + extension); } if (string.IsNullOrEmpty(type) == false) { entryName = Path.Combine(type, entryName); } entryName = Path.Combine("__UNKNOWN", entryName); } else { if (onlyUnknowns == true) { return(false); } entryName = FilterEntryName(entryName); } return(true); }
private static void Main(string[] args) { bool showHelp = false; bool verbose = false; bool compress = false; int packageVersion = 8; Big.Target packageTarget = Big.Target.Win64; 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 8)", v => packageVersion = ParsePackageVersion(v) }, { "pt|package-target", "package platform (default Win64)", v => packageTarget = ParsePackageTarget(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 <uint, 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 (index >= pieces.Length) { continue; } if (pieces[index].ToUpperInvariant() == "__UNKNOWN") { var partName = Path.GetFileNameWithoutExtension(partPath); if (string.IsNullOrEmpty(partName) == true) { continue; } if (partName.Length > 8) { partName = partName.Substring(0, 8); } pendingEntry.Name = null; pendingEntry.NameHash = uint.Parse(partName, NumberStyles.AllowHexSpecifier); } else { pendingEntry.Name = string.Join("\\", pieces.Skip(index).ToArray()).ToLowerInvariant(); pendingEntry.NameHash = ProjectHelpers.Hasher(ProjectHelpers.Modifier(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, Target = packageTarget, Platform = TargetToPlatform(packageTarget), }; // reasonable default? // need to figure out what this value actually does if (packageTarget == Big.Target.Win64) { fat.Unknown70 = 0x32; } else if (packageTarget == Big.Target.PS3 || packageTarget == Big.Target.Xbox360) { fat.Unknown70 = 0x37; } else { throw new InvalidOperationException("unknown target"); } 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.Target, 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); } }