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;
                }
            }
        }
Beispiel #2
0
        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();
                        }
                    }
                }
            }
        }