private static void DecompressXMemCompress(Entry entry, Stream input, Stream output) { var magic = input.ReadValueU32(Endian.Big); if (magic != 0x0FF512EE) { throw new FormatException("invalid magic"); } var version = input.ReadValueU32(Endian.Big); if (version != 0x01030000) { throw new FormatException("invalid version"); } var unknown08 = input.ReadValueU32(Endian.Big); if (unknown08 != 0) { throw new FormatException("don't know how to handle a non-zero unknown08"); } var unknown0C = input.ReadValueU32(Endian.Big); if (unknown0C != 0) { throw new FormatException("don't know how to handle a non-zero unknown0C"); } var windowSize = input.ReadValueU32(Endian.Big); var chunkSize = input.ReadValueU32(Endian.Big); var uncompressedSize = input.ReadValueS64(Endian.Big); var compressedSize = input.ReadValueS64(Endian.Big); var largestUncompressedChunkSize = input.ReadValueS32(Endian.Big); var largestCompressedChunkSize = input.ReadValueS32(Endian.Big); if (uncompressedSize < 0 || compressedSize < 0 || largestUncompressedChunkSize < 0 || largestCompressedChunkSize < 0) { throw new FormatException("bad size value"); } if (uncompressedSize != entry.UncompressedSize) { throw new InvalidOperationException("uncompressed size mismatch"); } var uncompressedBytes = new byte[largestUncompressedChunkSize]; var compressedBytes = new byte[largestCompressedChunkSize]; var remaining = uncompressedSize; while (remaining > 0) { using (var context = new XCompression.DecompressionContext(windowSize, chunkSize)) { var compressedChunkSize = input.ReadValueS32(Endian.Big); if (compressedChunkSize < 0 || compressedChunkSize > largestCompressedChunkSize) { throw new InvalidOperationException("compressed size mismatch"); } if (input.Read(compressedBytes, 0, compressedChunkSize) != compressedChunkSize) { throw new EndOfStreamException("could not read all compressed bytes"); } var uncompressedChunkSize = (int)Math.Min(largestUncompressedChunkSize, remaining); var actualUncompressedChunkSize = uncompressedChunkSize; var actualCompressedChunkSize = compressedChunkSize; var result = context.Decompress(compressedBytes, 0, ref actualCompressedChunkSize, uncompressedBytes, 0, ref actualUncompressedChunkSize); if (result != XCompression.ErrorCode.None) { throw new InvalidOperationException(string.Format("XCompression decompression failure ({0})", result)); } if (actualUncompressedChunkSize != uncompressedChunkSize) { throw new InvalidOperationException("XCompression decompression failure (uncompressed size mismatch)"); } output.Write(uncompressedBytes, 0, actualUncompressedChunkSize); remaining -= actualUncompressedChunkSize; } } }
public static void Main(string[] args) { bool showHelp = false; bool overwriteFiles = false; bool verbose = false; var options = new OptionSet() { { "o|overwrite", "overwrite existing files", v => overwriteFiles = 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_rcf [output_dir]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPath = Path.GetFullPath(extras[0]); var outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null) + "_unpack"; var kft = new KnownFileTypes(); var kftPath = Path.Combine(GetExecutablePath(), "archive_file_types.cfg"); if (File.Exists(kftPath) == true) { kft.Load(kftPath); } using (var input = File.OpenRead(inputPath)) { var archive = new ArchiveFile(); archive.Deserialize(input); long current = 0; long total = archive.Entries.Count; var padding = total.ToString(CultureInfo.InvariantCulture).Length; foreach (var entry in archive.Entries) { current++; var entryName = entry.Name; if (kft.Contains(entry.TypeHash) == false) { entryName += ".UNK#" + entry.TypeHash.ToString("X8"); } else { entryName += kft.GetExtension(entry.TypeHash) ?? "." + kft.GetName(entry.TypeHash); } 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 entryDirectory = Path.GetDirectoryName(entryPath); if (entryDirectory != null) { Directory.CreateDirectory(entryDirectory); } using (var output = File.Create(entryPath)) { input.Seek(entry.Offset, SeekOrigin.Begin); if (archive.Version == 8) { if (entry.CompressedSize == entry.UncompressedSize) { output.WriteFromStream(input, entry.UncompressedSize); } else { using (var temp = input.ReadToMemoryStream(entry.CompressedSize)) { var zlib = new InflaterInputStream(temp); output.WriteFromStream(zlib, entry.UncompressedSize); } } } else if (archive.Version == 17) { if (entry.CompressedSize == entry.UncompressedSize) { output.WriteFromStream(input, entry.UncompressedSize); } else { var compressed = input.ReadBytes(entry.CompressedSize); var uncompressed = new byte[entry.UncompressedSize]; using (var context = new XCompression.DecompressionContext(0x8000)) { var compressedSize = compressed.Length; var uncompressedSize = uncompressed.Length; if ( context.Decompress(compressed, 0, ref compressedSize, uncompressed, 0, ref uncompressedSize) != XCompression.ErrorCode.None) { throw new InvalidOperationException(); } if (uncompressedSize != uncompressed.Length || compressedSize != compressed.Length) { throw new InvalidOperationException(); } output.WriteBytes(uncompressed); } } } else { throw new NotSupportedException(); } } } } }