2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "TagLibVFSStream.h"
22 #include "filesystem/File.h"
23 #include "utils/StdString.h"
24 #include "utils/log.h"
25 #include <taglib/tiostream.h>
27 using namespace XFILE;
28 using namespace TagLib;
29 using namespace MUSIC_INFO;
33 #pragma comment(lib, "tag.lib")
37 * Construct a File object and opens the \a file. \a file should be a
40 TagLibVFSStream::TagLibVFSStream(const string& strFileName, bool readOnly)
45 if (!m_file.Open(strFileName))
50 if (!m_file.OpenForWrite(strFileName))
53 m_strFileName = strFileName;
57 * Destroys this ByteVectorStream instance.
59 TagLibVFSStream::~TagLibVFSStream()
65 * Returns the file name in the local file system encoding.
67 FileName TagLibVFSStream::name() const
69 return m_strFileName.c_str();
73 * Reads a block of size \a length at the current get pointer.
75 ByteVector TagLibVFSStream::readBlock(TagLib::ulong length)
77 ByteVector byteVector(static_cast<TagLib::uint>(length));
78 byteVector.resize(m_file.Read(byteVector.data(), length));
83 * Attempts to write the block \a data at the current get pointer. If the
84 * file is currently only opened read only -- i.e. readOnly() returns true --
85 * this attempts to reopen the file in read/write mode.
87 * \note This should be used instead of using the streaming output operator
88 * for a ByteVector. And even this function is significantly slower than
89 * doing output with a char[].
91 void TagLibVFSStream::writeBlock(const ByteVector &data)
93 m_file.Write(data.data(), data.size());
97 * Insert \a data at position \a start in the file overwriting \a replace
98 * bytes of the original content.
100 * \note This method is slow since it requires rewriting all of the file
101 * after the insertion point.
103 void TagLibVFSStream::insert(const ByteVector &data, TagLib::ulong start, TagLib::ulong replace)
105 if (data.size() == replace)
111 else if (data.size() < replace)
115 removeBlock(start + data.size(), replace - data.size());
118 // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
119 // and avoid TagLib's high level API for rendering just copying parts of
120 // the file that don't contain tag data.
122 // Now I'll explain the steps in this ugliness:
124 // First, make sure that we're working with a buffer that is longer than
125 // the *differnce* in the tag sizes. We want to avoid overwriting parts
126 // that aren't yet in memory, so this is necessary.
127 TagLib::ulong bufferLength = bufferSize();
129 while (data.size() - replace > bufferLength)
130 bufferLength += bufferSize();
132 // Set where to start the reading and writing.
133 long readPosition = start + replace;
134 long writePosition = start;
136 ByteVector aboutToOverwrite(static_cast<TagLib::uint>(bufferLength));
138 // This is basically a special case of the loop below. Here we're just
139 // doing the same steps as below, but since we aren't using the same buffer
140 // size -- instead we're using the tag size -- this has to be handled as a
141 // special case. We're also using File::writeBlock() just for the tag.
142 // That's a bit slower than using char *'s so, we're only doing it here.
144 int bytesRead = m_file.Read(aboutToOverwrite.data(), bufferLength);
145 readPosition += bufferLength;
149 writePosition += data.size();
151 buffer = aboutToOverwrite;
152 buffer.resize(bytesRead);
154 // Ok, here's the main loop. We want to loop until the read fails, which
155 // means that we hit the end of the file.
156 while (!buffer.isEmpty())
158 // Seek to the current read position and read the data that we're about
159 // to overwrite. Appropriately increment the readPosition.
161 bytesRead = m_file.Read(aboutToOverwrite.data(), bufferLength);
162 aboutToOverwrite.resize(bytesRead);
163 readPosition += bufferLength;
165 // Check to see if we just read the last block. We need to call clear()
166 // if we did so that the last write succeeds.
167 if (TagLib::ulong(bytesRead) < bufferLength)
170 // Seek to the write position and write our buffer. Increment the
173 m_file.Write(buffer.data(), buffer.size());
174 writePosition += buffer.size();
176 buffer = aboutToOverwrite;
177 bufferLength = bytesRead;
182 * Removes a block of the file starting a \a start and continuing for
185 * \note This method is slow since it involves rewriting all of the file
186 * after the removed portion.
188 void TagLibVFSStream::removeBlock(TagLib::ulong start, TagLib::ulong length)
190 TagLib::ulong bufferLength = bufferSize();
192 long readPosition = start + length;
193 long writePosition = start;
195 ByteVector buffer(static_cast<TagLib::uint>(bufferLength));
197 TagLib::ulong bytesRead = 1;
199 while(bytesRead != 0)
202 bytesRead = m_file.Read(buffer.data(), bufferLength);
203 readPosition += bytesRead;
205 // Check to see if we just read the last block. We need to call clear()
206 // if we did so that the last write succeeds.
207 if(bytesRead < bufferLength)
211 m_file.Write(buffer.data(), bytesRead);
212 writePosition += bytesRead;
214 truncate(writePosition);
218 * Returns true if the file is read only (or if the file can not be opened).
220 bool TagLibVFSStream::readOnly() const
222 return m_bIsReadOnly;
226 * Since the file can currently only be opened as an argument to the
227 * constructor (sort-of by design), this returns if that open succeeded.
229 bool TagLibVFSStream::isOpen() const
235 * Move the I/O pointer to \a offset in the file from position \a p. This
236 * defaults to seeking from the beginning of the file.
240 void TagLibVFSStream::seek(long offset, Position p)
245 m_file.Seek(offset, SEEK_SET);
248 m_file.Seek(offset, SEEK_CUR);
251 m_file.Seek(offset, SEEK_END);
257 * Reset the end-of-file and error flags on the file.
259 void TagLibVFSStream::clear()
264 * Returns the current offset within the file.
266 long TagLibVFSStream::tell() const
268 int64_t pos = m_file.GetPosition();
276 * Returns the length of the file.
278 long TagLibVFSStream::length()
280 return (long)m_file.GetLength();
284 * Truncates the file to a \a length.
286 void TagLibVFSStream::truncate(long length)
288 m_file.Truncate(length);