Version bump
[someplayer] / src / taglib / ogg / oggfile.cpp
1 /***************************************************************************
2     copyright            : (C) 2002 - 2008 by Scott Wheeler
3     email                : wheeler@kde.org
4  ***************************************************************************/
5
6 /***************************************************************************
7  *   This library is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU Lesser General Public License version   *
9  *   2.1 as published by the Free Software Foundation.                     *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful, but   *
12  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the Free Software   *
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19  *   USA                                                                   *
20  *                                                                         *
21  *   Alternatively, this file is available under the Mozilla Public        *
22  *   License Version 1.1.  You may obtain a copy of the License at         *
23  *   http://www.mozilla.org/MPL/                                           *
24  ***************************************************************************/
25
26 #include <tbytevectorlist.h>
27 #include <tmap.h>
28 #include <tstring.h>
29 #include <tdebug.h>
30
31 #include "oggfile.h"
32 #include "oggpage.h"
33 #include "oggpageheader.h"
34
35 using namespace TagLib;
36
37 class Ogg::File::FilePrivate
38 {
39 public:
40   FilePrivate() :
41     streamSerialNumber(0),
42     firstPageHeader(0),
43     lastPageHeader(0),
44     currentPage(0),
45     currentPacketPage(0)
46   {
47     pages.setAutoDelete(true);
48   }
49
50   ~FilePrivate()
51   {
52     delete firstPageHeader;
53     delete lastPageHeader;
54   }
55
56   uint streamSerialNumber;
57   List<Page *> pages;
58   PageHeader *firstPageHeader;
59   PageHeader *lastPageHeader;
60   std::vector< List<int> > packetToPageMap;
61   Map<int, ByteVector> dirtyPackets;
62   List<int> dirtyPages;
63
64   //! The current page for the reader -- used by nextPage()
65   Page *currentPage;
66   //! The current page for the packet parser -- used by packet()
67   Page *currentPacketPage;
68   //! The packets for the currentPacketPage -- used by packet()
69   ByteVectorList currentPackets;
70 };
71
72 ////////////////////////////////////////////////////////////////////////////////
73 // public members
74 ////////////////////////////////////////////////////////////////////////////////
75
76 Ogg::File::~File()
77 {
78   delete d;
79 }
80
81 ByteVector Ogg::File::packet(uint i)
82 {
83   // Check to see if we're called setPacket() for this packet since the last
84   // save:
85
86   if(d->dirtyPackets.contains(i))
87     return d->dirtyPackets[i];
88
89   // If we haven't indexed the page where the packet we're interested in starts,
90   // begin reading pages until we have.
91
92   while(d->packetToPageMap.size() <= i) {
93     if(!nextPage()) {
94       debug("Ogg::File::packet() -- Could not find the requested packet.");
95       return ByteVector::null;
96     }
97   }
98
99   // Start reading at the first page that contains part (or all) of this packet.
100   // If the last read stopped at the packet that we're interested in, don't
101   // reread its packet list.  (This should make sequential packet reads fast.)
102
103   uint pageIndex = d->packetToPageMap[i].front();
104   if(d->currentPacketPage != d->pages[pageIndex]) {
105     d->currentPacketPage = d->pages[pageIndex];
106     d->currentPackets = d->currentPacketPage->packets();
107   }
108
109   // If the packet is completely contained in the first page that it's in, then
110   // just return it now.
111
112   if(d->currentPacketPage->containsPacket(i) & Page::CompletePacket)
113     return d->currentPackets[i - d->currentPacketPage->firstPacketIndex()];
114
115   // If the packet is *not* completely contained in the first page that it's a
116   // part of then that packet trails off the end of the page.  Continue appending
117   // the pages' packet data until we hit a page that either does not end with the
118   // packet that we're fetching or where the last packet is complete.
119
120   ByteVector packet = d->currentPackets.back();
121   while(d->currentPacketPage->containsPacket(i) & Page::EndsWithPacket &&
122         !d->currentPacketPage->header()->lastPacketCompleted())
123   {
124     pageIndex++;
125     if(pageIndex == d->pages.size()) {
126       if(!nextPage()) {
127         debug("Ogg::File::packet() -- Could not find the requested packet.");
128         return ByteVector::null;
129       }
130     }
131     d->currentPacketPage = d->pages[pageIndex];
132     d->currentPackets = d->currentPacketPage->packets();
133     packet.append(d->currentPackets.front());
134   }
135
136   return packet;
137 }
138
139 void Ogg::File::setPacket(uint i, const ByteVector &p)
140 {
141   while(d->packetToPageMap.size() <= i) {
142     if(!nextPage()) {
143       debug("Ogg::File::setPacket() -- Could not set the requested packet.");
144       return;
145     }
146   }
147
148   List<int>::ConstIterator it = d->packetToPageMap[i].begin();
149   for(; it != d->packetToPageMap[i].end(); ++it)
150     d->dirtyPages.sortedInsert(*it, true);
151
152   d->dirtyPackets.insert(i, p);
153 }
154
155 const Ogg::PageHeader *Ogg::File::firstPageHeader()
156 {
157   if(d->firstPageHeader)
158     return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
159
160   long firstPageHeaderOffset = find("OggS");
161
162   if(firstPageHeaderOffset < 0)
163     return 0;
164
165   d->firstPageHeader = new PageHeader(this, firstPageHeaderOffset);
166   return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
167 }
168
169 const Ogg::PageHeader *Ogg::File::lastPageHeader()
170 {
171   if(d->lastPageHeader)
172     return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
173
174   long lastPageHeaderOffset = rfind("OggS");
175
176   if(lastPageHeaderOffset < 0)
177     return 0;
178
179   d->lastPageHeader = new PageHeader(this, lastPageHeaderOffset);
180   return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
181 }
182
183 bool Ogg::File::save()
184 {
185   if(readOnly()) {
186     debug("Ogg::File::save() - Cannot save to a read only file.");
187     return false;
188   }
189
190   List<int> pageGroup;
191
192   for(List<int>::ConstIterator it = d->dirtyPages.begin(); it != d->dirtyPages.end(); ++it) {
193     if(!pageGroup.isEmpty() && pageGroup.back() + 1 != *it) {
194       writePageGroup(pageGroup);
195       pageGroup.clear();
196     }
197     else
198       pageGroup.append(*it);
199   }
200   writePageGroup(pageGroup);
201   d->dirtyPages.clear();
202   d->dirtyPackets.clear();
203
204   return true;
205 }
206
207 ////////////////////////////////////////////////////////////////////////////////
208 // protected members
209 ////////////////////////////////////////////////////////////////////////////////
210
211 Ogg::File::File(FileName file) : TagLib::File(file)
212 {
213   d = new FilePrivate;
214 }
215
216 ////////////////////////////////////////////////////////////////////////////////
217 // private members
218 ////////////////////////////////////////////////////////////////////////////////
219
220 bool Ogg::File::nextPage()
221 {
222   long nextPageOffset;
223   int currentPacket;
224
225   if(d->pages.isEmpty()) {
226     currentPacket = 0;
227     nextPageOffset = find("OggS");
228     if(nextPageOffset < 0)
229       return false;
230   }
231   else {
232     if(d->currentPage->header()->lastPageOfStream())
233       return false;
234
235     if(d->currentPage->header()->lastPacketCompleted())
236       currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount();
237     else
238       currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount() - 1;
239
240     nextPageOffset = d->currentPage->fileOffset() + d->currentPage->size();
241   }
242
243   // Read the next page and add it to the page list.
244
245   d->currentPage = new Page(this, nextPageOffset);
246
247   if(!d->currentPage->header()->isValid()) {
248     delete d->currentPage;
249     d->currentPage = 0;
250     return false;
251   }
252
253   d->currentPage->setFirstPacketIndex(currentPacket);
254
255   if(d->pages.isEmpty())
256     d->streamSerialNumber = d->currentPage->header()->streamSerialNumber();
257
258   d->pages.append(d->currentPage);
259
260   // Loop through the packets in the page that we just read appending the
261   // current page number to the packet to page map for each packet.
262
263   for(uint i = 0; i < d->currentPage->packetCount(); i++) {
264     uint currentPacket = d->currentPage->firstPacketIndex() + i;
265     if(d->packetToPageMap.size() <= currentPacket)
266       d->packetToPageMap.push_back(List<int>());
267     d->packetToPageMap[currentPacket].append(d->pages.size() - 1);
268   }
269
270   return true;
271 }
272
273 void Ogg::File::writePageGroup(const List<int> &thePageGroup)
274 {
275   if(thePageGroup.isEmpty())
276     return;
277
278
279   // pages in the pageGroup and packets must be equivalent 
280   // (originalSize and size of packets would not work together), 
281   // therefore we sometimes have to add pages to the group
282   List<int> pageGroup(thePageGroup);
283   while (!d->pages[pageGroup.back()]->header()->lastPacketCompleted()) {
284     if (d->currentPage->header()->pageSequenceNumber() == pageGroup.back()) {
285       if (nextPage() == false) {
286         debug("broken ogg file");
287         return;
288       }
289       pageGroup.append(d->currentPage->header()->pageSequenceNumber());
290     } else {
291       pageGroup.append(pageGroup.back() + 1);
292     }
293   }
294
295   ByteVectorList packets;
296
297   // If the first page of the group isn't dirty, append its partial content here.
298
299   if(!d->dirtyPages.contains(d->pages[pageGroup.front()]->firstPacketIndex()))
300     packets.append(d->pages[pageGroup.front()]->packets().front());
301
302   int previousPacket = -1;
303   int originalSize = 0;
304
305   for(List<int>::ConstIterator it = pageGroup.begin(); it != pageGroup.end(); ++it) {
306     uint firstPacket = d->pages[*it]->firstPacketIndex();
307     uint lastPacket = firstPacket + d->pages[*it]->packetCount() - 1;
308
309     List<int>::ConstIterator last = --pageGroup.end();
310
311     for(uint i = firstPacket; i <= lastPacket; i++) {
312
313       if(it == last && i == lastPacket && !d->dirtyPages.contains(i))
314         packets.append(d->pages[*it]->packets().back());
315       else if(int(i) != previousPacket) {
316         previousPacket = i;
317         packets.append(packet(i));
318       }
319     }
320     originalSize += d->pages[*it]->size();
321   }
322
323   const bool continued = d->pages[pageGroup.front()]->header()->firstPacketContinued();
324   const bool completed = d->pages[pageGroup.back()]->header()->lastPacketCompleted();
325
326   // TODO: This pagination method isn't accurate for what's being done here.
327   // This should account for real possibilities like non-aligned packets and such.
328
329   List<Page *> pages = Page::paginate(packets, Page::SinglePagePerGroup,
330                                       d->streamSerialNumber, pageGroup.front(),
331                                       continued, completed);
332
333   List<Page *> renumberedPages;
334
335   // Correct the page numbering of following pages
336
337   if (pages.back()->header()->pageSequenceNumber() != pageGroup.back()) {
338
339     // TODO: change the internal data structure so that we don't need to hold the 
340     // complete file in memory (is unavoidable at the moment)
341
342     // read the complete stream
343     while(!d->currentPage->header()->lastPageOfStream()) {
344       if(nextPage() == false) {
345         debug("broken ogg file");
346         break;
347       }
348     }
349
350     // create a gap for the new pages
351     int numberOfNewPages = pages.back()->header()->pageSequenceNumber() - pageGroup.back();
352     List<Page *>::Iterator pageIter = d->pages.begin();
353     for(int i = 0; i < pageGroup.back(); i++) {
354       if(pageIter != d->pages.end()) {
355         ++pageIter;
356       }
357       else {
358         debug("Ogg::File::writePageGroup() -- Page sequence is broken in original file.");
359         break;
360       }
361     }
362
363     ++pageIter;
364     for(; pageIter != d->pages.end(); ++pageIter) {
365       Ogg::Page *newPage =
366         (*pageIter)->getCopyWithNewPageSequenceNumber(
367             (*pageIter)->header()->pageSequenceNumber() + numberOfNewPages);
368
369       ByteVector data;
370       data.append(newPage->render());
371       insert(data, newPage->fileOffset(), data.size());
372
373       renumberedPages.append(newPage);
374     }
375   }
376
377   // insert the new data
378
379   ByteVector data;
380   for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it)
381     data.append((*it)->render());
382
383   // The insertion algorithms could also be improve to queue and prioritize data
384   // on the way out.  Currently it requires rewriting the file for every page
385   // group rather than just once; however, for tagging applications there will
386   // generally only be one page group, so it's not worth the time for the
387   // optimization at the moment.
388
389   insert(data, d->pages[pageGroup.front()]->fileOffset(), originalSize);
390
391   // Update the page index to include the pages we just created and to delete the
392   // old pages.
393
394   // First step: Pages that contain the comment data
395
396   for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it) {
397     const unsigned int index = (*it)->header()->pageSequenceNumber();
398     if(index < d->pages.size()) {
399       delete d->pages[index];
400       d->pages[index] = *it;
401     }
402     else if(index == d->pages.size()) {
403       d->pages.append(*it);
404     }
405     else {
406       // oops - there's a hole in the sequence
407       debug("Ogg::File::writePageGroup() -- Page sequence is broken.");
408     }
409   }
410
411   // Second step: the renumbered pages
412
413   for(List<Page *>::ConstIterator it = renumberedPages.begin(); it != renumberedPages.end(); ++it) {
414     const unsigned int index = (*it)->header()->pageSequenceNumber();
415     if(index < d->pages.size()) {
416       delete d->pages[index];
417       d->pages[index] = *it;
418     }
419     else if(index == d->pages.size()) {
420       d->pages.append(*it);
421     }
422     else {
423       // oops - there's a hole in the sequence
424       debug("Ogg::File::writePageGroup() -- Page sequence is broken.");
425     }
426   }
427 }