1 /* 2 * Copyright (c) 2007, 2016, 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 final long ckSize; 43 private InputStream stream; 44 private long avail = 0xffffffffL; // MAX_UNSIGNED_INT 45 private RIFFReader lastiterator = null; 46 47 public RIFFReader(final InputStream stream) throws IOException { 48 49 if (stream instanceof RIFFReader) { 50 root = ((RIFFReader) stream).root; 51 } else { 52 root = this; 53 } 54 55 this.stream = stream; 56 57 // Check for RIFF null paddings, 58 int b; 59 while (true) { 60 b = read(); 61 if (b == -1) { 62 fourcc = ""; // don't put null value into fourcc, 63 // because it is expected to 64 // always contain a string value 65 riff_type = null; 66 ckSize = 0; 67 avail = 0; 68 return; 69 } 70 if (b != 0) { 71 break; 72 } 73 } 74 75 byte[] fourcc = new byte[4]; 76 fourcc[0] = (byte) b; 77 readFully(fourcc, 1, 3); 78 this.fourcc = new String(fourcc, "ascii"); 79 ckSize = readUnsignedInt(); 80 avail = 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 @Override 121 public int read() throws IOException { 122 if (avail == 0) { 123 return -1; 124 } 125 int b = stream.read(); 126 if (b == -1) { 127 avail = 0; 128 return -1; 129 } 130 avail--; 131 filepointer++; 132 return b; 133 } 134 135 @Override 136 public int read(byte[] b, int offset, int len) throws IOException { 137 if (avail == 0) { 138 return -1; 139 } 140 if (len > avail) { 141 int rlen = stream.read(b, offset, (int)avail); 142 if (rlen != -1) 143 filepointer += rlen; 144 avail = 0; 145 return rlen; 146 } else { 147 int ret = stream.read(b, offset, len); 148 if (ret == -1) { 149 avail = 0; 150 return -1; 151 } 152 avail -= ret; 153 filepointer += ret; 154 return ret; 155 } 156 } 157 158 public void readFully(byte b[]) throws IOException { 159 readFully(b, 0, b.length); 160 } 161 162 public void readFully(byte b[], int off, int len) throws IOException { 163 if (len < 0) 164 throw new IndexOutOfBoundsException(); 165 while (len > 0) { 166 int s = read(b, off, len); 167 if (s < 0) 168 throw new EOFException(); 169 if (s == 0) 170 Thread.yield(); 171 off += s; 172 len -= s; 173 } 174 } 175 176 @Override 177 public long skip(final long n) throws IOException { 178 if (n <= 0 || avail == 0) { 179 return 0; 180 } 181 // will not skip more than 182 long remaining = Math.min(n, avail); 183 while (remaining > 0) { 184 // Some input streams like FileInputStream can return more bytes, 185 // when EOF is reached. 186 long ret = Math.min(stream.skip(remaining), remaining); 187 if (ret == 0) { 188 // EOF or not? we need to check. 189 Thread.yield(); 190 if (stream.read() == -1) { 191 avail = 0; 192 break; 193 } 194 ret = 1; 195 } else if (ret < 0) { 196 // the skip should not return negative value, but check it also 197 avail = 0; 198 break; 199 } 200 remaining -= ret; 201 avail -= ret; 202 filepointer += ret; 203 } 204 return n - remaining; 205 } 206 207 @Override 208 public int available() { 209 return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) avail; 210 } 211 212 public void finish() throws IOException { 213 if (avail != 0) { 214 skip(avail); 215 } 216 } 217 218 // Read ASCII chars from stream 219 public String readString(final int len) throws IOException { 220 final byte[] buff; 221 try { 222 buff = new byte[len]; 223 } catch (final OutOfMemoryError oom) { 224 throw new IOException("Length too big", oom); 225 } 226 readFully(buff); 227 for (int i = 0; i < buff.length; i++) { 228 if (buff[i] == 0) { 229 return new String(buff, 0, i, "ascii"); 230 } 231 } 232 return new String(buff, "ascii"); 233 } 234 235 // Read 8 bit signed integer from stream 236 public byte readByte() throws IOException { 237 int ch = read(); 238 if (ch < 0) 239 throw new EOFException(); 240 return (byte) ch; 241 } 242 243 // Read 16 bit signed integer from stream 244 public short readShort() throws IOException { 245 int ch1 = read(); 246 int ch2 = read(); 247 if (ch1 < 0) 248 throw new EOFException(); 249 if (ch2 < 0) 250 throw new EOFException(); 251 return (short)(ch1 | (ch2 << 8)); 252 } 253 254 // Read 32 bit signed integer from stream 255 public int readInt() throws IOException { 256 int ch1 = read(); 257 int ch2 = read(); 258 int ch3 = read(); 259 int ch4 = read(); 260 if (ch1 < 0) 261 throw new EOFException(); 262 if (ch2 < 0) 263 throw new EOFException(); 264 if (ch3 < 0) 265 throw new EOFException(); 266 if (ch4 < 0) 267 throw new EOFException(); 268 return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24); 269 } 270 271 // Read 64 bit signed integer from stream 272 public long readLong() throws IOException { 273 long ch1 = read(); 274 long ch2 = read(); 275 long ch3 = read(); 276 long ch4 = read(); 277 long ch5 = read(); 278 long ch6 = read(); 279 long ch7 = read(); 280 long ch8 = read(); 281 if (ch1 < 0) 282 throw new EOFException(); 283 if (ch2 < 0) 284 throw new EOFException(); 285 if (ch3 < 0) 286 throw new EOFException(); 287 if (ch4 < 0) 288 throw new EOFException(); 289 if (ch5 < 0) 290 throw new EOFException(); 291 if (ch6 < 0) 292 throw new EOFException(); 293 if (ch7 < 0) 294 throw new EOFException(); 295 if (ch8 < 0) 296 throw new EOFException(); 297 return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24) 298 | (ch5 << 32) | (ch6 << 40) | (ch7 << 48) | (ch8 << 56); 299 } 300 301 // Read 8 bit unsigned integer from stream 302 public int readUnsignedByte() throws IOException { 303 int ch = read(); 304 if (ch < 0) 305 throw new EOFException(); 306 return ch; 307 } 308 309 // Read 16 bit unsigned integer from stream 310 public int readUnsignedShort() throws IOException { 311 int ch1 = read(); 312 int ch2 = read(); 313 if (ch1 < 0) 314 throw new EOFException(); 315 if (ch2 < 0) 316 throw new EOFException(); 317 return ch1 | (ch2 << 8); 318 } 319 320 // Read 32 bit unsigned integer from stream 321 public long readUnsignedInt() throws IOException { 322 long ch1 = read(); 323 long ch2 = read(); 324 long ch3 = read(); 325 long ch4 = read(); 326 if (ch1 < 0) 327 throw new EOFException(); 328 if (ch2 < 0) 329 throw new EOFException(); 330 if (ch3 < 0) 331 throw new EOFException(); 332 if (ch4 < 0) 333 throw new EOFException(); 334 return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24); 335 } 336 337 @Override 338 public void close() throws IOException { 339 finish(); 340 if (this == root) 341 stream.close(); 342 stream = null; 343 } 344 }