📄 tag_parse_musicmatch.cpp
字号:
// $Id: tag_parse_musicmatch.cpp,v 1.19 2002/07/02 22:15:18 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 <ctype.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"using namespace dami;namespace { uint32 readSeconds(ID3_Reader& reader, size_t len) { io::ExitTrigger et(reader); io::WindowedReader wr(reader, len); ID3_Reader::pos_type beg = wr.getCur(); uint32 seconds = 0; uint32 cur = 0; while (!wr.atEnd()) { ID3_Reader::char_type ch = wr.readChar(); if (':' == ch) { seconds += 60 * cur; cur = 0; } else if (!isdigit(ch)) { return 0; } else { cur = cur * 10 + (ch - '0'); } } et.release(); return seconds + cur; } ID3_Frame* readTextFrame(ID3_Reader& reader, ID3_FrameID id, const String desc = "") { uint32 size = io::readLENumber(reader, 2); ID3D_NOTICE( "readTextFrame: size = " << size ); if (size == 0) { return NULL; } String text; if (ID3FID_SONGLEN != id) { io::LineFeedReader lfr(reader); text = io::readText(lfr, size); ID3D_NOTICE( "readTextFrame: text = " << text ); } else { text = toString(readSeconds(reader, size) * 1000); ID3D_NOTICE( "readTextFrame: songlen = " << text ); } ID3_Frame* frame = new ID3_Frame(id); if (frame) { if (frame->Contains(ID3FN_TEXT)) { frame->GetField(ID3FN_TEXT)->Set(text.c_str()); } else if (frame->Contains(ID3FN_URL)) { frame->GetField(ID3FN_URL)->Set(text.c_str()); } if (frame->Contains(ID3FN_LANGUAGE)) { frame->GetField(ID3FN_LANGUAGE)->Set("XXX"); } if (frame->Contains(ID3FN_DESCRIPTION)) { frame->GetField(ID3FN_DESCRIPTION)->Set(desc.c_str()); } } return frame; }}; bool mm::parse(ID3_TagImpl& tag, ID3_Reader& rdr){ io::ExitTrigger et(rdr); ID3_Reader::pos_type end = rdr.getCur(); if (end < rdr.getBeg() + 48) { ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse, pos = " << end ); return false; } rdr.setCur(end - 48); String version; { if (io::readText(rdr, 32) != "Brava Software Inc. ") { ID3D_NOTICE( "mm::parse: bailing, couldn't find footer" ); return false; } version = io::readText(rdr, 4); if (version.size() != 4 || !isdigit(version[0]) || version[1] != '.' || !isdigit(version[2]) || !isdigit(version[3])) { ID3D_WARNING( "mm::parse: bailing, nonstandard version = " << version ); return false; } } ID3_Reader::pos_type beg = rdr.setCur(end - 48); et.setExitPos(beg); if (end < 68) { ID3D_NOTICE( "mm::parse: bailing, not enough bytes to parse offsets, pos = " << end ); return false; } rdr.setCur(end - 68); io::WindowedReader dataWindow(rdr); dataWindow.setEnd(rdr.getCur()); uint32 offsets[5]; io::WindowedReader offsetWindow(rdr, 20); for (size_t i = 0; i < 5; ++i) { offsets[i] = io::readLENumber(rdr, sizeof(uint32)); } size_t metadataSize = 0; if (version <= "3.00") { // All MusicMatch tags up to and including version 3.0 had metadata // sections exactly 7868 bytes in length. metadataSize = 7868; } else { // MusicMatch tags after version 3.0 had three possible lengths for their // metadata sections. We can determine which it was by searching for // the version section signature that should precede the metadata section // by exactly 256 bytes. size_t possibleSizes[] = { 8132, 8004, 7936 }; for (size_t i = 0; i < sizeof(possibleSizes)/sizeof(size_t); ++i) { dataWindow.setCur(dataWindow.getEnd()); // Our offset will be exactly 256 bytes prior to our potential metadata // section size_t offset = possibleSizes[i] + 256; if (dataWindow.getCur() < offset) { // if our filesize is less than the offset, then it can't possibly // be the correct offset, so try again. continue; } dataWindow.setCur(dataWindow.getCur() - offset); // now read in the signature to see if it's a match if (io::readText(dataWindow, 8) == "18273645") { metadataSize = possibleSizes[i]; break; } } } if (0 == metadataSize) { // if we didn't establish a size for the metadata, then something is // wrong. probably should log this. ID3D_WARNING( "mm::parse: bailing, couldn't find meta data signature, end = " << end ); return false; } // parse the offset pointers to determine the actual sizes of all the // sections size_t sectionSizes[5]; size_t tagSize = metadataSize; // we already know the size of the last section sectionSizes[4] = metadataSize; size_t lastOffset = 0; for (int i = 0; i < 5; i++) { size_t thisOffset = offsets[i]; //ASSERT(thisOffset > lastOffset); if (i > 0) { size_t sectionSize = thisOffset - lastOffset; sectionSizes[i-1] = sectionSize; tagSize += sectionSize; } lastOffset = thisOffset; } // now check to see that our tag size is reasonable if (dataWindow.getEnd() < tagSize) { // Ack! The tag size doesn't jive with the tag's ending position in // the file. Bail! ID3D_WARNING( "mm::parse: bailing, tag size is too big, tag size = " << tagSize << ", end = " << end ); return false; } dataWindow.setBeg(dataWindow.getEnd() - tagSize); dataWindow.setCur(dataWindow.getBeg()); // Now calculate the adjusted offsets offsets[0] = dataWindow.getBeg(); for (size_t i = 0; i < 4; ++i) { offsets[i+1] = offsets[i] + sectionSizes[i]; } // now check for a tag header and adjust the tag_beg pointer appropriately if (dataWindow.getBeg() >= 256) { rdr.setCur(dataWindow.getBeg() - 256); if (io::readText(rdr, 8) == "18273645") { et.setExitPos(rdr.getCur() - 8); } else { et.setExitPos(dataWindow.getBeg()); } dataWindow.setCur(dataWindow.getBeg()); } // Now parse the various sections... // Parse the image extension at offset 0 dataWindow.setCur(offsets[0]); String imgExt = io::readTrailingSpaces(dataWindow, 4); // Parse the image binary at offset 1 dataWindow.setCur(offsets[1]); uint32 imgSize = io::readLENumber(dataWindow, 4); if (imgSize == 0) { // no image binary. don't do anything. } else { io::WindowedReader imgWindow(dataWindow, imgSize); if (imgWindow.getEnd() < imgWindow.getBeg() + imgSize) { // Ack! The image size given extends beyond the next offset! This is // not good... log? } else { BString imgData = io::readAllBinary(imgWindow); ID3_Frame* frame = new ID3_Frame(ID3FID_PICTURE); if (frame) { String mimetype("image/"); mimetype += imgExt; frame->GetField(ID3FN_MIMETYPE)->Set(mimetype.c_str()); frame->GetField(ID3FN_IMAGEFORMAT)->Set(""); frame->GetField(ID3FN_PICTURETYPE)->Set(static_cast<unsigned int>(0)); frame->GetField(ID3FN_DESCRIPTION)->Set(""); frame->GetField(ID3FN_DATA)->Set(reinterpret_cast<const uchar*>(imgData.data()), imgData.size()); tag.AttachFrame(frame); } } } //file.seekg(offsets[2]); //file.seekg(offsets[3]); dataWindow.setCur(offsets[4]); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_TITLE)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_ALBUM)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_LEADARTIST)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_CONTENTTYPE)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Tempo")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Mood")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Situation")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Preference")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_SONGLEN)); // The next 12 bytes can be ignored. The first 8 represent the // creation date as a 64 bit floating point number. The last 4 are // for a play counter. dataWindow.skipChars(12); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Path")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Serial")); // 2 bytes for track uint32 trkNum = io::readLENumber(dataWindow, 2); if (trkNum > 0) { String trkStr = toString(trkNum); ID3_Frame* frame = new ID3_Frame(ID3FID_TRACKNUM); if (frame) { frame->GetField(ID3FN_TEXT)->Set(trkStr.c_str()); tag.AttachFrame(frame); } } tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Notes")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_Bio")); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_UNSYNCEDLYRICS)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWARTIST)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_WWWCOMMERCIALINFO)); tag.AttachFrame(readTextFrame(dataWindow, ID3FID_COMMENT, "MusicMatch_ArtistEmail")); // email? return true;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -