aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--taglib/ape/apefile.cpp12
-rw-r--r--taglib/ape/apefile.h9
-rw-r--r--taglib/asf/asffile.cpp13
-rw-r--r--taglib/asf/asffile.h9
-rw-r--r--taglib/fileref.cpp213
-rw-r--r--taglib/fileref.h4
-rw-r--r--taglib/flac/flacfile.cpp12
-rw-r--r--taglib/flac/flacfile.h9
-rw-r--r--taglib/mp4/mp4file.cpp18
-rw-r--r--taglib/mp4/mp4file.h9
-rw-r--r--taglib/mpc/mpcfile.cpp13
-rw-r--r--taglib/mpc/mpcfile.h9
-rw-r--r--taglib/mpeg/mpegfile.cpp57
-rw-r--r--taglib/mpeg/mpegfile.h9
-rw-r--r--taglib/mpeg/mpegheader.cpp21
-rw-r--r--taglib/mpeg/mpegutils.h6
-rw-r--r--taglib/ogg/flac/oggflacfile.cpp13
-rw-r--r--taglib/ogg/flac/oggflacfile.h8
-rw-r--r--taglib/ogg/opus/opusfile.cpp13
-rw-r--r--taglib/ogg/opus/opusfile.h9
-rw-r--r--taglib/ogg/speex/speexfile.cpp13
-rw-r--r--taglib/ogg/speex/speexfile.h9
-rw-r--r--taglib/ogg/vorbis/vorbisfile.cpp14
-rw-r--r--taglib/ogg/vorbis/vorbisfile.h8
-rw-r--r--taglib/riff/aiff/aifffile.cpp13
-rw-r--r--taglib/riff/aiff/aifffile.h8
-rw-r--r--taglib/riff/wav/wavfile.cpp21
-rw-r--r--taglib/riff/wav/wavfile.h9
-rw-r--r--taglib/tagutils.cpp22
-rw-r--r--taglib/tagutils.h5
-rw-r--r--taglib/trueaudio/trueaudiofile.cpp12
-rw-r--r--taglib/trueaudio/trueaudiofile.h9
-rw-r--r--taglib/wavpack/wavpackfile.cpp12
-rw-r--r--taglib/wavpack/wavpackfile.h8
-rw-r--r--tests/test_fileref.cpp104
35 files changed, 613 insertions, 120 deletions
diff --git a/taglib/ape/apefile.cpp b/taglib/ape/apefile.cpp
index 9f298aaf..fb01e4a2 100644
--- a/taglib/ape/apefile.cpp
+++ b/taglib/ape/apefile.cpp
@@ -84,6 +84,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h
index cfb19ff7..a7c870b4 100644
--- a/taglib/ape/apefile.h
+++ b/taglib/ape/apefile.h
@@ -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 &);
diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp
index 8f395265..c209a640 100644
--- a/taglib/asf/asffile.cpp
+++ b/taglib/asf/asffile.cpp
@@ -27,6 +27,7 @@
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
+#include <tagutils.h>
#include "asffile.h"
#include "asftag.h"
@@ -474,6 +475,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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h
index b674da79..7df5a797 100644
--- a/taglib/asf/asffile.h
+++ b/taglib/asf/asffile.h
@@ -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();
diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp
index dca69f6a..cad1ae50 100644
--- a/taglib/fileref.cpp
+++ b/taglib/fileref.cpp
@@ -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);
}
diff --git a/taglib/fileref.h b/taglib/fileref.h
index a12b1a9b..c36f54cb 100644
--- a/taglib/fileref.h
+++ b/taglib/fileref.h
@@ -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;
};
diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp
index b6b72960..780ab1c3 100644
--- a/taglib/flac/flacfile.cpp
+++ b/taglib/flac/flacfile.cpp
@@ -96,6 +96,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/flac/flacfile.h b/taglib/flac/flacfile.h
index 65d85679..56755ec5 100644
--- a/taglib/flac/flacfile.h
+++ b/taglib/flac/flacfile.h
@@ -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 &);
diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp
index 3733fb40..f06ae068 100644
--- a/taglib/mp4/mp4file.cpp
+++ b/taglib/mp4/mp4file.cpp
@@ -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())
diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h
index 3840bd02..ca2f70de 100644
--- a/taglib/mp4/mp4file.h
+++ b/taglib/mp4/mp4file.h
@@ -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);
diff --git a/taglib/mpc/mpcfile.cpp b/taglib/mpc/mpcfile.cpp
index daf24c8f..b8544bb8 100644
--- a/taglib/mpc/mpcfile.cpp
+++ b/taglib/mpc/mpcfile.cpp
@@ -76,6 +76,19 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/mpc/mpcfile.h b/taglib/mpc/mpcfile.h
index 541724dc..43759ea5 100644
--- a/taglib/mpc/mpcfile.h
+++ b/taglib/mpc/mpcfile.h
@@ -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 &);
diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp
index 74bf779b..74f304f8 100644
--- a/taglib/mpeg/mpegfile.cpp
+++ b/taglib/mpeg/mpegfile.cpp
@@ -77,6 +77,63 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h
index e9e97387..71410fe7 100644
--- a/taglib/mpeg/mpegfile.h
+++ b/taglib/mpeg/mpegfile.h
@@ -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 &);
diff --git a/taglib/mpeg/mpegheader.cpp b/taglib/mpeg/mpegheader.cpp
index 610b0320..5a5015d6 100644
--- a/taglib/mpeg/mpegheader.cpp
+++ b/taglib/mpeg/mpegheader.cpp
@@ -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.
diff --git a/taglib/mpeg/mpegutils.h b/taglib/mpeg/mpegutils.h
index 1cee918a..31b45a43 100644
--- a/taglib/mpeg/mpegutils.h
+++ b/taglib/mpeg/mpegutils.h
@@ -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);
}
diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp
index fe4d8830..e294d411 100644
--- a/taglib/ogg/flac/oggflacfile.cpp
+++ b/taglib/ogg/flac/oggflacfile.cpp
@@ -27,6 +27,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include <xiphcomment.h>
#include "oggflacfile.h"
@@ -66,6 +67,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h
index 05762f9b..5f4313da 100644
--- a/taglib/ogg/flac/oggflacfile.h
+++ b/taglib/ogg/flac/oggflacfile.h
@@ -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 &);
diff --git a/taglib/ogg/opus/opusfile.cpp b/taglib/ogg/opus/opusfile.cpp
index ff1bfe2d..85d995bc 100644
--- a/taglib/ogg/opus/opusfile.cpp
+++ b/taglib/ogg/opus/opusfile.cpp
@@ -30,6 +30,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "opusfile.h"
@@ -54,6 +55,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/opus/opusfile.h b/taglib/ogg/opus/opusfile.h
index b718f0d7..60f60c3f 100644
--- a/taglib/ogg/opus/opusfile.h
+++ b/taglib/ogg/opus/opusfile.h
@@ -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 &);
diff --git a/taglib/ogg/speex/speexfile.cpp b/taglib/ogg/speex/speexfile.cpp
index 7af71d50..d3368774 100644
--- a/taglib/ogg/speex/speexfile.cpp
+++ b/taglib/ogg/speex/speexfile.cpp
@@ -30,6 +30,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "speexfile.h"
@@ -54,6 +55,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/speex/speexfile.h b/taglib/ogg/speex/speexfile.h
index 58b001dd..eda347e0 100644
--- a/taglib/ogg/speex/speexfile.h
+++ b/taglib/ogg/speex/speexfile.h
@@ -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 &);
diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp
index 2773bd3b..7f02fff5 100644
--- a/taglib/ogg/vorbis/vorbisfile.cpp
+++ b/taglib/ogg/vorbis/vorbisfile.cpp
@@ -28,10 +28,10 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "vorbisfile.h"
-
using namespace TagLib;
class Vorbis::File::FilePrivate
@@ -60,6 +60,18 @@ namespace TagLib {
}
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h
index 9e71dcbe..9f7cb7e4 100644
--- a/taglib/ogg/vorbis/vorbisfile.h
+++ b/taglib/ogg/vorbis/vorbisfile.h
@@ -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 &);
diff --git a/taglib/riff/aiff/aifffile.cpp b/taglib/riff/aiff/aifffile.cpp
index 1a29938c..72705a9a 100644
--- a/taglib/riff/aiff/aifffile.cpp
+++ b/taglib/riff/aiff/aifffile.cpp
@@ -28,6 +28,7 @@
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "aifffile.h"
@@ -54,6 +55,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/riff/aiff/aifffile.h b/taglib/riff/aiff/aifffile.h
index a79d76b2..611b7338 100644
--- a/taglib/riff/aiff/aifffile.h
+++ b/taglib/riff/aiff/aifffile.h
@@ -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 &);
diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp
index 79ff9167..5e92b29e 100644
--- a/taglib/riff/wav/wavfile.cpp
+++ b/taglib/riff/wav/wavfile.cpp
@@ -23,10 +23,11 @@
* 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"
@@ -61,6 +62,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h
index 80f17a85..47e8116d 100644
--- a/taglib/riff/wav/wavfile.h
+++ b/taglib/riff/wav/wavfile.h
@@ -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 &);
diff --git a/taglib/tagutils.cpp b/taglib/tagutils.cpp
index dc047040..e27fd8bd 100644
--- a/taglib/tagutils.cpp
+++ b/taglib/tagutils.cpp
@@ -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;
+}
diff --git a/taglib/tagutils.h b/taglib/tagutils.h
index fb11d1e0..4014c673 100644
--- a/taglib/tagutils.h
+++ b/taglib/tagutils.h
@@ -30,9 +30,12 @@
#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);
}
}
diff --git a/taglib/trueaudio/trueaudiofile.cpp b/taglib/trueaudio/trueaudiofile.cpp
index fc123ba3..c9b62bd7 100644
--- a/taglib/trueaudio/trueaudiofile.cpp
+++ b/taglib/trueaudio/trueaudiofile.cpp
@@ -74,6 +74,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/trueaudio/trueaudiofile.h b/taglib/trueaudio/trueaudiofile.h
index 4bcb722a..c5b05d9f 100644
--- a/taglib/trueaudio/trueaudiofile.h
+++ b/taglib/trueaudio/trueaudiofile.h
@@ -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 &);
diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp
index ef92f4bd..03f5ee07 100644
--- a/taglib/wavpack/wavpackfile.cpp
+++ b/taglib/wavpack/wavpackfile.cpp
@@ -72,6 +72,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// 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
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/wavpack/wavpackfile.h b/taglib/wavpack/wavpackfile.h
index 7e0bd27a..14bc823b 100644
--- a/taglib/wavpack/wavpackfile.h
+++ b/taglib/wavpack/wavpackfile.h
@@ -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 &);
diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp
index 1b899975..c0d171be 100644
--- a/tests/test_fileref.cpp
+++ b/tests/test_fileref.cpp
@@ -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>
@@ -39,9 +39,10 @@
#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);
}
}