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