public static void WriteFileNameHash(this Stream stream, FileNameHash hash) { stream.WriteValueU32(hash.A, Endian.Big); stream.WriteValueU32(hash.B, Endian.Big); stream.WriteValueU32(hash.C, Endian.Big); stream.WriteValueU32(hash.D, Endian.Big); }
public int CompareTo(object obj) { if (obj is FileNameHash) { FileNameHash temp = (FileNameHash)obj; string a = this.ToString(); string b = obj.ToString(); return(a.CompareTo(b)); } throw new ArgumentException(); }
public static void Main(string[] args) { bool showHelp = false; bool extractUnknowns = true; bool overwriteFiles = false; bool verbose = false; var options = new OptionSet() { { "o|overwrite", "overwrite existing files", v => overwriteFiles = v != null }, { "nu|no-unknowns", "don't extract unknown files", v => extractUnknowns = v == null }, { "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_sfar [output_dir]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPath = extras[0]; var outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null); var manager = ProjectData.Manager.Load(); if (manager.ActiveProject == null) { Console.WriteLine("Warning: no active project loaded."); } var hashes = manager.LoadLists( "*.filelist", FileNameHash.Compute, s => s.Replace("\\", "/")); using (var input = File.OpenRead(inputPath)) { var sfx = new SFXArchiveFile(); sfx.Deserialize(input); long current = 0; long total = sfx.Entries.Count; var padding = total.ToString(CultureInfo.InvariantCulture).Length; if (sfx.CompressionScheme != SFXArchive.CompressionScheme.None && sfx.CompressionScheme != SFXArchive.CompressionScheme.Lzma && sfx.CompressionScheme != SFXArchive.CompressionScheme.Lzx) { Console.WriteLine("Unsupported compression scheme!"); return; } var inputBlock = new byte[sfx.MaximumBlockSize]; var outputBlock = new byte[sfx.MaximumBlockSize]; var hashesFromFile = new Dictionary<FileNameHash, string>(); // todo: figure out what the file name is var fileNameListNameHash = new FileNameHash( new byte[] { 0xB5, 0x50, 0x19, 0xCB, 0xF9, 0xD3, 0xDA, 0x65, 0xD5, 0x5B, 0x32, 0x1C, 0x00, 0x19, 0x69, 0x7C, }); var fileNameListEntry = sfx.Entries .FirstOrDefault(e => e.NameHash == fileNameListNameHash); if (fileNameListEntry != null) { using (var temp = new MemoryStream()) { DecompressEntry( sfx, fileNameListEntry, input, inputBlock, temp, outputBlock); temp.Position = 0; var reader = new StreamReader(temp); while (reader.EndOfStream == false) { var line = reader.ReadLine(); hashesFromFile.Add(FileNameHash.Compute(line), line); } } } foreach (var entry in sfx.Entries) { current++; var entryName = hashes[entry.NameHash]; if (entryName == null) { if (hashesFromFile.ContainsKey(entry.NameHash) == true) { entryName = hashesFromFile[entry.NameHash]; } } if (entryName == null) { if (extractUnknowns == false) { continue; } entryName = entry.NameHash.ToString(); entryName = Path.Combine("__UNKNOWN", entryName); } else { entryName = entryName.Replace("/", "\\"); if (entryName.StartsWith("\\") == true) { entryName = entryName.Substring(1); } } 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); Directory.CreateDirectory(Path.GetDirectoryName(entryPath)); using (var output = File.Create(entryPath)) { DecompressEntry( sfx, entry, input, inputBlock, output, outputBlock); } } } }