Esempio n. 1
0
        private static Dictionary <string, MetadataValue> ParseMetadata(byte[] inputArray, bool shouldSwapEndianness)
        {
            Dictionary <string, MetadataValue> returnDictionary = new Dictionary <string, MetadataValue>();
            int position = 0;

            while (position < inputArray.Length)
            {
                uint combinedKeyAndValueSizeInBytes = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(BitConverter.ToUInt32(inputArray, position)) : BitConverter.ToUInt32(inputArray, position);

                // Pair must be larger than 0 bytes
                if (combinedKeyAndValueSizeInBytes == 0)
                {
                    throw new InvalidOperationException("Metadata: combinedKeyAndValueSize cannot be 0!");
                }

                position += Common.sizeOfUint;

                // Error out in case size is larger than bytes left
                if (combinedKeyAndValueSizeInBytes + 4 > (uint)inputArray.Length)
                {
                    throw new InvalidOperationException("Metadata: combinedKeyAndValueSize cannot be larger than whole metadata!");
                }

                // Find NUL since key should always have it
                int indexOfFirstNul = Array.IndexOf(inputArray, Common.nulByte, position);

                if (indexOfFirstNul < 0)
                {
                    throw new InvalidOperationException("Metadata: No Nul found when looking for key");
                }

                int keyLength = indexOfFirstNul - position;

                if (keyLength > combinedKeyAndValueSizeInBytes)
                {
                    throw new InvalidOperationException("Metadata: Key length is longer than combinedKeyAndValueSizeInBytes!");
                }

                string key = System.Text.Encoding.UTF8.GetString(bytes: inputArray, index: position, count: keyLength);

                position += (keyLength + 1 /* Because we have to skip nul byte*/);

                int    valueLength  = (int)combinedKeyAndValueSizeInBytes - keyLength;
                byte[] bytesOfValue = new byte[valueLength];
                Buffer.BlockCopy(src: inputArray, srcOffset: position, dst: bytesOfValue, dstOffset: 0, count: valueLength);

                returnDictionary[key] = new MetadataValue(bytesOfValue);

                position += valueLength;

                // Skip value paddings if there are any
                while (position % 4 != 0)
                {
                    position++;
                }
            }

            return(returnDictionary);
        }
Esempio n. 2
0
        public static (bool isValid, string possibleError) ValidateTextureData(MemoryStream memoryStream, KtxHeader header, uint expectedTextureDataSize)
        {
            // Use the memory stream in a binary reader.
            try
            {
                using (BinaryReader reader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: true))
                {
                    // Specs say that if value of certain things is zero (0) then it should be used as one (1)
                    uint mipmapLevels = (header.numberOfMipmapLevels == 0) ? 1 : header.numberOfMipmapLevels;

                    uint numberOfArrayElements = (header.numberOfArrayElements == 0) ? 1 : header.numberOfArrayElements;

                    uint pixelDepth = (header.pixelDepth == 0) ? 1 : header.pixelDepth;

                    uint pixelHeight = (header.pixelHeight == 0) ? 1 : header.pixelHeight;

                    uint totalLengthOfTextureDataSection = 0;

                    // Check if length reads should be endian swapped
                    bool shouldSwapEndianness = (header.endiannessValue != Common.expectedEndianValue);

                    // Check each mipmap level separately
                    for (uint u = 0; u < mipmapLevels; u++)
                    {
                        uint imageSize = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32();
                        totalLengthOfTextureDataSection += (imageSize + (uint)Common.sizeOfUint);
                        if (imageSize > expectedTextureDataSize || totalLengthOfTextureDataSection > expectedTextureDataSize)
                        {
                            return(isValid : false, "Texture data: More data than expected!");
                        }

                        // TODO: More checks!

                        // Read but do not use data for anything
                        reader.ReadBytes((int)imageSize);

                        // Skip possible padding bytes
                        while (imageSize % 4 != 0)
                        {
                            imageSize++;
                            // Read but ignore values
                            reader.ReadByte();
                        }
                    }
                }
            }
            catch (Exception e)
            {
                return(isValid : false, e.ToString());
            }

            return(isValid : true, possibleError : "");
        }
Esempio n. 3
0
        private static (bool isValid, string possibleError) ValidateMetadata(BinaryReader reader, uint bytesOfKeyValueData, bool shouldSwapEndianness)
        {
            uint currentPosition = 0;

            while (currentPosition < bytesOfKeyValueData)
            {
                uint combinedKeyAndValueSize = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32();
                currentPosition += (uint)Common.sizeOfUint;

                if ((currentPosition + combinedKeyAndValueSize) > bytesOfKeyValueData)
                {
                    return(isValid : false, possibleError : "Metadata: combinedKeyAndValueSize would go beyond Metadata array!");
                }

                // There should be at least NUL
                byte[] keyAndValueAsBytes = reader.ReadBytes((int)combinedKeyAndValueSize);

                if (!keyAndValueAsBytes.Contains(Common.nulByte))
                {
                    return(isValid : false, possibleError : "Metadata: KeyValue pair does not contain NUL byte!");
                }

                // Check if key is valid UTF-8 byte combination
                try
                {
                    UTF8Encoding utf8ThrowException = new UTF8Encoding(false, true);
                    string       notUsed            = utf8ThrowException.GetString(keyAndValueAsBytes, 0, keyAndValueAsBytes.Length);
                }
                catch (Exception e)
                {
                    return(isValid : false, possibleError : $"Byte array to UTF-8 failed: {e}!");
                }

                currentPosition += combinedKeyAndValueSize;

                // Skip value paddings if there are any
                while (currentPosition % 4 != 0)
                {
                    currentPosition++;
                    reader.ReadByte();
                }
            }

            return(isValid : true, possibleError : "");
        }
