예제 #1
0
        public void ExportToFile(string path)
        {
            using (FileStream sds = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
            {
                long currentPosition;
                long blockTableOffsetPosition;
                long xmlOffsetPosition;
                long headerChecksumPosition;

                sds.WriteString("SDS", sizeof(uint));
                sds.WriteUInt32(Header.Version);
                sds.WriteString(Header.Platform.ToString(), sizeof(uint));

                using (MemoryStream ms = new MemoryStream())
                {
                    ms.WriteString("SDS", sizeof(uint));
                    ms.WriteUInt32(Header.Version);
                    ms.WriteString(Header.Platform.ToString(), sizeof(uint));
                    sds.WriteUInt32(FNV.Hash32(ms.ReadAllBytes()));
                }

                Header.ResourceTypeTableOffset = SdsHeader.HeaderSize;
                sds.WriteUInt32(Header.ResourceTypeTableOffset);
                blockTableOffsetPosition = sds.Position;
                sds.Seek(sizeof(uint), SeekOrigin.Current);
                xmlOffsetPosition = sds.Position;
                sds.Seek(sizeof(uint), SeekOrigin.Current);

                sds.WriteUInt32(SlotRamRequired);
                sds.WriteUInt32(SlotVRamRequired);
                sds.WriteUInt32(OtherRamRequired);
                sds.WriteUInt32(OtherVRamRequired);

                sds.WriteUInt32(SdsHeader.Unknown32_2C);
                sds.WriteUInt64((ulong)Header.GameVersion);

                sds.Seek(sizeof(ulong), SeekOrigin.Current);
                sds.WriteUInt32((uint)_resources.Count);

                headerChecksumPosition = sds.Position;
                sds.Seek(sizeof(uint), SeekOrigin.Current);

                sds.WriteUInt32((uint)_resourceTypes.Count);
                foreach (ResourceType resourceType in _resourceTypes)
                {
                    sds.WriteUInt32(resourceType.Id);
                    sds.WriteUInt32((uint)resourceType.ToString().Length);
                    sds.WriteString(resourceType.ToString());
                    sds.WriteUInt32(resourceType.Unknown32);
                }

                currentPosition         = sds.Position;
                Header.BlockTableOffset = (uint)currentPosition;
                sds.Seek(blockTableOffsetPosition, SeekOrigin.Begin);
                sds.WriteUInt32((uint)currentPosition);
                sds.Seek(currentPosition, SeekOrigin.Begin);

                sds.WriteUInt32(UEzl);

                if (Header?.Version == 19)
                {
                    sds.WriteUInt32(MaxBlockSizeV19);
                }

                else if (Header?.Version == 20)
                {
                    sds.WriteUInt32(MaxBlockSizeV20);
                }

                sds.WriteUInt8(4);

                bool first     = true;
                int  blockSize = Header?.Version == 19U ? MaxBlockSizeV19 : MaxBlockSizeV20;
                foreach (MemoryStream block in MergeDataIntoBlocks(blockSize))
                {
                    block.SeekToStart();

                    if ((first || block.Length >= 10240) && Header?.Version != 20U)
                    {
                        byte[] blockData = block.ReadAllBytes();

                        #region Version 19

                        if (Header?.Version == 19)
                        {
                            MemoryStream  compressedBlock = new MemoryStream();
                            ZOutputStream compressStream  = new ZOutputStream(compressedBlock,
                                                                              zlibConst.Z_BEST_COMPRESSION);
                            compressStream.Write(blockData, 0, blockData.Length);
                            compressStream.finish();

                            sds.WriteUInt32((uint)compressStream.TotalOut + 32U);
                            sds.WriteUInt8((byte)EDataBlockType.Compressed);
                            sds.WriteUInt32((uint)block.Length);

                            sds.WriteUInt32(32);
                            sds.WriteUInt32(81920);
                            sds.WriteUInt32(135200769);

                            sds.WriteUInt32((uint)compressStream.TotalOut);
                            sds.WriteUInt64(0);
                            sds.WriteUInt32(0);

                            compressedBlock.SeekToStart();
                            sds.Write(compressedBlock.ReadAllBytes());
                        }

                        #endregion

                        #region Version 20

                        //else if (Header?.Version == 20)
                        //{
                        //    byte[] compressed = Oodle.Compress(blockData, blockData.Length);
                        //    sds.WriteUInt32((uint)compressed.Length + 128U);
                        //    sds.WriteUInt8((byte)EDataBlockType.Compressed);
                        //    sds.WriteUInt32((uint)block.Length);

                        //    sds.WriteUInt32(128);
                        //    sds.WriteUInt32(65537);
                        //    sds.WriteUInt32((uint)block.Length);

                        //    sds.WriteUInt32((uint)compressed.Length);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt64(0);
                        //    sds.WriteUInt32(0);

                        //    sds.Write(compressed);
                        //}

                        #endregion
                    }

                    else
                    {
                        sds.WriteUInt32((uint)block.Length);
                        sds.WriteUInt8((byte)EDataBlockType.Uncompressed);
                        sds.Write(block.ReadAllBytes());
                    }

                    first = false;
                }

                sds.WriteUInt32(0);
                sds.WriteUInt8(0);

                if (Header?.Version == 20U)
                {
                    Header.XmlOffset = 0;
                    sds.Seek(xmlOffsetPosition, SeekOrigin.Begin);
                    sds.WriteUInt32(Header.XmlOffset);
                }

                else
                {
                    currentPosition  = sds.Position;
                    Header.XmlOffset = (uint)currentPosition;
                    sds.Seek(xmlOffsetPosition, SeekOrigin.Begin);
                    sds.WriteUInt32((uint)currentPosition);
                }

                sds.Seek(headerChecksumPosition, SeekOrigin.Begin);

                sds.WriteUInt32(FNV.Hash32(Header.ResourceTypeTableOffset, Header.BlockTableOffset, Header.XmlOffset, SlotRamRequired, SlotVRamRequired,
                                           OtherRamRequired, OtherVRamRequired, SdsHeader.Unknown32_2C, (ulong)Header.GameVersion, 0UL, (uint)_resources.Count));

                if (Header?.Version == 19U)
                {
                    sds.Seek(currentPosition, SeekOrigin.Begin);
                    sds.WriteString(XmlString);
                }
            }
        }
예제 #2
0
        public static SdsHeader FromFile(string sdsFilePath)
        {
            SdsHeader header = new SdsHeader();

            header.Name = Path.GetFileName(sdsFilePath);

            using (FileStream fileStream = new FileStream(sdsFilePath, FileMode.Open, FileAccess.Read))
            {
                if (fileStream.Length < HeaderSize)
                {
                    throw new InvalidDataException("Invalid file!");
                }

                if (fileStream.ReadString(sizeof(uint)) != "SDS")
                {
                    throw new InvalidDataException("This file does not contain SDS header!");
                }

                header.Version = fileStream.ReadUInt32();
                if (header.Version > MaxSupportedVersion)
                {
                    throw new NotSupportedException($"Version {header.Version} not supported");
                }

                string platformString = fileStream.ReadString(sizeof(uint));
                if (Enum.TryParse(platformString, out EPlatform platform))
                {
                    header.Platform = platform;
                }

                else
                {
                    throw new InvalidDataException(platformString);
                }

                if (header.Platform != EPlatform.PC) // In future will be added multiplatform support
                {
                    throw new NotSupportedException($"Platform {platform} not supported");
                }

                uint hash = fileStream.ReadUInt32();
                using (MemoryStream ms = new MemoryStream())
                {
                    ms.WriteString("SDS", sizeof(uint));
                    ms.WriteUInt32(header.Version);
                    ms.WriteString(header.Platform.ToString(), sizeof(uint));
                    var computedHash = FNV.Hash32(ms.ReadAllBytes());
                    if (computedHash != hash)
                    {
                        throw new InvalidDataException("Checksum difference.");
                    }
                }

                header.ResourceTypeTableOffset = fileStream.ReadUInt32();
                header.BlockTableOffset        = fileStream.ReadUInt32();
                header.XmlOffset = fileStream.ReadUInt32();

                if (header.Version == 19U &&
                    header.XmlOffset == Encrypted)
                {
                    throw new NotSupportedException("This SDS file is encrypted.");
                }

                uint slotRamRequired   = fileStream.ReadUInt32();
                uint slotVRamRequired  = fileStream.ReadUInt32();
                uint otherRamRequired  = fileStream.ReadUInt32();
                uint otherVRamRequired = fileStream.ReadUInt32();

                if (fileStream.ReadUInt32() != SdsHeader.Unknown32_2C)
                {
                    throw new Exception("Bytes do not match.");
                }

                header.GameVersion = (EGameVersion)fileStream.ReadUInt64();

                if (header.GameVersion != EGameVersion.Classic && header.GameVersion != EGameVersion.DefinitiveEdition)
                {
                    throw new NotSupportedException(header.GameVersion.ToString());
                }

                // Skipping of null bytes
                fileStream.Seek(sizeof(ulong), SeekOrigin.Current);

                uint numberOfFiles = fileStream.ReadUInt32();

                uint checksum = fileStream.ReadUInt32();

                uint calculatedChecksum = FNV.Hash32(header.ResourceTypeTableOffset, header.BlockTableOffset, header.XmlOffset, slotRamRequired,
                                                     slotVRamRequired, otherRamRequired, otherVRamRequired, Unknown32_2C, (ulong)header.GameVersion, 0UL, numberOfFiles);

                if (calculatedChecksum != checksum)
                {
                    throw new Exception("Checksum difference!");
                }
            }

            return(header);
        }