diff --git a/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp b/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp index e4e97d01..c31e1076 100644 --- a/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp +++ b/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp @@ -152,7 +152,7 @@ ByteVector AttachedPictureFrame::renderFields() const { ByteVector data; - String::Type encoding = checkEncoding(d->description, d->textEncoding); + String::Type encoding = checkTextEncoding(d->description, d->textEncoding); data.append(char(encoding)); data.append(d->mimeType.data(String::Latin1)); diff --git a/taglib/mpeg/id3v2/frames/commentsframe.cpp b/taglib/mpeg/id3v2/frames/commentsframe.cpp index 406598d9..377a7ee3 100644 --- a/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/commentsframe.cpp @@ -155,8 +155,8 @@ ByteVector CommentsFrame::renderFields() const String::Type encoding = d->textEncoding; - encoding = checkEncoding(d->description, encoding); - encoding = checkEncoding(d->text, encoding); + encoding = checkTextEncoding(d->description, encoding); + encoding = checkTextEncoding(d->text, encoding); v.append(char(encoding)); v.append(d->language.size() == 3 ? d->language : "XXX"); diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 7c2ab909..cf724922 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -139,7 +139,7 @@ void TextIdentificationFrame::parseFields(const ByteVector &data) ByteVector TextIdentificationFrame::renderFields() const { - String::Type encoding = checkEncoding(d->fieldList, d->textEncoding); + String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding); ByteVector v; diff --git a/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/taglib/mpeg/id3v2/frames/urllinkframe.cpp index 7756c4ad..09edec40 100644 --- a/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ b/taglib/mpeg/id3v2/frames/urllinkframe.cpp @@ -175,7 +175,7 @@ ByteVector UserUrlLinkFrame::renderFields() const { ByteVector v; - String::Type encoding = checkEncoding(d->description, d->textEncoding); + String::Type encoding = checkTextEncoding(d->description, d->textEncoding); v.append(char(encoding)); v.append(d->description.data(encoding)); diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index ef8e3eed..59559178 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -230,19 +230,38 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static { + return checkEncoding(fields, encoding, 4); +} + +String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding, uint version) // static +{ + if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4) + return String::UTF16; + if(encoding != String::Latin1) return encoding; for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { if(!(*it).isLatin1()) { - debug("Frame::checkEncoding() -- Rendering using UTF8."); - return String::UTF8; + if(version == 4) { + debug("Frame::checkEncoding() -- Rendering using UTF8."); + return String::UTF8; + } + else { + debug("Frame::checkEncoding() -- Rendering using UTF16."); + return String::UTF16; + } } } return String::Latin1; } +String::Type Frame::checkTextEncoding(const StringList &fields, String::Type encoding) const +{ + return checkEncoding(fields, encoding, header()->version()); +} + //////////////////////////////////////////////////////////////////////////////// // Frame::Header class //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/mpeg/id3v2/id3v2frame.h b/taglib/mpeg/id3v2/id3v2frame.h index cd401c09..2b6bcd88 100644 --- a/taglib/mpeg/id3v2/id3v2frame.h +++ b/taglib/mpeg/id3v2/id3v2frame.h @@ -199,9 +199,29 @@ namespace TagLib { * Checks a the list of string values to see if they can be used with the * specified encoding and returns the recommended encoding. */ + // BIC: remove and make non-static static String::Type checkEncoding(const StringList &fields, String::Type encoding); + /*! + * Checks a the list of string values to see if they can be used with the + * specified encoding and returns the recommended encoding. This method + * also checks the ID3v2 version and makes sure the encoding can be used + * in the specified version. + */ + // BIC: remove and make non-static + static String::Type checkEncoding(const StringList &fields, + String::Type encoding, uint version); + + /*! + * Checks a the list of string values to see if they can be used with the + * specified encoding and returns the recommended encoding. This method + * also checks the ID3v2 version and makes sure the encoding can be used + * in the version specified by the frame's header. + */ + String::Type checkTextEncoding(const StringList &fields, + String::Type encoding) const; + private: Frame(const Frame &); Frame &operator=(const Frame &); diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 4276963d..3460168c 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -1,9 +1,12 @@ #include #include #include +// so evil :( +#define protected public #include #include #include +#undef protected #include #include #include @@ -33,6 +36,7 @@ class TestID3v2 : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestID3v2); CPPUNIT_TEST(testUnsynchDecode); + CPPUNIT_TEST(testDowngradeUTF8ForID3v23); CPPUNIT_TEST(testUTF16BEDelimiter); CPPUNIT_TEST(testUTF16Delimiter); CPPUNIT_TEST(testReadStringField); @@ -73,6 +77,20 @@ public: CPPUNIT_ASSERT_EQUAL(String("My babe just cares for me"), f.tag()->title()); } + void testDowngradeUTF8ForID3v23() + { + ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF8); + StringList sl; + sl.append("Foo"); + f.setText(sl); + f.header()->setVersion(3); + ByteVector data = f.render(); + CPPUNIT_ASSERT_EQUAL((unsigned int)(4+4+2+1+6+2), data.size()); + ID3v2::TextIdentificationFrame f2(data); + CPPUNIT_ASSERT_EQUAL(sl, f2.fieldList()); + CPPUNIT_ASSERT_EQUAL(String::UTF16, f2.textEncoding()); + } + void testUTF16BEDelimiter() { ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF16BE);