Esempio n. 4
0
        private static (bool isValid, string possibleError) ValidateMetadata(BinaryReader reader, uint bytesOfKeyValueData, bool shouldSwapEndianness)
        {
            uint currentPosition = 0;

            while (currentPosition < bytesOfKeyValueData)
            {
                uint combinedKeyAndValueSize = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32();
                currentPosition += (uint)Common.sizeOfUint;

                if ((currentPosition + combinedKeyAndValueSize) > bytesOfKeyValueData)
                {
                    return(isValid : false, possibleError : "Metadata: combinedKeyAndValueSize would go beyond Metadata array!");
                }

                // There should be at least NUL
                byte[] keyAndValueAsBytes = reader.ReadBytes((int)combinedKeyAndValueSize);

                if (!keyAndValueAsBytes.Contains(Common.nulByte))
                {
                    return(isValid : false, possibleError : "Metadata: KeyValue pair does not contain NUL byte!");
                }

                // TODO: Check if key is valid UTF-8 byte combination

                currentPosition += combinedKeyAndValueSize;

                // Skip value paddings if there are any
                while (currentPosition % 4 != 0)
                {
                    currentPosition++;
                    reader.ReadByte();
                }
            }

            return(isValid : true, possibleError : "");
        }
Esempio n. 5
0
        /// <summary>
        /// Constructor for texture data
        /// </summary>
        /// <param name="header">Header</param>
        /// <param name="stream">Stream for reading</param>
        public KtxTextureData(KtxHeader header, Stream stream)
        {
            //this.totalTextureDataLength = (uint)stream.Length;

            // Try to figure out texture type basic
            bool containsMipmaps = header.numberOfMipmapLevels > 1;

            if (header.numberOfArrayElements == 0 || header.numberOfArrayElements == 1)
            {
                // Is NOT texture array

                if (header.numberOfFaces == 0 || header.numberOfFaces == 1)
                {
                    // Is NOT cube texture

                    if (header.pixelDepth == 0 || header.pixelDepth == 1)
                    {
                        // Is not 3D texture

                        if (header.pixelHeight == 0 || header.pixelHeight == 1)
                        {
                            // 1D texture
                            this.textureType = containsMipmaps ? TextureTypeBasic.Basic1DWithMipmaps : TextureTypeBasic.Basic1DNoMipmaps;
                        }
                        else
                        {
                            // 2D texture
                            this.textureType = containsMipmaps ? TextureTypeBasic.Basic2DWithMipmaps : TextureTypeBasic.Basic2DNoMipmaps;
                        }
                    }
                    else
                    {
                        // Is 3D texture
                        this.textureType = containsMipmaps ? TextureTypeBasic.Basic3DWithMipmaps : TextureTypeBasic.Basic3DNoMipmaps;
                    }
                }
                else
                {
                    // Is cube texture
                }
            }
            else
            {
                // Is Texture array
            }

            uint mipmapLevels = header.numberOfMipmapLevels;

            if (mipmapLevels == 0)
            {
                mipmapLevels = 1;
            }

            // Since we know how many mipmap levels there are, allocate the capacity
            this.textureDataOfMipmapLevel = new List <byte[]>((int)mipmapLevels);

            // Check if length reads should be endian swapped
            bool shouldSwapEndianness = (header.endiannessValue != Common.expectedEndianValue);

            using (BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true))
            {
                for (int i = 0; i < mipmapLevels; i++)
                {
                    uint amountOfDataInThisMipmapLevel = shouldSwapEndianness ? KtxBitFiddling.SwapEndian(reader.ReadUInt32()) : reader.ReadUInt32();
                    this.textureDataOfMipmapLevel.Add(reader.ReadBytes((int)amountOfDataInThisMipmapLevel));

                    // Skip possible padding bytes
                    while (amountOfDataInThisMipmapLevel % 4 != 0)
                    {
                        amountOfDataInThisMipmapLevel++;
                        // Read but ignore values
                        reader.ReadByte();
                    }
                }
            }
        }
Esempio n. 6
0
 private static void WriteUintAsBigEndian(BinaryWriter writer, uint value)
 {
     writer.Write(KtxBitFiddling.SwapEndian(value));
 }
Esempio n. 7
0
        /// <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);
            }
        }
Esempio n. 8
0
        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 : "");
        }