/// <summary> /// KtxHeader constructor /// </summary> /// <param name="stream">Stream for reading</param> public KtxHeader(Stream stream) { // Skip first 12 bytes since they only contain identifier stream.Seek(12, SeekOrigin.Begin); // Read endianness as bytes byte[] endiannessBytes = new byte[4]; int bytesRead = stream.Read(buffer: endiannessBytes, offset: 0, count: endiannessBytes.Length); if (bytesRead != 4) { throw new InvalidOperationException("Cannot read enough bytes from stream!"); } if (!Common.littleEndianAsBytes.SequenceEqual(endiannessBytes) && !Common.bigEndianAsBytes.SequenceEqual(endiannessBytes)) { throw new InvalidOperationException("Endianness info in header is not valid!"); } this.isInputLittleEndian = Common.littleEndianAsBytes.SequenceEqual(endiannessBytes); // Turn endianness as bytes to uint this.endiannessValue = BitConverter.ToUInt32(endiannessBytes, 0); // See if following uint reads need endian swap bool shouldSwapEndianness = (this.endiannessValue != Common.expectedEndianValue); // Use the stream in a binary reader. using (BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true)) { // Swap endianness for every KTX variable if needed this.glTypeAsUint = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (GlDataType.IsDefined(typeof(GlDataType), this.glTypeAsUint)) { this.glDataType = (GlDataType)this.glTypeAsUint; } else { this.glDataType = GlDataType.NotKnown; } this.glTypeSizeAsUint = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.glFormatAsUint = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (GlPixelFormat.IsDefined(typeof(GlPixelFormat), this.glFormatAsUint)) { this.glFormat = (GlPixelFormat)this.glFormatAsUint; } else { this.glFormat = GlPixelFormat.NotKnown; } this.glInternalFormatAsUint = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (GlInternalFormat.IsDefined(typeof(GlInternalFormat), this.glInternalFormatAsUint)) { this.glInternalFormat = (GlInternalFormat)this.glInternalFormatAsUint; } else { this.glInternalFormat = GlInternalFormat.NotKnown; } this.glBaseInternalFormatAsUint = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (GlPixelFormat.IsDefined(typeof(GlPixelFormat), this.glBaseInternalFormatAsUint)) { this.glPixelFormat = (GlPixelFormat)this.glBaseInternalFormatAsUint; } else { this.glPixelFormat = GlPixelFormat.NotKnown; } this.pixelWidth = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.pixelHeight = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.pixelDepth = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.numberOfArrayElements = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.numberOfFaces = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.numberOfMipmapLevels = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); this.bytesOfKeyValueData = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); // Check that bytesOfKeyValueData is mod 4 if (this.bytesOfKeyValueData % 4 != 0) { throw new InvalidOperationException(ErrorGen.Modulo4Error(nameof(this.bytesOfKeyValueData), this.bytesOfKeyValueData)); } this.metadataDictionary = ParseMetadata(reader.ReadBytes((int)this.bytesOfKeyValueData), shouldSwapEndianness); } }