Refactor reading to properly read in item props, ignore thumbnails, add auxl iref for alpha
diff --git a/src/read.c b/src/read.c
index 0cf1cad..c491a04 100644
--- a/src/read.c
+++ b/src/read.c
@@ -8,7 +8,15 @@
 
 #include <string.h>
 
+#define MAX_COMPATIBLE_BRANDS 32
 #define MAX_ITEMS 8
+#define MAX_PROPERTIES 24
+
+typedef struct avifImageSpatialExtents
+{
+    uint32_t width;
+    uint32_t height;
+} avifImageSpatialExtents;
 
 typedef struct avifItem
 {
@@ -16,14 +24,34 @@
     uint8_t type[4];
     uint32_t offset;
     uint32_t size;
+    avifBool ispe_seen;
+    avifImageSpatialExtents ispe;
+    int thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
+    int alphaForID;     // if non-zero, this item is an alpha plane for Item #{alphaForID}
 } avifItem;
 
+typedef struct avifProperty
+{
+    uint8_t type[4];
+    avifImageSpatialExtents ispe;
+} avifProperty;
+
+typedef struct avifFileType
+{
+    uint8_t majorBrand[4];
+    uint32_t minorVersion;
+    uint8_t compatibleBrands[4 * MAX_COMPATIBLE_BRANDS];
+    int compatibleBrandsCount;
+} avifFileType;
+
 typedef struct avifData
 {
-    avifBool ispe_seen;
-    uint32_t ispe_width;
-    uint32_t ispe_height;
+    // TODO: Everything in here using a MAX_* constant is a bit lazy; it should all be dynamic
+
+    avifFileType ftyp;
     avifItem items[MAX_ITEMS];
+    avifProperty properties[MAX_PROPERTIES];
+    int propertyCount;
 } avifData;
 
 int findItemID(avifData * data, int itemID)
@@ -48,132 +76,375 @@
     return -1;
 }
 
