/* --------------------------------------------------------- *
* __________ D E L T A S C R I P T *
* (_________() *
* / === / - A fast, dynamic scripting language *
* | == | - Version 4.13.11.0 *
* / === / - Developed by Adam R. Nelson *
* | = = | - 2011-2013 *
* / === / - Distributed under GNU LGPL v3 *
* (________() - http://github.com/ar-nelson/deltascript *
* *
* --------------------------------------------------------- */
package com.sector91.delta.script.parser;
import static com.sector91.util.StringTemplate.$;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import com.sector91.delta.script.DScriptContext;
import com.sector91.delta.script.DeltaScript;
import com.sector91.delta.script.instrs.DSInstr;
import com.sector91.delta.script.instrs.InstrType;
public class DScriptBinaryReader implements Closeable
{
/**
* The byte order of multibyte numbers stored in compiled DeltaScript
* files.
*/
public static final ByteOrder ORDER = ByteOrder.BIG_ENDIAN;
private final InputStream in;
public DScriptBinaryReader(InputStream is)
{this.in = is;}
private short readShort() throws IOException
{
final byte[] bytes = new byte[2];
in.read(bytes);
return ByteBuffer.wrap(bytes).order(ORDER).getShort();
}
private int readInt() throws IOException
{
final byte[] bytes = new byte[4];
in.read(bytes);
return ByteBuffer.wrap(bytes).order(ORDER).getInt();
}
private long readLong() throws IOException
{
final byte[] bytes = new byte[8];
in.read(bytes);
return ByteBuffer.wrap(bytes).order(ORDER).getLong();
}
public DSInstr read(DScriptContext context) throws IOException
{return read(context, null);}
public DSInstr read(DScriptContext context, String name) throws IOException
{
final byte[] header = new byte[DScriptBinaryWriter.FILE_HEADER.length];
in.read(header);
if (!Arrays.equals(header, DScriptBinaryWriter.FILE_HEADER))
throw new IOException("Input does not have valid file header; not" +
" a DeltaScript binary file.");
final byte versionByte = (byte)in.read();
if (versionByte != DeltaScript.VERSION.majorVersion())
throw new IOException($("Input is a binary file from a different" +
" major DeltaScript version. (Expected {}, got {}.)",
DeltaScript.VERSION.majorVersion(), versionByte));
final long flags = readLong();
final boolean debug =
(flags & DScriptBinaryWriter.FLAG_DEBUG) != 0;
return readInstr(context, debug);
}
private DSInstr readInstr(DScriptContext context, boolean debug)
throws IOException
{
final int opcode = in.read();
final InstrType type = InstrType.forOpcode(opcode);
if (type == null)
throw new IOException($("0x{} is not a valid DeltaScript" +
" {} instruction opcode.", Integer.toHexString(opcode),
DeltaScript.VERSION.majorVersion()));
final int headLength = readInt();
final String head;
if (headLength < 0)
head = null;
else
{
final byte[] headBytes = new byte[headLength];
final int read = in.read(headBytes);
if (read < headLength)
throw new IOException("Encountered EOF when attempting to" +
" read instruction header string of length " + headLength +
".");
head = new String(headBytes, DScriptBinaryWriter.CHARSET);
}
final int start;
final short len;
if (debug)
{
start = readInt();
len = readShort();
}
else
{
start = -1;
len = 0;
}
final int tailLength = readInt();
final DSInstr[] tail = new DSInstr[tailLength];
for (int i=0; i<tailLength; i++)
tail[i] = readInstr(context, debug);
return DSInstr.create(type, context, start, len, head, tail);
}
public void close() throws IOException
{in.close();}
}