/// <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); } }
public static (bool isValid, string possibleError) ValidateHeaderData(MemoryStream memoryStream) { // Use the memory stream in a binary reader. try { using (BinaryReader reader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: true)) { // Start validating header byte[] tempIdentifier = reader.ReadBytes(Common.onlyValidIdentifier.Length); if (!Common.onlyValidIdentifier.SequenceEqual(tempIdentifier)) { return(isValid : false, possibleError : "Identifier does not match requirements!"); } uint tempEndian = reader.ReadUInt32(); if (Common.expectedEndianValue != tempEndian && Common.otherValidEndianValue != tempEndian) { return(isValid : false, possibleError : "Endianness does not match requirements!"); } bool shouldSwapEndianness = (tempEndian != Common.expectedEndianValue); uint glTypeTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); // TODO: uint glType to enum // If glType is 0 it should mean that this is compressed texture bool assumeCompressedTexture = (glTypeTemp == 0); uint glTypeSizeTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (assumeCompressedTexture && glTypeSizeTemp != 1) { return(isValid : false, possibleError : "glTypeSize should be 1 for compressed textures!"); } uint glFormatTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (assumeCompressedTexture && glFormatTemp != 0) { return(isValid : false, possibleError : "glFormat should be 0 for compressed textures!"); } uint glInternalFormatTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint glBaseInternalFormatTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint pixelWidthTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint pixelHeightTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint pixelDepthTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint numberOfArrayElementsTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint numberOfFacesTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint numberOfMipmapLevelsTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); uint sizeOfKeyValueDataTemp = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32(); if (sizeOfKeyValueDataTemp % 4 != 0) { return(isValid : false, possibleError : ErrorGen.Modulo4Error(nameof(sizeOfKeyValueDataTemp), sizeOfKeyValueDataTemp)); } // Validate metadata (bool validMedata, string possibleMetadataError) = ValidateMetadata(reader, sizeOfKeyValueDataTemp, shouldSwapEndianness); if (!validMedata) { return(isValid : false, possibleError : possibleMetadataError); } } } catch (Exception e) { return(isValid : false, e.ToString()); } return(isValid : true, possibleError : ""); }