1 | package org.farng.mp3.id3; |
2 | |
3 | import org.farng.mp3.InvalidTagException; |
4 | import org.farng.mp3.TagUtility; |
5 | |
6 | import java.io.IOException; |
7 | import java.io.RandomAccessFile; |
8 | |
9 | /** |
10 | * <p class=t> The headers of the frames are similar in their construction. They consist of one three character |
11 | * identifier (capital A-Z and 0-9) and one three byte size field, making a total of six bytes. The header is excluded |
12 | * from the size. Identifiers beginning with "X", "Y" and "Z" are for experimental use and free for everyone to use. |
13 | * Have in mind that someone else might have used the same identifier as you. All other identifiers are either used or |
14 | * reserved for future use. <i>This gives us 46656 combinations of frame identifiers. </i></p> |
15 | * <p/> |
16 | * <p class=t> The three character frame identifier is followed by a three byte size descriptor, making a total header |
17 | * size of six bytes in every frame. The size is calculated as framesize excluding frame identifier and size descriptor |
18 | * (frame size - 6). </p> |
19 | * <p/> |
20 | * <p class=t><i> The decision to have a 6 byte frame header was taken in an attempt to balance big frames against |
21 | * little overhead. One might think that it's stupid to optimize away a few bytes when the entire MP3-file is soo huge. |
22 | * On the other hand I thought it was really cool that most ID3v1 tags, when converted to ID3v2 was smaller than before. |
23 | * Size does matter. </i></p> |
24 | * <p/> |
25 | * <p class=t> There is no fixed order of the frames' appearance in the tag, although it is desired that the frames are |
26 | * arranged in order of significance concerning the recognition of the file. <i>The reason for this is to make it faster |
27 | * to search for a specific file by scanning the ID3v2 tags; an intelligent parser wouldn't have to keep reading the |
28 | * entire tag after having found that the file isn't the one being looked for.</i> An example of such order: <a |
29 | * href="#ufi">UFI</a>, <a href="#mci">MCI</a>, <a href="#tt2">TT2</a> ...<br> </p> |
30 | * <p/> |
31 | * <p class=t> A tag must contain at least one frame. A frame must be at least 1 byte big, excluding the 6-byte header. |
32 | * </p> |
33 | * <p/> |
34 | * <p class=t> If nothing else is said, a string is represented as <a href="#iso8859">ISO-8859-1</a> characters in the |
35 | * range $20 - $FF. All <a href="#unicode">unicode</a> strings use 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). All |
36 | * numeric strings are always encoded as <a href="#iso8859">ISO-8859-1</a>. Terminated strings are terminated with $00 |
37 | * if encoded with <a href="#iso8859">ISO-8859-1</a> and $00 00 if encoded as unicode. If nothing else is said, newline |
38 | * characters are forbidden. In <a href="#iso8859">ISO-8859-1</a>, a new line is represented, when allowed, with $0A |
39 | * only. Frames that allow different types of text encoding have a text encoding description byte directly after the |
40 | * frame size. If <a href="#iso8859">ISO-8859-1</a> is used this byte should be $00, if <a href="#unicode">unicode</a> |
41 | * is used it should be $01. </p> |
42 | * <p/> |
43 | * <p class=t> The three byte language field is used to describe the language of the frame's content, according to <a |
44 | * href="#iso639-2">ISO-639-2</a>.<br> <i>ISO-639-1 is not used since its supported languages are just a subset of those |
45 | * in ISO-639-2.</i> </p> |
46 | * <p/> |
47 | * <p class=t> All <a href="#url">URL</a>s may be relative, e.g. "picture.png", "../doc.txt". </p> |
48 | * <p/> |
49 | * <p class=t> If a frame is longer than it should be, e.g. having more fields than specified in this document, that |
50 | * indicates that additions to the frame have been made in a later version of the ID3 standard. This is reflected by the |
51 | * revision number in the header of the tag.<br> <i>This allows us to fix our mistakes as well as introducing new |
52 | * features in the already existing frames. </i></p> |
53 | * |
54 | * @author Eric Farng |
55 | * @version $Revision: 1.6 $ |
56 | */ |
57 | public class ID3v2_2Frame extends AbstractID3v2Frame { |
58 | |
59 | /** |
60 | * Creates a new ID3v2_2Frame object. |
61 | */ |
62 | public ID3v2_2Frame() { |
63 | // base empty constructor |
64 | } |
65 | |
66 | /** |
67 | * Creates a new ID3v2_2Frame object. |
68 | */ |
69 | public ID3v2_2Frame(final AbstractID3v2FrameBody body) { |
70 | super(body); |
71 | } |
72 | |
73 | /** |
74 | * Creates a new ID3v2_2Frame object. |
75 | */ |
76 | public ID3v2_2Frame(final ID3v2_2Frame frame) { |
77 | super(frame); |
78 | } |
79 | |
80 | /** |
81 | * Creates a new ID3v2_3Frame object. |
82 | */ |
83 | public ID3v2_2Frame(final AbstractID3v2Frame frame) { |
84 | if (frame.getBody() == null) { |
85 | // do nothing |
86 | } else if (TagUtility.isID3v2_2FrameIdentifier(frame.getIdentifier())) { |
87 | this.setBody((AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody())); |
88 | // } else if (TagUtility.isID3v2_3FrameIdentifier(frame.getIdentifier())) { |
89 | // // @todo correctly convert tags |
90 | // this.setBody((AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody())); |
91 | // } else if (TagUtility.isID3v2_4FrameIdentifier(frame.getIdentifier())) { |
92 | // // @todo correctly convert tags |
93 | // this.setBody((AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody())); |
94 | } |
95 | } |
96 | |
97 | /** |
98 | * Creates a new ID3v2_2Frame object. |
99 | */ |
100 | public ID3v2_2Frame(final RandomAccessFile file) throws IOException, InvalidTagException { |
101 | this.read(file); |
102 | } |
103 | |
104 | public int getSize() { |
105 | return this.getBody().getSize() + 3 + 3; |
106 | } |
107 | |
108 | public void read(final RandomAccessFile file) throws IOException, InvalidTagException { |
109 | final byte[] buffer = new byte[3]; |
110 | |
111 | // lets scan for a non-zero byte; |
112 | long filePointer; |
113 | byte b; |
114 | do { |
115 | filePointer = file.getFilePointer(); |
116 | b = file.readByte(); |
117 | org.farng.mp3.id3.AbstractID3v2.incrementPaddingCounter(); |
118 | } while (b == 0); |
119 | file.seek(filePointer); |
120 | org.farng.mp3.id3.AbstractID3v2.decrementPaddingCounter(); |
121 | |
122 | // read the 3 chracter identifier |
123 | file.read(buffer, 0, 3); |
124 | final String identifier = new String(buffer, 0, 3); |
125 | |
126 | // is this a valid identifier? |
127 | if (isValidID3v2FrameIdentifier(identifier) == false) { |
128 | file.seek(file.getFilePointer() - 2); |
129 | throw new InvalidTagException(identifier + " is not a valid ID3v2.20 frame"); |
130 | } |
131 | this.setBody(readBody(identifier, file)); |
132 | } |
133 | |
134 | public void write(final RandomAccessFile file) throws IOException { |
135 | final byte[] buffer = new byte[4]; |
136 | final String str = TagUtility.truncate(getIdentifier(), 3); |
137 | for (int i = 0; i < str.length(); i++) { |
138 | buffer[i] = (byte) str.charAt(i); |
139 | } |
140 | file.write(buffer, 0, str.length()); |
141 | this.getBody().write(file); |
142 | } |
143 | } |