]> granicus.if.org Git - taglib/commitdiff
Enable FileRef to detect file types by the actual content of a stream.
authorTsuda Kageyu <tsuda.kageyu@gmail.com>
Fri, 3 Feb 2017 08:52:27 +0000 (17:52 +0900)
committerTsuda Kageyu <tsuda.kageyu@gmail.com>
Sat, 4 Feb 2017 14:31:08 +0000 (23:31 +0900)
FileRef doesn't work with ByteVectorStream as reported at #796, since ByteVectorStream is not associated with a file name and FileRef detects file types based on file extensions.
This commit makes FileRef to work with ByteVectorStream by enabling it to detect file types based on the actual content of a stream.

35 files changed:
taglib/ape/apefile.cpp
taglib/ape/apefile.h
taglib/asf/asffile.cpp
taglib/asf/asffile.h
taglib/fileref.cpp
taglib/fileref.h
taglib/flac/flacfile.cpp
taglib/flac/flacfile.h
taglib/mp4/mp4file.cpp
taglib/mp4/mp4file.h
taglib/mpc/mpcfile.cpp
taglib/mpc/mpcfile.h
taglib/mpeg/mpegfile.cpp
taglib/mpeg/mpegfile.h
taglib/mpeg/mpegheader.cpp
taglib/mpeg/mpegutils.h
taglib/ogg/flac/oggflacfile.cpp
taglib/ogg/flac/oggflacfile.h
taglib/ogg/opus/opusfile.cpp
taglib/ogg/opus/opusfile.h
taglib/ogg/speex/speexfile.cpp
taglib/ogg/speex/speexfile.h
taglib/ogg/vorbis/vorbisfile.cpp
taglib/ogg/vorbis/vorbisfile.h
taglib/riff/aiff/aifffile.cpp
taglib/riff/aiff/aifffile.h
taglib/riff/wav/wavfile.cpp
taglib/riff/wav/wavfile.h
taglib/tagutils.cpp
taglib/tagutils.h
taglib/trueaudio/trueaudiofile.cpp
taglib/trueaudio/trueaudiofile.h
taglib/wavpack/wavpackfile.cpp
taglib/wavpack/wavpackfile.h
tests/test_fileref.cpp

