int archive_mxf_read_footer_metadata(const char* filename, MXFDataModel* dataModel, MXFHeaderMetadata** headerMetadata)
{
    MXFPageFile* mxfPageFile = NULL;
    MXFFile* mxfFile = NULL;
    MXFRIP rip;
    MXFRIPEntry* lastRIPEntry = NULL;
    mxfKey key;
    uint8_t llen;
    uint64_t len;
    MXFPartition* footerPartition = NULL;
    int result = 0;
    MXFHeaderMetadata* newHeaderMetadata = NULL;
    
    memset(&rip, 0, sizeof(rip));
 
    
    /* open MXF file */    
    if (strstr(filename, "%d") != NULL)
    {
        CHK_OFAIL_NOMSG(mxf_page_file_open_read(filename, &mxfPageFile));
        mxfFile = mxf_page_file_get_file(mxfPageFile);
    }
    else
    {
        CHK_OFAIL_NOMSG(mxf_disk_file_open_read(filename, &mxfFile));
    }
    
    /* read the RIP */
    CHK_OFAIL_NOMSG(mxf_read_rip(mxfFile, &rip));
    
    /* read footer partition pack */
    CHK_OFAIL_NOMSG((lastRIPEntry = (MXFRIPEntry*)mxf_get_last_list_element(&rip.entries)) != NULL);
    CHK_OFAIL_NOMSG(mxf_file_seek(mxfFile, mxf_get_runin_len(mxfFile) + lastRIPEntry->thisPartition, SEEK_SET));
    CHK_OFAIL_NOMSG(mxf_read_kl(mxfFile, &key, &llen, &len));
    CHK_OFAIL_NOMSG(mxf_is_partition_pack(&key));
    result = 2; /* the file is complete and the presence, or not, of the header metadata will not change */
    *headerMetadata = NULL;
    
    CHK_OFAIL_NOMSG(mxf_is_footer_partition_pack(&key));
    CHK_OFAIL_NOMSG(mxf_read_partition(mxfFile, &key, &footerPartition));
    
    /* read the header metadata */
    CHK_OFAIL_NOMSG(mxf_read_next_nonfiller_kl(mxfFile, &key, &llen, &len));
    CHK_OFAIL_NOMSG(mxf_is_header_metadata(&key));
    CHK_OFAIL_NOMSG(mxf_create_header_metadata(&newHeaderMetadata, dataModel));
    CHK_OFAIL_NOMSG(mxf_read_header_metadata(mxfFile, newHeaderMetadata, 
        footerPartition->headerByteCount, &key, llen, len));

    mxf_free_partition(&footerPartition);
    mxf_clear_rip(&rip);
    mxf_file_close(&mxfFile);
    
    *headerMetadata = newHeaderMetadata;
    newHeaderMetadata = NULL;
    return 1;
 
    
fail:
    mxf_free_header_metadata(&newHeaderMetadata);
    mxf_free_partition(&footerPartition);
    mxf_clear_rip(&rip);
    mxf_file_close(&mxfFile);
    return result;
}
int test_read(const char *filename)
{
    MXFFile *mxfFile = NULL;
    MXFFilePartitions partitions;
    MXFPartition *headerPartition = NULL;
    MXFPartition *bodyPartition1 = NULL;
    MXFPartition *bodyPartition2 = NULL;
    MXFPartition *footerPartition = NULL;
    MXFListIterator iter;
    mxfKey key;
    uint8_t llen;
    uint64_t len;
    int i;
    MXFRIP rip;

    if (!mxf_disk_file_open_read(filename, &mxfFile))
    {
        mxf_log_error("Failed to open '%s'" LOG_LOC_FORMAT, filename, LOG_LOC_PARAMS);
        return 0;
    }

    mxf_initialise_file_partitions(&partitions);


    /* TEST */

    /* read RIP */
    CHK_OFAIL(mxf_read_rip(mxfFile, &rip));
    CHK_OFAIL(mxf_get_list_length(&rip.entries) == 4);
    CHK_OFAIL(mxf_file_seek(mxfFile, 0, SEEK_SET));
    mxf_initialise_list_iter(&iter, &rip.entries);
    i = 0;
    while (mxf_next_list_iter_element(&iter))
    {
        MXFRIPEntry *entry = (MXFRIPEntry*)mxf_get_iter_element(&iter);
        if (i == 0)
        {
            CHK_OFAIL(entry->bodySID == 0);
            CHK_OFAIL(entry->thisPartition == RUNIN_LEN);
        }
        else if (i == 1)
        {
            CHK_OFAIL(entry->bodySID == 2);
            CHK_OFAIL(entry->thisPartition == RUNIN_LEN + 1161);
        }
        else if (i == 2)
        {
            CHK_OFAIL(entry->bodySID == 3);
            CHK_OFAIL(entry->thisPartition == RUNIN_LEN + 1298);
        }
        else
        {
            CHK_OFAIL(entry->bodySID == 0);
            CHK_OFAIL(entry->thisPartition == RUNIN_LEN + 1554);
        }
        i++;
    }

    /* read header pp, allowing for runin */
    CHK_OFAIL(mxf_read_header_pp_kl_with_runin(mxfFile, &key, &llen, &len));
    CHK_OFAIL(mxf_read_partition(mxfFile, &key, &headerPartition));
    CHK_OFAIL(mxf_append_partition(&partitions, headerPartition));
    CHK_OFAIL(headerPartition->indexSID == 1);
    CHK_OFAIL(headerPartition->bodySID == 0);
    CHK_OFAIL(mxf_get_list_length(&headerPartition->essenceContainers) == 2);
    mxf_initialise_list_iter(&iter, &headerPartition->essenceContainers);
    i = 0;
    while (mxf_next_list_iter_element(&iter))
    {
        mxfUL *label = (mxfUL*)mxf_get_iter_element(&iter);
        if (i == 0)
        {
            CHK_OFAIL(mxf_equals_ul(label, &MXF_EC_L(SD_Unc_625_50i_422_135_FrameWrapped)));
        }
        else
        {
            CHK_OFAIL(mxf_equals_ul(label, &MXF_EC_L(BWFFrameWrapped)));
        }
        i++;
    }

    /* skip filler and read body pp 1 */
    CHK_OFAIL(mxf_read_next_nonfiller_kl(mxfFile, &key, &llen, &len));
    CHK_OFAIL(mxf_is_partition_pack(&key));
    CHK_OFAIL(mxf_read_partition(mxfFile, &key, &bodyPartition1));
    CHK_OFAIL(bodyPartition1->indexSID == 0);
    CHK_OFAIL(bodyPartition1->bodySID == 2);
    CHK_OFAIL(mxf_append_partition(&partitions, bodyPartition1));

    /* skip filler and read body pp 2 */
    CHK_OFAIL(mxf_read_next_nonfiller_kl(mxfFile, &key, &llen, &len));
    CHK_OFAIL(mxf_is_partition_pack(&key));
    CHK_OFAIL(mxf_read_partition(mxfFile, &key, &bodyPartition2));
    CHK_OFAIL(bodyPartition2->indexSID == 0);
    CHK_OFAIL(bodyPartition2->bodySID == 3);
    CHK_OFAIL(mxf_append_partition(&partitions, bodyPartition2));

    /* skip filler and read footer pp */
    CHK_OFAIL(mxf_read_next_nonfiller_kl(mxfFile, &key, &llen, &len));
    CHK_OFAIL(mxf_is_partition_pack(&key));
    CHK_OFAIL(mxf_read_partition(mxfFile, &key, &footerPartition));
    CHK_OFAIL(footerPartition->bodySID == 0);
    CHK_OFAIL(footerPartition->indexSID == 1);
    CHK_OFAIL(mxf_append_partition(&partitions, footerPartition));
    mxf_initialise_list_iter(&iter, &footerPartition->essenceContainers);
    i = 0;
    while (mxf_next_list_iter_element(&iter))
    {
        mxfUL *label = (mxfUL*)mxf_get_iter_element(&iter);
        if (i == 0)
        {
            CHK_OFAIL(mxf_equals_ul(label, &MXF_EC_L(SD_Unc_625_50i_422_135_FrameWrapped)));
        }
        else
        {
            CHK_OFAIL(mxf_equals_ul(label, &MXF_EC_L(BWFFrameWrapped)));
        }
        i++;
    }



    mxf_clear_rip(&rip);
    mxf_file_close(&mxfFile);
    mxf_clear_file_partitions(&partitions);
    return 1;

fail:
    mxf_clear_rip(&rip);
    mxf_file_close(&mxfFile);
    mxf_clear_file_partitions(&partitions);
    return 0;
}
static int get_file_partitions(MXFFile *mxfFile, MXFPartition *headerPartition, MXFList *partitions)
{
    mxfKey key;
    uint8_t llen;
    uint64_t len;
    MXFPartition *partition = NULL;
    MXFPartition *partitionRef;
    uint64_t thisPartition;
    MXFRIP rip;
    MXFRIPEntry *ripEntry;
    MXFListIterator iter;

    mxf_initialise_list(partitions, free_partition_in_list);
    memset(&rip, 0, sizeof(MXFRIP));

    /* use the RIP if there is one */
    if (mxf_read_rip(mxfFile, &rip))
    {
        mxf_initialise_list_iter(&iter, &rip.entries);
        while (mxf_next_list_iter_element(&iter))
        {
            ripEntry = (MXFRIPEntry*)mxf_get_iter_element(&iter);

            /* seek to partition and read and add to list */
            CHK_OFAIL(mxf_file_seek(mxfFile, mxf_get_runin_len(mxfFile) + ripEntry->thisPartition,
                SEEK_SET));
            CHK_OFAIL(mxf_read_kl(mxfFile, &key, &llen, &len));
            CHK_OFAIL(mxf_is_partition_pack(&key));
            CHK_OFAIL(mxf_read_partition(mxfFile, &key, &partition));
            CHK_OFAIL(mxf_append_list_element(partitions, partition));
            partition = NULL; /* owned by list */
        }
    }

    /* start from footer partition and index back to the header partition */
    else
    {
        if (headerPartition->footerPartition == 0)
        {
            /* no footer partition or at unknown position, so we only index the header partition */
            goto fail;
        }

        thisPartition = headerPartition->footerPartition;
        do
        {
            /* seek to partition and read and add to list */
            CHK_OFAIL(mxf_file_seek(mxfFile, mxf_get_runin_len(mxfFile) + thisPartition, SEEK_SET));
            CHK_OFAIL(mxf_read_kl(mxfFile, &key, &llen, &len));
            CHK_OFAIL(mxf_is_partition_pack(&key));
            CHK_OFAIL(mxf_read_partition(mxfFile, &key, &partition));
            CHK_OFAIL(mxf_prepend_list_element(partitions, partition));
            partitionRef = partition;
            partition = NULL; /* owned by list */

            thisPartition = partitionRef->previousPartition;
        }
        while (partitionRef->thisPartition != partitionRef->previousPartition);
    }


    mxf_clear_rip(&rip);
    return 1;

fail:
    /* if something failed then just add the header partition
       Note: some Omneon files had references to a footer partition which was not
       actually present in the file */

    mxf_clear_list(partitions);
    mxf_free_partition(&partition);
    mxf_clear_rip(&rip);

    /* create copy of header partition pack */
    CHK_ORET(mxf_create_from_partition(headerPartition, &partition));
    partition->key = headerPartition->key;
    partition->majorVersion = headerPartition->majorVersion;
    partition->minorVersion = headerPartition->minorVersion;
    partition->kagSize = headerPartition->kagSize;
    partition->thisPartition = headerPartition->thisPartition;
    partition->previousPartition = headerPartition->previousPartition;
    partition->footerPartition = headerPartition->footerPartition;
    partition->headerByteCount = headerPartition->headerByteCount;
    partition->indexByteCount = headerPartition->indexByteCount;
    partition->indexSID = headerPartition->indexSID;
    partition->bodyOffset = headerPartition->bodyOffset;
    partition->bodySID = headerPartition->bodySID;

    /* add partition to list */
    if (!mxf_append_list_element(partitions, partition))
    {
        mxf_free_partition(&partition);
        mxf_log_error("Failed to append header partition to list" LOG_LOC_FORMAT, LOG_LOC_PARAMS);
        return 0;
    }

    return 1;
}