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 }