id3lib 3.8.3
|
00001 // $Id: io_helpers.cpp,v 1.13 2002/07/02 22:13:56 t1mpy Exp $ 00002 00003 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags 00004 // Copyright 1999, 2000 Scott Thomas Haug 00005 00006 // This library is free software; you can redistribute it and/or modify it 00007 // under the terms of the GNU Library General Public License as published by 00008 // the Free Software Foundation; either version 2 of the License, or (at your 00009 // option) any later version. 00010 // 00011 // This library is distributed in the hope that it will be useful, but WITHOUT 00012 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00013 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00014 // License for more details. 00015 // 00016 // You should have received a copy of the GNU Library General Public License 00017 // along with this library; if not, write to the Free Software Foundation, 00018 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00019 00020 // The id3lib authors encourage improvements and optimisations to be sent to 00021 // the id3lib coordinator. Please see the README file for details on where to 00022 // send such submissions. See the AUTHORS file for a list of people who have 00023 // contributed to id3lib. See the ChangeLog file for a list of changes to 00024 // id3lib. These files are distributed with id3lib at 00025 // http://download.sourceforge.net/id3lib/ 00026 00027 #if defined HAVE_CONFIG_H 00028 #include <config.h> 00029 #endif 00030 00031 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h" 00032 00033 using namespace dami; 00034 00035 String io::readString(ID3_Reader& reader) 00036 { 00037 String str; 00038 while (!reader.atEnd()) 00039 { 00040 ID3_Reader::char_type ch = reader.readChar(); 00041 if (ch == '\0') 00042 { 00043 break; 00044 } 00045 str += static_cast<char>(ch); 00046 } 00047 return str; 00048 } 00049 00050 String io::readText(ID3_Reader& reader, size_t len) 00051 { 00052 String str; 00053 str.reserve(len); 00054 const size_t SIZE = 1024; 00055 ID3_Reader::char_type buf[SIZE]; 00056 size_t remaining = len; 00057 while (remaining > 0 && !reader.atEnd()) 00058 { 00059 size_t numRead = reader.readChars(buf, min(remaining, SIZE)); 00060 remaining -= numRead; 00061 str.append(reinterpret_cast<String::value_type *>(buf), numRead); 00062 } 00063 return str; 00064 } 00065 00066 namespace 00067 { 00068 bool isNull(unsigned char ch1, unsigned char ch2) 00069 { 00070 return ch1 == '\0' && ch2 == '\0'; 00071 } 00072 00073 int isBOM(unsigned char ch1, unsigned char ch2) 00074 { 00075 // The following is taken from the following URL: 00076 // http://community.roxen.com/developers/idocs/rfc/rfc2781.html 00077 /* The Unicode Standard and ISO 10646 define the character "ZERO WIDTH 00078 NON-BREAKING SPACE" (0xFEFF), which is also known informally as 00079 "BYTE ORDER MARK" (abbreviated "BOM"). The latter name hints at a 00080 second possible usage of the character, in addition to its normal 00081 use as a genuine "ZERO WIDTH NON-BREAKING SPACE" within text. This 00082 usage, suggested by Unicode section 2.4 and ISO 10646 Annex F 00083 (informative), is to prepend a 0xFEFF character to a stream of 00084 Unicode characters as a "signature"; a receiver of such a serialized 00085 stream may then use the initial character both as a hint that the 00086 stream consists of Unicode characters and as a way to recognize the 00087 serialization order. In serialized UTF-16 prepended with such a 00088 signature, the order is big-endian if the first two octets are 0xFE 00089 followed by 0xFF; if they are 0xFF followed by 0xFE, the order is 00090 little-endian. Note that 0xFFFE is not a Unicode character, 00091 precisely to preserve the usefulness of 0xFEFF as a byte-order 00092 mark. */ 00093 00094 if (ch1 == 0xFE && ch2 == 0xFF) 00095 { 00096 return 1; 00097 } 00098 else if (ch1 == 0xFF && ch2 == 0xFE) 00099 { 00100 return -1; 00101 } 00102 return 0; 00103 } 00104 00105 bool readTwoChars(ID3_Reader& reader, 00106 ID3_Reader::char_type& ch1, 00107 ID3_Reader::char_type& ch2) 00108 { 00109 if (reader.atEnd()) 00110 { 00111 return false; 00112 } 00113 io::ExitTrigger et(reader); 00114 ch1 = reader.readChar(); 00115 if (reader.atEnd()) 00116 { 00117 return false; 00118 } 00119 et.release(); 00120 ch2 = reader.readChar(); 00121 return true; 00122 } 00123 } 00124 00125 String io::readUnicodeString(ID3_Reader& reader) 00126 { 00127 String unicode; 00128 ID3_Reader::char_type ch1, ch2; 00129 if (!readTwoChars(reader, ch1, ch2) || isNull(ch1, ch2)) 00130 { 00131 return unicode; 00132 } 00133 int bom = isBOM(ch1, ch2); 00134 if (!bom) 00135 { 00136 unicode += static_cast<char>(ch1); 00137 unicode += static_cast<char>(ch2); 00138 } 00139 while (!reader.atEnd()) 00140 { 00141 if (!readTwoChars(reader, ch1, ch2) || isNull(ch1, ch2)) 00142 { 00143 break; 00144 } 00145 if (bom == -1) 00146 { 00147 unicode += static_cast<char>(ch2); 00148 unicode += static_cast<char>(ch1); 00149 } 00150 else 00151 { 00152 unicode += static_cast<char>(ch1); 00153 unicode += static_cast<char>(ch2); 00154 } 00155 } 00156 return unicode; 00157 } 00158 00159 String io::readUnicodeText(ID3_Reader& reader, size_t len) 00160 { 00161 String unicode; 00162 ID3_Reader::char_type ch1, ch2; 00163 if (!readTwoChars(reader, ch1, ch2)) 00164 { 00165 return unicode; 00166 } 00167 len -= 2; 00168 int bom = isBOM(ch1, ch2); 00169 if (!bom) 00170 { 00171 unicode += ch1; 00172 unicode += ch2; 00173 unicode += readText(reader, len); 00174 } 00175 else if (bom == 1) 00176 { 00177 unicode = readText(reader, len); 00178 } 00179 else 00180 { 00181 for (size_t i = 0; i < len; i += 2) 00182 { 00183 if (!readTwoChars(reader, ch1, ch2)) 00184 { 00185 break; 00186 } 00187 unicode += ch2; 00188 unicode += ch1; 00189 } 00190 } 00191 return unicode; 00192 } 00193 00194 BString io::readAllBinary(ID3_Reader& reader) 00195 { 00196 return readBinary(reader, reader.remainingBytes()); 00197 } 00198 00199 BString io::readBinary(ID3_Reader& reader, size_t len) 00200 { 00201 BString binary; 00202 binary.reserve(len); 00203 00204 size_t remaining = len; 00205 const size_t SIZE = 1024; 00206 ID3_Reader::char_type buf[SIZE]; 00207 while (!reader.atEnd() && remaining > 0) 00208 { 00209 size_t numRead = reader.readChars(buf, min(remaining, SIZE)); 00210 remaining -= numRead; 00211 binary.append(reinterpret_cast<BString::value_type *>(buf), numRead); 00212 } 00213 00214 return binary; 00215 } 00216 00217 uint32 io::readLENumber(ID3_Reader& reader, size_t len) 00218 { 00219 uint32 val = 0; 00220 for (size_t i = 0; i < len; i++) 00221 { 00222 if (reader.atEnd()) 00223 { 00224 break; 00225 } 00226 val += (static_cast<uint32>(0xFF & reader.readChar()) << (i * 8)); 00227 } 00228 return val; 00229 } 00230 00231 uint32 io::readBENumber(ID3_Reader& reader, size_t len) 00232 { 00233 uint32 val = 0; 00234 00235 for (ID3_Reader::size_type i = 0; i < len && !reader.atEnd(); ++i) 00236 { 00237 val *= 256; // 2^8 00238 val += static_cast<uint32>(0xFF & reader.readChar()); 00239 } 00240 return val; 00241 } 00242 00243 String io::readTrailingSpaces(ID3_Reader& reader, size_t len) 00244 { 00245 io::WindowedReader wr(reader, len); 00246 String str; 00247 String spaces; 00248 str.reserve(len); 00249 spaces.reserve(len); 00250 while (!wr.atEnd()) 00251 { 00252 ID3_Reader::char_type ch = wr.readChar(); 00253 if (ch == '\0' || ch == ' ') 00254 { 00255 spaces += ch; 00256 } 00257 else 00258 { 00259 str += spaces + (char) ch; 00260 spaces.erase(); 00261 } 00262 } 00263 return str; 00264 } 00265 00266 uint32 io::readUInt28(ID3_Reader& reader) 00267 { 00268 uint32 val = 0; 00269 const unsigned short BITSUSED = 7; 00270 const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32)); 00271 // For each byte of the first 4 bytes in the string... 00272 for (size_t i = 0; i < sizeof(uint32); ++i) 00273 { 00274 if (reader.atEnd()) 00275 { 00276 break; 00277 } 00278 // ...append the last 7 bits to the end of the temp integer... 00279 val = (val << BITSUSED) | static_cast<uint32>(reader.readChar()) & MASK(BITSUSED); 00280 } 00281 00282 // We should always parse 4 characters 00283 return min(val, MAXVAL); 00284 } 00285 00286 size_t io::writeBENumber(ID3_Writer& writer, uint32 val, size_t len) 00287 { 00288 ID3_Writer::char_type bytes[sizeof(uint32)]; 00289 ID3_Writer::size_type size = min<ID3_Reader::size_type>(len, sizeof(uint32)); 00290 renderNumber(bytes, val, size); 00291 return writer.writeChars(bytes, size); 00292 } 00293 00294 size_t io::writeTrailingSpaces(ID3_Writer& writer, String buf, size_t len) 00295 { 00296 ID3_Writer::pos_type beg = writer.getCur(); 00297 ID3_Writer::size_type strLen = buf.size(); 00298 ID3_Writer::size_type size = min((unsigned int)len, (unsigned int)strLen); 00299 writer.writeChars(buf.data(), size); 00300 for (; size < len; ++size) 00301 { 00302 writer.writeChar('\0'); 00303 } 00304 return writer.getCur() - beg; 00305 } 00306 00307 size_t io::writeUInt28(ID3_Writer& writer, uint32 val) 00308 { 00309 uchar data[sizeof(uint32)]; 00310 const unsigned short BITSUSED = 7; 00311 const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32)); 00312 val = min(val, MAXVAL); 00313 // This loop renders the value to the character buffer in reverse order, as 00314 // it is easy to extract the last 7 bits of an integer. This is why the 00315 // loop shifts the value of the integer by 7 bits for each iteration. 00316 for (size_t i = 0; i < sizeof(uint32); ++i) 00317 { 00318 // Extract the last BITSUSED bits from val and put it in its appropriate 00319 // place in the data buffer 00320 data[sizeof(uint32) - i - 1] = static_cast<uchar>(val & MASK(BITSUSED)); 00321 00322 // The last BITSUSED bits were extracted from the val. So shift it to the 00323 // right by that many bits for the next iteration 00324 val >>= BITSUSED; 00325 } 00326 00327 // Should always render 4 bytes 00328 return writer.writeChars(data, sizeof(uint32)); 00329 } 00330 00331 size_t io::writeString(ID3_Writer& writer, String data) 00332 { 00333 size_t size = writeText(writer, data); 00334 writer.writeChar('\0'); 00335 return size + 1; 00336 } 00337 00338 size_t io::writeText(ID3_Writer& writer, String data) 00339 { 00340 ID3_Writer::pos_type beg = writer.getCur(); 00341 writer.writeChars(data.data(), data.size()); 00342 return writer.getCur() - beg; 00343 } 00344 00345 size_t io::writeUnicodeString(ID3_Writer& writer, String data, bool bom) 00346 { 00347 size_t size = writeUnicodeText(writer, data, bom); 00348 unicode_t null = NULL_UNICODE; 00349 writer.writeChars((const unsigned char*) &null, 2); 00350 return size + 2; 00351 } 00352 00353 size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom) 00354 { 00355 ID3_Writer::pos_type beg = writer.getCur(); 00356 size_t size = (data.size() / 2) * 2; 00357 if (size == 0) 00358 { 00359 return 0; 00360 } 00361 if (bom) 00362 { 00363 // Write the BOM: 0xFEFF 00364 unicode_t BOM = 0xFEFF; 00365 writer.writeChars((const unsigned char*) &BOM, 2); 00366 // Patch from Spoon : 2004-08-25 14:17 00367 // http://sourceforge.net/tracker/index.php?func=detail&aid=1016290&group_id=979&atid=300979 00368 // Wrong code 00369 //for (size_t i = 0; i < size; i += 2) 00370 //{ 00371 // unicode_t ch = (data[i] << 8) | data[i+1]; 00372 // writer.writeChars((const unsigned char*) &ch, 2); 00373 //} 00374 // Right code 00375 unsigned char *pdata = (unsigned char *) 00376 data.c_str(); 00377 for (size_t i = 0; i < size; i += 2) 00378 { 00379 unicode_t ch = (pdata[i] << 8) | pdata[i+1]; 00380 writer.writeChars((const unsigned char*) &ch, 2); 00381 } 00382 // End patch 00383 } 00384 return writer.getCur() - beg; 00385 } 00386