Example #1
0
        public static void Main(string[] args)
        {
            var showHelp = false;

            var options = new OptionSet()
            {
                {
                    "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]+ -j input_pcc [output_pcc]", GetExecutableName());
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return;
            }

            var inputPath = extras[0];
            var outputPath = extras.Count >= 2 ? extras[1] : inputPath;
            var tempPath = Path.ChangeExtension(inputPath, "decompressing");

            if (File.Exists(tempPath) == true)
            {
                throw new InvalidOperationException("temporary path already exists?");
            }

            using (var input = File.OpenRead(inputPath))
            {
                var magic = input.ReadValueU32(Endian.Little);
                if (magic != 0x9E2A83C1 &&
                    magic.Swap() != 0x9E2A83C1)
                {
                    throw new FormatException("not a package");
                }
                var endian = magic == 0x9E2A83C1 ? Endian.Little : Endian.Big;

                var versionLo = input.ReadValueU16(endian);
                var versionHi = input.ReadValueU16(endian);

                if (versionLo != 684 &&
                    versionHi != 194)
                {
                    throw new FormatException("unsupported version");
                }

                long headerSize = 8;

                input.Seek(4, SeekOrigin.Current);
                headerSize += 4;

                var folderNameLength = input.ReadValueS32(endian);
                headerSize += 4;

                var folderNameByteLength =
                    folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2);
                input.Seek(folderNameByteLength, SeekOrigin.Current);
                headerSize += folderNameByteLength;

                var packageFlagsOffset = input.Position;
                var packageFlags = input.ReadValueU32(endian);
                headerSize += 4;

                if ((packageFlags & 0x02000000u) == 0)
                {
                    throw new FormatException("package is not flagged as compressed");
                }

                if ((packageFlags & 8) != 0)
                {
                    input.Seek(4, SeekOrigin.Current);
                    headerSize += 4;
                }

                input.Seek(60, SeekOrigin.Current);
                headerSize += 60;

                var generationsCount = input.ReadValueU32(endian);
                headerSize += 4;

                input.Seek(generationsCount * 12, SeekOrigin.Current);
                headerSize += generationsCount * 12;

                input.Seek(20, SeekOrigin.Current);
                headerSize += 20;

                var blockCount = input.ReadValueU32(endian);

                var blockInfos = new CompressedBlockInfo[blockCount];
                for (uint i = 0; i < blockCount; i++)
                {
                    blockInfos[i].UncompressedOffset = input.ReadValueU32(endian);
                    blockInfos[i].UncompressedSize = input.ReadValueU32(endian);
                    blockInfos[i].CompressedOffset = input.ReadValueU32(endian);
                    blockInfos[i].CompressedSize = input.ReadValueU32(endian);
                }

                var outputHeaderSize = headerSize + 4 + 8;
                var afterBlockTableOffset = input.Position;

                if (outputHeaderSize != blockInfos.First().UncompressedOffset)
                {
                    throw new FormatException();
                }

                using (var output = File.Create(tempPath))
                {
                    input.Seek(0, SeekOrigin.Begin);
                    output.Seek(0, SeekOrigin.Begin);

                    output.WriteFromStream(input, headerSize);
                    output.WriteValueU32(0, endian); // block count
                    input.Seek(afterBlockTableOffset, SeekOrigin.Begin);
                    output.WriteFromStream(input, 8);

                    output.Seek(packageFlagsOffset, SeekOrigin.Begin);
                    output.WriteValueU32(packageFlags & ~0x02000000u, endian);

                    foreach (var blockInfo in blockInfos)
                    {
                        input.Seek(blockInfo.CompressedOffset, SeekOrigin.Begin);
                        var blockMagic = input.ReadValueU32(endian);
                        if (blockMagic != 0x9E2A83C1)
                        {
                            throw new FormatException("bad compressed block magic");
                        }

                        var blockSegmentSize = input.ReadValueU32(endian);
                        /*var blockCompressedSize =*/ input.ReadValueU32(endian);
                        var blockUncompressedSize = input.ReadValueU32(endian);
                        if (blockUncompressedSize != blockInfo.UncompressedSize)
                        {
                            throw new FormatException("uncompressed size mismatch");
                        }

                        uint segmentCount = ((blockUncompressedSize + blockSegmentSize) - 1) / blockSegmentSize;

                        var segmentInfos = new CompressedSegmentInfo[segmentCount];
                        for (uint i = 0; i < segmentCount; i++)
                        {
                            segmentInfos[i].CompressedSize = input.ReadValueU32(endian);
                            segmentInfos[i].UncompressedSize = input.ReadValueU32(endian);
                        }

                        if (segmentInfos.Sum(si => si.UncompressedSize) != blockInfo.UncompressedSize)
                        {
                            throw new FormatException("uncompressed size mismatch");
                        }

                        output.Seek(blockInfo.UncompressedOffset, SeekOrigin.Begin);
                        foreach (var segmentInfo in segmentInfos)
                        {
                            using (var temp = input.ReadToMemoryStream(segmentInfo.CompressedSize))
                            {
                                var zlib = new InflaterInputStream(temp);
                                output.WriteFromStream(zlib, segmentInfo.UncompressedSize);
                            }
                        }
                    }
                }
            }

            File.Delete(outputPath);
            File.Move(tempPath, outputPath);
        }
        public void Deserialize(Stream input)
        {
            var magic = input.ReadValueU32(Endian.Little);

            if (magic != Signature && magic.Swap() != Signature)
            {
                throw new FormatException();
            }
            var endian = magic == Signature ? Endian.Little : Endian.Big;

            var majorVersion = input.ReadValueU16(endian);
            var minorVersion = input.ReadValueU16(endian);

            if (majorVersion != 2 || minorVersion != 1)
            {
                throw new FormatException();
            }

            var alignment = input.ReadValueU32(endian);
            var unknown0C = input.ReadValueU32(endian);
            var maxCompressedBlockSize = input.ReadValueU32(endian);
            var uncompressedBlockSize  = input.ReadValueU32(endian);

            if (alignment != 0x1000 || unknown0C != 0)
            {
                throw new FormatException();
            }

            var compressedBlockCount = input.ReadValueU32(endian);
            var compressedBlocks     = new CompressedBlockInfo[compressedBlockCount];

            for (uint i = 0; i < compressedBlockCount; i++)
            {
                RawCompressedBlockInfo raw;
                raw.CompressedSize   = input.ReadValueU32(endian);
                raw.UncompressedSize = input.ReadValueU32(endian);
                compressedBlocks[i]  = new CompressedBlockInfo(raw);
            }

            var entries = new List <EntryInfo>();

            while (input.Position + 20 <= input.Length)
            {
                RawEntryInfo raw;
                raw.NameHash             = input.ReadValueU32(endian);
                raw.Offset               = input.ReadValueU32(endian);
                raw.CompressedSize       = input.ReadValueU32(endian);
                raw.UncompressedSize     = input.ReadValueU32(endian);
                raw.CompressedBlockIndex = input.ReadValueU16();
                raw.CompressionType      = (CompressionType)input.ReadValueU8();
                var compressionFlags = input.ReadValueU8();
                if ((compressionFlags & ~1) != 0)
                {
                    throw new FormatException("unknown compression flags");
                }
                raw.CompressionFlags = (CompressionFlags)compressionFlags;
                entries.Add(new EntryInfo(raw));
            }

            this._Endian    = endian;
            this._Alignment = alignment;
            this._MaxCompressedBlockSize = maxCompressedBlockSize;
            this._UncompressedBlockSize  = uncompressedBlockSize;
            this._CompressedBlocks.Clear();
            this._CompressedBlocks.AddRange(compressedBlocks);
            this._Entries.Clear();
            this._Entries.AddRange(entries);
        }