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 
RIFFReader(final InputStream stream)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 
getFilePointer()90     public long getFilePointer() throws IOException {
91         return root.filepointer;
92     }
93 
hasNextChunk()94     public boolean hasNextChunk() throws IOException {
95         if (lastiterator != null)
96             lastiterator.finish();
97         return avail != 0;
98     }
99 
nextChunk()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 
getFormat()109     public String getFormat() {
110         return fourcc;
111     }
112 
getType()113     public String getType() {
114         return riff_type;
115     }
116 
getSize()117     public long getSize() {
118         return ckSize;
119     }
120 
121     @Override
read()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
read(byte[] b, int offset, int len)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 
readFully(byte[] b)159     public void readFully(byte[] b) throws IOException {
160         readFully(b, 0, b.length);
161     }
162 
readFully(byte[] b, int off, int len)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
skip(final long n)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
available()209     public int available() {
210         return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) avail;
211     }
212 
finish()213     public void finish() throws IOException {
214         if (avail != 0) {
215             skip(avail);
216         }
217     }
218 
219     // Read ASCII chars from stream
readString(final int len)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
readByte()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
readShort()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
readInt()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
readLong()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
readUnsignedByte()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
readUnsignedShort()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
readUnsignedInt()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
close()339     public void close() throws IOException {
340         finish();
341         stream.close();
342     }
343 }
344