-static avifBool avifParse(avifData * data, uint8_t * raw, size_t rawLen)
+// ---------------------------------------------------------------------------
+// BMFF Parsing
+
+#define BEGIN_STREAM(VARNAME, PTR, SIZE)             \
+    avifStream VARNAME;                              \
+    avifRawData VARNAME ## _rawData = { PTR, SIZE }; \
+    avifStreamStart(&VARNAME, &VARNAME ## _rawData)
+
+static avifBool avifParseItemLocationBox(avifData * data, uint8_t * raw, size_t rawLen)
 {
-    avifStream s;
-    avifRawData rawData = { raw, rawLen };
-    avifStreamStart(&s, &rawData);
+    BEGIN_STREAM(s, raw, rawLen);
 
-    while (avifStreamHasBytesLeft(&s, 1)) {
-        size_t contentSize;
-        uint8_t type[4];
-        CHECK(avifStreamReadBoxHeader(&s, type, &contentSize));
+    avifBoxHeader header;
+    CHECK(avifStreamReadAndEnforceVersion(&s, 0));
 
-        if (!memcmp(type, "meta", 4)) {
-            // Basic container FullBoxes, skip version and parse contents
+    uint8_t offsetSizeAndLengthSize;
+    CHECK(avifStreamRead(&s, &offsetSizeAndLengthSize, 1));
+    uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
+    uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
 
-            CHECK(avifStreamReadAndEnforceVersion(&s, 0));
-            contentSize -= 4;
+    uint8_t baseOffsetSizeAndReserved;
+    CHECK(avifStreamRead(&s, &baseOffsetSizeAndReserved, 1));
+    uint8_t baseOffsetSize = (baseOffsetSizeAndReserved >> 4) & 0xf; // unsigned int(4) base_offset_size;
 
-            CHECK(avifParse(data, raw + s.offset, contentSize));
-
-        } else if (!memcmp(type, "iprp", 4) || !memcmp(type, "ipco", 4)) {
-            // Basic container Boxes, just parse contents
-
-            CHECK(avifParse(data, raw + s.offset, contentSize));
-
-        } else if (!memcmp(type, "ispe", 4)) {
-            // ImageSpatialExtentsProperty
-
-            CHECK(avifStreamReadAndEnforceVersion(&s, 0));
-            CHECK(avifStreamReadU32(&s, &data->ispe_width));
-            CHECK(avifStreamReadU32(&s, &data->ispe_height));
-            data->ispe_seen = AVIF_TRUE;
-
-        } else if (!memcmp(type, "iloc", 4)) {
-            // ItemLocationBox
-
-            CHECK(avifStreamReadAndEnforceVersion(&s, 0)); // TODO: support more
-
-            uint8_t offsetSizeAndLengthSize;
-            CHECK(avifStreamRead(&s, &offsetSizeAndLengthSize, 1));
-            uint8_t offsetSize = (offsetSizeAndLengthSize >> 4) & 0xf; // unsigned int(4) offset_size;
-            uint8_t lengthSize = (offsetSizeAndLengthSize >> 0) & 0xf; // unsigned int(4) length_size;
-
-            uint8_t baseOffsetSizeAndReserved;
-            CHECK(avifStreamRead(&s, &baseOffsetSizeAndReserved, 1));
-            uint8_t baseOffsetSize = (baseOffsetSizeAndReserved >> 4) & 0xf; // unsigned int(4) base_offset_size;
-
-            uint16_t itemCount;
-            CHECK(avifStreamReadU16(&s, &itemCount)); // unsigned int(16) item_count;
-            for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
-                uint16_t itemID;                                           // unsigned int(16) item_ID;
-                CHECK(avifStreamReadU16(&s, &itemID));                     //
-                uint16_t dataReferenceIndex;                               // unsigned int(16) data_reference_index;
-                CHECK(avifStreamReadU16(&s, &dataReferenceIndex));         //
-                uint64_t baseOffset;                                       // unsigned int(base_offset_size*8) base_offset;
-                CHECK(avifStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
-                uint16_t extentCount;                                      // unsigned int(16) extent_count;
-                CHECK(avifStreamReadU16(&s, &extentCount));                //
-                if (extentCount == 1) {
-                    uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
-                    CHECK(avifStreamReadUX8(&s, &extentOffset, offsetSize));
-                    uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
-                    CHECK(avifStreamReadUX8(&s, &extentLength, lengthSize));
-
-                    int itemIndex = findItemID(data, itemID);
-                    if (itemIndex == -1) {
-                        return AVIF_FALSE;
-                    }
-                    data->items[itemIndex].id = itemID;
-                    data->items[itemIndex].offset = (uint32_t)(baseOffset + extentOffset);
-                    data->items[itemIndex].size = (uint32_t)extentLength;
-                } else {
-                    // TODO: support more than one extent
-                    return AVIF_FALSE;
-                }
-            }
-
-        } else if (!memcmp(type, "iinf", 4)) {
-            // ItemInfoBox
-
-            uint8_t version;
-            CHECK(avifStreamReadVersionAndFlags(&s, &version));
-            contentSize -= 4;
-            if (version == 0) {
-                uint16_t entryCount;
-                CHECK(avifStreamReadU16(&s, &entryCount)); // unsigned int(16) entry_count;
-                contentSize -= sizeof(uint16_t);
-            } else if (version == 1) {
-                uint32_t entryCount;
-                CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(16) entry_count;
-                contentSize -= sizeof(uint32_t);
-            } else {
-                return AVIF_FALSE;
-            }
-
-            CHECK(avifParse(data, raw + s.offset, contentSize));
-
-        } else if (!memcmp(type, "infe", 4)) {
-            // ItemInfoEntry
-
-            size_t startOffset = s.offset;
-            CHECK(avifStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
-
-            uint16_t itemID;                                    // unsigned int(16) item_ID;
-            CHECK(avifStreamReadU16(&s, &itemID));              //
-            uint16_t itemProtectionIndex;                       // unsigned int(16) item_protection_index;
-            CHECK(avifStreamReadU16(&s, &itemProtectionIndex)); //
-            uint8_t itemType[4];                                // unsigned int(32) item_type;
-            CHECK(avifStreamRead(&s, itemType, 4));             //
-
-            // Skip the remainder of the box
-            CHECK(avifStreamSkip(&s, contentSize - (s.offset - startOffset)));
+    uint16_t itemCount;
+    CHECK(avifStreamReadU16(&s, &itemCount)); // unsigned int(16) item_count;
+    for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
+        uint16_t itemID;                                           // unsigned int(16) item_ID;
+        CHECK(avifStreamReadU16(&s, &itemID));                     //
+        uint16_t dataReferenceIndex;                               // unsigned int(16) data_reference_index;
+        CHECK(avifStreamReadU16(&s, &dataReferenceIndex));         //
+        uint64_t baseOffset;                                       // unsigned int(base_offset_size*8) base_offset;
+        CHECK(avifStreamReadUX8(&s, &baseOffset, baseOffsetSize)); //
+        uint16_t extentCount;                                      // unsigned int(16) extent_count;
+        CHECK(avifStreamReadU16(&s, &extentCount));                //
+        if (extentCount == 1) {
+            uint64_t extentOffset; // unsigned int(offset_size*8) extent_offset;
+            CHECK(avifStreamReadUX8(&s, &extentOffset, offsetSize));
+            uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
+            CHECK(avifStreamReadUX8(&s, &extentLength, lengthSize));
 
             int itemIndex = findItemID(data, itemID);
             if (itemIndex == -1) {
                 return AVIF_FALSE;
             }
-            memcpy(data->items[itemIndex].type, itemType, sizeof(itemType));
-
+            data->items[itemIndex].id = itemID;
+            data->items[itemIndex].offset = (uint32_t)(baseOffset + extentOffset);
+            data->items[itemIndex].size = (uint32_t)extentLength;
         } else {
-            // Unsupported box, move on
-            CHECK(avifStreamSkip(&s, contentSize));
+            // TODO: support more than one extent
+            return AVIF_FALSE;
         }
     }
     return AVIF_TRUE;
 }
 
+static avifBool avifParseImageSpatialExtentsProperty(avifData * data, uint8_t * raw, size_t rawLen, int propertyIndex)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+    CHECK(avifStreamReadAndEnforceVersion(&s, 0, NULL));
+
+    uint32_t width, height;
+    CHECK(avifStreamReadU32(&s, &data->properties[propertyIndex].ispe.width));
+    CHECK(avifStreamReadU32(&s, &data->properties[propertyIndex].ispe.height));
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseItemPropertyContainerBox(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    data->propertyCount = 0;
+
+    while (avifStreamHasBytesLeft(&s, 1)) {
+        avifBoxHeader header;
+        CHECK(avifStreamReadBoxHeader(&s, &header));
+
+        if (data->propertyCount >= MAX_PROPERTIES) {
+            return AVIF_FALSE;
+        }
+        int propertyIndex = data->propertyCount;
+        ++data->propertyCount;
+
+        memcpy(data->properties[propertyIndex].type, header.type, 4);
+        if (!memcmp(header.type, "ispe", 4)) {
+            CHECK(avifParseImageSpatialExtentsProperty(data, avifStreamCurrent(&s), header.size, propertyIndex));
+        }
+
+        CHECK(avifStreamSkip(&s, header.size));
+    }
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseItemPropertyAssociation(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    uint8_t version;
+    uint8_t flags[3];
+    CHECK(avifStreamReadVersionAndFlags(&s, &version, flags));
+    avifBool propertyIndexIsU16 = (flags[2] & 0x1) ? AVIF_TRUE : AVIF_FALSE; // is flags[2] correct?
+
+    uint32_t entryCount;
+    CHECK(avifStreamReadU32(&s, &entryCount));
+    for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
+        unsigned int itemID;
+        if (version < 1) {
+            uint16_t tmp;
+            CHECK(avifStreamReadU16(&s, &tmp));
+            itemID = tmp;
+        } else {
+            CHECK(avifStreamReadU32(&s, &itemID));
+        }
+        uint8_t associationCount;
+        CHECK(avifStreamRead(&s, &associationCount, 1));
+        for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) {
+            avifBool essential = AVIF_FALSE;
+            uint16_t propertyIndex = 0;
+            if (propertyIndexIsU16) {
+                CHECK(avifStreamReadU16(&s, &propertyIndex));
+                essential = (propertyIndex & 0x8000) ? AVIF_TRUE : AVIF_FALSE;
+                propertyIndex &= 0x7fff;
+            } else {
+                uint8_t tmp;
+                CHECK(avifStreamRead(&s, &tmp, 1));
+                essential = (tmp & 0x80) ? AVIF_TRUE : AVIF_FALSE;
+                propertyIndex = tmp & 0x7f;
+            }
+
+            if (propertyIndex == 0) {
+                // Not associated with any item
+                continue;
+            }
+            --propertyIndex; // 1-indexed
+
+            if (propertyIndex >= MAX_PROPERTIES) {
+                return AVIF_FALSE;
+            }
+
+            int itemIndex = findItemID(data, itemID);
+            if (itemIndex == -1) {
+                return AVIF_FALSE;
+            }
+
+            // Associate property with item
+            avifProperty * prop = &data->properties[propertyIndex];
+            if (!memcmp(prop->type, "ispe", 4)) {
+                data->items[itemIndex].ispe_seen = AVIF_TRUE;
+                memcpy(&data->items[itemIndex].ispe, &prop->ispe, sizeof(avifImageSpatialExtents));
+            }
+        }
+    }
+
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseItemPropertiesBox(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    avifBoxHeader ipcoHeader;
+    CHECK(avifStreamReadBoxHeader(&s, &ipcoHeader));
+    if (memcmp(ipcoHeader.type, "ipco", 4) != 0) {
+        return AVIF_FALSE;
+    }
+
+    // Read all item properties inside of ItemPropertyContainerBox
+    CHECK(avifParseItemPropertyContainerBox(data, avifStreamCurrent(&s), ipcoHeader.size));
+    CHECK(avifStreamSkip(&s, ipcoHeader.size));
+
+    // Now read all ItemPropertyAssociation until the end of the box, and make associations
+    while (avifStreamHasBytesLeft(&s, 1)) {
+        avifBoxHeader ipmaHeader;
+        CHECK(avifStreamReadBoxHeader(&s, &ipmaHeader));
+
+        if (!memcmp(ipmaHeader.type, "ipma", 4)) {
+            CHECK(avifParseItemPropertyAssociation(data, avifStreamCurrent(&s), ipmaHeader.size));
+        } else {
+            // These must all be type ipma
+            return AVIF_FALSE;
+        }
+
+        CHECK(avifStreamSkip(&s, ipmaHeader.size));
+    }
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseItemInfoEntry(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    CHECK(avifStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
+
+    uint16_t itemID;                                    // unsigned int(16) item_ID;
+    CHECK(avifStreamReadU16(&s, &itemID));              //
+    uint16_t itemProtectionIndex;                       // unsigned int(16) item_protection_index;
+    CHECK(avifStreamReadU16(&s, &itemProtectionIndex)); //
+    uint8_t itemType[4];                                // unsigned int(32) item_type;
+    CHECK(avifStreamRead(&s, itemType, 4));             //
+
+    int itemIndex = findItemID(data, itemID);
+    if (itemIndex == -1) {
+        return AVIF_FALSE;
+    }
+    memcpy(data->items[itemIndex].type, itemType, sizeof(itemType));
+
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseItemInfoBox(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    uint8_t version;
+    CHECK(avifStreamReadVersionAndFlags(&s, &version, NULL));
+    uint32_t entryCount;
+    if (version == 0) {
+        uint16_t tmp;
+        CHECK(avifStreamReadU16(&s, &tmp)); // unsigned int(16) entry_count;
+        entryCount = tmp;
+    } else if (version == 1) {
+        CHECK(avifStreamReadU32(&s, &entryCount)); // unsigned int(16) entry_count;
+    } else {
+        return AVIF_FALSE;
+    }
+
+    for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
+        avifBoxHeader infeHeader;
+        CHECK(avifStreamReadBoxHeader(&s, &infeHeader));
+
+        if (!memcmp(infeHeader.type, "infe", 4)) {
+            CHECK(avifParseItemInfoEntry(data, avifStreamCurrent(&s), infeHeader.size));
+        } else {
+            // These must all be type ipma
+            return AVIF_FALSE;
+        }
+
+        CHECK(avifStreamSkip(&s, infeHeader.size));
+    }
+
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseItemReferenceBox(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    uint8_t version;
+    CHECK(avifStreamReadVersionAndFlags(&s, &version, NULL));
+
+    while (avifStreamHasBytesLeft(&s, 1)) {
+        avifBoxHeader irefHeader;
+        CHECK(avifStreamReadBoxHeader(&s, &irefHeader));
+
+        uint32_t fromID = 0;
+        if (version == 0) {
+            uint16_t tmp;
+            CHECK(avifStreamReadU16(&s, &tmp)); // unsigned int(16) from_item_ID;
+            fromID = tmp;
+        } else if (version == 1) {
+            CHECK(avifStreamReadU32(&s, &fromID)); //  unsigned int(32) from_item_ID;
+        } else {
+            // unsupported iref version, skip it
+            break;
+        }
+
+        uint16_t referenceCount = 0;
+        CHECK(avifStreamReadU16(&s, &referenceCount)); // unsigned int(16) reference_count;
+
+        for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) {
+            uint32_t toID = 0;
+            if (version == 0) {
+                uint16_t tmp;
+                CHECK(avifStreamReadU16(&s, &tmp)); // unsigned int(16) to_item_ID;
+                toID = tmp;
+            } else if (version == 1) {
+                CHECK(avifStreamReadU32(&s, &toID)); //  unsigned int(32) to_item_ID;
+            } else {
+                // unsupported iref version, skip it
+                break;
+            }
+
+            // Read this reference as "{fromID} is a {irefType} for {toID}"
+            if (fromID && toID) {
+                int itemIndex = findItemID(data, fromID);
+                if (itemIndex == -1) {
+                    return AVIF_FALSE;
+                }
+                if (!memcmp(irefHeader.type, "thmb", 4)) {
+                    data->items[itemIndex].thumbnailForID = toID;
+                }
+                if (!memcmp(irefHeader.type, "auxl", 4)) {
+                    data->items[itemIndex].alphaForID = toID;
+                }
+            }
+        }
+    }
+
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseMetaBox(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    CHECK(avifStreamReadAndEnforceVersion(&s, 0, NULL));
+
+    while (avifStreamHasBytesLeft(&s, 1)) {
+        avifBoxHeader header;
+        CHECK(avifStreamReadBoxHeader(&s, &header));
+
+        if (!memcmp(header.type, "iloc", 4)) {
+            CHECK(avifParseItemLocationBox(data, avifStreamCurrent(&s), header.size));
+        } else if (!memcmp(header.type, "iprp", 4)) {
+            CHECK(avifParseItemPropertiesBox(data, avifStreamCurrent(&s), header.size));
+        } else if (!memcmp(header.type, "iinf", 4)) {
+            CHECK(avifParseItemInfoBox(data, avifStreamCurrent(&s), header.size));
+        } else if (!memcmp(header.type, "iref", 4)) {
+            CHECK(avifParseItemReferenceBox(data, avifStreamCurrent(&s), header.size));
+        }
+
+        CHECK(avifStreamSkip(&s, header.size));
+    }
+    return AVIF_TRUE;
+}
+
+static avifBool avifParseFileTypeBox(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    CHECK(avifStreamRead(&s, data->ftyp.majorBrand, 4));
+    CHECK(avifStreamReadU32(&s, &data->ftyp.minorVersion));
+
+    size_t compatibleBrandsBytes = avifStreamRemainingBytes(&s);
+    if ((compatibleBrandsBytes % 4) != 0) {
+        return AVIF_FALSE;
+    }
+    if (compatibleBrandsBytes > (4 * MAX_COMPATIBLE_BRANDS)) {
+        // TODO: stop clamping and resize this
+        compatibleBrandsBytes = (4 * MAX_COMPATIBLE_BRANDS);
+    }
+    CHECK(avifStreamRead(&s, data->ftyp.compatibleBrands, compatibleBrandsBytes));
+    data->ftyp.compatibleBrandsCount = compatibleBrandsBytes / 4;
+
+    return AVIF_TRUE;
+}
+
+static avifBool avifParse(avifData * data, uint8_t * raw, size_t rawLen)
+{
+    BEGIN_STREAM(s, raw, rawLen);
+
+    while (avifStreamHasBytesLeft(&s, 1)) {
+        avifBoxHeader header;
+        CHECK(avifStreamReadBoxHeader(&s, &header));
+
+        if (!memcmp(header.type, "ftyp", 4)) {
+            CHECK(avifParseFileTypeBox(data, avifStreamCurrent(&s), header.size));
+        } else if (!memcmp(header.type, "meta", 4)) {
+            CHECK(avifParseMetaBox(data, avifStreamCurrent(&s), header.size));
+        }
+
+        CHECK(avifStreamSkip(&s, header.size));
+    }
+    return AVIF_TRUE;
+}
+
+// ---------------------------------------------------------------------------
+// OBU Decode
+
 static aom_image_t * decodeOBU(aom_codec_ctx_t * decoder, avifRawData * inputOBU)
 {
     aom_codec_stream_info_t si;
@@ -199,6 +470,8 @@
     return aom_codec_get_frame(decoder, &iter); // It doesn't appear that I own this / need to free this
 }
 
+// ---------------------------------------------------------------------------
+
 avifResult avifImageRead(avifImage * image, avifRawData * input)
 {
     // -----------------------------------------------------------------------
@@ -210,11 +483,28 @@
         return AVIF_RESULT_BMFF_PARSE_FAILED;
     }
 
+    avifBool avifCompatible = (memcmp(data.ftyp.majorBrand, "avif", 4) == 0) ? AVIF_TRUE : AVIF_FALSE;
+    if (!avifCompatible) {
+        for (int compatibleBrandIndex = 0; compatibleBrandIndex < data.ftyp.compatibleBrandsCount; ++compatibleBrandIndex) {
+            uint8_t * compatibleBrand = &data.ftyp.compatibleBrands[4 * compatibleBrandIndex];
+            if (!memcmp(compatibleBrand, "avif", 4)) {
+                avifCompatible = AVIF_TRUE;
+                break;
+            }
+        }
+    }
+    if (!avifCompatible) {
+        return AVIF_RESULT_INVALID_FTYP;
+    }
+
     // -----------------------------------------------------------------------
 
     avifRawData colorOBU = AVIF_RAW_DATA_EMPTY;
     avifRawData alphaOBU = AVIF_RAW_DATA_EMPTY;
+    avifItem * colorOBUItem = NULL;
+    avifItem * alphaOBUItem = NULL;
 
+    // Find the colorOBU item
     for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) {
         avifItem * item = &data.items[itemIndex];
         if (!item->id || !item->size) {
@@ -226,19 +516,49 @@
         if ((item->offset + item->size) > input->size) {
             break;
         }
-
         if (memcmp(item->type, "av01", 4)) {
             // probably exif or some other data
             continue;
         }
+        if (item->thumbnailForID != 0) {
+            // It's a thumbnail, skip it
+            continue;
+        }
 
-        if (colorOBU.size == 0) {
-            colorOBU.data = input->data + item->offset;
-            colorOBU.size = item->size;
-        } else {
-            alphaOBU.data = input->data + item->offset;
-            alphaOBU.size = item->size;
-            break;
+        colorOBUItem = item;
+        colorOBU.data = input->data + item->offset;
+        colorOBU.size = item->size;
+        break;
+    }
+
+    // Find the alphaOBU item, if any
+    if (colorOBUItem) {
+        for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) {
+            avifItem * item = &data.items[itemIndex];
+            if (!item->id || !item->size) {
+                break;
+            }
+            if (item->offset > input->size) {
+                break;
+            }
+            if ((item->offset + item->size) > input->size) {
+                break;
+            }
+            if (memcmp(item->type, "av01", 4)) {
+                // probably exif or some other data
+                continue;
+            }
+            if (item->thumbnailForID != 0) {
+                // It's a thumbnail, skip it
+                continue;
+            }
+
+            if (item->alphaForID == colorOBUItem->id) {
+                alphaOBUItem = item;
+                alphaOBU.data = input->data + item->offset;
+                alphaOBU.size = item->size;
+                break;
+            }
         }
     }
 
@@ -270,7 +590,11 @@
         }
     }
 
-    if (data.ispe_seen && ((data.ispe_width != aomColorImage->d_w) || (data.ispe_height != aomColorImage->d_h))) {
+    if (
+        (colorOBUItem && aomColorImage && colorOBUItem->ispe_seen && ((colorOBUItem->ispe.width != aomColorImage->d_w) || (colorOBUItem->ispe.height != aomColorImage->d_h))) ||
+        (alphaOBUItem && aomAlphaImage && alphaOBUItem->ispe_seen && ((alphaOBUItem->ispe.width != aomAlphaImage->d_w) || (alphaOBUItem->ispe.height != aomAlphaImage->d_h)))
+        )
+    {
         aom_codec_destroy(&colorDecoder);
         if (hasAlpha) {
             aom_codec_destroy(&alphaDecoder);
diff --git a/src/stream.c b/src/stream.c
index 8b92cf8..2b3d44c 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -5,6 +5,11 @@
 
 #include <string.h>
 
+uint8_t * avifStreamCurrent(avifStream * stream)
+{
+    return stream->raw->data + stream->offset;
+}
+
 void avifStreamStart(avifStream * stream, avifRawData * raw)
 {
     stream->raw = raw;
@@ -19,6 +24,11 @@
     return (stream->offset + byteCount) <= stream->raw->size;
 }
 
+size_t avifStreamRemainingBytes(avifStream * stream)
+{
+    return stream->raw->size - stream->offset;
+}
+
 avifBool avifStreamSkip(avifStream * stream, size_t byteCount)
 {
     if (!avifStreamHasBytesLeft(stream, byteCount)) {
@@ -88,41 +98,44 @@
     return AVIF_TRUE;
 }
 
-avifBool avifStreamReadBoxHeader(avifStream * stream, uint8_t outputType[4], size_t * outputContentSize)
+avifBool avifStreamReadBoxHeader(avifStream * stream, avifBoxHeader * header)
 {
     size_t startOffset = stream->offset;
 
     uint32_t smallSize;
     CHECK(avifStreamReadU32(stream, &smallSize));
-    CHECK(avifStreamRead(stream, outputType, 4));
+    CHECK(avifStreamRead(stream, header->type, 4));
 
     uint64_t size = smallSize;
     if (size == 1) {
         CHECK(avifStreamReadU64(stream, &size));
     }
 
-    if (!memcmp(outputType, "uuid", 4)) {
+    if (!memcmp(header->type, "uuid", 4)) {
         CHECK(avifStreamSkip(stream, 16));
     }
 
-    *outputContentSize = (size_t)(size - (stream->offset - startOffset));
+    header->size = (size_t)(size - (stream->offset - startOffset));
     return AVIF_TRUE;
 }
 
-avifBool avifStreamReadVersionAndFlags(avifStream * stream, uint8_t * version)
+avifBool avifStreamReadVersionAndFlags(avifStream * stream, uint8_t * version, uint8_t * flags)
 {
     uint8_t versionAndFlags[4];
     CHECK(avifStreamRead(stream, versionAndFlags, 4));
     if (version) {
         *version = versionAndFlags[0];
     }
+    if (flags) {
+        memcpy(flags, &versionAndFlags[1], 3);
+    }
     return AVIF_TRUE;
 }
 
 avifBool avifStreamReadAndEnforceVersion(avifStream * stream, uint8_t enforcedVersion)
 {
     uint8_t version;
-    CHECK(avifStreamReadVersionAndFlags(stream, &version));
+    CHECK(avifStreamReadVersionAndFlags(stream, &version, NULL));
     return (version == enforcedVersion) ? AVIF_TRUE : AVIF_FALSE;
 }
 
diff --git a/src/write.c b/src/write.c
index ce0981c..cd4ae05 100644
--- a/src/write.c
+++ b/src/write.c
@@ -157,6 +157,19 @@
     avifStreamFinishBox(&s, iinf);
 
     // -----------------------------------------------------------------------
+    // Write iref (auxl) for alpha, if any
+
+    if (hasAlpha) {
+        avifBoxMarker iref = avifStreamWriteBox(&s, "iref", 0, 0);
+        avifBoxMarker auxl = avifStreamWriteBox(&s, "auxl", -1, 0);
+        avifStreamWriteU16(&s, 2); // unsigned int(16) from_item_ID;
+        avifStreamWriteU16(&s, 1); // unsigned int(16) reference_count;
+        avifStreamWriteU16(&s, 1); // unsigned int(16) to_item_ID;
+        avifStreamFinishBox(&s, auxl);
+        avifStreamFinishBox(&s, iref);
+    }
+
+    // -----------------------------------------------------------------------
     // Write iprp->ipco->ispe
 
     avifBoxMarker iprp = avifStreamWriteBox(&s, "iprp", -1, 0);