Пример #1
0
        public static SaveFile Deserialize(Stream input, Platform platform, DeserializeSettings settings)
        {
            if (platform != Platform.PC &&
                platform != Platform.X360 &&
                platform != Platform.PS3)
            {
                throw new ArgumentException("unsupported platform", "platform");
            }

            if (input.Position + 20 > input.Length)
            {
                throw new SaveCorruptionException("not enough data for save header");
            }

            var check = input.ReadValueU32(Endian.Big);

            if (check == 0x434F4E20)
            {
                throw new SaveFormatException("cannot load XBOX 360 CON files, extract save using Modio or equivalent");
            }
            input.Seek(-4, SeekOrigin.Current);

            var readSha1Hash = input.ReadBytes(20);

            using (var data = input.ReadToMemoryStream(input.Length - 20))
            {
                byte[] computedSha1Hash;
                using (var sha1 = new System.Security.Cryptography.SHA1Managed())
                {
                    computedSha1Hash = sha1.ComputeHash(data);
                }

                if ((settings & DeserializeSettings.IgnoreSha1Mismatch) == 0 &&
                    readSha1Hash.SequenceEqual(computedSha1Hash) == false)
                {
                    throw new SaveCorruptionException("invalid SHA1 hash");
                }

                data.Position = 0;
                var uncompressedSize = data.ReadValueU32(Endian.Big);

                var uncompressedBytes = new byte[uncompressedSize];
                if (uncompressedSize <= BlockSize)
                {
                    if (platform == Platform.PC ||
                        platform == Platform.X360)
                    {
                        var actualUncompressedSize = (int)uncompressedSize;
                        var compressedSize         = (uint)(data.Length - 4);
                        var compressedBytes        = data.ReadBytes(compressedSize);
                        var result = LZO.Decompress(compressedBytes,
                                                    0,
                                                    (int)compressedSize,
                                                    uncompressedBytes,
                                                    0,
                                                    ref actualUncompressedSize);
                        if (result != LZO.ErrorCode.Success)
                        {
                            throw new SaveCorruptionException(string.Format("LZO decompression failure ({0})", result));
                        }

                        if (actualUncompressedSize != (int)uncompressedSize)
                        {
                            throw new SaveCorruptionException("LZO decompression failure (uncompressed size mismatch)");
                        }
                    }
                    else
                    {
                        var compressedSize = (uint)(data.Length - 4);
                        using (var temp = data.ReadToMemoryStream(compressedSize))
                        {
                            var zlib = new InflaterInputStream(temp);
                            try
                            {
                                if (zlib.Read(uncompressedBytes, 0, uncompressedBytes.Length) !=
                                    uncompressedBytes.Length)
                                {
                                    throw new SaveCorruptionException(
                                              "zlib decompression failure (uncompressed size mismatch)");
                                }
                            }
                            catch (ICSharpCode.SharpZipLib.SharpZipBaseException e)
                            {
                                throw new SaveCorruptionException(string.Format("zlib decompression failure ({0})",
                                                                                e.Message),
                                                                  e);
                            }
                        }
                    }
                }
                else
                {
                    if (platform == Platform.PC ||
                        platform == Platform.X360)
                    {
                        var blockCount = data.ReadValueU32(Endian.Big);
                        var blockInfos = new List <Tuple <uint, uint> >();
                        for (uint i = 0; i < blockCount; i++)
                        {
                            var blockCompressedSize   = data.ReadValueU32(Endian.Big);
                            var blockUncompressedSize = data.ReadValueU32(Endian.Big);
                            blockInfos.Add(new Tuple <uint, uint>(blockCompressedSize, blockUncompressedSize));
                        }

                        int uncompressedOffset   = 0;
                        int uncompressedSizeLeft = (int)uncompressedSize;
                        foreach (var blockInfo in blockInfos)
                        {
                            var blockUncompressedSize  = Math.Min((int)blockInfo.Item2, uncompressedSizeLeft);
                            var actualUncompressedSize = blockUncompressedSize;
                            var compressedSize         = (int)blockInfo.Item1;
                            var compressedBytes        = data.ReadBytes(compressedSize);
                            var result = LZO.Decompress(compressedBytes,
                                                        0,
                                                        compressedSize,
                                                        uncompressedBytes,
                                                        uncompressedOffset,
                                                        ref actualUncompressedSize);
                            if (result != LZO.ErrorCode.Success)
                            {
                                throw new SaveCorruptionException(string.Format("LZO decompression failure ({0})",
                                                                                result));
                            }

                            if (actualUncompressedSize != blockUncompressedSize)
                            {
                                throw new SaveCorruptionException(
                                          "LZO decompression failure (uncompressed size mismatch)");
                            }

                            uncompressedOffset   += blockUncompressedSize;
                            uncompressedSizeLeft -= blockUncompressedSize;
                        }

                        if (uncompressedSizeLeft != 0)
                        {
                            throw new SaveCorruptionException("LZO decompression failure (uncompressed size left != 0)");
                        }
                    }
                    else if (platform == Platform.PS3)
                    {
                        var blockCount = data.ReadValueU32(Endian.Big);
                        var blockInfos = new List <Tuple <uint, uint> >();
                        for (uint i = 0; i < blockCount; i++)
                        {
                            var blockCompressedSize   = data.ReadValueU32(Endian.Big);
                            var blockUncompressedSize = data.ReadValueU32(Endian.Big);
                            blockInfos.Add(new Tuple <uint, uint>(blockCompressedSize, blockUncompressedSize));
                        }

                        int uncompressedOffset   = 0;
                        int uncompressedSizeLeft = (int)uncompressedSize;
                        foreach (var blockInfo in blockInfos)
                        {
                            var blockUncompressedSize = Math.Min((int)blockInfo.Item2, uncompressedSizeLeft);
                            int actualUncompressedSize;
                            var compressedSize = (int)blockInfo.Item1;

                            using (var temp = data.ReadToMemoryStream(compressedSize))
                            {
                                var zlib = new InflaterInputStream(temp);
                                try
                                {
                                    actualUncompressedSize = zlib.Read(uncompressedBytes,
                                                                       uncompressedOffset,
                                                                       uncompressedBytes.Length);
                                }
                                catch (ICSharpCode.SharpZipLib.SharpZipBaseException e)
                                {
                                    throw new SaveCorruptionException(string.Format("zlib decompression failure ({0})",
                                                                                    e.Message),
                                                                      e);
                                }
                            }

                            if (actualUncompressedSize != blockUncompressedSize)
                            {
                                throw new SaveCorruptionException(
                                          "zlib decompression failure (uncompressed size mismatch)");
                            }

                            uncompressedOffset   += blockUncompressedSize;
                            uncompressedSizeLeft -= blockUncompressedSize;
                        }

                        if (uncompressedSizeLeft != 0)
                        {
                            throw new SaveCorruptionException("zlib decompression failure (uncompressed size left != 0)");
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("unsupported platform");
                    }
                }

                using (var outerData = new MemoryStream(uncompressedBytes))
                {
                    var endian = platform == Platform.PC ? Endian.Little : Endian.Big;

                    var innerSize = outerData.ReadValueU32(Endian.Big);
                    var magic     = outerData.ReadString(3);
                    if (magic != "WSG")
                    {
                        throw new SaveCorruptionException("invalid magic");
                    }

                    var version = outerData.ReadValueU32(endian);
                    if (version != 2)
                    {
                        throw new SaveCorruptionException("invalid or unsupported version");
                    }

                    var readCRC32Hash         = outerData.ReadValueU32(endian);
                    var innerUncompressedSize = outerData.ReadValueS32(endian);

                    var innerCompressedBytes   = outerData.ReadBytes(innerSize - 3 - 4 - 4 - 4);
                    var innerUncompressedBytes = Huffman.Decoder.Decode(innerCompressedBytes,
                                                                        innerUncompressedSize);
                    if (innerUncompressedBytes.Length != innerUncompressedSize)
                    {
                        throw new SaveCorruptionException("huffman decompression failure");
                    }

                    var computedCRC32Hash = CRC32.Hash(innerUncompressedBytes, 0, innerUncompressedBytes.Length);
                    if ((settings & DeserializeSettings.IgnoreCrc32Mismatch) == 0 &&
                        computedCRC32Hash != readCRC32Hash)
                    {
                        throw new SaveCorruptionException("invalid CRC32 hash");
                    }

                    using (var innerUncompressedData = new MemoryStream(innerUncompressedBytes))
                    {
                        var saveGame =
                            ProtoBuf.Serializer.Deserialize <WillowTwoSave.WillowTwoPlayerSaveGame>(innerUncompressedData);

                        PlayerStats playerStats = null;
                        if (saveGame.StatsData != null &&
                            PlayerStats.IsSupportedVersion(saveGame.StatsData, endian) == true)
                        {
                            playerStats = new PlayerStats();
                            playerStats.Deserialize(saveGame.StatsData, endian);
                        }

                        if ((settings & DeserializeSettings.IgnoreReencodeMismatch) == 0)
                        {
                            using (var testData = new MemoryStream())
                            {
                                byte[] oldStatsData = saveGame.StatsData;
                                if (playerStats != null)
                                {
                                    saveGame.StatsData = playerStats.Serialize(endian);
                                }

                                ProtoBuf.Serializer.Serialize(testData, saveGame);

                                if (playerStats != null)
                                {
                                    saveGame.StatsData = oldStatsData;
                                }

                                testData.Position = 0;
                                var testBytes = testData.ReadBytes((uint)testData.Length);
                                if (innerUncompressedBytes.SequenceEqual(testBytes) == false)
                                {
                                    throw new SaveCorruptionException("reencode mismatch");
                                }
                            }
                        }

                        return(new SaveFile()
                        {
                            Platform = platform,
                            SaveGame = saveGame,
                            PlayerStats = playerStats,
                        });
                    }
                }
            }
        }
Пример #2
0
        public static SaveFile Deserialize(Stream input, DeserializeSettings settings)
        {
            if (input.Position + 20 > input.Length)
            {
                throw new SaveCorruptionException("not enough data for save header");
            }

            var check = input.ReadValueU32(Endian.Big);

            if (check == 0x434F4E20)
            {
                throw new SaveFormatException("Xbox 360 save game loading is in the works");
            }
            input.Seek(-4, SeekOrigin.Current);

            var readSha1Hash = input.ReadBytes(20);

            using (var data = input.ReadToMemoryStream(input.Length - 20))
            {
                byte[] computedSha1Hash;
                using (var sha1 = new System.Security.Cryptography.SHA1Managed())
                {
                    computedSha1Hash = sha1.ComputeHash(data);
                }

                if ((settings & DeserializeSettings.IgnoreSha1Mismatch) == 0 &&
                    readSha1Hash.SequenceEqual(computedSha1Hash) == false)
                {
                    throw new SaveCorruptionException("invalid SHA1 hash");
                }

                data.Position = 0;
                var uncompressedSize = data.ReadValueU32(Endian.Big);

                var uncompressedBytes = new byte[uncompressedSize];
                if (uncompressedSize <= BlockSize)
                {
                    var actualUncompressedSize = (int)uncompressedSize;
                    var compressedSize         = (uint)(data.Length - 4);
                    var compressedBytes        = data.ReadBytes(compressedSize);
                    var result = LZO.Decompress(compressedBytes,
                                                0,
                                                (int)compressedSize,
                                                uncompressedBytes,
                                                0,
                                                ref actualUncompressedSize);
                    if (result != LZO.ErrorCode.Success)
                    {
                        throw new SaveCorruptionException(string.Format("LZO decompression failure ({0})", result));
                    }

                    if (actualUncompressedSize != (int)uncompressedSize)
                    {
                        throw new SaveCorruptionException("LZO decompression failure (uncompressed size mismatch)");
                    }
                }
                else
                {
                    var blockCount = data.ReadValueU32(Endian.Big);
                    var blockInfos = new List <Tuple <uint, uint> >();
                    for (uint i = 0; i < blockCount; i++)
                    {
                        var blockCompressedSize   = data.ReadValueU32(Endian.Big);
                        var blockUncompressedSize = data.ReadValueU32(Endian.Big);
                        blockInfos.Add(new Tuple <uint, uint>(blockCompressedSize, blockUncompressedSize));
                    }

                    int uncompressedOffset   = 0;
                    int uncompressedSizeLeft = (int)uncompressedSize;
                    foreach (var blockInfo in blockInfos)
                    {
                        var blockUncompressedSize  = Math.Min((int)blockInfo.Item2, uncompressedSizeLeft);
                        var actualUncompressedSize = blockUncompressedSize;
                        var compressedSize         = (int)blockInfo.Item1;
                        var compressedBytes        = data.ReadBytes(compressedSize);
                        var result = LZO.Decompress(compressedBytes,
                                                    0,
                                                    compressedSize,
                                                    uncompressedBytes,
                                                    uncompressedOffset,
                                                    ref actualUncompressedSize);
                        if (result != LZO.ErrorCode.Success)
                        {
                            throw new SaveCorruptionException(string.Format("LZO decompression failure ({0})", result));
                        }

                        if (actualUncompressedSize != blockUncompressedSize)
                        {
                            throw new SaveCorruptionException("LZO decompression failure (uncompressed size mismatch)");
                        }

                        uncompressedOffset   += blockUncompressedSize;
                        uncompressedSizeLeft -= blockUncompressedSize;
                    }

                    if (uncompressedSizeLeft != 0)
                    {
                        throw new SaveCorruptionException("LZO decompression failure (uncompressed size left != 0)");
                    }
                }

                using (var outerData = new MemoryStream(uncompressedBytes))
                {
                    var innerSize = outerData.ReadValueU32(Endian.Big);
                    var magic     = outerData.ReadString(3);
                    if (magic != "WSG")
                    {
                        throw new SaveCorruptionException("invalid magic");
                    }

                    var version = outerData.ReadValueU32(Endian.Little);
                    if (version != 2 &&
                        version.Swap() != 2)
                    {
                        throw new SaveCorruptionException("invalid or unsupported version");
                    }
                    var endian = version == 2 ? Endian.Little : Endian.Big;

                    var readCRC32Hash         = outerData.ReadValueU32(endian);
                    var innerUncompressedSize = outerData.ReadValueS32(endian);

                    var innerCompressedBytes   = outerData.ReadBytes(innerSize - 3 - 4 - 4 - 4);
                    var innerUncompressedBytes = Huffman.Decoder.Decode(innerCompressedBytes,
                                                                        innerUncompressedSize);
                    if (innerUncompressedBytes.Length != innerUncompressedSize)
                    {
                        throw new SaveCorruptionException("huffman decompression failure");
                    }

                    var computedCRC32Hash = CRC32.Hash(innerUncompressedBytes, 0, innerUncompressedBytes.Length);
                    if ((settings & DeserializeSettings.IgnoreCrc32Mismatch) == 0 &&
                        computedCRC32Hash != readCRC32Hash)
                    {
                        throw new SaveCorruptionException("invalid CRC32 hash");
                    }

                    using (var innerUncompressedData = new MemoryStream(innerUncompressedBytes))
                    {
                        var saveGame =
                            ProtoBuf.Serializer.Deserialize <WillowTwoSave.WillowTwoPlayerSaveGame>(innerUncompressedData);

                        if ((settings & DeserializeSettings.IgnoreReencodeMismatch) == 0)
                        {
                            using (var testData = new MemoryStream())
                            {
                                ProtoBuf.Serializer.Serialize(testData, saveGame);

                                testData.Position = 0;
                                var testBytes = testData.ReadBytes((uint)testData.Length);
                                if (innerUncompressedBytes.SequenceEqual(testBytes) == false)
                                {
                                    throw new SaveCorruptionException("reencode mismatch");
                                }
                            }
                        }

                        saveGame.Decompose();
                        return(new SaveFile()
                        {
                            Endian = endian,
                            SaveGame = saveGame,
                        });
                    }
                }
            }
        }