package net.kennux.cubicworld.serialization; import java.nio.ByteBuffer; import java.nio.ByteOrder; import net.kennux.cubicworld.voxel.VoxelData; import com.badlogic.gdx.math.Vector3; /** * Gets used to read datapackets. * * @author KennuX * */ public class BitReader { /** * Contains the packet's byte. */ private byte[] data; /** * The current data-array pointer. */ private int pointer; public BitReader(byte[] data) { this.data = data; this.pointer = 0; } /** * Returns this bit reader's data array. * * @return */ public byte[] getData() { return this.data; } /** * Retruns true if this bit reader still has data to read. * * @return */ public boolean hasDataLeft() { return this.pointer < this.data.length; } /** * Reads one boolean (1 byte) from the packet data. * * @return */ public boolean readBoolean() { this.pointer++; return this.data[this.pointer - 1] == 1 ? true : false; } /** * Reads one byte from the packet data. * * @return */ public byte readByte() { this.pointer++; return this.data[this.pointer - 1]; } /** * Reads bytes from the data. * First reads the 4-bytes length integer and then all data. * * @return */ public byte[] readBytes() { int len = this.readInt(); byte[] data = new byte[len]; for (int i = 0; i < len; i++) { data[i] = this.readByte(); } return data; } /** * Writes the given character to the packet data. * * @param c */ public char readChar() { this.pointer++; return (char) this.data[this.pointer - 1]; } /** * Reads one float (4 byte) from the packet data. */ public float readFloat() { this.pointer += 4; byte[] data = new byte[] { this.data[this.pointer - 4], this.data[this.pointer - 3], this.data[this.pointer - 2], this.data[this.pointer - 1] }; return ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).getFloat(); } /** * Reads one integer (4 byte) from the packet data. */ public int readInt() { this.pointer += 4; byte[] data = new byte[] { this.data[this.pointer - 4], this.data[this.pointer - 3], this.data[this.pointer - 2], this.data[this.pointer - 1] }; return ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).getInt(); } /** * Reads one long (8 byte) from the packet data. */ public long readLong() { this.pointer += 8; byte[] data = new byte[] { this.data[this.pointer - 8], this.data[this.pointer - 7], this.data[this.pointer - 6], this.data[this.pointer - 5], this.data[this.pointer - 4], this.data[this.pointer - 3], this.data[this.pointer - 2], this.data[this.pointer - 1] }; return ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).getLong(); } /** * Reads one short (2 byte) from the packet data. */ public short readShort() { this.pointer += 2; return (short) (this.data[this.pointer - 1] & 0xFF | (this.data[this.pointer - 2] & 0xFF) << 8); } /** * Reads an integer (string length) and some characters from the data. * * @param len */ public String readString() { // Prepare int stringLen = this.readInt(); char[] characters = new char[stringLen]; // Read characters for (int i = 0; i < characters.length; i++) { characters[i] = this.readChar(); } return new String(characters); } /** * Reads a vector3 (12 bytes) from the packet data. * * @return */ public Vector3 readVector3() { return new Vector3(this.readFloat(), this.readFloat(), this.readFloat()); } /** * Reads a voxel data object from the packet data. * * @param v */ public VoxelData readVoxelData() { return VoxelData.deserialize(this); } /** * Reads an object with given type serializationtypes. * This method uses a switch-case to forward the calls to readInt(), readShort(), etc. * Returns null in case of an error. * @param type * @return */ public Object readField(SerializationTypes type) { switch (type) { case BYTE: return this.readByte(); case SHORT: return this.readShort(); case CHAR: return this.readChar(); case BYTEARRAY: return this.readBytes(); case FLOAT: return this.readFloat(); case INTEGER: return this.readInt(); case LONG: return this.readLong(); case STRING: return this.readString(); case VECTOR3: return this.readVector3(); case VOXELDATA: return this.readVoxelData(); case BOOLEAN: return this.readBoolean(); } return null; } }