package org.cmc.music.myid3.id3v2; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Vector; import org.cmc.music.common.ID3ReadException; import org.cmc.music.metadata.IMusicMetadata; import org.cmc.music.myid3.ID3Tag; import org.cmc.music.myid3.MyID3Listener; import org.cmc.music.myid3.id3v1.MyID3v1Constants; import org.cmc.music.util.Debug; import org.cmc.music.util.FileUtils; public class MyID3v2 implements MyID3v1Constants { private static final int ID3v2_HEADER_LENGTH = 10; public byte[] readID3v2Head(File file, boolean strict) throws IOException { if (file == null || !file.exists()) return null; long length = file.length(); if (length < ID3v2_HEADER_LENGTH) return null; InputStream is = null; try { is = new FileInputStream(file); is = new BufferedInputStream(is); byte header[]; header = FileUtils.readArray(is, ID3v2_HEADER_LENGTH); if (header[0] != 0x49) // I return null; if (header[1] != 0x44) // D return null; if (header[2] != 0x33) // 3 return null; int flags = header[5]; boolean has_footer = (flags & (1 << 4)) > 0; Number tagLength = MyID3v2Read.readSynchsafeInt(header, 6); if (tagLength == null) return null; int bodyLength = tagLength.intValue(); if (has_footer) bodyLength += ID3v2_HEADER_LENGTH; if (ID3v2_HEADER_LENGTH + bodyLength > length) return null; byte body[] = FileUtils.readArray(is, bodyLength); byte result[] = new byte[header.length + body.length]; System.arraycopy(header, 0, result, 0, header.length); System.arraycopy(body, 0, result, header.length, body.length); return result; } finally { try { if (is != null) is.close(); } catch (IOException e) { Debug.debug(e); } } } public long findID3v2HeadLength(File file) throws IOException { if (file == null || !file.exists()) return 0; long length = file.length(); if (length < ID3v2_HEADER_LENGTH) return 0; InputStream is = null; try { is = new FileInputStream(file); is = new BufferedInputStream(is); byte header[]; header = FileUtils.readArray(is, ID3v2_HEADER_LENGTH); if (header[0] != 0x49) // I return 0; if (header[1] != 0x44) // D return 0; if (header[2] != 0x33) // 3 return 0; int flags = header[5]; boolean has_footer = (flags & (1 << 4)) > 0; Number tagLength = MyID3v2Read.readSynchsafeInt(header, 6); if (tagLength == null) return 0; int totalLength = ID3v2_HEADER_LENGTH + tagLength.intValue(); if (has_footer) totalLength += ID3v2_HEADER_LENGTH; return totalLength; } finally { try { if (is != null) is.close(); } catch (IOException e) { Debug.debug(e); } } } public int findID3v2TailLength(File file, boolean hasId3v1) throws IOException { if (file == null || !file.exists()) return 0; long length = file.length(); int index = hasId3v1 ? ID3_V1_TAG_LENGTH : 0; index += ID3v2_HEADER_LENGTH; if (index > length) return 0; InputStream is = null; try { is = new FileInputStream(file); is = new BufferedInputStream(is); is.skip(length - index); byte footer[]; footer = FileUtils.readArray(is, ID3v2_HEADER_LENGTH); if (footer[0] != 0x33) // 3 return 0; if (footer[1] != 0x44) // D return 0; if (footer[2] != 0x49) // I return 0; Number tagLength = MyID3v2Read.readSynchsafeInt(footer, 6); if (tagLength == null) return 0; int totalLength = ID3v2_HEADER_LENGTH + ID3v2_HEADER_LENGTH + tagLength.intValue(); return totalLength; } finally { try { if (is != null) is.close(); } catch (IOException e) { Debug.debug(e); } } } public byte[] readID3v2Tail(File file, boolean hasId3v1, boolean strict) throws IOException { if (file == null || !file.exists()) return null; long length = file.length(); int index = hasId3v1 ? ID3_V1_TAG_LENGTH : 0; index += ID3v2_HEADER_LENGTH; if (index > length) return null; InputStream is = null; try { is = new FileInputStream(file); is = new BufferedInputStream(is); is.skip(length - index); byte footer[]; footer = FileUtils.readArray(is, ID3v2_HEADER_LENGTH); if (footer[2] != 0x33) // 3 return null; if (footer[1] != 0x44) // D return null; if (footer[0] != 0x49) // I return null; Number tagLength = MyID3v2Read.readSynchsafeInt(footer, 6); if (tagLength == null) return null; int bodyLength = tagLength.intValue(); if (index + bodyLength > length) return null; is.close(); is = null; is = new FileInputStream(file); is = new BufferedInputStream(is); long skip = length; skip -= ID3v2_HEADER_LENGTH; skip -= bodyLength; skip -= ID3v2_HEADER_LENGTH; if (hasId3v1) skip -= ID3_V1_TAG_LENGTH; is.skip(skip); byte header_and_body[] = FileUtils.readArray(is, ID3v2_HEADER_LENGTH + bodyLength + ID3v2_HEADER_LENGTH); byte result[] = header_and_body; return result; } finally { try { if (is != null) is.close(); } catch (IOException e) { Debug.debug(e); } } } public ID3Tag.V2 readID3v2(MyID3Listener listener, byte bytes[], boolean strict) throws IOException, ID3ReadException { MyID3v2Read parser = new MyID3v2Read(listener, new ByteArrayInputStream(bytes), false); while (!parser.isComplete()) { parser.iteration(); } if (parser.isError()) { if (listener != null) listener.log("id3v2 error", parser.getErrorMessage()); parser.dump(); return null; } if (!parser.hasTags()) return null; Vector frames = parser.getTags(); // Debug.debug("tags" , tags.toString()); IMusicMetadata metadata = ID3v2FrameTranslation .translateFramesToMetadata(listener, strict, frames); // IMusicMetadata values = ID3v2DataMapping.translateFrames(listener, // strict, tags); byte version_major = parser.getVersionMajor(); byte version_minor = parser.getVersionMinor(); if (null != listener) listener.log(); return new ID3Tag.V2(version_major, version_minor, bytes, metadata, frames); } public ID3Tag.V2 readID3v2(MyID3Listener listener, File file, boolean hasId3v1, boolean strict) throws IOException, ID3ReadException { if (file == null || !file.exists()) return null; byte bytes[] = null; bytes = readID3v2Tail(file, hasId3v1, strict); if (bytes == null) bytes = readID3v2Head(file, strict); if (bytes == null) return null; if (null != listener) listener.log("ID3v2 tag found: " + bytes.length + " bytes"); return readID3v2(listener, bytes, strict); } }