1 // $Id: io_helpers.cpp,v 1.13 2002/07/02 22:13:56 t1mpy Exp $
2
3 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
4 // Copyright 1999, 2000 Scott Thomas Haug
5
6 // This library is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU Library General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or (at your
9 // option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 // License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public License
17 // along with this library; if not, write to the Free Software Foundation,
18 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 // The id3lib authors encourage improvements and optimisations to be sent to
21 // the id3lib coordinator. Please see the README file for details on where to
22 // send such submissions. See the AUTHORS file for a list of people who have
23 // contributed to id3lib. See the ChangeLog file for a list of changes to
24 // id3lib. These files are distributed with id3lib at
25 // http://download.sourceforge.net/id3lib/
26
27 #if defined HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
32
33 using namespace dami;
34
readString(ID3_Reader & reader)35 String io::readString(ID3_Reader& reader)
36 {
37 String str;
38 while (!reader.atEnd())
39 {
40 ID3_Reader::char_type ch = reader.readChar();
41 if (ch == '\0')
42 {
43 break;
44 }
45 str += static_cast<char>(ch);
46 }
47 return str;
48 }
49
readText(ID3_Reader & reader,size_t len)50 String io::readText(ID3_Reader& reader, size_t len)
51 {
52 String str;
53 str.reserve(len);
54 const size_t SIZE = 1024;
55 ID3_Reader::char_type buf[SIZE];
56 size_t remaining = len;
57 while (remaining > 0 && !reader.atEnd())
58 {
59 size_t numRead = reader.readChars(buf, min(remaining, SIZE));
60 remaining -= numRead;
61 str.append(reinterpret_cast<String::value_type *>(buf), numRead);
62 }
63 return str;
64 }
65
66 namespace
67 {
isNull(unsigned char ch1,unsigned char ch2)68 bool isNull(unsigned char ch1, unsigned char ch2)
69 {
70 return ch1 == '\0' && ch2 == '\0';
71 }
72
isBOM(unsigned char ch1,unsigned char ch2)73 int isBOM(unsigned char ch1, unsigned char ch2)
74 {
75 // The following is taken from the following URL:
76 // http://community.roxen.com/developers/idocs/rfc/rfc2781.html
77 /* The Unicode Standard and ISO 10646 define the character "ZERO WIDTH
78 NON-BREAKING SPACE" (0xFEFF), which is also known informally as
79 "BYTE ORDER MARK" (abbreviated "BOM"). The latter name hints at a
80 second possible usage of the character, in addition to its normal
81 use as a genuine "ZERO WIDTH NON-BREAKING SPACE" within text. This
82 usage, suggested by Unicode section 2.4 and ISO 10646 Annex F
83 (informative), is to prepend a 0xFEFF character to a stream of
84 Unicode characters as a "signature"; a receiver of such a serialized
85 stream may then use the initial character both as a hint that the
86 stream consists of Unicode characters and as a way to recognize the
87 serialization order. In serialized UTF-16 prepended with such a
88 signature, the order is big-endian if the first two octets are 0xFE
89 followed by 0xFF; if they are 0xFF followed by 0xFE, the order is
90 little-endian. Note that 0xFFFE is not a Unicode character,
91 precisely to preserve the usefulness of 0xFEFF as a byte-order
92 mark. */
93
94 if (ch1 == 0xFE && ch2 == 0xFF)
95 {
96 return 1;
97 }
98 else if (ch1 == 0xFF && ch2 == 0xFE)
99 {
100 return -1;
101 }
102 return 0;
103 }
104
readTwoChars(ID3_Reader & reader,ID3_Reader::char_type & ch1,ID3_Reader::char_type & ch2)105 bool readTwoChars(ID3_Reader& reader,
106 ID3_Reader::char_type& ch1,
107 ID3_Reader::char_type& ch2)
108 {
109 if (reader.atEnd())
110 {
111 return false;
112 }
113 io::ExitTrigger et(reader);
114 ch1 = reader.readChar();
115 if (reader.atEnd())
116 {
117 return false;
118 }
119 et.release();
120 ch2 = reader.readChar();
121 return true;
122 }
123 }
124
readUnicodeString(ID3_Reader & reader)125 String io::readUnicodeString(ID3_Reader& reader)
126 {
127 String unicode;
128 ID3_Reader::char_type ch1, ch2;
129 if (!readTwoChars(reader, ch1, ch2) || isNull(ch1, ch2))
130 {
131 return unicode;
132 }
133 int bom = isBOM(ch1, ch2);
134 if (!bom)
135 {
136 unicode += static_cast<char>(ch1);
137 unicode += static_cast<char>(ch2);
138 }
139 while (!reader.atEnd())
140 {
141 if (!readTwoChars(reader, ch1, ch2) || isNull(ch1, ch2))
142 {
143 break;
144 }
145 if (bom == -1)
146 {
147 unicode += static_cast<char>(ch2);
148 unicode += static_cast<char>(ch1);
149 }
150 else
151 {
152 unicode += static_cast<char>(ch1);
153 unicode += static_cast<char>(ch2);
154 }
155 }
156 return unicode;
157 }
158
readUnicodeText(ID3_Reader & reader,size_t len)159 String io::readUnicodeText(ID3_Reader& reader, size_t len)
160 {
161 String unicode;
162 ID3_Reader::char_type ch1, ch2;
163 if (!readTwoChars(reader, ch1, ch2))
164 {
165 return unicode;
166 }
167 len -= 2;
168 int bom = isBOM(ch1, ch2);
169 if (!bom)
170 {
171 unicode += ch1;
172 unicode += ch2;
173 unicode += readText(reader, len);
174 }
175 else if (bom == 1)
176 {
177 unicode = readText(reader, len);
178 }
179 else
180 {
181 for (size_t i = 0; i < len; i += 2)
182 {
183 if (!readTwoChars(reader, ch1, ch2))
184 {
185 break;
186 }
187 unicode += ch2;
188 unicode += ch1;
189 }
190 }
191 return unicode;
192 }
193
readAllBinary(ID3_Reader & reader)194 BString io::readAllBinary(ID3_Reader& reader)
195 {
196 return readBinary(reader, reader.remainingBytes());
197 }
198
readBinary(ID3_Reader & reader,size_t len)199 BString io::readBinary(ID3_Reader& reader, size_t len)
200 {
201 BString binary;
202 binary.reserve(len);
203
204 size_t remaining = len;
205 const size_t SIZE = 1024;
206 ID3_Reader::char_type buf[SIZE];
207 while (!reader.atEnd() && remaining > 0)
208 {
209 size_t numRead = reader.readChars(buf, min(remaining, SIZE));
210 remaining -= numRead;
211 binary.append(reinterpret_cast<BString::value_type *>(buf), numRead);
212 }
213
214 return binary;
215 }
216
readLENumber(ID3_Reader & reader,size_t len)217 uint32 io::readLENumber(ID3_Reader& reader, size_t len)
218 {
219 uint32 val = 0;
220 for (size_t i = 0; i < len; i++)
221 {
222 if (reader.atEnd())
223 {
224 break;
225 }
226 val += (static_cast<uint32>(0xFF & reader.readChar()) << (i * 8));
227 }
228 return val;
229 }
230
readBENumber(ID3_Reader & reader,size_t len)231 uint32 io::readBENumber(ID3_Reader& reader, size_t len)
232 {
233 uint32 val = 0;
234
235 for (ID3_Reader::size_type i = 0; i < len && !reader.atEnd(); ++i)
236 {
237 val *= 256; // 2^8
238 val += static_cast<uint32>(0xFF & reader.readChar());
239 }
240 return val;
241 }
242
readTrailingSpaces(ID3_Reader & reader,size_t len)243 String io::readTrailingSpaces(ID3_Reader& reader, size_t len)
244 {
245 io::WindowedReader wr(reader, len);
246 String str;
247 String spaces;
248 str.reserve(len);
249 spaces.reserve(len);
250 while (!wr.atEnd())
251 {
252 ID3_Reader::char_type ch = wr.readChar();
253 if (ch == '\0' || ch == ' ')
254 {
255 spaces += ch;
256 }
257 else
258 {
259 str += spaces + (char) ch;
260 spaces.erase();
261 }
262 }
263 return str;
264 }
265
readUInt28(ID3_Reader & reader)266 uint32 io::readUInt28(ID3_Reader& reader)
267 {
268 uint32 val = 0;
269 const unsigned short BITSUSED = 7;
270 const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32));
271 // For each byte of the first 4 bytes in the string...
272 for (size_t i = 0; i < sizeof(uint32); ++i)
273 {
274 if (reader.atEnd())
275 {
276 break;
277 }
278 // ...append the last 7 bits to the end of the temp integer...
279 val = (val << BITSUSED) | static_cast<uint32>(reader.readChar()) & MASK(BITSUSED);
280 }
281
282 // We should always parse 4 characters
283 return min(val, MAXVAL);
284 }
285
writeBENumber(ID3_Writer & writer,uint32 val,size_t len)286 size_t io::writeBENumber(ID3_Writer& writer, uint32 val, size_t len)
287 {
288 ID3_Writer::char_type bytes[sizeof(uint32)];
289 ID3_Writer::size_type size = min<ID3_Reader::size_type>(len, sizeof(uint32));
290 renderNumber(bytes, val, size);
291 return writer.writeChars(bytes, size);
292 }
293
writeTrailingSpaces(ID3_Writer & writer,String buf,size_t len)294 size_t io::writeTrailingSpaces(ID3_Writer& writer, String buf, size_t len)
295 {
296 ID3_Writer::pos_type beg = writer.getCur();
297 ID3_Writer::size_type strLen = buf.size();
298 ID3_Writer::size_type size = min((unsigned int)len, (unsigned int)strLen);
299 writer.writeChars(buf.data(), size);
300 for (; size < len; ++size)
301 {
302 writer.writeChar('\0');
303 }
304 return writer.getCur() - beg;
305 }
306
writeUInt28(ID3_Writer & writer,uint32 val)307 size_t io::writeUInt28(ID3_Writer& writer, uint32 val)
308 {
309 uchar data[sizeof(uint32)];
310 const unsigned short BITSUSED = 7;
311 const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32));
312 val = min(val, MAXVAL);
313 // This loop renders the value to the character buffer in reverse order, as
314 // it is easy to extract the last 7 bits of an integer. This is why the
315 // loop shifts the value of the integer by 7 bits for each iteration.
316 for (size_t i = 0; i < sizeof(uint32); ++i)
317 {
318 // Extract the last BITSUSED bits from val and put it in its appropriate
319 // place in the data buffer
320 data[sizeof(uint32) - i - 1] = static_cast<uchar>(val & MASK(BITSUSED));
321
322 // The last BITSUSED bits were extracted from the val. So shift it to the
323 // right by that many bits for the next iteration
324 val >>= BITSUSED;
325 }
326
327 // Should always render 4 bytes
328 return writer.writeChars(data, sizeof(uint32));
329 }
330
writeString(ID3_Writer & writer,String data)331 size_t io::writeString(ID3_Writer& writer, String data)
332 {
333 size_t size = writeText(writer, data);
334 writer.writeChar('\0');
335 return size + 1;
336 }
337
writeText(ID3_Writer & writer,String data)338 size_t io::writeText(ID3_Writer& writer, String data)
339 {
340 ID3_Writer::pos_type beg = writer.getCur();
341 writer.writeChars(data.data(), data.size());
342 return writer.getCur() - beg;
343 }
344
writeUnicodeString(ID3_Writer & writer,String data,bool bom)345 size_t io::writeUnicodeString(ID3_Writer& writer, String data, bool bom)
346 {
347 size_t size = writeUnicodeText(writer, data, bom);
348 unicode_t null = NULL_UNICODE;
349 writer.writeChars((const unsigned char*) &null, 2);
350 return size + 2;
351 }
352
writeUnicodeText(ID3_Writer & writer,String data,bool bom)353 size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom)
354 {
355 ID3_Writer::pos_type beg = writer.getCur();
356 size_t size = (data.size() / 2) * 2;
357 if (size == 0)
358 {
359 return 0;
360 }
361 if (bom)
362 {
363 // Write the BOM: 0xFEFF
364 unicode_t BOM = 0xFEFF;
365 writer.writeChars((const unsigned char*) &BOM, 2);
366 const unsigned char* pdata = (const unsigned char*)data.c_str();
367 for (size_t i = 0; i < size; i += 2)
368 {
369 unicode_t ch = (pdata[i] << 8) | pdata[i+1];
370 writer.writeChars((const unsigned char*) &ch, 2);
371 }
372 }
373 return writer.getCur() - beg;
374 }
375
376