index 9f298aaf4ca1d8dad6a03670d7138b4e02026d6c..fb01e4a2e56cdab6362db4f1a0e71dbdbf986f21 100644 (file)
@@ -83,6 +83,18 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool APE::File::isValidStream(IOStream *stream)
+{
+  // An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
+
+  const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
+  return (buffer.find("MAC ") >= 0);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index cfb19ff739db254b7de7346f2a915df99327abbf..a7c870b468b1bb934b9dbedbab2e5e02dec6318d 100644 (file)
@@ -211,6 +211,15 @@ namespace TagLib {
        */
       bool hasID3v1Tag() const;
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as an APE
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index 8f39526582f2e6acd3e731fdf749397458b4e81b..c209a6408682b7e19b27bfe48ae757de912ff99e 100644 (file)
@@ -27,6 +27,7 @@
 #include <tbytevectorlist.h>
 #include <tpropertymap.h>
 #include <tstring.h>
+#include <tagutils.h>
 
 #include "asffile.h"
 #include "asftag.h"
@@ -473,6 +474,18 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
   }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool ASF::File::isValidStream(IOStream *stream)
+{
+  // An ASF file has to start with the designated GUID.
+
+  const ByteVector id = Utils::readHeader(stream, 16, false);
+  return (id == headerGuid);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index b674da79c1479507e0f548c1dfa0ba39fd9215bf..7df5a797c0db7bb0d6aaa8f2fb8db704e8cc904d 100644 (file)
@@ -115,6 +115,15 @@ namespace TagLib {
        */
       virtual bool save();
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as an ASF
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       void read();
 
index dca69f6a15235f8a37a524d56ec644350cac9090..cad1ae5067c4f0971c5f2a8888b7af420916d391 100644 (file)
@@ -28,6 +28,7 @@
  ***************************************************************************/
 
 #include <tfile.h>
+#include <tfilestream.h>
 #include <tstring.h>
 #include <tdebug.h>
 #include <trefcounter.h>
@@ -59,41 +60,14 @@ namespace
   typedef List<const FileRef::FileTypeResolver *> ResolverList;
   ResolverList fileTypeResolvers;
 
-  // Templatized internal functions. T should be String or IOStream*.
+  // Detect the file type by user-defined resolvers.
 
-  template <typename T>
-  FileName toFileName(T arg);
-
-  template <>
-  FileName toFileName<IOStream *>(IOStream *arg)
-  {
-    return arg->name();
-  }
-
-  template <>
-  FileName toFileName<FileName>(FileName arg)
-  {
-    return arg;
-  }
-
-  template <typename T>
-  File *resolveFileType(T arg, bool readProperties,
-                        AudioProperties::ReadStyle style);
-
-  template <>
-  File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
-                                    AudioProperties::ReadStyle style)
-  {
-    return 0;
-  }
-
-  template <>
-  File *resolveFileType<FileName>(FileName arg, bool readProperties,
-                                  AudioProperties::ReadStyle style)
+  File *detectByResolvers(FileName fileName, bool readAudioProperties,
+                          AudioProperties::ReadStyle audioPropertiesStyle)
   {
     ResolverList::ConstIterator it = fileTypeResolvers.begin();
     for(; it != fileTypeResolvers.end(); ++it) {
-      File *file = (*it)->createFile(arg, readProperties, style);
+      File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
       if(file)
         return file;
     }
@@ -101,18 +75,15 @@ namespace
     return 0;
   }
 
-  template <typename T>
-  File* createInternal(T arg, bool readAudioProperties,
-                       AudioProperties::ReadStyle audioPropertiesStyle)
-  {
-    File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
-    if(file)
-      return file;
+  // Detect the file type based on the file extension.
 
+  File* detectByExtension(IOStream *stream, bool readAudioProperties,
+                          AudioProperties::ReadStyle audioPropertiesStyle)
+  {
 #ifdef _WIN32
-    const String s = toFileName(arg).toString();
+    const String s = stream->name().toString();
 #else
-    const String s(toFileName(arg));
+    const String s(stream->name());
 #endif
 
     String ext;
@@ -127,49 +98,91 @@ namespace
     if(ext.isEmpty())
       return 0;
 
+    // .oga can be any audio in the Ogg container. So leave it to content-based detection.
+
     if(ext == "MP3")
-      return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+      return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
     if(ext == "OGG")
-      return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
-    if(ext == "OGA") {
-      /* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
-      File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
-      if(file->isValid())
-        return file;
-      delete file;
-      return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
-    }
+      return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "FLAC")
-      return new FLAC::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+      return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
     if(ext == "MPC")
-      return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "WV")
-      return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "SPX")
-      return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "OPUS")
-      return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "TTA")
-      return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
-      return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "WMA" || ext == "ASF")
-      return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
-      return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "WAV")
-      return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "APE")
-      return new APE::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
     // module, nst and wow are possible but uncommon extensions
     if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
-      return new Mod::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "S3M")
-      return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "IT")
-      return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
     if(ext == "XM")
-      return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
+      return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
+
+    return 0;
+  }
+
+  // Detect the file type based on the actual content of the stream.
+
+  File *detectByContent(IOStream *stream, bool readAudioProperties,
+                        AudioProperties::ReadStyle audioPropertiesStyle)
+  {
+    File *file = 0;
+
+    if(MPEG::File::isValidStream(stream))
+      file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+    else if(Ogg::Vorbis::File::isValidStream(stream))
+      file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(Ogg::FLAC::File::isValidStream(stream))
+      file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(FLAC::File::isValidStream(stream))
+      file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+    else if(MPC::File::isValidStream(stream))
+      file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(WavPack::File::isValidStream(stream))
+      file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(Ogg::Speex::File::isValidStream(stream))
+      file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(Ogg::Opus::File::isValidStream(stream))
+      file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(TrueAudio::File::isValidStream(stream))
+      file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(MP4::File::isValidStream(stream))
+      file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(ASF::File::isValidStream(stream))
+      file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(RIFF::AIFF::File::isValidStream(stream))
+      file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(RIFF::WAV::File::isValidStream(stream))
+      file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
+    else if(APE::File::isValidStream(stream))
+      file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
+
+    // isValidStream() only does a quick check, so double check the file here.
+
+    if(file) {
+      if(file->isValid())
+        return file;
+      else
+        delete file;
+    }
 
     return 0;
   }
@@ -178,15 +191,18 @@ namespace
 class FileRef::FileRefPrivate : public RefCounter
 {
 public:
-  FileRefPrivate(File *f) :
+  FileRefPrivate() :
     RefCounter(),
-    file(f) {}
+    file(0),
+    stream(0) {}
 
   ~FileRefPrivate() {
     delete file;
+    delete stream;
   }
 
-  File *file;
+  File     *file;
+  IOStream *stream;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -194,24 +210,27 @@ public:
 ////////////////////////////////////////////////////////////////////////////////
 
 FileRef::FileRef() :
-  d(new FileRefPrivate(0))
+  d(new FileRefPrivate())
 {
 }
 
 FileRef::FileRef(FileName fileName, bool readAudioProperties,
                  AudioProperties::ReadStyle audioPropertiesStyle) :
-  d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle)))
+  d(new FileRefPrivate())
 {
+  parse(fileName, readAudioProperties, audioPropertiesStyle);
 }
 
 FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
-  d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
+  d(new FileRefPrivate())
 {
+  parse(stream, readAudioProperties, audioPropertiesStyle);
 }
 
 FileRef::FileRef(File *file) :
-  d(new FileRefPrivate(file))
+  d(new FileRefPrivate())
 {
+  d->file = file;
 }
 
 FileRef::FileRef(const FileRef &ref) :
@@ -331,5 +350,53 @@ bool FileRef::operator!=(const FileRef &ref) const
 File *FileRef::create(FileName fileName, bool readAudioProperties,
                       AudioProperties::ReadStyle audioPropertiesStyle) // static
 {
-  return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FileRef::parse(FileName fileName, bool readAudioProperties,
+                    AudioProperties::ReadStyle audioPropertiesStyle)
+{
+  // Try user-defined resolvers.
+
+  d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
+  if(d->file)
+    return;
+
+  // Try to resolve file types based on the file extension.
+
+  d->stream = new FileStream(fileName);
+  d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
+  if(d->file)
+    return;
+
+  // At last, try to resolve file types based on the actual content.
+
+  d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
+  if(d->file)
+    return;
+
+  // Stream have to be closed here if failed to resolve file types.
+
+  delete d->stream;
+  d->stream = 0;
+}
+
+void FileRef::parse(IOStream *stream, bool readAudioProperties,
+                    AudioProperties::ReadStyle audioPropertiesStyle)
+{
+  // User-defined resolvers won't work with a stream.
+
+  // Try to resolve file types based on the file extension.
+
+  d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
+  if(d->file)
+    return;
+
+  // At last, try to resolve file types based on the actual content of the file.
+
+  d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
 }
index a12b1a9b10d30d51a577a03a22c52e8a44492312..c36f54cbd33af8b5ebe4ea317959512111bd2732 100644 (file)
@@ -274,8 +274,10 @@ namespace TagLib {
                         bool readAudioProperties = true,
                         AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
 
-
   private:
+    void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
+    void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
+
     class FileRefPrivate;
     FileRefPrivate *d;
   };
index b6b72960d43ddc81f8578ad85c07baf4ee4c187d..780ab1c32cbfee9a4ac01c62255c1ec6686bbb09 100644 (file)
@@ -95,6 +95,18 @@ public:
   bool scanned;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool FLAC::File::isValidStream(IOStream *stream)
+{
+  // A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
+
+  const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
+  return (buffer.find("fLaC") >= 0);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 65d856792b608ce099bc157b168e72046359e120..56755ec5f67efcd06f0c50fc037b8f8f3ad249c0 100644 (file)
@@ -318,6 +318,15 @@ namespace TagLib {
        */
       bool hasID3v2Tag() const;
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as a FLAC
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index 3733fb40dce58e5d067e52ba3323ac5d93860fc0..f06ae0684fc3cf234a64712e37a38c76aca86821 100644 (file)
@@ -26,6 +26,8 @@
 #include <tdebug.h>
 #include <tstring.h>
 #include <tpropertymap.h>
+#include <tagutils.h>
+
 #include "mp4atom.h"
 #include "mp4tag.h"
 #include "mp4file.h"
@@ -69,6 +71,22 @@ public:
   MP4::Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool MP4::File::isValidStream(IOStream *stream)
+{
+  // An MP4 file has to have an "ftyp" box first.
+
+  const ByteVector id = Utils::readHeader(stream, 8, false);
+  return id.containsAt("ftyp", 4);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
 MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
   TagLib::File(file),
   d(new FilePrivate())
index 3840bd0220f9fac83762602679d2d3f14a475e53..ca2f70de198870336a012281f36ca87237cf950c 100644 (file)
@@ -120,6 +120,15 @@ namespace TagLib {
        */
       bool hasMP4Tag() const;
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as an ASF
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       void read(bool readProperties);
 
index daf24c8fb7be565c20cb9e944ad1b367b763aa78..b8544bb867cf0da399552c788ad494c92021806c 100644 (file)
@@ -75,6 +75,19 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool MPC::File::isValidStream(IOStream *stream)
+{
+  // A newer MPC file has to start with "MPCK" or "MP+", but older files don't
+  // have keys to do a quick check.
+
+  const ByteVector id = Utils::readHeader(stream, 4, false);
+  return (id == "MPCK" || id.startsWith("MP+"));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 541724dc206c4f705733321dd40c20bee462a53e..43759ea53ee0fa9ea3c3e463f05a94d00fea4944 100644 (file)
@@ -214,6 +214,15 @@ namespace TagLib {
        */
       bool hasAPETag() const;
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as an MPC
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index 74bf779b62ed63051f8d88c985682a23c3ed7197..74f304f8575b9df783f467d6da76b31e4d3adf7f 100644 (file)
@@ -76,6 +76,63 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+  // Dummy file class to make a stream work with MPEG::Header.
+
+  class AdapterFile : public TagLib::File
+  {
+  public:
+    AdapterFile(IOStream *stream) : File(stream) {}
+
+    Tag *tag() const { return 0; }
+    AudioProperties *audioProperties() const { return 0; }
+    bool save() { return false; }
+  };
+}
+
+bool MPEG::File::isValidStream(IOStream *stream)
+{
+  if(!stream || !stream->isOpen())
+    return false;
+
+  // An MPEG file has MPEG frame headers. An ID3v2 tag may precede.
+
+  // MPEG frame headers are really confusing with irrelevant binary data.
+  // So we check if a frame header is really valid.
+
+  const long originalPosition = stream->tell();
+
+  long bufferOffset = 0;
+
+  stream->seek(0);
+  const ByteVector data = stream->readBlock(ID3v2::Header::size());
+  if(data.startsWith(ID3v2::Header::fileIdentifier()))
+    bufferOffset = ID3v2::Header(data).completeTagSize();
+
+  stream->seek(bufferOffset);
+  const ByteVector buffer = stream->readBlock(bufferSize());
+
+  AdapterFile file(stream);
+
+  for(unsigned int i = 0; i < buffer.size() - 1; ++i) {
+    if(isFrameSync(buffer, i)) {
+      const Header header(&file, bufferOffset + i, true);
+      if(header.isValid()) {
+        stream->seek(originalPosition);
+        return true;
+      }
+    }
+  }
+
+  stream->seek(originalPosition);
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index e9e9738794f4354a9811020367d86353b65eca9c..71410fe721f2c18a33b2732f1f9745fe7e4a8daa 100644 (file)
@@ -370,6 +370,15 @@ namespace TagLib {
        */
       bool hasAPETag() const;
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as an MPEG
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index 610b0320a6360defb0ced9c57df52a0048e7309a..5a5015d612d3c3b7343babe85215eed2551b8286 100644 (file)
@@ -197,10 +197,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
     d->version = Version2;
   else if(versionBits == 3)
     d->version = Version1;
-  else {
-    debug("MPEG::Header::parse() -- Invalid MPEG version bits.");
+  else
     return;
-  }
 
   // Set the MPEG layer
 
@@ -212,10 +210,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
     d->layer = 2;
   else if(layerBits == 3)
     d->layer = 1;
-  else {
-    debug("MPEG::Header::parse() -- Invalid MPEG layer bits.");
+  else
     return;
-  }
 
   d->protectionEnabled = (static_cast<unsigned char>(data[1] & 0x01) == 0);
 
@@ -244,10 +240,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
 
   d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex];
 
-  if(d->bitrate == 0) {
-    debug("MPEG::Header::parse() -- Invalid bit rate.");
+  if(d->bitrate == 0)
     return;
-  }
 
   // Set the sample rate
 
@@ -264,7 +258,6 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
   d->sampleRate = sampleRates[d->version][samplerateIndex];
 
   if(d->sampleRate == 0) {
-    debug("MPEG::Header::parse() -- Invalid sample rate.");
     return;
   }
 
@@ -311,20 +304,16 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
     file->seek(offset + d->frameLength);
     const ByteVector nextData = file->readBlock(4);
 
-    if(nextData.size() < 4) {
-      debug("MPEG::Header::parse() -- Could not read the next frame header.");
+    if(nextData.size() < 4)
       return;
-    }
 
     const unsigned int HeaderMask = 0xfffe0c00;
 
     const unsigned int header     = data.toUInt(0, true)     & HeaderMask;
     const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask;
 
-    if(header != nextHeader) {
-      debug("MPEG::Header::parse() -- The next frame was not consistent with this frame.");
+    if(header != nextHeader)
       return;
-    }
   }
 
   // Now that we're done parsing, set this to be a valid frame.
index 1cee918a6cddc78f3d3832adc2770c0a4484b031..31b45a43b6308bc5c8a0d94709812c6b5ff81a1d 100644 (file)
@@ -45,12 +45,12 @@ namespace TagLib
        * \note This does not check the length of the vector, since this is an
        * internal utility function.
        */
-      inline bool isFrameSync(const ByteVector &bytes)
+      inline bool isFrameSync(const ByteVector &bytes, unsigned int offset = 0)
       {
         // 0xFF in the second byte is possible in theory, but it's very unlikely.
 
-        const unsigned char b1 = bytes[0];
-        const unsigned char b2 = bytes[1];
+        const unsigned char b1 = bytes[offset + 0];
+        const unsigned char b2 = bytes[offset + 1];
         return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0);
       }
 
index fe4d8830addc41b1bc5fa841b36675d83af948b9..e294d4112f848177b364d7d4cb9e533587631cb2 100644 (file)
@@ -27,6 +27,7 @@
 #include <tstring.h>
 #include <tdebug.h>
 #include <tpropertymap.h>
+#include <tagutils.h>
 
 #include <xiphcomment.h>
 #include "oggflacfile.h"
@@ -65,6 +66,18 @@ public:
   int commentPacket;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::FLAC::File::isValidStream(IOStream *stream)
+{
+  // An Ogg FLAC file has IDs "OggS" and "fLaC" somewhere.
+
+  const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+  return (buffer.find("OggS") >= 0 && buffer.find("fLaC") >= 0);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 05762f9b828b415fafc6fddee0ffdf708ffb04e3..5f4313da64fe89073a5ba1a8f5e0682d5c99f2dd 100644 (file)
@@ -143,6 +143,14 @@ namespace TagLib {
        */
       bool hasXiphComment() const;
 
+      /*!
+       * Check if the given \a stream can be opened as an Ogg FLAC file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index ff1bfe2d8f7233e65448cad30046674fbe7ae278..85d995bc907de84a58ce9693470d0e790e8554c3 100644 (file)
@@ -30,6 +30,7 @@
 #include <tstring.h>
 #include <tdebug.h>
 #include <tpropertymap.h>
+#include <tagutils.h>
 
 #include "opusfile.h"
 
@@ -53,6 +54,18 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::Opus::File::isValidStream(IOStream *stream)
+{
+  // An Opus file has IDs "OggS" and "OpusHead" somewhere.
+
+  const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+  return (buffer.find("OggS") >= 0 && buffer.find("OpusHead") >= 0);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index b718f0d77e25308cc74f49338de2e075760c5759..60f60c3fc90e6f2c29d404a95692dd231f51d425 100644 (file)
@@ -113,6 +113,15 @@ namespace TagLib {
          */
         virtual bool save();
 
+        /*!
+         * Returns whether or not the given \a stream can be opened as an Opus
+         * file.
+         *
+         * \note This method is designed to do a quick check.  The result may
+         * not necessarily be correct.
+         */
+        static bool isValidStream(IOStream *stream);
+
       private:
         File(const File &);
         File &operator=(const File &);
index 7af71d503d18004ae82af354ddb1a0288744da37..d336877482643b865defe1199ab52949ace73f98 100644 (file)
@@ -30,6 +30,7 @@
 #include <tstring.h>
 #include <tdebug.h>
 #include <tpropertymap.h>
+#include <tagutils.h>
 
 #include "speexfile.h"
 
@@ -53,6 +54,18 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::Speex::File::isValidStream(IOStream *stream)
+{
+  // A Speex file has IDs "OggS" and "Speex   " somewhere.
+
+  const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+  return (buffer.find("OggS") >= 0 && buffer.find("Speex   ") >= 0);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 58b001dd4d7b04e15f691bae62361ab796d35528..eda347e0328cfef9cc43722bbd2798ed7bb906a0 100644 (file)
@@ -113,6 +113,15 @@ namespace TagLib {
          */
         virtual bool save();
 
+        /*!
+         * Returns whether or not the given \a stream can be opened as a Speex
+         * file.
+         *
+         * \note This method is designed to do a quick check.  The result may
+         * not necessarily be correct.
+         */
+        static bool isValidStream(IOStream *stream);
+
       private:
         File(const File &);
         File &operator=(const File &);
index 2773bd3ba3fa20136412c4c5105b113f12071724..7f02fff53b825bcc81b6c7578b12a83262ff2cff 100644 (file)
 #include <tstring.h>
 #include <tdebug.h>
 #include <tpropertymap.h>
+#include <tagutils.h>
 
 #include "vorbisfile.h"
 
-
 using namespace TagLib;
 
 class Vorbis::File::FilePrivate
@@ -59,6 +59,18 @@ namespace TagLib {
   static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Vorbis::File::isValidStream(IOStream *stream)
+{
+  // An Ogg Vorbis file has IDs "OggS" and "\x01vorbis" somewhere.
+
+  const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+  return (buffer.find("OggS") >= 0 && buffer.find("\x01vorbis") >= 0);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 9e71dcbe83a8731aa7e3bdc598a9525b7d408127..9f7cb7e4d26c79a347e1bb1d8258353ff1dcaeb3 100644 (file)
@@ -121,6 +121,14 @@ namespace TagLib {
        */
       virtual bool save();
 
+      /*!
+       * Check if the given \a stream can be opened as an Ogg Vorbis file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index 1a29938ce541b76a4567788c6d50821a3a2c890a..72705a9a34fa3ffdc7fc5bb272cf9d9547d5d78a 100644 (file)
@@ -28,6 +28,7 @@
 #include <id3v2tag.h>
 #include <tstringlist.h>
 #include <tpropertymap.h>
+#include <tagutils.h>
 
 #include "aifffile.h"
 
@@ -53,6 +54,18 @@ public:
   bool hasID3v2;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool RIFF::AIFF::File::isValidStream(IOStream *stream)
+{
+  // An AIFF file has to start with "FORM????AIFF" or "FORM????AIFC".
+
+  const ByteVector id = Utils::readHeader(stream, 12, false);
+  return (id.startsWith("FORM") && (id.containsAt("AIFF", 8) || id.containsAt("AIFC", 8)));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index a79d76b2be0af8c7b38538fcb5383a21d36e633e..611b7338ad52375a7a777ea4111fe319d9b173a9 100644 (file)
@@ -126,6 +126,14 @@ namespace TagLib {
          */
         bool hasID3v2Tag() const;
 
+        /*!
+         * Check if the given \a stream can be opened as an AIFF file.
+         *
+         * \note This method is designed to do a quick check.  The result may
+         * not necessarily be correct.
+         */
+        static bool isValidStream(IOStream *stream);
+
       private:
         File(const File &);
         File &operator=(const File &);
index 79ff91675ffcad34598891f18eb48ed6edd5bc60..5e92b29e59e96e322b7e0345402142f09f119090 100644 (file)
  *   http://www.mozilla.org/MPL/                                           *
  ***************************************************************************/
 
-#include "tbytevector.h"
-#include "tdebug.h"
-#include "tstringlist.h"
-#include "tpropertymap.h"
+#include <tbytevector.h>
+#include <tdebug.h>
+#include <tstringlist.h>
+#include <tpropertymap.h>
+#include <tagutils.h>
 
 #include "wavfile.h"
 #include "id3v2tag.h"
@@ -60,6 +61,18 @@ public:
   bool hasInfo;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool RIFF::WAV::File::isValidStream(IOStream *stream)
+{
+  // A WAV file has to start with "RIFF????WAVE".
+
+  const ByteVector id = Utils::readHeader(stream, 12, false);
+  return (id.startsWith("RIFF") && id.containsAt("WAVE", 8));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 80f17a852aa0f750eef733389881a5c506bb515f..47e8116d1eb44ffaf668e93cf4363f20e7c51774 100644 (file)
@@ -175,6 +175,15 @@ namespace TagLib {
          */
         bool hasInfoTag() const;
 
+        /*!
+         * Returns whether or not the given \a stream can be opened as a WAV
+         * file.
+         *
+         * \note This method is designed to do a quick check.  The result may
+         * not necessarily be correct.
+         */
+        static bool isValidStream(IOStream *stream);
+
       private:
         File(const File &);
         File &operator=(const File &);
index dc047040ca470c20c8d20fd0f518dfbe7c38ea96..e27fd8bd1338d4c42924590cc0406b21995803e2 100644 (file)
@@ -77,3 +77,25 @@ long Utils::findAPE(File *file, long id3v1Location)
 
   return -1;
 }
+
+ByteVector TagLib::Utils::readHeader(IOStream *stream, unsigned int length, bool skipID3v2)
+{
+  if(!stream || !stream->isOpen())
+    return ByteVector();
+
+  const long originalPosition = stream->tell();
+  long bufferOffset = 0;
+
+  if(skipID3v2) {
+    stream->seek(0);
+    const ByteVector data = stream->readBlock(ID3v2::Header::size());
+    if(data.startsWith(ID3v2::Header::fileIdentifier()))
+      bufferOffset = ID3v2::Header(data).completeTagSize();
+  }
+
+  stream->seek(bufferOffset);
+  const ByteVector header = stream->readBlock(length);
+  stream->seek(originalPosition);
+
+  return header;
+}
index fb11d1e06130cffd606144dc662044e5725ccecf..4014c673a43fc30349a48a6561a1612e022bcf76 100644 (file)
 
 #ifndef DO_NOT_DOCUMENT  // tell Doxygen not to document this header
 
+#include <tbytevector.h>
+
 namespace TagLib {
 
   class File;
+  class IOStream;
 
   namespace Utils {
 
@@ -41,6 +44,8 @@ namespace TagLib {
     long findID3v2(File *file);
 
     long findAPE(File *file, long id3v1Location);
+
+    ByteVector readHeader(IOStream *stream, unsigned int length, bool skipID3v2);
   }
 }
 
index fc123ba34eb94007b1ce0218a5c99abbc46c3f32..c9b62bd7b4d660577e7d2b8904ad7bdfe4aee700 100644 (file)
@@ -73,6 +73,18 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool TrueAudio::File::isValidStream(IOStream *stream)
+{
+  // A TrueAudio file has to start with "TTA". An ID3v2 tag may precede.
+
+  const ByteVector id = Utils::readHeader(stream, 3, true);
+  return (id == "TTA");
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 4bcb722af664a8493d18d2ee4566c803b33bfe51..c5b05d9fe934f7963f31ff991373c229c04bc4fa 100644 (file)
@@ -235,6 +235,15 @@ namespace TagLib {
        */
       bool hasID3v2Tag() const;
 
+      /*!
+       * Returns whether or not the given \a stream can be opened as a TrueAudio
+       * file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index ef92f4bdfc812657820e14bbf4ebf5b4e1fc394b..03f5ee0772ca60bfc8ec5d02cf7bdc27dfa3d564 100644 (file)
@@ -71,6 +71,18 @@ public:
   Properties *properties;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool WavPack::File::isValidStream(IOStream *stream)
+{
+  // A WavPack file has to start with "wvpk".
+
+  const ByteVector id = Utils::readHeader(stream, 4, false);
+  return (id == "wvpk");
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
index 7e0bd27a9a7520c4767e8aa88bccc8adfb25ca48..14bc823bddedb73a6e062f92c4360db4537f6eaa 100644 (file)
@@ -200,6 +200,14 @@ namespace TagLib {
        */
       bool hasAPETag() const;
 
+      /*!
+       * Check if the given \a stream can be opened as a WavPack file.
+       *
+       * \note This method is designed to do a quick check.  The result may
+       * not necessarily be correct.
+       */
+      static bool isValidStream(IOStream *stream);
+
     private:
       File(const File &);
       File &operator=(const File &);
index 1b899975c44a1bfd55a937653b8bcf6f757bd4ac..c0d171bea874c3ef7c2dcbf9f9531404bb20151c 100644 (file)
@@ -1,27 +1,27 @@
 /***************************************************************************
-    copyright           : (C) 2007 by Lukas Lalinsky
-    email               : lukas@oxygene.sk
- ***************************************************************************/
+copyright           : (C) 2007 by Lukas Lalinsky
+email               : lukas@oxygene.sk
+***************************************************************************/
 
 /***************************************************************************
- *   This library is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU Lesser General Public License version   *
- *   2.1 as published by the Free Software Foundation.                     *
- *                                                                         *
- *   This library is distributed in the hope that it will be useful, but   *
- *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
- *   Lesser General Public License for more details.                       *
- *                                                                         *
- *   You should have received a copy of the GNU Lesser General Public      *
- *   License along with this library; if not, write to the Free Software   *
- *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
- *   02110-1301  USA                                                       *
- *                                                                         *
- *   Alternatively, this file is available under the Mozilla Public        *
- *   License Version 1.1.  You may obtain a copy of the License at         *
- *   http://www.mozilla.org/MPL/                                           *
- ***************************************************************************/
+*   This library is free software; you can redistribute it and/or modify  *
+*   it under the terms of the GNU Lesser General Public License version   *
+*   2.1 as published by the Free Software Foundation.                     *
+*                                                                         *
+*   This library is distributed in the hope that it will be useful, but   *
+*   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+*   Lesser General Public License for more details.                       *
+*                                                                         *
+*   You should have received a copy of the GNU Lesser General Public      *
+*   License along with this library; if not, write to the Free Software   *
+*   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
+*   02110-1301  USA                                                       *
+*                                                                         *
+*   Alternatively, this file is available under the Mozilla Public        *
+*   License Version 1.1.  You may obtain a copy of the License at         *
+*   http://www.mozilla.org/MPL/                                           *
+***************************************************************************/
 
 #include <string>
 #include <stdio.h>
 #include <wavfile.h>
 #include <apefile.h>
 #include <aifffile.h>
+#include <tfilestream.h>
+#include <tbytevectorstream.h>
 #include <cppunit/extensions/HelperMacros.h>
 #include "utils.h"
-#include <tfilestream.h>
 
 using namespace std;
 using namespace TagLib;
@@ -129,6 +130,7 @@ public:
       CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
       CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
     }
+
     {
       FileStream fs(newname.c_str());
       FileRef f(&fs);
@@ -140,6 +142,64 @@ public:
       CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm"));
       CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
       CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
+      f.tag()->setArtist("test artist");
+      f.tag()->setTitle("test title");
+      f.tag()->setGenre("Test!");
+      f.tag()->setAlbum("albummmm");
+      f.tag()->setTrack(5);
+      f.tag()->setYear(2020);
+      f.save();
+    }
+
+    ByteVector fileContent;
+    {
+      FileStream fs(newname.c_str());
+      FileRef f(&fs);
+      CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
+      CPPUNIT_ASSERT(!f.isNull());
+      CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5);
+      CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020);
+
+      fs.seek(0);
+      fileContent = fs.readBlock(fs.length());
+    }
+
+    {
+      ByteVectorStream bs(fileContent);
+      FileRef f(&bs);
+      CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
+      CPPUNIT_ASSERT(!f.isNull());
+      CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5);
+      CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020);
+      f.tag()->setArtist("ttest artist");
+      f.tag()->setTitle("ytest title");
+      f.tag()->setGenre("uTest!");
+      f.tag()->setAlbum("ialbummmm");
+      f.tag()->setTrack(7);
+      f.tag()->setYear(2080);
+      f.save();
+
+      fileContent = *bs.data();
+    }
+    {
+      ByteVectorStream bs(fileContent);
+      FileRef f(&bs);
+      CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
+      CPPUNIT_ASSERT(!f.isNull());
+      CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm"));
+      CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
+      CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
     }
   }