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 26 package com.sun.media.sound; 27 28 import java.io.EOFException; 29 import java.io.IOException; 30 import java.io.InputStream; 31 32 /** 33 * Resource Interchange File Format (RIFF) stream decoder. 34 * 35 * @author Karl Helgason 36 */ 37 public final class RIFFReader extends InputStream { 38 39 private final RIFFReader root; 40 private long filepointer = 0; 41 private final String fourcc; 42 private String riff_type = null; 43 private final long ckSize; 44 private InputStream stream; 45 private long avail = 0xffffffffL; // MAX_UNSIGNED_INT 46 private RIFFReader lastiterator = null; 47 48 public RIFFReader(final InputStream stream) throws IOException { 49 50 if (stream instanceof RIFFReader) { 51 root = ((RIFFReader) stream).root; 52 } else { 53 root = this; 54 } 55 56 this.stream = stream; 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 ckSize = 0; 68 avail = 0; 69 return; 70 } 71 if (b != 0) { 72 break; 73 } 74 } 75 76 byte[] fourcc = new byte[4]; 77 fourcc[0] = (byte) b; 78 readFully(fourcc, 1, 3); 79 this.fourcc = new String(fourcc, "ascii"); 80 ckSize = readUnsignedInt(); 81 avail = ckSize; 82 83 if (getFormat().equals("RIFF") || getFormat().equals("LIST")) { 84 byte[] format = new byte[4]; 85 readFully(format); 86 this.riff_type = new String(format, "ascii"); 87 } 88 } 89 90 public long getFilePointer() throws IOException { 91 return root.filepointer; 92 } 93 94 public boolean hasNextChunk() throws IOException { 95 if (lastiterator != null) 96 lastiterator.finish(); 97 return avail != 0; 98 } 99 100 public RIFFReader nextChunk() throws IOException { 101 if (lastiterator != null) 102 lastiterator.finish(); 103 if (avail == 0) 104 return null; 105 lastiterator = new RIFFReader(this); 106 return lastiterator; 107 } 108 109 public String getFormat() { 110 return fourcc; 111 } 112 113 public String getType() { 114 return riff_type; 115 } 116 117 public long getSize() { 118 return ckSize; 119 } 120 121 @Override 122 public int read() throws IOException { 123 if (avail == 0) { 124 return -1; 125 } 126 int b = stream.read(); 127 if (b == -1) { 128 avail = 0; 129 return -1; 130 } 131 avail--; 132 filepointer++; 133 return b; 134 } 135 136 @Override 137 public int read(byte[] b, int offset, int len) throws IOException { 138 if (avail == 0) { 139 return -1; 140 } 141 if (len > avail) { 142 int rlen = stream.read(b, offset, (int)avail); 143 if (rlen != -1) 144 filepointer += rlen; 145 avail = 0; 146 return rlen; 147 } else { 148 int ret = stream.read(b, offset, len); 149 if (ret == -1) { 150 avail = 0; 151 return -1; 152 } 153 avail -= ret; 154 filepointer += ret; 155 return ret; 156 } 157 } 158 159 public void readFully(byte b[]) throws IOException { 160 readFully(b, 0, b.length); 161 } 162 163 public void readFully(byte b[], int off, int len) throws IOException { 164 if (len < 0) 165 throw new IndexOutOfBoundsException(); 166 while (len > 0) { 167 int s = read(b, off, len); 168 if (s < 0) 169 throw new EOFException(); 170 if (s == 0) 171 Thread.yield(); 172 off += s; 173 len -= s; 174 } 175 } 176 177 @Override 178 public long skip(final long n) throws IOException { 179 if (n <= 0 || avail == 0) { 180 return 0; 181 } 182 // will not skip more than 183 long remaining = Math.min(n, avail); 184 while (remaining > 0) { 185 // Some input streams like FileInputStream can return more bytes, 186 // when EOF is reached. 187 long ret = Math.min(stream.skip(remaining), remaining); 188 if (ret == 0) { 189 // EOF or not? we need to check. 190 Thread.yield(); 191 if (stream.read() == -1) { 192 avail = 0; 193 break; 194 } 195 ret = 1; 196 } else if (ret < 0) { 197 // the skip should not return negative value, but check it also 198 avail = 0; 199 break; 200 } 201 remaining -= ret; 202 avail -= ret; 203 filepointer += ret; 204 } 205 return n - remaining; 206 } 207 208 @Override 209 public int available() { 210 return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) avail; 211 } 212 213 public void finish() throws IOException { 214 if (avail != 0) { 215 skip(avail); 216 } 217 } 218 219 // Read ASCII chars from stream 220 public String readString(final int len) throws IOException { 221 final byte[] buff; 222 try { 223 buff = new byte[len]; 224 } catch (final OutOfMemoryError oom) { 225 throw new IOException("Length too big", oom); 226 } 227 readFully(buff); 228 for (int i = 0; i < buff.length; i++) { 229 if (buff[i] == 0) { 230 return new String(buff, 0, i, "ascii"); 231 } 232 } 233 return new String(buff, "ascii"); 234 } 235 236 // Read 8 bit signed integer from stream 237 public byte readByte() throws IOException { 238 int ch = read(); 239 if (ch < 0) 240 throw new EOFException(); 241 return (byte) ch; 242 } 243 244 // Read 16 bit signed integer from stream 245 public short readShort() throws IOException { 246 int ch1 = read(); 247 int ch2 = read(); 248 if (ch1 < 0) 249 throw new EOFException(); 250 if (ch2 < 0) 251 throw new EOFException(); 252 return (short)(ch1 | (ch2 << 8)); 253 } 254 255 // Read 32 bit signed integer from stream 256 public int readInt() throws IOException { 257 int ch1 = read(); 258 int ch2 = read(); 259 int ch3 = read(); 260 int ch4 = read(); 261 if (ch1 < 0) 262 throw new EOFException(); 263 if (ch2 < 0) 264 throw new EOFException(); 265 if (ch3 < 0) 266 throw new EOFException(); 267 if (ch4 < 0) 268 throw new EOFException(); 269 return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24); 270 } 271 272 // Read 64 bit signed integer from stream 273 public long readLong() throws IOException { 274 long ch1 = read(); 275 long ch2 = read(); 276 long ch3 = read(); 277 long ch4 = read(); 278 long ch5 = read(); 279 long ch6 = read(); 280 long ch7 = read(); 281 long ch8 = read(); 282 if (ch1 < 0) 283 throw new EOFException(); 284 if (ch2 < 0) 285 throw new EOFException(); 286 if (ch3 < 0) 287 throw new EOFException(); 288 if (ch4 < 0) 289 throw new EOFException(); 290 if (ch5 < 0) 291 throw new EOFException(); 292 if (ch6 < 0) 293 throw new EOFException(); 294 if (ch7 < 0) 295 throw new EOFException(); 296 if (ch8 < 0) 297 throw new EOFException(); 298 return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24) 299 | (ch5 << 32) | (ch6 << 40) | (ch7 << 48) | (ch8 << 56); 300 } 301 302 // Read 8 bit unsigned integer from stream 303 public int readUnsignedByte() throws IOException { 304 int ch = read(); 305 if (ch < 0) 306 throw new EOFException(); 307 return ch; 308 } 309 310 // Read 16 bit unsigned integer from stream 311 public int readUnsignedShort() throws IOException { 312 int ch1 = read(); 313 int ch2 = read(); 314 if (ch1 < 0) 315 throw new EOFException(); 316 if (ch2 < 0) 317 throw new EOFException(); 318 return ch1 | (ch2 << 8); 319 } 320 321 // Read 32 bit unsigned integer from stream 322 public long readUnsignedInt() throws IOException { 323 long ch1 = read(); 324 long ch2 = read(); 325 long ch3 = read(); 326 long ch4 = read(); 327 if (ch1 < 0) 328 throw new EOFException(); 329 if (ch2 < 0) 330 throw new EOFException(); 331 if (ch3 < 0) 332 throw new EOFException(); 333 if (ch4 < 0) 334 throw new EOFException(); 335 return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24); 336 } 337 338 @Override 339 public void close() throws IOException { 340 finish(); 341 if (this == root) 342 stream.close(); 343 stream = null; 344 } 345 }