package pt.tumba.parser.swf;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.InflaterInputStream;
/**
* Input Stream Wrapper
*
*@author unknown
*@created 15 de Setembro de 2002
*/
public class InStream {
protected InputStream in;
protected long bytesRead = 0L;
//--Bit buffer..
protected int bitBuf;
protected int bitPos;
/**
* Constructor for the InStream object
*
*@param in Description of the Parameter
*/
public InStream(InputStream in) {
this.in = in;
bitBuf = 0;
bitPos = 0;
}
/**
* Constructor for the InStream object
*
*@param bytes Description of the Parameter
*/
public InStream(byte[] bytes) {
this(new ByteArrayInputStream(bytes));
}
/**
* Read a string from the input stream
*
* @return Description of the Return Value
* @exception IOException Description of the Exception
*/
public byte[] readStringBytes() throws IOException {
synchBits();
List<Byte> chars = new ArrayList();
byte[] aChar = new byte[1];
int num = 0;
while ((num = in.read(aChar)) == 1) {
bytesRead++;
if (aChar[0] == 0) { //end of string
byte[] string = new byte[chars.size()];
int i = 0;
for (Object b : chars) {
string[i++] = ((Byte) b).byteValue();
}
return string;
}
chars.add(new Byte(aChar[0]));
}
throw new IOException("Unterminated string - reached end of input before null char");
}
/**
* Read a null terminated string using the default character encoding
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public String readString() throws IOException {
return new String(readStringBytes());
}
/**
* Read all remaining bytes from the stream
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public byte[] read() throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
int b = 0;
while ((b = in.read()) >= 0) {
bout.write(b);
}
return bout.toByteArray();
}
/**
* Read bytes from the input stream
*
*@param length Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public byte[] read(int length) throws IOException {
byte[] data = new byte[length];
if (length > 0) {
int read = 0;
while (read < length) {
int count = in.read(data, read, length - read);
if (count < 0) {
bytesRead += read;
throw new IOException("Unexpected end of input while reading a specified number of bytes");
}
read += count;
}
bytesRead += read;
}
return data;
}
/**
* Reset the bit buffer
*/
public void synchBits() {
bitBuf = 0;
bitPos = 0;
}
/**
* Gets the bytesRead attribute of the InStream object
*
*@return The bytesRead value
*/
public long getBytesRead() {
return bytesRead;
}
/**
* Sets the bytesRead attribute of the InStream object
*
*@param read The new bytesRead value
*/
public void setBytesRead(long read) {
bytesRead = read;
}
/**
* Skip a number of bytes from the input stream
*
*@param length Description of the Parameter
*@exception IOException Description of the Exception
*/
public void skipBytes(long length) throws IOException {
long skipped = 0;
while (skipped < length) {
int val = in.read();
if (val < 0) {
throw new IOException("Unexpected end of input");
}
skipped++;
}
bytesRead += length;
}
/**
* Read an unsigned value from the given number of bits
*
*@param numBits Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public long readUBits(int numBits) throws IOException {
if (numBits == 0) {
return 0;
}
int bitsLeft = numBits;
long result = 0;
if (bitPos == 0) {
//no value in the buffer - read a byte
bitBuf = in.read();
bitPos = 8;
bytesRead++;
}
while (true) {
int shift = bitsLeft - bitPos;
if (shift > 0) {
// Consume the entire buffer
result |= bitBuf << shift;
bitsLeft -= bitPos;
// Get the next byte from the input stream
bitBuf = in.read();
bitPos = 8;
bytesRead++;
} else {
// Consume a portion of the buffer
result |= bitBuf >> -shift;
bitPos -= bitsLeft;
bitBuf &= 0xff >> (8 - bitPos);
// mask off the consumed bits
return result;
}
}
}
/**
* Read an unsigned 8 bit value
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public int readUI8() throws IOException {
synchBits();
int ui8 = in.read();
if (ui8 < 0) {
throw new IOException("Unexpected end of input");
}
bytesRead++;
return ui8;
}
/**
* Read an unsigned 16 bit value
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public int readUI16() throws IOException {
synchBits();
int ui16 = in.read();
if (ui16 < 0) {
throw new IOException("Unexpected end of input");
}
int val = in.read();
if (val < 0) {
throw new IOException("Unexpected end of input");
}
ui16 += val << 8;
bytesRead += 2;
return ui16;
}
/**
* Read a signed 16 bit value
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public short readSI16() throws IOException {
synchBits();
int lowerByte = in.read();
if (lowerByte < 0) {
throw new IOException("Unexpected end of input");
}
byte[] aByte = new byte[1];
int count = in.read(aByte);
if (count < 1) {
throw new IOException("Unexpected end of input");
}
bytesRead += 2;
return (short) ((aByte[0] * 256) + lowerByte);
}
/**
* Read an unsigned 32 bit value
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public long readUI32() throws IOException {
synchBits();
long ui32 = in.read();
if (ui32 < 0) {
throw new IOException("Unexpected end of input");
}
long val = in.read();
if (val < 0) {
throw new IOException("Unexpected end of input");
}
ui32 += val << 8;
val = in.read();
if (val < 0) {
throw new IOException("Unexpected end of input");
}
ui32 += val << 16;
val = in.read();
if (val < 0) {
throw new IOException("Unexpected end of input");
}
ui32 += val << 24;
bytesRead += 4;
return ui32;
}
/**
* Read a signed value from the given number of bits
*
*@param numBits Description of the Parameter
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public int readSBits(int numBits) throws IOException {
// Get the number as an unsigned value.
long uBits = readUBits(numBits);
// Is the number negative?
if ((uBits & (1L << (numBits - 1))) != 0) {
// Yes. Extend the sign.
uBits |= -1L << numBits;
}
return (int) uBits;
}
/**
* Read a 32 bit signed number
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public int readSI32() throws IOException {
synchBits();
int b0 = in.read();
if (b0 < 0) {
throw new IOException("Unexpected end of input");
}
int b1 = in.read();
if (b1 < 0) {
throw new IOException("Unexpected end of input");
}
int b2 = in.read();
if (b2 < 0) {
throw new IOException("Unexpected end of input");
}
byte[] aByte = new byte[1];
int count = in.read(aByte);
if (count < 1) {
throw new IOException("Unexpected end of input");
}
bytesRead += 4;
return (int)
((aByte[0] * 256 * 256 * 256) + (b2 * 256 * 256) + (b1 * 256) + b0);
}
/**
* Read a 32 bit floating point number
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public float readFloat() throws IOException {
return Float.intBitsToFloat(readSI32());
}
/**
* Read a 64 bit floating point number
*
*@return Description of the Return Value
*@exception IOException Description of the Exception
*/
public double readDouble() throws IOException {
byte[] bytes = read(8);
byte[] bytes2 = new byte[8];
bytes2[0] = bytes[3];
bytes2[1] = bytes[2];
bytes2[2] = bytes[1];
bytes2[3] = bytes[0];
bytes2[4] = bytes[7];
bytes2[5] = bytes[6];
bytes2[6] = bytes[5];
bytes2[7] = bytes[4];
ByteArrayInputStream bin = new ByteArrayInputStream(bytes2);
return new DataInputStream(bin).readDouble();
}
/**
* Start reading compressed data - all further input is assumed to come from
* a zip compressed stream.
*/
public void readCompressed() {
in = new InflaterInputStream(in);
}
/**
* Util to convert an unsigned byte to an unsigned int
*
*@param b Description of the Parameter
*@return Description of the Return Value
*/
public static int ubyteToInt(byte b2) {
boolean highbit = b2 < 0;
byte b = (byte)(b2 & 0x7f);
int i = (int) b;
if (highbit) {
i += 128;
}
return i;
}
/**
* Util to convert 2 bytes to a signed value
*
*@param lo Description of the Parameter
*@param hi Description of the Parameter
*@return Description of the Return Value
*/
public static int bytesToSigned(byte lo, byte hi) {
int low = ubyteToInt(lo);
int high = ubyteToInt(hi);
int value = (high << 8) + low;
if (value > 0x7fff) {
value -= 65536;
}
return value;
}
}