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); }