diff options
Diffstat (limited to 'src/tag_parse_lyrics3.cpp')
-rw-r--r-- | src/tag_parse_lyrics3.cpp | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/src/tag_parse_lyrics3.cpp b/src/tag_parse_lyrics3.cpp new file mode 100644 index 0000000..e502888 --- /dev/null +++ b/src/tag_parse_lyrics3.cpp @@ -0,0 +1,370 @@ +// $Id: tag_parse_lyrics3.cpp,v 1.35 2002/10/04 08:52:23 t1mpy Exp $ + +// id3lib: a C++ library for creating and manipulating id3v1/v2 tags +// Copyright 1999, 2000 Scott Thomas Haug +// Copyright 2002 Thijmen Klok (thijmen@id3lib.org) + +// 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/ + +#include <ctype.h> +#include <memory.h> +#include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h" +#include "helpers.h" +#include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h" +#include "io_strings.h" + +using namespace dami; + +namespace +{ + uint32 readIntegerString(ID3_Reader& reader, size_t numBytes) + { + uint32 val = 0; + for (size_t i = 0; i < numBytes && isdigit(reader.peekChar()); ++i) + { + val = (val * 10) + (reader.readChar() - '0'); + } + ID3D_NOTICE( "readIntegerString: val = " << val ); + return val; + } + + uint32 readIntegerString(ID3_Reader& reader) + { + return readIntegerString(reader, reader.remainingBytes()); + } + + bool isTimeStamp(ID3_Reader& reader) + { + ID3_Reader::pos_type cur = reader.getCur(); + if (reader.getEnd() < cur + 7) + { + return false; + } + bool its = ('[' == reader.readChar() && + isdigit(reader.readChar()) && isdigit(reader.readChar()) && + ':' == reader.readChar() && + isdigit(reader.readChar()) && isdigit(reader.readChar()) && + ']' == reader.readChar()); + reader.setCur(cur); + if (its) + { + ID3D_NOTICE( "isTimeStamp(): found timestamp, cur = " << reader.getCur() ); + } + return its; + } + + uint32 readTimeStamp(ID3_Reader& reader) + { + reader.skipChars(1); + size_t sec = readIntegerString(reader, 2) * 60; + reader.skipChars(1); + sec += readIntegerString(reader, 2); + reader.skipChars(1); + ID3D_NOTICE( "readTimeStamp(): timestamp = " << sec ); + return sec * 1000; + } + + bool findText(ID3_Reader& reader, String text) + { + if (text.empty()) + { + return true; + } + + size_t index = 0; + while (!reader.atEnd()) + { + ID3_Reader::char_type ch = reader.readChar(); + if (ch == text[index]) + { + index++; + } + else if (ch == text[0]) + { + index = 1; + } + else + { + index = 0; + } + if (index == text.size()) + { + reader.setCur(reader.getCur() - index); + ID3D_NOTICE( "findText: found \"" << text << "\" at " << + reader.getCur() ); + break; + } + } + return !reader.atEnd(); + }; + + void lyrics3ToSylt(ID3_Reader& reader, ID3_Writer& writer) + { + while (!reader.atEnd()) + { + bool lf = false; + size_t ms = 0; + size_t count = 0; + while (isTimeStamp(reader)) + { + // For now, just skip over multiple time stamps + if (count++ > 0) + { + readTimeStamp(reader); + } + else + { + ms = readTimeStamp(reader); + } + } + while (!reader.atEnd() && !isTimeStamp(reader)) + { + ID3_Reader::char_type ch = reader.readChar(); + if (0x0A == ch && (reader.atEnd() || isTimeStamp(reader))) + { + lf = true; + break; + } + else + { + writer.writeChar(ch); + } + } + + // put synch identifier + writer.writeChar('\0'); + + // put timestamp + ID3D_NOTICE( "lyrics3toSylt: ms = " << ms ); + + io::writeBENumber(writer, ms, sizeof(uint32)); + if (lf) + { + ID3D_NOTICE( "lyrics3toSylt: adding lf" ); + + // put the LF + writer.writeChar(0x0A); + } + } + } +}; + +bool lyr3::v1::parse(ID3_TagImpl& tag, ID3_Reader& reader) +{ + io::ExitTrigger et(reader); + ID3_Reader::pos_type end = reader.getCur(); + if (end < reader.getBeg() + 9 + 128) + { + ID3D_NOTICE( "id3::v1::parse: bailing, not enough bytes to parse, pos = " << end ); + return false; + } + reader.setCur(end - (9 + 128)); + + { + if (io::readText(reader, 9) != "LYRICSEND" || + io::readText(reader, 3) != "TAG") + { + return false; + } + } + + // we have a Lyrics3 v1.00 tag + if (end < reader.getBeg() + 11 + 9 + 128) + { + // the file size isn't large enough to actually hold lyrics + ID3D_WARNING( "id3::v1::parse: not enough data to parse lyrics3" ); + return false; + } + + // reserve enough space for lyrics3 + id3v1 tag + size_t window = end - reader.getBeg(); + size_t lyrDataSize = min<size_t>(window, 11 + 5100 + 9 + 128); + reader.setCur(end - lyrDataSize); + io::WindowedReader wr(reader, lyrDataSize - (9 + 128)); + + if (!findText(wr, "LYRICSBEGIN")) + { + ID3D_WARNING( "id3::v1::parse: couldn't find LYRICSBEGIN, bailing" ); + return false; + } + + et.setExitPos(wr.getCur()); + wr.skipChars(11); + wr.setBeg(wr.getCur()); + + io::LineFeedReader lfr(wr); + String lyrics = io::readText(lfr, wr.remainingBytes()); + id3::v2::setLyrics(tag, lyrics, "Converted from Lyrics3 v1.00", "XXX"); + + return true; +} + +//bool parse(TagImpl& tag, ID3_Reader& reader) +bool lyr3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader) +{ + io::ExitTrigger et(reader); + ID3_Reader::pos_type end = reader.getCur(); + if (end < reader.getBeg() + 6 + 9 + 128) + { + ID3D_NOTICE( "lyr3::v2::parse: bailing, not enough bytes to parse, pos = " << reader.getCur() ); + return false; + } + + reader.setCur(end - (6 + 9 + 128)); + uint32 lyrSize = 0; + + ID3_Reader::pos_type beg = reader.getCur(); + lyrSize = readIntegerString(reader, 6); + if (reader.getCur() < beg + 6) + { + ID3D_NOTICE( "lyr3::v2::parse: couldn't find numeric string, lyrSize = " << + lyrSize ); + return false; + } + + if (io::readText(reader, 9) != "LYRICS200" || + io::readText(reader, 3) != "TAG") + { + return false; + } + + if (end < reader.getBeg() + lyrSize + 6 + 9 + 128) + { + ID3D_WARNING( "lyr3::v2::parse: not enough data to parse tag, lyrSize = " << lyrSize ); + return false; + } + reader.setCur(end - (lyrSize + 6 + 9 + 128)); + + io::WindowedReader wr(reader); + wr.setWindow(wr.getCur(), lyrSize); + + beg = wr.getCur(); + + if (io::readText(wr, 11) != "LYRICSBEGIN") + { + // not a lyrics v2.00 tag + ID3D_WARNING( "lyr3::v2::parse: couldn't find LYRICSBEGIN, bailing" ); + return false; + } + + bool has_time_stamps = false; + + ID3_Frame* lyr_frame = NULL; + + while (!wr.atEnd()) + { + uint32 fldSize; + + String fldName = io::readText(wr, 3); + ID3D_NOTICE( "lyr3::v2::parse: fldName = " << fldName ); + fldSize = readIntegerString(wr, 5); + ID3D_NOTICE( "lyr3::v2::parse: fldSize = " << fldSize ); + + String fldData; + + io::WindowedReader wr2(wr, fldSize); + io::LineFeedReader lfr(wr2); + + fldData = io::readText(lfr, fldSize); + ID3D_NOTICE( "lyr3::v2::parse: fldData = \"" << fldData << "\"" ); + + // the IND field + if (fldName == "IND") + { + has_time_stamps = (fldData.size() > 1 && fldData[1] == '1'); + } + + // the TITLE field + else if (fldName == "ETT" && !id3::v2::hasTitle(tag)) + { + //tag.setTitle(fldData); + id3::v2::setTitle(tag, fldData); + } + + // the ARTIST field + else if (fldName == "EAR" && !id3::v2::hasArtist(tag)) + { + //tag.setArtist(fldData); + id3::v2::setArtist(tag, fldData); + } + + // the ALBUM field + else if (fldName == "EAL" && !id3::v2::hasAlbum(tag)) + { + //tag.setAlbum(fldData); + id3::v2::setAlbum(tag, fldData); + } + + // the Lyrics/Music AUTHOR field + else if (fldName == "AUT") + { + //tag.setAuthor(fldData); + id3::v2::setLyricist(tag, fldData); + } + + // the INFORMATION field + else if (fldName == "INF") + { + //tag.setInfo(fldData); + id3::v2::setComment(tag, fldData, "Lyrics3 v2.00 INF", "XXX"); + } + + // the LYRICS field + else if (fldName == "LYR") + { + // if already found an INF field, use it as description + String desc = "Converted from Lyrics3 v2.00"; + //tag.setLyrics(fldData); + if (!has_time_stamps) + { + lyr_frame = id3::v2::setLyrics(tag, fldData, desc, "XXX"); + } + else + { + // converts from lyrics3 to SYLT in-place + io::StringReader sr(fldData); + ID3D_NOTICE( "lyr3::v2::parse: determining synced lyrics" ); + BString sylt; + io::BStringWriter sw(sylt); + lyrics3ToSylt(sr, sw); + + lyr_frame = id3::v2::setSyncLyrics(tag, sylt, ID3TSF_MS, desc, + "XXX", ID3CT_LYRICS); + ID3D_NOTICE( "lyr3::v2::parse: determined synced lyrics" ); + } + } + else if (fldName == "IMG") + { + // currently unsupported + ID3D_WARNING( "lyr3::v2::parse: IMG field unsupported" ); + } + else + { + ID3D_WARNING( "lyr3::v2::parse: undefined field id: " << + fldName ); + } + } + + et.setExitPos(beg); + return true; +} + |