diff options
Diffstat (limited to 'src/io_helpers.cpp')
-rw-r--r-- | src/io_helpers.cpp | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/src/io_helpers.cpp b/src/io_helpers.cpp new file mode 100644 index 0000000..a4bbbe4 --- /dev/null +++ b/src/io_helpers.cpp @@ -0,0 +1,404 @@ +// $Id: io_helpers.cpp,v 1.13 2002/07/02 22:13:56 t1mpy Exp $ + +// id3lib: a C++ library for creating and manipulating id3v1/v2 tags +// Copyright 1999, 2000 Scott Thomas Haug + +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Library General Public License as published by +// the Free Software Foundation; either version 2 of the License, or (at your +// option) any later version. +// +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +// License for more details. +// +// You should have received a copy of the GNU Library General Public License +// along with this library; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +// The id3lib authors encourage improvements and optimisations to be sent to +// the id3lib coordinator. Please see the README file for details on where to +// send such submissions. See the AUTHORS file for a list of people who have +// contributed to id3lib. See the ChangeLog file for a list of changes to +// id3lib. These files are distributed with id3lib at +// http://download.sourceforge.net/id3lib/ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h" + +using namespace dami; + +String io::readString(ID3_Reader& reader) +{ + String str; + while (!reader.atEnd()) + { + ID3_Reader::char_type ch = reader.readChar(); + if (ch == '\0') + { + break; + } + str += static_cast<char>(ch); + } + return str; +} + +String io::readText(ID3_Reader& reader, size_t len) +{ + String str; + str.reserve(len); + const size_t SIZE = 1024; + ID3_Reader::char_type buf[SIZE]; + size_t remaining = len; + while (remaining > 0 && !reader.atEnd()) + { + size_t numRead = reader.readChars(buf, min(remaining, SIZE)); + remaining -= numRead; + str.append(reinterpret_cast<String::value_type *>(buf), numRead); + } + return str; +} + +namespace +{ + bool isNull(unsigned char ch1, unsigned char ch2) + { + return ch1 == '\0' && ch2 == '\0'; + } + + int isBOM(unsigned char ch1, unsigned char ch2) + { + // The following is taken from the following URL: + // http://community.roxen.com/developers/idocs/rfc/rfc2781.html + /* The Unicode Standard and ISO 10646 define the character "ZERO WIDTH + NON-BREAKING SPACE" (0xFEFF), which is also known informally as + "BYTE ORDER MARK" (abbreviated "BOM"). The latter name hints at a + second possible usage of the character, in addition to its normal + use as a genuine "ZERO WIDTH NON-BREAKING SPACE" within text. This + usage, suggested by Unicode section 2.4 and ISO 10646 Annex F + (informative), is to prepend a 0xFEFF character to a stream of + Unicode characters as a "signature"; a receiver of such a serialized + stream may then use the initial character both as a hint that the + stream consists of Unicode characters and as a way to recognize the + serialization order. In serialized UTF-16 prepended with such a + signature, the order is big-endian if the first two octets are 0xFE + followed by 0xFF; if they are 0xFF followed by 0xFE, the order is + little-endian. Note that 0xFFFE is not a Unicode character, + precisely to preserve the usefulness of 0xFEFF as a byte-order + mark. */ + + if (ch1 == 0xFE && ch2 == 0xFF) + { + return 1; + } + else if (ch1 == 0xFF && ch2 == 0xFE) + { + return -1; + } + return 0; + } + + bool readTwoChars(ID3_Reader& reader, + ID3_Reader::char_type& ch1, + ID3_Reader::char_type& ch2) + { + if (reader.atEnd()) + { + return false; + } + io::ExitTrigger et(reader); + ch1 = reader.readChar(); + if (reader.atEnd()) + { + return false; + } + et.release(); + ch2 = reader.readChar(); + return true; + } +} + +String io::readUnicodeString(ID3_Reader& reader) +{ + String unicode; + ID3_Reader::char_type ch1, ch2; + if (!readTwoChars(reader, ch1, ch2) || isNull(ch1, ch2)) + { + return unicode; + } + int bom = isBOM(ch1, ch2); + if (!bom) + { + unicode += static_cast<char>(ch1); + unicode += static_cast<char>(ch2); + } + while (!reader.atEnd()) + { + if (!readTwoChars(reader, ch1, ch2) || isNull(ch1, ch2)) + { + break; + } + if (bom == -1) + { + unicode += static_cast<char>(ch2); + unicode += static_cast<char>(ch1); + } + else + { + unicode += static_cast<char>(ch1); + unicode += static_cast<char>(ch2); + } + } + return unicode; +} + +String io::readUnicodeText(ID3_Reader& reader, size_t len) +{ + String unicode; + ID3_Reader::char_type ch1, ch2; + if (!readTwoChars(reader, ch1, ch2)) + { + return unicode; + } + len -= 2; + int bom = isBOM(ch1, ch2); +#ifdef WORDS_BIGENDIAN + bom = -bom; // switch things around for big endian +#endif + if (!bom) + { +#ifdef WORDS_BIGENDIAN + unicode += ch2; + unicode += ch1; + for (size_t i = 0; i < len; i += 2) + { + if (!readTwoChars(reader, ch1, ch2)) + { + break; + } + unicode += ch2; + unicode += ch1; + } +#else + unicode += ch1; + unicode += ch2; + unicode += readText(reader, len); +#endif + } + else if (bom == -1) + { + unicode = readText(reader, len); + } + else + { + for (size_t i = 0; i < len; i += 2) + { + if (!readTwoChars(reader, ch1, ch2)) + { + break; + } + unicode += ch2; + unicode += ch1; + } + } + unicode += '\0'; // ESL: need to terminate double-byte string properly. + return unicode; +} + +BString io::readAllBinary(ID3_Reader& reader) +{ + return readBinary(reader, reader.remainingBytes()); +} + +BString io::readBinary(ID3_Reader& reader, size_t len) +{ + BString binary; + binary.reserve(len); + + size_t remaining = len; + const size_t SIZE = 1024; + ID3_Reader::char_type buf[SIZE]; + while (!reader.atEnd() && remaining > 0) + { + size_t numRead = reader.readChars(buf, min(remaining, SIZE)); + remaining -= numRead; + binary.append(reinterpret_cast<BString::value_type *>(buf), numRead); + } + + return binary; +} + +uint32 io::readLENumber(ID3_Reader& reader, size_t len) +{ + uint32 val = 0; + for (size_t i = 0; i < len; i++) + { + if (reader.atEnd()) + { + break; + } + val += (static_cast<uint32>(0xFF & reader.readChar()) << (i * 8)); + } + return val; +} + +uint32 io::readBENumber(ID3_Reader& reader, size_t len) +{ + uint32 val = 0; + + for (ID3_Reader::size_type i = 0; i < len && !reader.atEnd(); ++i) + { + val *= 256; // 2^8 + val += static_cast<uint32>(0xFF & reader.readChar()); + } + return val; +} + +String io::readTrailingSpaces(ID3_Reader& reader, size_t len) +{ + io::WindowedReader wr(reader, len); + String str; + String spaces; + str.reserve(len); + spaces.reserve(len); + while (!wr.atEnd()) + { + ID3_Reader::char_type ch = wr.readChar(); + if (ch == '\0' || ch == ' ') + { + spaces += ch; + } + else + { + str += spaces + (char) ch; + spaces.erase(); + } + } + return str; +} + +uint32 io::readUInt28(ID3_Reader& reader) +{ + uint32 val = 0; + const unsigned short BITSUSED = 7; + const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32)); + // For each byte of the first 4 bytes in the string... + for (size_t i = 0; i < sizeof(uint32); ++i) + { + if (reader.atEnd()) + { + break; + } + // ...append the last 7 bits to the end of the temp integer... + val = (val << BITSUSED) | static_cast<uint32>(reader.readChar()) & MASK(BITSUSED); + } + + // We should always parse 4 characters + return min(val, MAXVAL); +} + +size_t io::writeBENumber(ID3_Writer& writer, uint32 val, size_t len) +{ + ID3_Writer::char_type bytes[sizeof(uint32)]; + ID3_Writer::size_type size = min<ID3_Reader::size_type>(len, sizeof(uint32)); + renderNumber(bytes, val, size); + return writer.writeChars(bytes, size); +} + +size_t io::writeTrailingSpaces(ID3_Writer& writer, String buf, size_t len) +{ + ID3_Writer::pos_type beg = writer.getCur(); + ID3_Writer::size_type strLen = buf.size(); + ID3_Writer::size_type size = min((unsigned int)len, (unsigned int)strLen); + writer.writeChars(buf.data(), size); + for (; size < len; ++size) + { + writer.writeChar('\0'); + } + return writer.getCur() - beg; +} + +size_t io::writeUInt28(ID3_Writer& writer, uint32 val) +{ + uchar data[sizeof(uint32)]; + const unsigned short BITSUSED = 7; + const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32)); + val = min(val, MAXVAL); + // This loop renders the value to the character buffer in reverse order, as + // it is easy to extract the last 7 bits of an integer. This is why the + // loop shifts the value of the integer by 7 bits for each iteration. + for (size_t i = 0; i < sizeof(uint32); ++i) + { + // Extract the last BITSUSED bits from val and put it in its appropriate + // place in the data buffer + data[sizeof(uint32) - i - 1] = static_cast<uchar>(val & MASK(BITSUSED)); + + // The last BITSUSED bits were extracted from the val. So shift it to the + // right by that many bits for the next iteration + val >>= BITSUSED; + } + + // Should always render 4 bytes + return writer.writeChars(data, sizeof(uint32)); +} + +size_t io::writeString(ID3_Writer& writer, String data) +{ + size_t size = writeText(writer, data); + writer.writeChar('\0'); + return size + 1; +} + +size_t io::writeText(ID3_Writer& writer, String data) +{ + ID3_Writer::pos_type beg = writer.getCur(); + writer.writeChars(data.data(), data.size()); + return writer.getCur() - beg; +} + +size_t io::writeUnicodeString(ID3_Writer& writer, String data, bool bom) +{ + size_t size = writeUnicodeText(writer, data, bom); + unicode_t null = NULL_UNICODE; + writer.writeChars((const unsigned char*) &null, 2); + return size + 2; +} + +size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom) +{ + ID3_Writer::pos_type beg = writer.getCur(); + size_t size = (data.size() / 2) * 2; + if (size == 0) + { + return 0; + } + int is_bom = isBOM(data[0],data[1]); + if (!is_bom && bom) { + // Write the BOM: 0xFEFF + const unsigned char BOMch1 = 0xFE; + const unsigned char BOMch2 = 0xFF; + writer.writeChars(&BOMch1, 1); + writer.writeChars(&BOMch2, 1); + } + for (size_t i = 0; i < size; i += 2) + { + if (!i && !bom && is_bom) { + // Skip unneeded leading BOM + continue; + } + if (is_bom >= 0) { + writer.writeChars(data.data()+i, 1); + writer.writeChars(data.data()+i+1, 1); + } + else { + writer.writeChars(data.data()+i+1, 1); + writer.writeChars(data.data()+i, 1); + } + } + return writer.getCur() - beg; +} + |