Пример #1
0
        public static SdsFile FromFile(string sdsPath)
        {
            SdsFile file = new SdsFile();

            file.Path   = System.IO.Path.GetFullPath(sdsPath);
            file.Header = SdsHeader.FromFile(sdsPath);

            using (FileStream fileStream = new FileStream(sdsPath, FileMode.Open, FileAccess.Read))
            {
                fileStream.Seek(file.Header.ResourceTypeTableOffset, SeekOrigin.Begin);
                uint numberOfResources = fileStream.ReadUInt32();
                for (int i = 0; i < numberOfResources; i++)
                {
                    ResourceType resourceType = new ResourceType();
                    resourceType.Id = fileStream.ReadUInt32();
                    uint   resourceLenght = fileStream.ReadUInt32();
                    string typeStr        = fileStream.ReadString((int)resourceLenght).Replace(" ", "");
                    resourceType.Name = (EResourceType)Enum.Parse(typeof(EResourceType), typeStr);
                    uint unknown32 = fileStream.ReadUInt32();
                    if (unknown32 != resourceType.Unknown32)
                    {
                        throw new InvalidDataException(unknown32.ToString());
                    }
                    file.AddResourceType(resourceType);
                }

                fileStream.Seek(file.Header.BlockTableOffset, SeekOrigin.Begin);

                #region Version 19

                if (file.Header.Version == 19U)
                {
                    if (fileStream.ReadUInt32() != UEzl)
                    {
                        throw new Exception("Invalid SDS file!");
                    }

                    fileStream.Seek(5, SeekOrigin.Current);

                    using (MemoryStream decompressedData = new MemoryStream())
                    {
                        while (true)
                        {
                            uint           blockSize = fileStream.ReadUInt32();
                            EDataBlockType blockType = (EDataBlockType)fileStream.ReadUInt8();

                            if (blockSize == 0U)
                            {
                                break;
                            }

                            if (blockType == EDataBlockType.Compressed)
                            {
                                fileStream.Seek(16, SeekOrigin.Current);
                                uint compressedBlockSize = fileStream.ReadUInt32();

                                if (blockSize - 32U != compressedBlockSize)
                                {
                                    throw new Exception("Invalid block!");
                                }

                                fileStream.Seek(12, SeekOrigin.Current);
                                byte[] compressedBlock = new byte[compressedBlockSize];
                                fileStream.Read(compressedBlock, 0, compressedBlock.Length);

                                ZOutputStream decompressStream = new ZOutputStream(decompressedData);
                                decompressStream.Write(compressedBlock, 0, compressedBlock.Length);
                                decompressStream.finish();
                            }

                            else if (blockType == EDataBlockType.Uncompressed)
                            {
                                byte[] decompressedBlock = new byte[blockSize];
                                fileStream.Read(decompressedBlock, 0, decompressedBlock.Length);
                                decompressedData.Write(decompressedBlock, 0, decompressedBlock.Length);
                            }

                            else
                            {
                                throw new Exception("Invalid block type!");
                            }
                        }

                        decompressedData.SeekToStart();

                        fileStream.Seek(file.Header.XmlOffset, SeekOrigin.Begin);
                        byte[] xmlBytes = fileStream.ReadBytes((int)fileStream.Length - (int)fileStream.Position);
                        using (MemoryStream memoryStream = new MemoryStream(xmlBytes))
                            using (XmlReader xmlReader = XmlReader.Create(memoryStream))
                            {
                                ResourceInfo resourceInfo = new ResourceInfo();

                                List <Type> resourceTypes = new List <Type>();
                                foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
                                {
                                    if (type.BaseType == typeof(Resource))
                                    {
                                        resourceTypes.Add(type);
                                    }
                                }

                                while (xmlReader.Read())
                                {
                                    if (xmlReader.NodeType == XmlNodeType.Element)
                                    {
                                        if (xmlReader.Name == "TypeName")
                                        {
                                            resourceInfo = new ResourceInfo();
                                            var resourceType = (EResourceType)Enum.Parse(typeof(EResourceType), xmlReader.ReadElementContentAsString());
                                            resourceInfo.Type = file._resourceTypes.First(x => x.Name == resourceType);
                                        }

                                        else if (xmlReader.Name == "SourceDataDescription")
                                        {
                                            resourceInfo.SourceDataDescription = xmlReader.ReadElementContentAsString();

                                            uint resId = decompressedData.ReadUInt32();
                                            if (resId != resourceInfo.Type.Id)
                                            {
                                                throw new InvalidDataException();
                                            }

                                            uint   size              = decompressedData.ReadUInt32();
                                            ushort version           = decompressedData.ReadUInt16();
                                            uint   slotRamRequired   = decompressedData.ReadUInt32();
                                            uint   slotVRamRequired  = decompressedData.ReadUInt32();
                                            uint   otherRamRequired  = decompressedData.ReadUInt32();
                                            uint   otherVRamRequired = decompressedData.ReadUInt32();
                                            uint   checksum          = decompressedData.ReadUInt32();
                                            byte[] rawData           = decompressedData.ReadBytes((int)size - Resource.StandardHeaderSizeV19);

                                            var targetType        = resourceTypes.First(x => x.Name == resourceInfo.Type.ToString());
                                            var deserializeMethod = targetType.GetMethod(nameof(Resource.Deserialize));
                                            var resourecInstance  = (Resource)deserializeMethod.Invoke(null, new object[] {
                                                resourceInfo, version, slotRamRequired, slotVRamRequired, otherRamRequired, otherVRamRequired, null, rawData, file._mapper
                                            });
                                            file._resources.Add(resourecInstance);
                                        }
                                    }
                                }
                            }
                    }
                }

                #endregion

                #region Version 20

                else if (file.Header.Version == 20U)
                {
                    if (fileStream.ReadUInt32() != UEzl)
                    {
                        throw new Exception("Invalid SDS file!");
                    }

                    fileStream.Seek(5, SeekOrigin.Current);

                    using (MemoryStream decompressedData = new MemoryStream())
                    {
                        while (true)
                        {
                            uint           blockSize = fileStream.ReadUInt32();
                            EDataBlockType blockType = (EDataBlockType)fileStream.ReadUInt8();

                            if (blockSize == 0U)
                            {
                                break;
                            }

                            if (blockType == EDataBlockType.Compressed)
                            {
                                uint uncompressedSize = fileStream.ReadUInt32();
                                fileStream.Seek(12, SeekOrigin.Current);
                                uint compressedBlockSize = fileStream.ReadUInt32();

                                if (blockSize - 128U != compressedBlockSize)
                                {
                                    throw new Exception("Invalid block!");
                                }

                                fileStream.Seek(108, SeekOrigin.Current);
                                byte[] compressedBlock = new byte[compressedBlockSize];
                                fileStream.Read(compressedBlock, 0, compressedBlock.Length);
                                byte[] decompressed = Oodle.Decompress(compressedBlock, compressedBlock.Length, (int)uncompressedSize);
                                decompressedData.Write(decompressed);
                            }

                            else if (blockType == EDataBlockType.Uncompressed)
                            {
                                byte[] decompressedBlock = new byte[blockSize];
                                fileStream.Read(decompressedBlock, 0, decompressedBlock.Length);
                                decompressedData.Write(decompressedBlock, 0, decompressedBlock.Length);
                            }

                            else
                            {
                                throw new Exception("Invalid block type!");
                            }
                        }

                        List <Type> resourceTypes = new List <Type>();
                        foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
                        {
                            if (type.BaseType == typeof(Resource))
                            {
                                resourceTypes.Add(type);
                            }
                        }

                        decompressedData.SeekToStart();
                        while (decompressedData.Position != decompressedData.Length)
                        {
                            uint resId = decompressedData.ReadUInt32();

                            ResourceInfo resourceInfo = new ResourceInfo();
                            resourceInfo.Type = file._resourceTypes.First(x => x.Id == resId);

                            uint   size              = decompressedData.ReadUInt32();
                            ushort version           = decompressedData.ReadUInt16();
                            ulong  nameHash          = decompressedData.ReadUInt64();
                            uint   slotRamRequired   = decompressedData.ReadUInt32();
                            uint   slotVRamRequired  = decompressedData.ReadUInt32();
                            uint   otherRamRequired  = decompressedData.ReadUInt32();
                            uint   otherVRamRequired = decompressedData.ReadUInt32();
                            uint   checksum          = decompressedData.ReadUInt32();
                            byte[] rawData           = decompressedData.ReadBytes((int)size - Resource.StandardHeaderSizeV20);

                            var targetType        = resourceTypes.First(x => x.Name == resourceInfo.Type.ToString());
                            var deserializeMethod = targetType.GetMethod(nameof(Resource.Deserialize));
                            var resourceInstance  = (Resource)deserializeMethod.Invoke(null, new object[] {
                                resourceInfo, version, slotRamRequired, slotVRamRequired, otherRamRequired, otherVRamRequired, nameHash, rawData, file._mapper
                            });
                            file._resources.Add(resourceInstance);
                        }
                    }
                }

                #endregion
            }

            return(file);
        }
Пример #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);
        }