1 /*
   2  * Copyright (c) 2007, 2018, 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 final 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         stream.close();
 342     }
 343 }