Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / riff / rifffile.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 <tbytevector.h>
27 #include <tdebug.h>
28 #include <tstring.h>
29
30 #include "rifffile.h"
31 #include <vector>
32
33 using namespace TagLib;
34
35 class RIFF::File::FilePrivate
36 {
37 public:
38   FilePrivate() :
39     endianness(BigEndian),
40     size(0)
41   {
42
43   }
44   Endianness endianness;
45   ByteVector type;
46   uint size;
47   ByteVector format;
48
49   std::vector<ByteVector> chunkNames;
50   std::vector<uint> chunkOffsets;
51   std::vector<uint> chunkSizes;
52   std::vector<char> chunkPadding;
53 };
54
55 ////////////////////////////////////////////////////////////////////////////////
56 // public members
57 ////////////////////////////////////////////////////////////////////////////////
58
59 RIFF::File::~File()
60 {
61   delete d;
62 }
63
64 ////////////////////////////////////////////////////////////////////////////////
65 // protected members
66 ////////////////////////////////////////////////////////////////////////////////
67
68 RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file)
69 {
70   d = new FilePrivate;
71   d->endianness = endianness;
72
73   if(isOpen())
74     read();
75 }
76
77 TagLib::uint RIFF::File::chunkCount() const
78 {
79   return d->chunkNames.size();
80 }
81
82 TagLib::uint RIFF::File::chunkOffset(uint i) const
83 {
84   return d->chunkOffsets[i];
85 }
86
87 ByteVector RIFF::File::chunkName(uint i) const
88 {
89   if(i >= chunkCount())
90     return ByteVector::null;
91
92   return d->chunkNames[i];
93 }
94
95 ByteVector RIFF::File::chunkData(uint i)
96 {
97   if(i >= chunkCount())
98     return ByteVector::null;
99
100   // Offset for the first subchunk's data
101
102   long begin = 12 + 8;
103
104   for(uint it = 0; it < i; it++)
105     begin += 8 + d->chunkSizes[it] + d->chunkPadding[it];
106
107   seek(begin);
108
109   return readBlock(d->chunkSizes[i]);
110 }
111
112 void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
113 {
114   if(d->chunkNames.size() == 0)
115   {
116     debug("RIFF::File::setChunkData - No valid chunks found.");
117     return;
118   }
119
120   for(uint i = 0; i < d->chunkNames.size(); i++) {
121     if(d->chunkNames[i] == name) {
122
123       int sizeDifference = data.size() - d->chunkSizes[i];
124
125       // First we update the global size
126
127       insert(ByteVector::fromUInt(d->size + sizeDifference,
128                                   d->endianness == BigEndian), 4, 4);
129
130       // Now update the specific chunk
131
132       writeChunk(name, data, d->chunkOffsets[i] - 8, d->chunkSizes[i] + d->chunkPadding[i] + 8);
133
134       d->chunkSizes[i] = data.size();
135       d->chunkPadding[i] = (data.size() & 0x01) ? 1 : 0;
136
137       // Now update the internal offsets
138
139       for(i++; i < d->chunkNames.size(); i++)
140         d->chunkOffsets[i] = d->chunkOffsets[i-1] + 8 + d->chunkSizes[i-1] + d->chunkPadding[i-1];
141
142       return;
143     }
144   }
145
146   // Couldn't find an existing chunk, so let's create a new one.  First update
147   // the global size:
148
149   insert(ByteVector::fromUInt(d->size + data.size() + 8, d->endianness == BigEndian), 4, 4);
150   writeChunk(name, data, d->chunkOffsets.back() + d->chunkSizes.back());
151 }
152
153 ////////////////////////////////////////////////////////////////////////////////
154 // private members
155 ////////////////////////////////////////////////////////////////////////////////
156
157 void RIFF::File::read()
158 {
159   bool bigEndian = (d->endianness == BigEndian);
160
161   d->type = readBlock(4);
162   d->size = readBlock(4).toUInt(bigEndian);
163   d->format = readBlock(4);
164
165   // + 8: chunk header at least, fix for additional junk bytes
166   while(tell() + 8 <= length()) {
167     ByteVector chunkName = readBlock(4);
168     uint chunkSize = readBlock(4).toUInt(bigEndian);
169
170     if(tell() + chunkSize > uint(length())) {
171       // something wrong
172       break;
173     }
174
175     d->chunkNames.push_back(chunkName);
176     d->chunkSizes.push_back(chunkSize);
177
178     d->chunkOffsets.push_back(tell());
179
180     seek(chunkSize, Current);
181
182     // check padding
183     char paddingSize = 0;
184     long uPosNotPadded = tell();
185     if((uPosNotPadded & 0x01) != 0) {
186       ByteVector iByte = readBlock(1);
187       if((iByte.size() != 1) || (iByte[0] != 0)) {
188         // not well formed, re-seek
189         seek(uPosNotPadded, Beginning);
190       }
191       else {
192         paddingSize = 1;
193       }
194     }
195     d->chunkPadding.push_back(paddingSize);
196
197   }
198 }
199
200 void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
201                             ulong offset, ulong replace)
202 {
203   ByteVector combined = name;
204   combined.append(ByteVector::fromUInt(data.size(), d->endianness == BigEndian));
205   combined.append(data);
206   if((data.size() & 0x01) != 0) {
207     // padding
208     combined.append('\x00');
209   }
210   insert(combined, offset, replace);
211 }