package net.scapeemulator.game.net.game; import io.netty.buffer.ByteBuf; import net.scapeemulator.util.ByteBufUtils; public final class GameFrameReader { private static final int[] BITMASKS = { 0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, -1 }; private final ByteBuf buffer; private AccessMode mode = AccessMode.BYTE_ACCESS; private int bitIndex; public GameFrameReader(GameFrame frame) { buffer = frame.getPayload(); } public int getLength() { checkByteAccess(); return buffer.readableBytes(); } public void switchToByteAccess() { if (mode == AccessMode.BYTE_ACCESS) { throw new IllegalStateException("Already in byte access mode"); } mode = AccessMode.BYTE_ACCESS; buffer.readerIndex((bitIndex + 7) / 8); } public void switchToBitAccess() { if (mode == AccessMode.BIT_ACCESS) { throw new IllegalStateException("Already in bit access mode"); } mode = AccessMode.BIT_ACCESS; bitIndex = buffer.readerIndex() * 8; } public String getString() { checkByteAccess(); return ByteBufUtils.readString(buffer); } public int getSignedSmart() { checkByteAccess(); int peek = buffer.getByte(buffer.readerIndex()); if (peek < 128) { return buffer.readByte() - 64; } else { return buffer.readShort() - 49152; } } public int getUnsignedSmart() { checkByteAccess(); int peek = buffer.getByte(buffer.readerIndex()); if (peek < 128) { return buffer.readByte(); } else { return buffer.readShort() - 32768; } } public long getSigned(DataType type) { return getSigned(type, DataOrder.BIG, DataTransformation.NONE); } public long getSigned(DataType type, DataOrder order) { return getSigned(type, order, DataTransformation.NONE); } public long getSigned(DataType type, DataTransformation transformation) { return getSigned(type, DataOrder.BIG, transformation); } public long getSigned(DataType type, DataOrder order, DataTransformation transformation) { long longValue = get(type, order, transformation); if (type != DataType.LONG) { int max = (int) (Math.pow(2, type.getBytes() * 8 - 1) - 1); if (longValue > max) { longValue -= (max + 1) * 2; } } return longValue; } public long getUnsigned(DataType type) { return getUnsigned(type, DataOrder.BIG, DataTransformation.NONE); } public long getUnsigned(DataType type, DataOrder order) { return getUnsigned(type, order, DataTransformation.NONE); } public long getUnsigned(DataType type, DataTransformation transformation) { return getUnsigned(type, DataOrder.BIG, transformation); } public long getUnsigned(DataType type, DataOrder order, DataTransformation transformation) { long longValue = type == DataType.LONG ? readUnsignedLong() : get(type, order, transformation); return longValue & 0xFFFFFFFFFFFFFFFFL; } private long readUnsignedLong() { return ((long) (buffer.readByte() & 0xff) << 56) | ((long) (buffer.readByte() & 0xff) << 48) | ((long) (buffer.readByte() & 0xff) << 40) | ((long) (buffer.readByte() & 0xff) << 32) | ((long) (buffer.readByte() & 0xff) << 24) | ((long) (buffer.readByte() & 0xff) << 16) | ((long) (buffer.readByte() & 0xff) << 8) | ((buffer.readByte() & 0xff)); } private long get(DataType type, DataOrder order, DataTransformation transformation) { checkByteAccess(); long longValue = 0; int length = type.getBytes(); if (order == DataOrder.BIG) { for (int i = length - 1; i >= 0; i--) { if (i == 0 && transformation != DataTransformation.NONE) { if (transformation == DataTransformation.ADD) { longValue |= (buffer.readByte() - 128) & 0xFF; } else if (transformation == DataTransformation.NEGATE) { longValue |= (-buffer.readByte()) & 0xFF; } else if (transformation == DataTransformation.SUBTRACT) { longValue |= (128 - buffer.readByte()) & 0xFF; } else { throw new IllegalArgumentException("unknown transformation"); } } else { longValue |= ((buffer.readByte() & 0xFF) << (i * 8)); } } } else if (order == DataOrder.LITTLE) { for (int i = 0; i < length; i++) { if (i == 0 && transformation != DataTransformation.NONE) { if (transformation == DataTransformation.ADD) { longValue |= (buffer.readByte() - 128) & 0xFF; } else if (transformation == DataTransformation.NEGATE) { longValue |= (-buffer.readByte()) & 0xFF; } else if (transformation == DataTransformation.SUBTRACT) { longValue |= (128 - buffer.readByte()) & 0xFF; } else { throw new IllegalArgumentException("unknown transformation"); } } else { longValue |= ((buffer.readByte() & 0xFF) << (i * 8)); } } } else if (order == DataOrder.MIDDLE) { if (transformation != DataTransformation.NONE) { throw new IllegalArgumentException("middle endian cannot be transformed"); } if (type != DataType.INT) { throw new IllegalArgumentException("middle endian can only be used with an integer"); } longValue |= (buffer.readByte() & 0xFF) << 8; longValue |= buffer.readByte() & 0xFF; longValue |= (buffer.readByte() & 0xFF) << 24; longValue |= (buffer.readByte() & 0xFF) << 16; } else if (order == DataOrder.INVERSED_MIDDLE) { if (transformation != DataTransformation.NONE) { throw new IllegalArgumentException("inversed middle endian cannot be transformed"); } if (type != DataType.INT) { throw new IllegalArgumentException("inversed middle endian can only be used with an integer"); } longValue |= (buffer.readByte() & 0xFF) << 16; longValue |= (buffer.readByte() & 0xFF) << 24; longValue |= buffer.readByte() & 0xFF; longValue |= (buffer.readByte() & 0xFF) << 8; } else { throw new IllegalArgumentException("unknown order"); } return longValue; } public void getBytes(byte[] bytes) { checkByteAccess(); for (int i = 0; i < bytes.length; i++) { bytes[i] = buffer.readByte(); } } public void getBytes(DataTransformation transformation, byte[] bytes) { if (transformation == DataTransformation.NONE) { getBytesReverse(bytes); } else { for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) getSigned(DataType.BYTE, transformation); } } } public void getBytesReverse(byte[] bytes) { checkByteAccess(); for (int i = bytes.length - 1; i >= 0; i--) { bytes[i] = buffer.readByte(); } } public void getBytesReverse(DataTransformation transformation, byte[] bytes) { if (transformation == DataTransformation.NONE) { getBytesReverse(bytes); } else { for (int i = bytes.length - 1; i >= 0; i--) { bytes[i] = (byte) getSigned(DataType.BYTE, transformation); } } } private void checkByteAccess() { if (mode != AccessMode.BYTE_ACCESS) { throw new IllegalStateException("For byte-based calls to work, the mode must be byte access"); } } private void checkBitAccess() { if (mode != AccessMode.BIT_ACCESS) { throw new IllegalStateException("For bit-based calls to work, the mode must be bit access"); } } public int getBit() { return getBits(1); } public int getBits(int numBits) { if (numBits < 0 || numBits > 32) { throw new IllegalArgumentException("Number of bits must be between 1 and 32 inclusive"); } checkBitAccess(); int bytePos = bitIndex >> 3; int bitOffset = 8 - (bitIndex & 7); int value = 0; bitIndex += numBits; for (; numBits > bitOffset; bitOffset = 8) { value += (buffer.getByte(bytePos++) & BITMASKS[bitOffset]) << numBits - bitOffset; numBits -= bitOffset; } if (numBits == bitOffset) { value += buffer.getByte(bytePos) & BITMASKS[bitOffset]; } else { value += buffer.getByte(bytePos) >> bitOffset - numBits & BITMASKS[numBits]; } return value; } }