1 /* 2 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.media.sound; 26 27 import java.io.EOFException; 28 import java.io.IOException; 29 import java.io.InputStream; 30 31 /** 32 * Resource Interchange File Format (RIFF) stream decoder. 33 * 34 * @author Karl Helgason 35 */ 36 public final class RIFFReader extends InputStream { 37 38 private final RIFFReader root; 39 private long filepointer = 0; 40 private final String fourcc; 41 private String riff_type = null; 42 private long ckSize = 0; 43 private InputStream stream; 44 private long avail; 45 private RIFFReader lastiterator = null; 46 47 public RIFFReader(InputStream stream) throws IOException { 48 49 if (stream instanceof RIFFReader) 50 root = ((RIFFReader)stream).root; 51 else 52 root = this; 53 54 this.stream = stream; 55 avail = Integer.MAX_VALUE; 56 ckSize = Integer.MAX_VALUE; 57 58 // Check for RIFF null paddings, 59 int b; 60 while (true) { 61 b = read(); 62 if (b == -1) { 63 fourcc = ""; // don't put null value into fourcc, 64 // because it is expected to 65 // always contain a string value 66 riff_type = null; 67 avail = 0; 68 return; 69 } 70 if (b != 0) 71 break; 72 } 73 74 byte[] fourcc = new byte[4]; 75 fourcc[0] = (byte) b; 76 readFully(fourcc, 1, 3); 77 this.fourcc = new String(fourcc, "ascii"); 78 ckSize = readUnsignedInt(); 79 80 avail = this.ckSize; 81 82 if (getFormat().equals("RIFF") || getFormat().equals("LIST")) { 83 byte[] format = new byte[4]; 84 readFully(format); 85 this.riff_type = new String(format, "ascii"); 86 } 87 } 88 89 public long getFilePointer() throws IOException { 90 return root.filepointer; 91 } 92 93 public boolean hasNextChunk() throws IOException { 94 if (lastiterator != null) 95 lastiterator.finish(); 96 return avail != 0; 97 } 98 99 public RIFFReader nextChunk() throws IOException { 100 if (lastiterator != null) 101 lastiterator.finish(); 102 if (avail == 0) 103 return null; 104 lastiterator = new RIFFReader(this); 105 return lastiterator; 106 } 107 108 public String getFormat() { 109 return fourcc; 110 } 111 112 public String getType() { 113 return riff_type; 114 } 115 116 public long getSize() { 117 return ckSize; 118 } 119 120 public int read() throws IOException { 121 if (avail == 0) 122 return -1; 123 int b = stream.read(); 124 if (b == -1) 125 return -1; 126 avail--; 127 filepointer++; 128 return b; 129 } 130 131 public int read(byte[] b, int offset, int len) throws IOException { 132 if (avail == 0) 133 return -1; 134 if (len > avail) { 135 int rlen = stream.read(b, offset, (int)avail); 136 if (rlen != -1) 137 filepointer += rlen; 138 avail = 0; 139 return rlen; 140 } else { 141 int ret = stream.read(b, offset, len); 142 if (ret == -1) 143 return -1; 144 avail -= ret; 145 filepointer += ret; 146 return ret; 147 } 148 } 149 150 public void readFully(byte b[]) throws IOException { 151 readFully(b, 0, b.length); 152 } 153 154 public void readFully(byte b[], int off, int len) throws IOException { 155 if (len < 0) 156 throw new IndexOutOfBoundsException(); 157 while (len > 0) { 158 int s = read(b, off, len); 159 if (s < 0) 160 throw new EOFException(); 161 if (s == 0) 162 Thread.yield(); 163 off += s; 164 len -= s; 165 } 166 } 167 168 public long skipBytes(long n) throws IOException { 169 if (n < 0) 170 return 0; 171 long skipped = 0; 172 while (skipped != n) { 173 long s = skip(n - skipped); 174 if (s < 0) 175 break; 176 if (s == 0) 177 Thread.yield(); 178 skipped += s; 179 } 180 return skipped; 181 } 182 183 public long skip(long n) throws IOException { 184 if (avail == 0) 185 return -1; 186 if (n > avail) { 187 long len = stream.skip(avail); 188 if (len != -1) 189 filepointer += len; 190 avail = 0; 191 return len; 192 } else { 193 long ret = stream.skip(n); 194 if (ret == -1) 195 return -1; 196 avail -= ret; 197 filepointer += ret; 198 return ret; 199 } 200 } 201 202 public int available() { 203 return (int)avail; 204 } 205 206 public void finish() throws IOException { 207 if (avail != 0) { 208 skipBytes(avail); 209 } 210 } 211 212 // Read ASCII chars from stream 213 public String readString(int len) throws IOException { 214 byte[] buff = new byte[len]; 215 readFully(buff); 216 for (int i = 0; i < buff.length; i++) { 217 if (buff[i] == 0) { 218 return new String(buff, 0, i, "ascii"); 219 } 220 } 221 return new String(buff, "ascii"); 222 } 223 224 // Read 8 bit signed integer from stream 225 public byte readByte() throws IOException { 226 int ch = read(); 227 if (ch < 0) 228 throw new EOFException(); 229 return (byte) ch; 230 } 231 232 // Read 16 bit signed integer from stream 233 public short readShort() throws IOException { 234 int ch1 = read(); 235 int ch2 = read(); 236 if (ch1 < 0) 237 throw new EOFException(); 238 if (ch2 < 0) 239 throw new EOFException(); 240 return (short)(ch1 | (ch2 << 8)); 241 } 242 243 // Read 32 bit signed integer from stream 244 public int readInt() throws IOException { 245 int ch1 = read(); 246 int ch2 = read(); 247 int ch3 = read(); 248 int ch4 = read(); 249 if (ch1 < 0) 250 throw new EOFException(); 251 if (ch2 < 0) 252 throw new EOFException(); 253 if (ch3 < 0) 254 throw new EOFException(); 255 if (ch4 < 0) 256 throw new EOFException(); 257 return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24); 258 } 259 260 // Read 64 bit signed integer from stream 261 public long readLong() throws IOException { 262 long ch1 = read(); 263 long ch2 = read(); 264 long ch3 = read(); 265 long ch4 = read(); 266 long ch5 = read(); 267 long ch6 = read(); 268 long ch7 = read(); 269 long ch8 = read(); 270 if (ch1 < 0) 271 throw new EOFException(); 272 if (ch2 < 0) 273 throw new EOFException(); 274 if (ch3 < 0) 275 throw new EOFException(); 276 if (ch4 < 0) 277 throw new EOFException(); 278 if (ch5 < 0) 279 throw new EOFException(); 280 if (ch6 < 0) 281 throw new EOFException(); 282 if (ch7 < 0) 283 throw new EOFException(); 284 if (ch8 < 0) 285 throw new EOFException(); 286 return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24) 287 | (ch5 << 32) | (ch6 << 40) | (ch7 << 48) | (ch8 << 56); 288 } 289 290 // Read 8 bit unsigned integer from stream 291 public int readUnsignedByte() throws IOException { 292 int ch = read(); 293 if (ch < 0) 294 throw new EOFException(); 295 return ch; 296 } 297 298 // Read 16 bit unsigned integer from stream 299 public int readUnsignedShort() throws IOException { 300 int ch1 = read(); 301 int ch2 = read(); 302 if (ch1 < 0) 303 throw new EOFException(); 304 if (ch2 < 0) 305 throw new EOFException(); 306 return ch1 | (ch2 << 8); 307 } 308 309 // Read 32 bit unsigned integer from stream 310 public long readUnsignedInt() throws IOException { 311 long ch1 = read(); 312 long ch2 = read(); 313 long ch3 = read(); 314 long ch4 = read(); 315 if (ch1 < 0) 316 throw new EOFException(); 317 if (ch2 < 0) 318 throw new EOFException(); 319 if (ch3 < 0) 320 throw new EOFException(); 321 if (ch4 < 0) 322 throw new EOFException(); 323 return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24); 324 } 325 326 public void close() throws IOException { 327 finish(); 328 if (this == root) 329 stream.close(); 330 stream = null; 331 } 332 }