| 1 | package org.farng.mp3.id3; |
| 2 | |
| 3 | import org.farng.mp3.InvalidTagException; |
| 4 | import org.farng.mp3.TagConstant; |
| 5 | import org.farng.mp3.TagUtility; |
| 6 | import org.farng.mp3.lyrics3.FieldBodyAUT; |
| 7 | import org.farng.mp3.lyrics3.FieldBodyEAL; |
| 8 | import org.farng.mp3.lyrics3.FieldBodyEAR; |
| 9 | import org.farng.mp3.lyrics3.FieldBodyETT; |
| 10 | import org.farng.mp3.lyrics3.FieldBodyINF; |
| 11 | import org.farng.mp3.lyrics3.FieldBodyLYR; |
| 12 | import org.farng.mp3.lyrics3.Lyrics3v2Field; |
| 13 | import org.farng.mp3.object.ObjectLyrics3Line; |
| 14 | |
| 15 | import java.io.IOException; |
| 16 | import java.io.RandomAccessFile; |
| 17 | import java.util.Iterator; |
| 18 | |
| 19 | /** |
| 20 | * <p> All ID3v2 frames consists of one frame header followed by one or more<br> fields |
| 21 | * containing the actual information. The header is always 10<br> bytes and laid out as follows:</p> |
| 22 | * <p/> |
| 23 | * <p> Frame ID $xx xx xx xx (four characters)<br> |
| 24 | * Size 4 * %0xxxxxxx<br> |
| 25 | * Flags $xx xx</p> |
| 26 | * <p/> |
| 27 | * <p> The frame ID is made out of the characters capital A-Z and 0-9.<br> Identifiers |
| 28 | * beginning with "X", "Y" and "Z" are for experimental<br> frames and free |
| 29 | * for everyone to use, without the need to set the<br> experimental bit in the tag header. Bear in mind |
| 30 | * that someone else<br> might have used the same identifier as you. All other identifiers are<br> |
| 31 | * either used or reserved for future use.</p> |
| 32 | * <p/> |
| 33 | * <p> The frame ID is followed by a size descriptor containing the size of<br> the data in the |
| 34 | * final frame, after encryption, compression and<br> unsynchronisation. The size is excluding the frame |
| 35 | * header ('total<br> frame size' - 10 bytes) and stored as a 32 bit synchsafe integer.</p> |
| 36 | * <p/> |
| 37 | * <p> In the frame header the size descriptor is followed by two flag<br> bytes. These flags |
| 38 | * are described in section 4.1.</p> |
| 39 | * <p/> |
| 40 | * <p> There is no fixed order of the frames' appearance in the tag,<br> although it is desired |
| 41 | * that the frames are arranged in order of<br> significance concerning the recognition of the file. An |
| 42 | * example of<br> such order: UFID, TIT2, MCDI, TRCK ...</p> |
| 43 | * <p/> |
| 44 | * <p> A tag MUST contain at least one frame. A frame must be at least 1<br> byte big, |
| 45 | * excluding the header.</p> |
| 46 | * <p/> |
| 47 | * <p> If nothing else is said, strings, including numeric strings and URLs<br> [URL], are |
| 48 | * represented as ISO-8859-1 [ISO-8859-1] characters in the<br> range $20 - $FF. Such strings are |
| 49 | * represented in frame descriptions<br> as <text string>, or <full text string> if newlines |
| 50 | * are allowed. If<br> nothing else is said newline character is forbidden. In ISO-8859-1 a<br> |
| 51 | * newline is represented, when allowed, with $0A only.</p> |
| 52 | * <p/> |
| 53 | * <p> Frames that allow different types of text encoding contains a text<br> encoding |
| 54 | * description byte. Possible encodings:</p> |
| 55 | * <p/> |
| 56 | * <p> $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.<br> |
| 57 | * $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All<br> |
| 58 | * strings in the same frame SHALL have the same |
| 59 | * byteorder.<br> Terminated with $00 00.<br> |
| 60 | * $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.<br> |
| 61 | * Terminated with $00 00.<br> |
| 62 | * $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.</p> |
| 63 | * <p/> |
| 64 | * <p> Strings dependent on encoding are represented in frame descriptions<br> as <text |
| 65 | * string according to encoding>, or <full text string<br> according to encoding> if newlines are |
| 66 | * allowed. Any empty strings of<br> type $01 which are NULL-terminated may have the Unicode BOM |
| 67 | * followed<br> by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).</p> |
| 68 | * <p/> |
| 69 | * <p> The timestamp fields are based on a subset of ISO 8601. When being as<br> precise as |
| 70 | * possible the format of a time string is<br> yyyy-MM-ddTHH:mm:ss (year, "-", month, |
| 71 | * "-", day, "T", hour (out of<br> 24), ":", minutes, ":", |
| 72 | * seconds), but the precision may be reduced by<br> removing as many time indicators as wanted. Hence |
| 73 | * valid timestamps<br> are<br> yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm |
| 74 | * and<br> yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use<br> the slash |
| 75 | * character as described in 8601, and for multiple non-<br> contiguous dates, use multiple strings, if |
| 76 | * allowed by the frame<br> definition.</p> |
| 77 | * <p/> |
| 78 | * <p> The three byte language field, present in several frames, is used to<br> describe the |
| 79 | * language of the frame's content, according to ISO-639-2<br> [ISO-639-2]. The language should be |
| 80 | * represented in lower case. If the<br> language is not known the string "XXX" should be |
| 81 | * used.</p> |
| 82 | * <p/> |
| 83 | * <p> All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt".</p> |
| 84 | * <p/> |
| 85 | * <p> If a frame is longer than it should be, e.g. having more fields than<br> specified in |
| 86 | * this document, that indicates that additions to the<br> frame have been made in a later version of the |
| 87 | * ID3v2 standard. This<br> is reflected by the revision number in the header of the tag.<br> </p> <a |
| 88 | * name="sec4.1"></a> |
| 89 | * <p/> |
| 90 | * <h3>4.1. Frame header flags</h3> |
| 91 | * <p/> |
| 92 | * <p> In the frame header the size descriptor is followed by two flag<br> bytes. All unused |
| 93 | * flags MUST be cleared. The first byte is for<br> 'status messages' and the second byte is a format |
| 94 | * description. If an<br> unknown flag is set in the first byte the frame MUST NOT be changed<br> |
| 95 | * without that bit cleared. If an unknown flag is set in the second<br> byte the frame is |
| 96 | * likely to not be readable. Some flags in the second<br> byte indicates that extra information is added |
| 97 | * to the header. These<br> fields of extra information is ordered as the flags that indicates<br> |
| 98 | * them. The flags field is defined as follows (l and o left out because<br> ther resemblence |
| 99 | * to one and zero):</p> |
| 100 | * <p/> |
| 101 | * <p> %0abc0000 %0h00kmnp</p> |
| 102 | * <p/> |
| 103 | * <p> Some frame format flags indicate that additional information fields<br> are added to the |
| 104 | * frame. This information is added after the frame<br> header and before the frame data in the same order |
| 105 | * as the flags that<br> indicates them. I.e. the four bytes of decompressed size will precede<br> |
| 106 | * the encryption method byte. These additions affects the 'frame size'<br> field, but are not |
| 107 | * subject to encryption or compression.<br> </p> |
| 108 | * <p/> |
| 109 | * <p> The default status flags setting for a frame is, unless stated<br> otherwise, 'preserved |
| 110 | * if tag is altered' and 'preserved if file is<br> altered', i.e. %00000000.<br> </p> <a |
| 111 | * name="sec4.1.1"></a> |
| 112 | * <p/> |
| 113 | * <h3>4.1.1. Frame status flags</h3> |
| 114 | * <p/> |
| 115 | * <p> a - Tag alter preservation</p> |
| 116 | * <p/> |
| 117 | * <p> This flag tells the tag parser what to do with this frame if it is<br> |
| 118 | * unknown and the tag is altered in any way. This applies to all<br> |
| 119 | * kinds of alterations, including adding more padding and reordering<br> the frames.</p> |
| 120 | * <p/> |
| 121 | * <p> 0 Frame should be preserved.<br> |
| 122 | * 1 Frame should be discarded.<br> </p> |
| 123 | * <p/> |
| 124 | * <p> b - File alter preservation</p> |
| 125 | * <p/> |
| 126 | * <p> This flag tells the tag parser what to do with this frame if it is<br> |
| 127 | * unknown and the file, excluding the tag, is altered. This does not<br> |
| 128 | * apply when the audio is completely replaced with other audio data.</p> |
| 129 | * <p/> |
| 130 | * <p> 0 Frame should be preserved.<br> |
| 131 | * 1 Frame should be discarded.<br> </p> |
| 132 | * <p/> |
| 133 | * <p> c - Read only</p> |
| 134 | * <p/> |
| 135 | * <p> This flag, if set, tells the software that the contents of this<br> |
| 136 | * frame are intended to be read only. Changing the contents might<br> |
| 137 | * break something, e.g. a signature. If the contents are changed,<br> |
| 138 | * without knowledge of why the frame was flagged read only and<br> |
| 139 | * without taking the proper means to compensate, e.g. recalculating<br> |
| 140 | * the signature, the bit MUST be cleared.<br> </p> <a name="sec4.1.2"></a> |
| 141 | * <p/> |
| 142 | * <h3>4.1.2. Frame format flags</h3> |
| 143 | * <p/> |
| 144 | * <p> h - Grouping identity</p> |
| 145 | * <p/> |
| 146 | * <p> This flag indicates whether or not this frame belongs in a group<br> |
| 147 | * with other frames. If set, a group identifier byte is added to the<br> |
| 148 | * frame. Every frame with the same group identifier belongs to the<br> |
| 149 | * same group.</p> |
| 150 | * <p/> |
| 151 | * <p> 0 Frame does not contain group information<br> |
| 152 | * 1 Frame contains group information<br> </p> |
| 153 | * <p/> |
| 154 | * <p> k - Compression</p> |
| 155 | * <p/> |
| 156 | * <p> This flag indicates whether or not the frame is compressed.<br> |
| 157 | * A 'Data Length Indicator' byte MUST be included in the frame.</p> |
| 158 | * <p/> |
| 159 | * <p> 0 Frame is not compressed.<br> |
| 160 | * 1 Frame is compressed using zlib [zlib] deflate method.<br> |
| 161 | * If set, this requires the 'Data Length Indicator' |
| 162 | * bit<br> to be set as well.<br> </p> |
| 163 | * <p/> |
| 164 | * <p> m - Encryption<br> </p> |
| 165 | * <p/> |
| 166 | * <p> This flag indicates whether or not the frame is encrypted. If set,<br> |
| 167 | * one byte indicating with which method it was encrypted will be<br> |
| 168 | * added to the frame. See description of the ENCR frame for more<br> |
| 169 | * information about encryption method registration. Encryption<br> |
| 170 | * should be done after compression. Whether or not setting this flag<br> |
| 171 | * requires the presence of a 'Data Length Indicator' depends on the<br> |
| 172 | * specific algorithm used.</p> |
| 173 | * <p/> |
| 174 | * <p> 0 Frame is not encrypted.<br> |
| 175 | * 1 Frame is encrypted.</p> |
| 176 | * <p/> |
| 177 | * <p> n - Unsynchronisation</p> |
| 178 | * <p/> |
| 179 | * <p> This flag indicates whether or not unsynchronisation was applied<br> |
| 180 | * to this frame. See section 6 for details on unsynchronisation.<br> |
| 181 | * If this flag is set all data from the end of this header to the<br> |
| 182 | * end of this frame has been unsynchronised. Although desirable, the<br> |
| 183 | * presence of a 'Data Length Indicator' is not made mandatory by<br> |
| 184 | * unsynchronisation.</p> |
| 185 | * <p/> |
| 186 | * <p> 0 Frame has not been unsynchronised.<br> |
| 187 | * 1 Frame has been unsyrchronised.</p> |
| 188 | * <p/> |
| 189 | * <p> p - Data length indicator</p> |
| 190 | * <p/> |
| 191 | * <p> This flag indicates that a data length indicator has been added to<br> |
| 192 | * the frame. The data length indicator is the value one would write<br> |
| 193 | * as the 'Frame length' if all of the frame format flags were<br> |
| 194 | * zeroed, represented as a 32 bit synchsafe integer.</p> |
| 195 | * <p/> |
| 196 | * <p> 0 There is no Data Length Indicator.<br> |
| 197 | * 1 A data length Indicator has been added to the |
| 198 | * frame.<br> </p> |
| 199 | * |
| 200 | * @author Eric Farng |
| 201 | * @version $Revision: 1.6 $ |
| 202 | */ |
| 203 | public class ID3v2_4Frame extends ID3v2_3Frame { |
| 204 | |
| 205 | protected boolean dataLengthIndicator = false; |
| 206 | protected boolean unsynchronization = false; |
| 207 | |
| 208 | /** |
| 209 | * Creates a new ID3v2_4Frame object. |
| 210 | */ |
| 211 | public ID3v2_4Frame() { |
| 212 | // base empty constructor |
| 213 | } |
| 214 | |
| 215 | /** |
| 216 | * Creates a new ID3v2_4Frame object. |
| 217 | */ |
| 218 | public ID3v2_4Frame(final ID3v2_4Frame copyObject) { |
| 219 | super(copyObject); |
| 220 | this.dataLengthIndicator = copyObject.dataLengthIndicator; |
| 221 | this.unsynchronization = copyObject.unsynchronization; |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * Creates a new ID3v2_4Frame object. |
| 226 | */ |
| 227 | public ID3v2_4Frame(final AbstractID3v2FrameBody body) { |
| 228 | super(body); |
| 229 | } |
| 230 | |
| 231 | /** |
| 232 | * Creates a new ID3v2_4Frame object. |
| 233 | */ |
| 234 | public ID3v2_4Frame(final AbstractID3v2Frame frame) { |
| 235 | if (frame instanceof ID3v2_4Frame) { |
| 236 | final ID3v2_4Frame f = (ID3v2_4Frame) frame; |
| 237 | this.unsynchronization = f.unsynchronization; |
| 238 | this.dataLengthIndicator = f.dataLengthIndicator; |
| 239 | } |
| 240 | if (frame instanceof ID3v2_3Frame) { |
| 241 | // a id3v2_4 frame is of type id3v2_3 frame also ... |
| 242 | final ID3v2_3Frame f = (ID3v2_3Frame) frame; |
| 243 | this.tagAlterPreservation = f.tagAlterPreservation; |
| 244 | this.fileAlterPreservation = f.fileAlterPreservation; |
| 245 | this.readOnly = f.readOnly; |
| 246 | this.groupingIdentity = f.groupingIdentity; |
| 247 | this.compression = f.compression; |
| 248 | this.encryption = f.encryption; |
| 249 | } |
| 250 | if (frame instanceof ID3v2_2Frame) { |
| 251 | // no variables yet |
| 252 | } |
| 253 | if (frame.getBody() == null) { |
| 254 | // do nothing |
| 255 | } else if (TagUtility.isID3v2_4FrameIdentifier(frame.getIdentifier())) { |
| 256 | this.setBody((AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody())); |
| 257 | // } else if (TagUtility.isID3v2_3FrameIdentifier(frame.getIdentifier())) { |
| 258 | // // @TODO correctly convert tags |
| 259 | // this.setBody((AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody())); |
| 260 | // } else if (TagUtility.isID3v2_2FrameIdentifier(frame.getIdentifier())) { |
| 261 | // // @TODO correctly convert tags |
| 262 | // this.setBody((AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody())); |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | /** |
| 267 | * Creates a new ID3v2_4Frame object. |
| 268 | */ |
| 269 | public ID3v2_4Frame(final boolean readOnly, |
| 270 | final boolean groupingIdentity, |
| 271 | final boolean compression, |
| 272 | final boolean encryption, |
| 273 | final boolean unsynchronization, |
| 274 | final boolean dataLengthIndicator, |
| 275 | final AbstractID3v2FrameBody body) { |
| 276 | super(body); |
| 277 | this.readOnly = readOnly; |
| 278 | this.groupingIdentity = groupingIdentity; |
| 279 | this.compression = compression; |
| 280 | this.encryption = encryption; |
| 281 | this.unsynchronization = unsynchronization; |
| 282 | this.dataLengthIndicator = dataLengthIndicator; |
| 283 | } |
| 284 | |
| 285 | /** |
| 286 | * Creates a new ID3v2_4Frame object. |
| 287 | */ |
| 288 | public ID3v2_4Frame(final Lyrics3v2Field field) throws InvalidTagException { |
| 289 | final String id = field.getIdentifier(); |
| 290 | final String value; |
| 291 | if (id.equals("IND")) { |
| 292 | throw new InvalidTagException("Cannot create ID3v2.40 frame from Lyrics3 indications field."); |
| 293 | } else if (id.equals("LYR")) { |
| 294 | final FieldBodyLYR lyric = (FieldBodyLYR) field.getBody(); |
| 295 | ObjectLyrics3Line line; |
| 296 | final Iterator iterator = lyric.iterator(); |
| 297 | final FrameBodySYLT sync; |
| 298 | final FrameBodyUSLT unsync; |
| 299 | final boolean hasTimeStamp = lyric.hasTimeStamp(); |
| 300 | |
| 301 | // we'll create only one frame here. |
| 302 | // if there is any timestamp at all, we will create a sync'ed frame. |
| 303 | sync = new FrameBodySYLT((byte) 0, "ENG", (byte) 2, (byte) 1, ""); |
| 304 | unsync = new FrameBodyUSLT((byte) 0, "ENG", "", ""); |
| 305 | while (iterator.hasNext()) { |
| 306 | line = (ObjectLyrics3Line) iterator.next(); |
| 307 | if (hasTimeStamp) { |
| 308 | sync.addLyric(line); |
| 309 | } else { |
| 310 | unsync.addLyric(line); |
| 311 | } |
| 312 | } |
| 313 | if (hasTimeStamp) { |
| 314 | this.setBody(sync); |
| 315 | } else { |
| 316 | this.setBody(unsync); |
| 317 | } |
| 318 | } else if (id.equals("INF")) { |
| 319 | value = ((FieldBodyINF) field.getBody()).getAdditionalInformation(); |
| 320 | this.setBody(new FrameBodyCOMM((byte) 0, "ENG", "", value)); |
| 321 | } else if (id.equals("AUT")) { |
| 322 | value = ((FieldBodyAUT) field.getBody()).getAuthor(); |
| 323 | this.setBody(new FrameBodyTCOM((byte) 0, value)); |
| 324 | } else if (id.equals("EAL")) { |
| 325 | value = ((FieldBodyEAL) field.getBody()).getAlbum(); |
| 326 | this.setBody(new FrameBodyTALB((byte) 0, value)); |
| 327 | } else if (id.equals("EAR")) { |
| 328 | value = ((FieldBodyEAR) field.getBody()).getArtist(); |
| 329 | this.setBody(new FrameBodyTPE1((byte) 0, value)); |
| 330 | } else if (id.equals("ETT")) { |
| 331 | value = ((FieldBodyETT) field.getBody()).getTitle(); |
| 332 | this.setBody(new FrameBodyTIT2((byte) 0, value)); |
| 333 | } else if (id.equals("IMG")) { |
| 334 | throw new InvalidTagException("Cannot create ID3v2.40 frame from Lyrics3 image field."); |
| 335 | } else { |
| 336 | throw new InvalidTagException("Cannot caret ID3v2.40 frame from " + id + " Lyrics3 field"); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | /** |
| 341 | * Creates a new ID3v2_4Frame object. |
| 342 | */ |
| 343 | public ID3v2_4Frame(final RandomAccessFile file) throws IOException, InvalidTagException { |
| 344 | this.read(file); |
| 345 | } |
| 346 | |
| 347 | public int getSize() { |
| 348 | return this.getBody().getSize() + 4 + 2 + 4; |
| 349 | } |
| 350 | |
| 351 | public boolean equals(final Object obj) { |
| 352 | if ((obj instanceof ID3v2_4Frame) == false) { |
| 353 | return false; |
| 354 | } |
| 355 | final ID3v2_4Frame id3v2_4Frame = (ID3v2_4Frame) obj; |
| 356 | if (this.unsynchronization != id3v2_4Frame.unsynchronization) { |
| 357 | return false; |
| 358 | } |
| 359 | if (this.dataLengthIndicator != id3v2_4Frame.dataLengthIndicator) { |
| 360 | return false; |
| 361 | } |
| 362 | return super.equals(obj); |
| 363 | } |
| 364 | |
| 365 | public void read(final RandomAccessFile file) throws IOException, InvalidTagException { |
| 366 | long filePointer; |
| 367 | final byte[] buffer = new byte[4]; |
| 368 | byte b; |
| 369 | |
| 370 | // lets scan for a non-zero byte; |
| 371 | do { |
| 372 | filePointer = file.getFilePointer(); |
| 373 | b = file.readByte(); |
| 374 | org.farng.mp3.id3.AbstractID3v2.incrementPaddingCounter(); |
| 375 | } while (b == 0); |
| 376 | file.seek(filePointer); |
| 377 | org.farng.mp3.id3.AbstractID3v2.decrementPaddingCounter(); |
| 378 | |
| 379 | // read the four character identifier |
| 380 | file.read(buffer, 0, 4); |
| 381 | final String identifier = new String(buffer, 0, 4); |
| 382 | |
| 383 | // is this a valid identifier? |
| 384 | if (isValidID3v2FrameIdentifier(identifier) == false) { |
| 385 | file.seek(file.getFilePointer() - 3); |
| 386 | throw new InvalidTagException(identifier + " is not a valid ID3v2.40 frame"); |
| 387 | } |
| 388 | filePointer = file.getFilePointer(); |
| 389 | |
| 390 | // skip the 4 byte size |
| 391 | file.skipBytes(4); |
| 392 | |
| 393 | // read the flag bytes |
| 394 | file.read(buffer, 0, 2); |
| 395 | this.tagAlterPreservation = (buffer[0] & TagConstant.MASK_V24_TAG_ALTER_PRESERVATION) != 0; |
| 396 | this.fileAlterPreservation = (buffer[0] & TagConstant.MASK_V24_FILE_ALTER_PRESERVATION) != 0; |
| 397 | this.readOnly = (buffer[0] & TagConstant.MASK_V24_READ_ONLY) != 0; |
| 398 | this.groupingIdentity = (buffer[1] & TagConstant.MASK_V24_GROUPING_IDENTITY) != 0; |
| 399 | this.compression = (buffer[1] & TagConstant.MASK_V24_COMPRESSION) != 0; |
| 400 | this.encryption = (buffer[1] & TagConstant.MASK_V24_ENCRYPTION) != 0; |
| 401 | this.unsynchronization = (buffer[1] & TagConstant.MASK_V24_FRAME_UNSYNCHRONIZATION) != 0; |
| 402 | this.dataLengthIndicator = (buffer[1] & TagConstant.MASK_V24_DATA_LENGTH_INDICATOR) != 0; |
| 403 | file.seek(filePointer); |
| 404 | this.setBody(readBody(identifier, file)); |
| 405 | } |
| 406 | |
| 407 | public void write(final RandomAccessFile file) throws IOException { |
| 408 | final byte[] buffer = new byte[4]; |
| 409 | final long filePointer; |
| 410 | final String str = TagUtility.truncate(getIdentifier(), 4); |
| 411 | for (int i = 0; i < str.length(); i++) { |
| 412 | buffer[i] = (byte) str.charAt(i); |
| 413 | } |
| 414 | file.write(buffer, 0, str.length()); |
| 415 | filePointer = file.getFilePointer(); |
| 416 | |
| 417 | // skip the size bytes |
| 418 | file.skipBytes(4); |
| 419 | setAlterPreservation(); |
| 420 | buffer[0] = 0; |
| 421 | buffer[1] = 0; |
| 422 | if (this.tagAlterPreservation) { |
| 423 | buffer[0] |= TagConstant.MASK_V24_TAG_ALTER_PRESERVATION; |
| 424 | } |
| 425 | if (this.fileAlterPreservation) { |
| 426 | buffer[0] |= TagConstant.MASK_V24_FILE_ALTER_PRESERVATION; |
| 427 | } |
| 428 | if (this.readOnly) { |
| 429 | buffer[0] |= TagConstant.MASK_V24_READ_ONLY; |
| 430 | } |
| 431 | if (this.groupingIdentity) { |
| 432 | buffer[1] |= TagConstant.MASK_V24_GROUPING_IDENTITY; |
| 433 | } |
| 434 | if (this.compression) { |
| 435 | buffer[1] |= TagConstant.MASK_V24_COMPRESSION; |
| 436 | } |
| 437 | if (this.encryption) { |
| 438 | buffer[1] |= TagConstant.MASK_V24_ENCRYPTION; |
| 439 | } |
| 440 | if (this.unsynchronization) { |
| 441 | buffer[1] |= TagConstant.MASK_V24_FRAME_UNSYNCHRONIZATION; |
| 442 | } |
| 443 | if (this.dataLengthIndicator) { |
| 444 | buffer[1] |= TagConstant.MASK_V24_DATA_LENGTH_INDICATOR; |
| 445 | } |
| 446 | file.write(buffer, 0, 2); |
| 447 | file.seek(filePointer); |
| 448 | this.getBody().write(file); |
| 449 | } |
| 450 | } |