Commit message: fix for #184
CVE: CVE-2017-14641
CWE: CWE-476
Vulnerability description: A NULL pointer dereference was discovered in the AP4_DataAtom class in MetaData/Ap4MetaData.cpp in Bento4 version 1.5.0-617. The vulnerability causes a segmentation fault and application crash, which leads to remote denial of service.

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Value::GetTypeCategory() const
{
    return MapTypeToCategory(m_Type);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::ParseUdta(AP4_ContainerAtom* udta, const char* namespc)
{
    // check that the atom is indeed a 'udta' atom
    if (udta->GetType() != AP4_ATOM_TYPE_UDTA) {
        return AP4_ERROR_INVALID_PARAMETERS;
    }
    
    AP4_List<AP4_Atom>::Item* udta_item = udta->GetChildren().FirstItem();
    for (; udta_item; udta_item = udta_item->GetNext()) {
        AP4_3GppLocalizedStringAtom* _3gpp_atom = AP4_DYNAMIC_CAST(AP4_3GppLocalizedStringAtom, udta_item->GetData()); 
        if (_3gpp_atom) {
            Add3GppEntry(_3gpp_atom, namespc);
            continue;
        } 
        
        AP4_DcfStringAtom* dcfs_atom = AP4_DYNAMIC_CAST(AP4_DcfStringAtom, udta_item->GetData());
        if (dcfs_atom) {
            AddDcfStringEntry(dcfs_atom, namespc);
            continue;
        } 

        AP4_DcfdAtom* dcfd_atom = AP4_DYNAMIC_CAST(AP4_DcfdAtom, udta_item->GetData());
        if (dcfd_atom) {
            AddDcfdEntry(dcfd_atom, namespc);
        }
    }
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_StringMetaDataValue::ToInteger() const
{
    return 0;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaDataAtomTypeHandler::IsTypeInList(AP4_Atom::Type type, const AP4_MetaDataAtomTypeHandler::TypeList& list)
{
    for (unsigned int i=0; i<list.m_Size; i++) {
        if (type == list.m_Types[i]) return true;
    }
    return false;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfStringAtom::Create(Type type, AP4_UI32 size, AP4_ByteStream& stream) 
{
    AP4_UI08 version;
    AP4_UI32 flags;
    if (AP4_FAILED(AP4_Atom::ReadFullHeader(stream, version, flags))) return NULL;
    if (version != 0) return NULL;
    return new AP4_DcfStringAtom(type, size, version, flags, stream);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::InspectFields(AP4_AtomInspector& inspector)
{
    inspector.AddField("type", m_DataType);
    inspector.AddField("lang", m_DataLang);
    if (m_DataType == DATA_TYPE_STRING_UTF_8) {
        AP4_String* str;
        if (AP4_SUCCEEDED(LoadString(str))) {
            inspector.AddField("value", str->GetChars());
            delete str;
        }
    } else if (m_DataType == DATA_TYPE_SIGNED_INT_BE) {
        long value;
        if (AP4_SUCCEEDED(LoadInteger(value))) {
            inspector.AddField("value", value);
        }
    }

    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::FindInIlst(AP4_ContainerAtom* ilst) const
{
    if (m_Key.GetNamespace() == "meta") {
        AP4_Atom::Type atom_type = AP4_Atom::TypeFromString(m_Key.GetName().GetChars());
        return AP4_DYNAMIC_CAST(AP4_ContainerAtom, ilst->GetChild(atom_type));
    } else {
        AP4_List<AP4_Atom>::Item* ilst_item = ilst->GetChildren().FirstItem();
        while (ilst_item) {
            AP4_ContainerAtom* entry_atom = AP4_DYNAMIC_CAST(AP4_ContainerAtom, ilst_item->GetData()); 
            if (entry_atom) {
                AP4_MetaDataStringAtom* mean = static_cast<AP4_MetaDataStringAtom*>(entry_atom->GetChild(AP4_ATOM_TYPE_MEAN));
                AP4_MetaDataStringAtom* name = static_cast<AP4_MetaDataStringAtom*>(entry_atom->GetChild(AP4_ATOM_TYPE_NAME));
                if (mean && name &&
                    mean->GetValue() == m_Key.GetNamespace() &&
                    name->GetValue() == m_Key.GetName()) {
                    return entry_atom;
                }
            }
            ilst_item = ilst_item->GetNext();
        }
    }
    
    // not found
    return NULL;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfStringAtom::AP4_DcfStringAtom(Type type, const char* value) :
    AP4_Atom(type, AP4_FULL_ATOM_HEADER_SIZE, 0, 0),
    m_Value(value)
{
    m_Size32 += m_Value.GetLength();
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfdAtom::Create(AP4_UI32 size, AP4_ByteStream& stream) 
{
    AP4_UI08 version;
    AP4_UI32 flags;
    if (AP4_FAILED(AP4_Atom::ReadFullHeader(stream, version, flags))) return NULL;
    if (version != 0) return NULL;
    if (size != AP4_FULL_ATOM_HEADER_SIZE+4) return NULL;
    return new AP4_DcfdAtom(version, flags, stream);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfStringAtom::AP4_DcfStringAtom(Type            type, 
                                     AP4_UI32        size, 
                                     AP4_UI08        version,
                                     AP4_UI32        flags,
                                     AP4_ByteStream& stream) :
    AP4_Atom(type, size, version, flags)
{
    if (size > AP4_FULL_ATOM_HEADER_SIZE) {
        AP4_UI32 value_size = size-(AP4_FULL_ATOM_HEADER_SIZE);
        char* value = new char[value_size];
        stream.Read(value, value_size);
        m_Value.Assign(value, value_size);
        delete[] value;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaDataStringAtom::InspectFields(AP4_AtomInspector& inspector)
{
    inspector.AddField("value", m_Value.GetChars());
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::AP4_MetaData(AP4_File* file)
{
    // get the file's movie
    AP4_Movie* movie = file->GetMovie();

    // handle the movie's metadata if there is a movie in the file
    if (movie) {
        AP4_MoovAtom* moov = movie->GetMoovAtom();
        if (moov == NULL) return;
        ParseMoov(moov);
        AP4_Atom* udta = moov->GetChild(AP4_ATOM_TYPE_UDTA);
        if (udta) {
            AP4_ContainerAtom* udta_container = AP4_DYNAMIC_CAST(AP4_ContainerAtom, udta);
            if (udta_container) {
                ParseUdta(udta_container, "3gpp");
            }
        }
    } else {
        // if we don't have a movie, try to show metadata from a udta atom
        AP4_List<AP4_Atom>& top_level_atoms = file->GetTopLevelAtoms();
        
        AP4_List<AP4_Atom>::Item* atom_item = top_level_atoms.FirstItem();
        while (atom_item) {
            AP4_ContainerAtom* container = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom_item->GetData());
            if (container) {
                // look for a udta in a DCF layout
                AP4_Atom* udta = container->FindChild("odhe/udta");
                if (udta) {
                    AP4_ContainerAtom* udta_container = AP4_DYNAMIC_CAST(AP4_ContainerAtom, udta);
                    if (udta_container) {
                        ParseUdta(udta_container, "dcf");
                    }
                }
            }
            atom_item = atom_item->GetNext();
        }
    } 
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::AddToFileDcf(AP4_File& file, AP4_Ordinal index)
{
    // check that we have a correct entry
    if (m_Value == NULL) return AP4_ERROR_INVALID_STATE;
    
    // look for 'odrm/odhe'
    AP4_ContainerAtom* odhe = AP4_DYNAMIC_CAST(AP4_ContainerAtom, file.FindChild("odrm/odhe"));
    if (odhe == NULL) return AP4_ERROR_NO_SUCH_ITEM;

    // get/create the list of entries
    AP4_ContainerAtom* udta = AP4_DYNAMIC_CAST(AP4_ContainerAtom, odhe->FindChild("udta", true));
    if (udta == NULL) return AP4_ERROR_INTERNAL;
    
    // convert the entry into an atom
    AP4_Atom* data_atom;
    AP4_Result result = ToAtom(data_atom);
    if (AP4_FAILED(result)) return result;

    // add the entry's data to the container
    return udta->AddChild(data_atom, index);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::AP4_DataAtom(const AP4_MetaData::Value& value) :
    AP4_Atom(AP4_ATOM_TYPE_DATA, AP4_ATOM_HEADER_SIZE),
    m_DataType(DATA_TYPE_BINARY),
    m_Source(NULL)
{
    AP4_MemoryByteStream* memory = new AP4_MemoryByteStream();
    AP4_Size payload_size = 8;
    m_Source = memory;
    
    switch (value.GetType()) {
        case AP4_MetaData::Value::TYPE_STRING_UTF_8: {
            m_DataType = DATA_TYPE_STRING_UTF_8;
            AP4_String string_value = value.ToString();
            if (string_value.GetLength()) {
                memory->Write(string_value.GetChars(), string_value.GetLength());
            }
            payload_size += string_value.GetLength();
            break;
        }
         
        case AP4_MetaData::Value::TYPE_INT_08_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI08 int_value = (AP4_UI08)value.ToInteger();
            memory->Write(&int_value, 1);
            payload_size += 1;
            break;
        }
            
        case AP4_MetaData::Value::TYPE_INT_16_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI16 int_value = (AP4_UI16)value.ToInteger();
            memory->Write(&int_value, 2);
            payload_size += 2;
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_32_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI32 int_value = (AP4_UI32)value.ToInteger();
            memory->Write(&int_value, 4);
            payload_size += 4;
            break;
        }

        case AP4_MetaData::Value::TYPE_JPEG:
            m_DataType = DATA_TYPE_JPEG;
            // FALLTHROUGH
        case AP4_MetaData::Value::TYPE_GIF: 
            if (m_DataType == DATA_TYPE_BINARY) m_DataType = DATA_TYPE_GIF;
            // FALLTHROUGH
        case AP4_MetaData::Value::TYPE_BINARY: {
            AP4_DataBuffer buffer;
            value.ToBytes(buffer);
            if (buffer.GetDataSize()) {
                memory->Write(buffer.GetData(), buffer.GetDataSize());
            }
            payload_size += buffer.GetDataSize();
            break;
        }

        default:
            break;
    }
    
    const AP4_String& language = value.GetLanguage();
    if (language == "en") {
        m_DataLang = LANGUAGE_ENGLISH;
    } else {
        // default
        m_DataLang = LANGUAGE_ENGLISH;
    }
    
    m_Size32 += payload_size;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::RemoveFromFileDcf(AP4_File& file, AP4_Ordinal index)
{
    // look for 'odrm/odhe/udta'
    AP4_ContainerAtom* udta = AP4_DYNAMIC_CAST(AP4_ContainerAtom, file.FindChild("odrm/odhe/udta"));
    if (udta == NULL) return AP4_ERROR_NO_SUCH_ITEM;
                
    // remove the data atom in the entry
    AP4_UI32 type = AP4_BytesToUInt32BE((const unsigned char*)m_Key.GetName().GetChars());
    AP4_Result result = udta->DeleteChild(type, index);
    if (AP4_FAILED(result)) return result;
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_AtomMetaDataValue::AP4_AtomMetaDataValue(AP4_DataAtom*  atom,
                                             AP4_UI32       parent_type) :
    Value(atom->GetValueType()),
    m_DataAtom(atom)
{
    switch (parent_type) {
        case AP4_ATOM_TYPE_GNRE:
            m_Meaning = MEANING_ID3_GENRE;
            break;

        case AP4_ATOM_TYPE_CPIL:
            m_Meaning = MEANING_BOOLEAN;
            break;

        case AP4_ATOM_TYPE_PGAP:
        case AP4_ATOM_TYPE_PCST:
            m_Meaning = MEANING_BOOLEAN;
            break;

        case AP4_ATOM_TYPE_STIK:
            m_Meaning = MEANING_FILE_KIND;
            break;

        case AP4_ATOM_TYPE_PURL:
        case AP4_ATOM_TYPE_EGID:
            m_Meaning = MEANING_BINARY_ENCODED_CHARS;
            break;
            
        default:
            break;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_BinaryMetaDataValue::ToInteger() const
{
    return 0; // NOT SUPPORTED
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_IntegerMetaDataValue::ToString() const
{
    char value[16];
    AP4_FormatString(value, sizeof(value), "%ld", m_Value);
    return AP4_String(value);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_BinaryMetaDataValue::ToString() const
{
    return AP4_String(); // not supported
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_StringMetaDataValue::ToString() const
{
    return m_Value;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaDataStringAtom::AP4_MetaDataStringAtom(Type type, const char* value) :
    AP4_Atom(type, AP4_ATOM_HEADER_SIZE),
    m_Reserved(0),
    m_Value(value)
{
    m_Size32 += 4+m_Value.GetLength();
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_3GppLocalizedStringAtom::AP4_3GppLocalizedStringAtom(Type        type, 
                                                         const char* language, 
                                                         const char* value) :
    AP4_Atom(type, AP4_FULL_ATOM_HEADER_SIZE+2, 0, 0),
    m_Value(value)
{
    m_Language[0] = language[0];
    m_Language[1] = language[1];
    m_Language[2] = language[2];
    m_Language[3] = language[3];
    
    m_Size32 += m_Value.GetLength()+1;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::AddIlstEntries(AP4_ContainerAtom* atom, const char* namespc)
{
    AP4_MetaData::Value* value = NULL;

    if (atom->GetType() == AP4_ATOM_TYPE_dddd) {
        // look for the namespace
        AP4_MetaDataStringAtom* mean = static_cast<AP4_MetaDataStringAtom*>(atom->GetChild(AP4_ATOM_TYPE_MEAN));
        if (mean == NULL) return AP4_ERROR_INVALID_FORMAT;

        // look for the name
        AP4_MetaDataStringAtom* name = static_cast<AP4_MetaDataStringAtom*>(atom->GetChild(AP4_ATOM_TYPE_NAME));
        if (name == NULL) return AP4_ERROR_INVALID_FORMAT;

        // get the value
        AP4_DataAtom* data_atom = static_cast<AP4_DataAtom*>(atom->GetChild(AP4_ATOM_TYPE_DATA));
        if (data_atom == NULL) return AP4_ERROR_INVALID_FORMAT;
        value = new AP4_AtomMetaDataValue(data_atom, atom->GetType());
        
        return m_Entries.Add(new Entry(name->GetValue().GetChars(),
                                       mean->GetValue().GetChars(),
                                       value));
    } else {
        const char* key_name = NULL;
        char        four_cc[5];

        // convert the atom type to a name
        AP4_FormatFourChars(four_cc, (AP4_UI32)atom->GetType());
        key_name = four_cc;

        // add one entry for each data atom
        AP4_List<AP4_Atom>::Item* data_item = atom->GetChildren().FirstItem();
        while (data_item) {
            AP4_Atom* item_atom = data_item->GetData();
            if (item_atom->GetType() == AP4_ATOM_TYPE_DATA) {
                AP4_DataAtom* data_atom = static_cast<AP4_DataAtom*>(item_atom);
                value = new AP4_AtomMetaDataValue(data_atom, atom->GetType());
                m_Entries.Add(new Entry(key_name, namespc, value));
            }
            data_item = data_item->GetNext();
        }

        return AP4_SUCCESS;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Add3GppEntry(AP4_3GppLocalizedStringAtom* atom, const char* namespc)
{
    AP4_String key_name;
    ResolveKeyName(atom->GetType(), key_name);
    
    const char* language = NULL;
    if (atom->GetLanguage()[0]) {
        language = atom->GetLanguage();
    }
    AP4_MetaData::Value* value = new AP4_StringMetaDataValue(atom->GetValue().GetChars(),
                                                             language);
    m_Entries.Add(new Entry(key_name.GetChars(), namespc, value));
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfdAtom::InspectFields(AP4_AtomInspector& inspector)
{
    inspector.AddField("duration", m_Duration);
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::RemoveFromFileIlst(AP4_File& file, AP4_Ordinal index)
{
    // look for the 'moov'
    AP4_Movie* movie = file.GetMovie();
    if (movie == NULL) return AP4_ERROR_INVALID_FORMAT;
    AP4_MoovAtom* moov = movie->GetMoovAtom();
    if (moov == NULL) return AP4_ERROR_INVALID_FORMAT;
    
    // look for 'udta/meta/ilst'
    AP4_ContainerAtom* ilst = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov->FindChild("udta/meta/ilst"));
    if (ilst == NULL) return AP4_ERROR_NO_SUCH_ITEM;
    
    // look if there is already a container for this entry
    AP4_ContainerAtom* existing = FindInIlst(ilst);
    if (existing == NULL) return AP4_ERROR_NO_SUCH_ITEM;
            
    // remove the data atom in the entry
    AP4_Result result = existing->DeleteChild(AP4_ATOM_TYPE_DATA, index);
    if (AP4_FAILED(result)) return result;

    // cleanup
    if (existing->GetType() == AP4_ATOM_TYPE_dddd) {
        // custom entry: if there are no more 'data' children, remove the entry
        if (existing->GetChild(AP4_ATOM_TYPE_DATA) == NULL) {
            ilst->RemoveChild(existing);
            delete existing;
        }
    } else {
        // normal entry: if the entry is empty, remove it
        if (existing->GetChildren().ItemCount() == 0) {
            ilst->RemoveChild(existing);
            delete existing;
        }
    }
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::AddDcfdEntry(AP4_DcfdAtom* atom, const char* namespc)
{
    AP4_String key_name;
    ResolveKeyName(atom->GetType(), key_name);
    
    AP4_MetaData::Value* value = new AP4_IntegerMetaDataValue(AP4_MetaData::Value::TYPE_INT_32_BE, 
                                                              atom->GetDuration());
    m_Entries.Add(new Entry(key_name.GetChars(), namespc, value));
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaDataAtomTypeHandler::CreateAtom(AP4_Atom::Type  type,
                                        AP4_UI32        size,
                                        AP4_ByteStream& stream,
                                        AP4_Atom::Type  context,
                                        AP4_Atom*&      atom)
{
    atom = NULL;

    if (context == AP4_ATOM_TYPE_ILST) {
        if (IsTypeInList(type, IlstTypeList)) {
            m_AtomFactory->PushContext(type);
            atom = AP4_ContainerAtom::Create(type, size, false, false, stream, *m_AtomFactory);
            m_AtomFactory->PopContext();
        }
    } else if (type == AP4_ATOM_TYPE_DATA) {
        if (IsTypeInList(context, IlstTypeList)) {
            atom = new AP4_DataAtom(size, stream);
        }
    } else if (context == AP4_ATOM_TYPE_dddd) {
        if (type == AP4_ATOM_TYPE_MEAN || type == AP4_ATOM_TYPE_NAME) {
            atom = new AP4_MetaDataStringAtom(type, size, stream);
        }
    } else if (context == AP4_ATOM_TYPE_UDTA) {
        if (IsTypeInList(type, _3gppLocalizedStringTypeList)) {
            atom = AP4_3GppLocalizedStringAtom::Create(type, size, stream);
        } else if (IsTypeInList(type, DcfStringTypeList)) {
            atom = AP4_DcfStringAtom::Create(type, size, stream);
        } else if (type == AP4_ATOM_TYPE_DCFD) {
            atom = AP4_DcfdAtom::Create(size, stream);
        }
    }

    return atom?AP4_SUCCESS:AP4_FAILURE;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Initialized() { return AP4_MetaData::KeyInfos.ItemCount() != 0; }

Project: Bento4
File: Ap4MetaData.cpp

AP4_3GppLocalizedStringAtom::Create(Type type, AP4_UI32 size, AP4_ByteStream& stream) 
{
    AP4_UI08 version;
    AP4_UI32 flags;
    if (AP4_FAILED(AP4_Atom::ReadFullHeader(stream, version, flags))) return NULL;
    if (version != 0) return NULL;
    return new AP4_3GppLocalizedStringAtom(type, size, version, flags, stream);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::LoadString(AP4_String*& string)
{
    if (m_Source == NULL) {
        string = new AP4_String();
        return AP4_SUCCESS;
    } else {
        // create a string with enough capactiy for the data
        AP4_LargeSize size = 0;
        m_Source->GetSize(size);
        if (size > AP4_DATA_ATOM_MAX_SIZE) return AP4_ERROR_OUT_OF_RANGE;
        string = new AP4_String((AP4_Size)size);

        // read from the start of the stream
        m_Source->Seek(0);
        AP4_Result result = m_Source->Read(string->UseChars(), (AP4_Size)size);
        if (AP4_FAILED(result)) {
            delete string;
            string = NULL;
        }

        return result;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_IntegerMetaDataValue::ToInteger() const
{
    return m_Value;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::AddDcfStringEntry(AP4_DcfStringAtom* atom, const char* namespc)
{
    AP4_String key_name;
    ResolveKeyName(atom->GetType(), key_name);
    
    AP4_MetaData::Value* value = new AP4_StringMetaDataValue(atom->GetValue().GetChars());
    m_Entries.Add(new Entry(key_name.GetChars(), namespc, value));
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::ToAtom(AP4_Atom*& atom) const
{
    atom = NULL;
    
    if (m_Value == NULL) {
        return AP4_ERROR_INVALID_PARAMETERS;
    }
    
    if (m_Key.GetNamespace() == "meta") {
        // convert the name into an atom type
        if (m_Key.GetName().GetLength() != 4) {
            // the name is not in the right format
            return AP4_ERROR_INVALID_PARAMETERS;
        }
        AP4_Atom::Type atom_type = AP4_Atom::TypeFromString(m_Key.GetName().GetChars()); 
                                   
        // create a container atom for the data
        AP4_ContainerAtom* container = new AP4_ContainerAtom(atom_type);

        // add the data atom
        AP4_DataAtom* data = new AP4_DataAtom(*m_Value);
        container->AddChild(data);
        
        atom = container;
        return AP4_SUCCESS;
    } else if (m_Key.GetNamespace() == "dcf") {
        // convert the name into an atom type
        if (m_Key.GetName().GetLength() != 4) {
            // the name is not in the right format
            return AP4_ERROR_INVALID_PARAMETERS;
        }
        AP4_Atom::Type atom_type = AP4_Atom::TypeFromString(m_Key.GetName().GetChars()); 

        if (AP4_MetaDataAtomTypeHandler::IsTypeInList(atom_type, 
            AP4_MetaDataAtomTypeHandler::DcfStringTypeList)) {
            AP4_String atom_value = m_Value->ToString();
            atom = new AP4_DcfStringAtom(atom_type, atom_value.GetChars());
            return AP4_SUCCESS;
        } else if (AP4_MetaDataAtomTypeHandler::IsTypeInList(atom_type, 
                   AP4_MetaDataAtomTypeHandler::_3gppLocalizedStringTypeList)) {
            AP4_String atom_value = m_Value->ToString();
            const char* language = "eng"; // default
            if (m_Value->GetLanguage().GetLength() != 0) {
                language = m_Value->GetLanguage().GetChars();
            }
            atom = new AP4_3GppLocalizedStringAtom(atom_type, language, atom_value.GetChars());
            return AP4_SUCCESS;
        } else if (atom_type == AP4_ATOM_TYPE_DCFD) {
            atom = new AP4_DcfdAtom((AP4_UI32)m_Value->ToInteger());
            return AP4_SUCCESS;
        }
        
         // not supported
         return AP4_ERROR_NOT_SUPPORTED; 
    } else {
        // create a '----' atom
        AP4_ContainerAtom* container = new AP4_ContainerAtom(AP4_ATOM_TYPE_dddd);
        
        // add a 'mean' string
        container->AddChild(new AP4_MetaDataStringAtom(AP4_ATOM_TYPE_MEAN, m_Key.GetNamespace().GetChars()));
        
        // add a 'name' string
        container->AddChild(new AP4_MetaDataStringAtom(AP4_ATOM_TYPE_NAME, m_Key.GetName().GetChars()));

        // add the data atom
        AP4_DataAtom* data = new AP4_DataAtom(*m_Value);
        container->AddChild(data);
        
        atom = container;
        return AP4_SUCCESS;
    }
        
    // unreachable - return AP4_ERROR_NOT_SUPPORTED;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::LoadBytes(AP4_DataBuffer& bytes)
{
    if (m_Source == NULL) {
        bytes.SetDataSize(0);
        return AP4_SUCCESS;
    }
    AP4_LargeSize size = 0;
    m_Source->GetSize(size);
    if (size > AP4_DATA_ATOM_MAX_SIZE) return AP4_ERROR_OUT_OF_RANGE;
    bytes.SetDataSize((AP4_Size)size);
    m_Source->Seek(0);
    AP4_Result result = m_Source->Read(bytes.UseData(), (AP4_Size)size);
    if (AP4_FAILED(result)) {
        bytes.SetDataSize(0);
    }
    return result;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_3GppLocalizedStringAtom::InspectFields(AP4_AtomInspector& inspector)
{
    inspector.AddField("language", GetLanguage());
    inspector.AddField("value", m_Value.GetChars());
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfdAtom::WriteFields(AP4_ByteStream& stream)
{
    stream.WriteUI32(m_Duration);
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::WriteFields(AP4_ByteStream& stream)
{
    stream.WriteUI32(m_DataType);
    stream.WriteUI32(m_DataLang);
    if (m_Source) {
        AP4_LargeSize size = 0;
        m_Source->GetSize(size);
        m_Source->Seek(0);
        m_Source->CopyTo(stream, size);
    }

    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_StringMetaDataValue::ToBytes(AP4_DataBuffer& /* bytes */) const
{
    return AP4_ERROR_NOT_SUPPORTED;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Value::MapTypeToCategory(Type type)
{
    switch (type) {
        case AP4_MetaData::Value::TYPE_INT_08_BE:
        case AP4_MetaData::Value::TYPE_INT_16_BE:
        case AP4_MetaData::Value::TYPE_INT_32_BE:
            return AP4_MetaData::Value::TYPE_CATEGORY_INTEGER;

        case AP4_MetaData::Value::TYPE_STRING_UTF_8:
        case AP4_MetaData::Value::TYPE_STRING_UTF_16:
        case AP4_MetaData::Value::TYPE_STRING_PASCAL:
            return AP4_MetaData::Value::TYPE_CATEGORY_STRING;

        case AP4_MetaData::Value::TYPE_FLOAT_32_BE:
        case AP4_MetaData::Value::TYPE_FLOAT_64_BE:
            return AP4_MetaData::Value::TYPE_CATEGORY_FLOAT;
            
        default:
            return AP4_MetaData::Value::TYPE_CATEGORY_BINARY;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::RemoveFromFile(AP4_File& file, AP4_Ordinal index)
{
    // check the namespace of the key to know where to add the atom
    if (m_Key.GetNamespace() == "meta") {
        return RemoveFromFileIlst(file, index);
    } else if (m_Key.GetNamespace() == "dcf") {
        return RemoveFromFileDcf(file, index);
    } else {
        // custom namespace
        return RemoveFromFileIlst(file, index);
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_AtomMetaDataValue::ToInteger() const
{
    long value;
    if (AP4_SUCCEEDED(m_DataAtom->LoadInteger(value))) {
        return value;
    } else {
        return 0;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_BinaryMetaDataValue::ToBytes(AP4_DataBuffer& bytes) const
{
    bytes.SetDataSize(m_Value.GetDataSize());
    AP4_CopyMemory(bytes.UseData(), m_Value.GetData(), m_Value.GetDataSize());
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::AP4_DataAtom(AP4_UI32 size, AP4_ByteStream& stream) :
    AP4_Atom(AP4_ATOM_TYPE_DATA, size),
    m_Source(NULL)
{
    if (size < AP4_ATOM_HEADER_SIZE+8) return;

    AP4_UI32 i;
    stream.ReadUI32(i); m_DataType = (DataType)i;
    stream.ReadUI32(i); m_DataLang = (DataLang)i;

    // the stream for the data is a substream of this source
    AP4_Position data_offset;
    stream.Tell(data_offset);
    AP4_Size data_size = size-AP4_ATOM_HEADER_SIZE-8;
    m_Source = new AP4_SubStream(stream, data_offset, data_size);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::LoadInteger(long& value) 
{
    AP4_Result result = AP4_FAILURE;
    value = 0;
    if (m_Source == NULL) return AP4_SUCCESS;
    AP4_LargeSize size = 0;
    m_Source->GetSize(size);
    if (size > 4) {
        return AP4_ERROR_OUT_OF_RANGE;
    }
    unsigned char bytes[4];
    m_Source->Seek(0);
    m_Source->Read(bytes, (AP4_Size)size);
    result = AP4_SUCCESS;
    switch (size) {
        case 1: value = bytes[0]; break;
        case 2: value = AP4_BytesToInt16BE(bytes); break;
        case 4: value = AP4_BytesToInt32BE(bytes); break;
        default: value = 0; result = AP4_ERROR_INVALID_FORMAT; break;
    }
    return result;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_3GppLocalizedStringAtom::AP4_3GppLocalizedStringAtom(Type            type, 
                                                         AP4_UI32        size, 
                                                         AP4_UI08        version,
                                                         AP4_UI32        flags,
                                                         AP4_ByteStream& stream) :
    AP4_Atom(type, size, version, flags)
{
    // read the language code
    AP4_UI16 packed_language;
    stream.ReadUI16(packed_language);
    m_Language[0] = 0x60+((packed_language>>10)&0x1F);
    m_Language[1] = 0x60+((packed_language>> 5)&0x1F);
    m_Language[2] = 0x60+((packed_language    )&0x1F);
    m_Language[3] = '\0';
    
    // read the value (should be a NULL-terminated string, but we'll
    // allow for strings that are not terminated)
    if (size > AP4_FULL_ATOM_HEADER_SIZE+2) {
        AP4_UI32 value_size = size-(AP4_FULL_ATOM_HEADER_SIZE+2);
        char* value = new char[value_size];
        stream.Read(value, value_size);
        m_Value.Assign(value, value_size);
        delete[] value;
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_AtomMetaDataValue::ToString() const
{
    char string[256] = "";

    AP4_MetaData::Value::Type value_type = m_DataAtom->GetValueType();
    switch (AP4_MetaData::Value::MapTypeToCategory(value_type)) {
        case AP4_MetaData::Value::TYPE_CATEGORY_INTEGER: 
            {
                long value;
                if (AP4_SUCCEEDED(m_DataAtom->LoadInteger(value))) {
                    if (m_Meaning == MEANING_BOOLEAN) {
                        if (value) {
                            return "True";
                        } else {
                            return "False";
                        }
                    } else if (m_Meaning == MEANING_FILE_KIND) {
                        if (value >= 0 && ((unsigned int)value) <= sizeof(Ap4StikNames)/sizeof(Ap4StikNames[0])) {
                            AP4_FormatString(string, sizeof(string), "(%ld) %s", value, Ap4StikNames[value]);
                        } else {
                            return "Unknown";
                        }
                    } else {
                        AP4_FormatString(string, sizeof(string), "%ld", value);
                    }
                }
                return AP4_String((const char*)string);
                break;
            }

        case AP4_MetaData::Value::TYPE_CATEGORY_STRING:
            {
                AP4_String* category_string;
                if (AP4_SUCCEEDED(m_DataAtom->LoadString(category_string))) {
                    AP4_String result(*category_string);
                    delete category_string;
                    return result;
                }
                break;
            }

        case AP4_MetaData::Value::TYPE_CATEGORY_BINARY:
            {
                AP4_DataBuffer data;
                if (AP4_SUCCEEDED(m_DataAtom->LoadBytes(data))) {
                    if (m_Meaning == MEANING_ID3_GENRE && data.GetDataSize() == 2) {
                        unsigned int genre = (data.GetData()[0])*256+data.GetData()[1];
                        if (genre >= 1 && genre <= sizeof(Ap4Id3Genres)/sizeof(Ap4Id3Genres[0])) {
                            AP4_FormatString(string, sizeof(string), "(%d) %s", genre, Ap4Id3Genres[genre-1]);
                            return AP4_String((const char*)string);
                        } else {
                            return "Unknown";
                        }
                    } else if (m_Meaning == MEANING_BINARY_ENCODED_CHARS) {
                        AP4_String result;
                        result.Assign((const char*)data.GetData(), data.GetDataSize());
                        return result;
                    } else {
                        unsigned int dump_length = data.GetDataSize();
                        bool truncate = false;
                        if (dump_length > 16) {
                            dump_length = 16;
                            truncate = true;
                        }
                        char* out = string;
                        for (unsigned int i=0; i<dump_length; i++) {
                            AP4_FormatString(out, sizeof(string)-(out-string), "%02x ", data.GetData()[i]);
                            out += 3;
                        }
                        if (truncate) {
                            *out++='.'; *out++='.'; *out++='.'; *out++=' ';
                        }
                        AP4_FormatString(out, sizeof(string)-(out-string), "[%d bytes]", (int)data.GetDataSize());
                    }
                }
                return AP4_String(string);
            }
        default:
            return AP4_String();
    }

    return AP4_String();
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::GetValueType()
{
    switch (m_DataType) {
        case DATA_TYPE_BINARY:
            return AP4_MetaData::Value::TYPE_BINARY;

        case DATA_TYPE_SIGNED_INT_BE:
            switch (m_Size32-16) {
                case 1: return AP4_MetaData::Value::TYPE_INT_08_BE;
                case 2: return AP4_MetaData::Value::TYPE_INT_16_BE;
                case 4: return AP4_MetaData::Value::TYPE_INT_32_BE;
                default: return AP4_MetaData::Value::TYPE_BINARY;
            }
            break;
            
        case DATA_TYPE_STRING_UTF_8:
            return AP4_MetaData::Value::TYPE_STRING_UTF_8;
            
        case DATA_TYPE_STRING_UTF_16:
            return AP4_MetaData::Value::TYPE_STRING_UTF_16;
            
        case DATA_TYPE_STRING_PASCAL:
            return AP4_MetaData::Value::TYPE_STRING_PASCAL;

        case DATA_TYPE_GIF:
            return AP4_MetaData::Value::TYPE_GIF;

        case DATA_TYPE_JPEG:
            return AP4_MetaData::Value::TYPE_JPEG;

        default:
            return AP4_MetaData::Value::TYPE_BINARY;
    }
    
    // unreachable - return AP4_MetaData::Value::TYPE_BINARY;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfStringAtom::InspectFields(AP4_AtomInspector& inspector)
{
    inspector.AddField("value", m_Value.GetChars());
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfStringAtom::WriteFields(AP4_ByteStream& stream)
{
    if (m_Value.GetLength()) stream.Write(m_Value.GetChars(), m_Value.GetLength());
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::AddToFile(AP4_File& file, AP4_Ordinal index)
{
    // check that we have a correct entry
    if (m_Value == NULL) return AP4_ERROR_INVALID_STATE;
 
    // check the namespace of the key to know where to add the atom
    if (m_Key.GetNamespace() == "meta") {
        return AddToFileIlst(file, index);
    } else if (m_Key.GetNamespace() == "dcf") {
        return AddToFileDcf(file, index);
    } else {
        // custom namespace
        return AddToFileIlst(file, index);
    }
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Initialize() {
    unsigned int item_count = sizeof(AP4_MetaData_KeyInfos)/sizeof(KeyInfo);
    KeyInfos.SetItemCount(item_count);
    for (unsigned int i=0; i<item_count; i++) {
        KeyInfos[i] = AP4_MetaData_KeyInfos[i];
    }
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::~AP4_MetaData()
{
    m_Entries.DeleteReferences();
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::Entry::AddToFileIlst(AP4_File& file, AP4_Ordinal index)
{
    // check that we have a correct entry
    if (m_Value == NULL) return AP4_ERROR_INVALID_STATE;

    // convert the entry into an atom
    AP4_Atom* atom;
    AP4_Result result = ToAtom(atom);
    if (AP4_FAILED(result)) return result;
    AP4_ContainerAtom* entry_atom = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
    if (entry_atom == NULL) {
        return AP4_ERROR_INVALID_FORMAT;
    }

    // look for the 'moov'
    AP4_Movie* movie = file.GetMovie();
    if (movie == NULL) return AP4_ERROR_INVALID_FORMAT;
    AP4_MoovAtom* moov = movie->GetMoovAtom();
    if (moov == NULL) return AP4_ERROR_INVALID_FORMAT;
    
    // look for 'udta', and create if it does not exist 
    AP4_ContainerAtom* udta = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov->FindChild("udta", true));
    if (udta == NULL) return AP4_ERROR_INTERNAL;
    
    // look for 'meta', and create if it does not exist ('meta' is a FULL atom)
    AP4_ContainerAtom* meta = AP4_DYNAMIC_CAST(AP4_ContainerAtom, udta->FindChild("meta", true, true));
    if (meta == NULL) return AP4_ERROR_INTERNAL;

    // look for a 'hdlr' atom type 'mdir'
    AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, meta->FindChild("hdlr"));
    if (hdlr == NULL) {
        hdlr = new AP4_HdlrAtom(AP4_HANDLER_TYPE_MDIR, "");
        meta->AddChild(hdlr);
    } else {
        if (hdlr->GetHandlerType() != AP4_HANDLER_TYPE_MDIR) {
            return AP4_ERROR_INVALID_FORMAT;
        }
    }

    // get/create the list of entries
    AP4_ContainerAtom* ilst = AP4_DYNAMIC_CAST(AP4_ContainerAtom, meta->FindChild("ilst", true));
    if (ilst == NULL) return AP4_ERROR_INTERNAL;
    
    // look if there is already a container for this entry
    AP4_ContainerAtom* existing = FindInIlst(ilst);
    if (existing == NULL) {
        // just add the one we have
        ilst->AddChild(entry_atom);
    } else {
        // add the entry's data to the existing entry
        AP4_DataAtom* data_atom = AP4_DYNAMIC_CAST(AP4_DataAtom, entry_atom->GetChild(AP4_ATOM_TYPE_DATA));
        if (data_atom == NULL) return AP4_ERROR_INTERNAL;
        entry_atom->RemoveChild(data_atom);
        existing->AddChild(data_atom, index);
        delete entry_atom;
    }
    
    return AP4_SUCCESS;    
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_IntegerMetaDataValue::ToBytes(AP4_DataBuffer& /* bytes */) const
{
    return AP4_ERROR_NOT_SUPPORTED;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfdAtom::AP4_DcfdAtom(AP4_UI32 duration) :
    AP4_Atom(AP4_ATOM_TYPE_DCFD, AP4_FULL_ATOM_HEADER_SIZE+4, 0, 0),
    m_Duration(duration)
{
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_3GppLocalizedStringAtom::WriteFields(AP4_ByteStream& stream)
{
    AP4_UI16 packed_language = ((m_Language[0]-0x60)<<10) |
                               ((m_Language[1]-0x60)<< 5) |
                               ((m_Language[2]-0x60));
    stream.WriteUI16(packed_language);
    AP4_Size payload_size = (AP4_UI32)GetSize()-GetHeaderSize();
    if (payload_size < 2) return AP4_ERROR_INVALID_FORMAT;
    AP4_Size value_size = m_Value.GetLength()+1;
    if (value_size > payload_size-2) {
        value_size = payload_size-2;
    }
    stream.Write(m_Value.GetChars(), value_size);
    for (unsigned int i=value_size; i<payload_size-2; i++) {
        stream.WriteUI08(0);
    }
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_AtomMetaDataValue::ToBytes(AP4_DataBuffer& bytes) const
{
    return m_DataAtom->LoadBytes(bytes);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::ParseMoov(AP4_MoovAtom* moov)
{
    // look for a 'meta' atom with 'hdlr' type 'mdir'
    AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, moov->FindChild("udta/meta/hdlr"));
    if (hdlr == NULL || hdlr->GetHandlerType() != AP4_HANDLER_TYPE_MDIR) return AP4_ERROR_NO_SUCH_ITEM;

    // get the list of entries
    AP4_ContainerAtom* ilst = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov->FindChild("udta/meta/ilst"));
    if (ilst == NULL) return AP4_ERROR_NO_SUCH_ITEM;
    
    AP4_List<AP4_Atom>::Item* ilst_item = ilst->GetChildren().FirstItem();
    while (ilst_item) {
        AP4_ContainerAtom* entry_atom = AP4_DYNAMIC_CAST(AP4_ContainerAtom, ilst_item->GetData()); 
        if (entry_atom) {
            AddIlstEntries(entry_atom, "meta");
        }
        ilst_item = ilst_item->GetNext();
    }
    
    return AP4_SUCCESS;
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::UnInitialize() {
    return AP4_MetaData::KeyInfos.Clear();
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DcfdAtom::AP4_DcfdAtom(AP4_UI08        version,
                           AP4_UI32        flags,
                           AP4_ByteStream& stream) :
    AP4_Atom(AP4_ATOM_TYPE_DCFD, AP4_FULL_ATOM_HEADER_SIZE+4, version, flags),
    m_Duration(0)
{
    stream.ReadUI32(m_Duration);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaDataStringAtom::WriteFields(AP4_ByteStream& stream)
{
    stream.WriteUI32(m_Reserved);
    return stream.Write(m_Value.GetChars(), m_Value.GetLength());
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_DataAtom::~AP4_DataAtom()
{
    delete(m_Source);
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaDataStringAtom::AP4_MetaDataStringAtom(Type type, AP4_UI32 size, AP4_ByteStream& stream) :
    AP4_Atom(type, size),
    m_Reserved(0),
    m_Value((AP4_Size)(size-AP4_ATOM_HEADER_SIZE-4))
{
    stream.ReadUI32(m_Reserved);
    stream.Read(m_Value.UseChars(), m_Value.GetLength());
}

Project: Bento4
File: Ap4MetaData.cpp

AP4_MetaData::ResolveKeyName(AP4_Atom::Type atom_type, AP4_String& value)
{
    const char* key_name = NULL;
    char        four_cc[5];

    // look for a match in the key infos
    for (unsigned int i=0; 
         i<sizeof(AP4_MetaData_KeyInfos)/sizeof(AP4_MetaData_KeyInfos[0]); 
         i++) {
        if (AP4_MetaData_KeyInfos[i].four_cc == atom_type) {
            key_name = AP4_MetaData_KeyInfos[i].name;
            break;
        }
    }
    if (key_name == NULL) {
        // this key was not found in the key infos, create a name for it
        AP4_FormatFourChars(four_cc, (AP4_UI32)atom_type);
        key_name = four_cc;
    }
    value = key_name;
    
    return AP4_SUCCESS;
}