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 }