2 * Copyright (C) 2005-2008 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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "TagLibVFSStream.h"
23 #include "filesystem/File.h"
24 #include "utils/StdString.h"
25 #include "utils/log.h"
26 #include <taglib/tiostream.h>
28 using namespace XFILE;
29 using namespace TagLib;
30 using namespace MUSIC_INFO;
34 #pragma comment(lib, "tag.lib")
38 * Construct a File object and opens the \a file. \a file should be a
41 TagLibVFSStream::TagLibVFSStream(const string& strFileName, bool readOnly)
46 if (!m_file.Open(strFileName))
51 if (!m_file.OpenForWrite(strFileName))
54 m_strFileName = strFileName;
58 * Destroys this ByteVectorStream instance.
60 TagLibVFSStream::~TagLibVFSStream()
66 * Returns the file name in the local file system encoding.
68 FileName TagLibVFSStream::name() const
70 return m_strFileName.c_str();
74 * Reads a block of size \a length at the current get pointer.
76 ByteVector TagLibVFSStream::readBlock(ulong length)
78 ByteVector byteVector(static_cast<TagLib::uint>(length));
79 byteVector.resize(m_file.Read(byteVector.data(), length));
84 * Attempts to write the block \a data at the current get pointer. If the
85 * file is currently only opened read only -- i.e. readOnly() returns true --
86 * this attempts to reopen the file in read/write mode.
88 * \note This should be used instead of using the streaming output operator
89 * for a ByteVector. And even this function is significantly slower than
90 * doing output with a char[].
92 void TagLibVFSStream::writeBlock(const ByteVector &data)
94 m_file.Write(data.data(), data.size());
98 * Insert \a data at position \a start in the file overwriting \a replace
99 * bytes of the original content.
101 * \note This method is slow since it requires rewriting all of the file
102 * after the insertion point.
104 void TagLibVFSStream::insert(const ByteVector &data, ulong start, ulong replace)
106 if (data.size() == replace)
112 else if (data.size() < replace)
116 removeBlock(start + data.size(), replace - data.size());
119 // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
120 // and avoid TagLib's high level API for rendering just copying parts of
121 // the file that don't contain tag data.
123 // Now I'll explain the steps in this ugliness:
125 // First, make sure that we're working with a buffer that is longer than
126 // the *differnce* in the tag sizes. We want to avoid overwriting parts
127 // that aren't yet in memory, so this is necessary.
128 ulong bufferLength = bufferSize();
130 while (data.size() - replace > bufferLength)
131 bufferLength += bufferSize();
133 // Set where to start the reading and writing.
134 long readPosition = start + replace;
135 long writePosition = start;
137 ByteVector aboutToOverwrite(static_cast<TagLib::uint>(bufferLength));
139 // This is basically a special case of the loop below. Here we're just
140 // doing the same steps as below, but since we aren't using the same buffer
141 // size -- instead we're using the tag size -- this has to be handled as a
142 // special case. We're also using File::writeBlock() just for the tag.
143 // That's a bit slower than using char *'s so, we're only doing it here.
145 int bytesRead = m_file.Read(aboutToOverwrite.data(), bufferLength);
146 readPosition += bufferLength;
150 writePosition += data.size();
152 buffer = aboutToOverwrite;
153 buffer.resize(bytesRead);
155 // Ok, here's the main loop. We want to loop until the read fails, which
156 // means that we hit the end of the file.
157 while (!buffer.isEmpty())
159 // Seek to the current read position and read the data that we're about
160 // to overwrite. Appropriately increment the readPosition.
162 bytesRead = m_file.Read(aboutToOverwrite.data(), bufferLength);
163 aboutToOverwrite.resize(bytesRead);
164 readPosition += bufferLength;
166 // Check to see if we just read the last block. We need to call clear()
167 // if we did so that the last write succeeds.
168 if (ulong(bytesRead) < bufferLength)
171 // Seek to the write position and write our buffer. Increment the
174 m_file.Write(buffer.data(), buffer.size());
175 writePosition += buffer.size();
177 buffer = aboutToOverwrite;
178 bufferLength = bytesRead;
183 * Removes a block of the file starting a \a start and continuing for
186 * \note This method is slow since it involves rewriting all of the file
187 * after the removed portion.
189 void TagLibVFSStream::removeBlock(ulong start, ulong length)
191 ulong bufferLength = bufferSize();
193 long readPosition = start + length;
194 long writePosition = start;
196 ByteVector buffer(static_cast<TagLib::uint>(bufferLength));
200 while(bytesRead != 0)
203 bytesRead = m_file.Read(buffer.data(), bufferLength);
204 readPosition += bytesRead;
206 // Check to see if we just read the last block. We need to call clear()
207 // if we did so that the last write succeeds.
208 if(bytesRead < bufferLength)
212 m_file.Write(buffer.data(), bytesRead);
213 writePosition += bytesRead;
215 truncate(writePosition);
219 * Returns true if the file is read only (or if the file can not be opened).
221 bool TagLibVFSStream::readOnly() const
223 return m_bIsReadOnly;
227 * Since the file can currently only be opened as an argument to the
228 * constructor (sort-of by design), this returns if that open succeeded.
230 bool TagLibVFSStream::isOpen() const
236 * Move the I/O pointer to \a offset in the file from position \a p. This
237 * defaults to seeking from the beginning of the file.
241 void TagLibVFSStream::seek(long offset, Position p)
246 m_file.Seek(offset, SEEK_SET);
249 m_file.Seek(offset, SEEK_CUR);
252 m_file.Seek(offset, SEEK_END);
258 * Reset the end-of-file and error flags on the file.
260 void TagLibVFSStream::clear()
265 * Returns the current offset within the file.
267 long TagLibVFSStream::tell() const
269 int64_t pos = m_file.GetPosition();
277 * Returns the length of the file.
279 long TagLibVFSStream::length()
281 return m_file.GetLength();
285 * Truncates the file to a \a length.
287 void TagLibVFSStream::truncate(long length)
289 m_file.Truncate(length);