package redone.net; import org.apache.mina.common.IoSession; /** * Immutable packet object. * * @author Graham */ public final class Packet { public static enum Size { Fixed, VariableByte, VariableShort }; /** * The associated IO session */ private final IoSession session; /** * The ID of the packet */ private final int pID; /** * The length of the payload */ private final int pLength; /** * The payload */ private final byte[] pData; /** * The current index into the payload buffer for reading */ private int caret = 0; /** * Whether this packet is without the standard packet header */ private final boolean bare; private Size size = Size.Fixed; public Packet(IoSession session, int pID, byte[] pData, boolean bare, Size s) { this.session = session; this.pID = pID; this.pData = pData; pLength = pData.length; this.bare = bare; size = s; } /** * Creates a new packet with the specified parameters. * * @param session * The session to associate with the packet * @param pID * The ID of the packet * @param pData * The payload of the packet * @param bare * Whether this packet is bare, which means that it does not * include the standard packet header */ public Packet(IoSession session, int pID, byte[] pData, boolean bare) { this(session, pID, pData, bare, Size.Fixed); } /** * Creates a new packet with the specified parameters. The packet is * considered not to be a bare packet. * * @param session * The session to associate with the packet * @param pID * The ID of the packet * @param pData * The payload the packet */ public Packet(IoSession session, int pID, byte[] pData) { this(session, pID, pData, false); } /** * Returns the IO session associated with the packet, if any. * * @return The IoSession object, or null if none. */ public IoSession getSession() { return session; } /** * Checks if this packet is considered to be a bare packet, which means that * it does not include the standard packet header (ID and length values). * * @return Whether this packet is a bare packet */ public boolean isBare() { return bare; } public Size getSize() { return size; } /** * Returns the packet ID. * * @return The packet ID */ public int getId() { return pID; } /** * Returns the length of the payload of this packet. * * @return The length of the packet's payload */ public int getLength() { return pLength; } /** * Returns the entire payload data of this packet. * * @return The payload byte array */ public byte[] getData() { return pData; } /** * Returns the remaining payload data of this packet. * * @return The payload byte array */ public byte[] getRemainingData() { byte[] data = new byte[pLength - caret]; for (int i = 0; i < data.length; i++) { data[i] = pData[i + caret]; } caret += data.length; return data; } /** * Reads the next byte from the payload. * * @return A byte */ public byte readByte() { return pData[caret++]; } /** * Reads the next short from the payload. * * @return A short */ public short readShort() { return (short) ((short) ((pData[caret++] & 0xff) << 8) | (short) (pData[caret++] & 0xff)); } public int readLEShortA() { int i = (pData[caret++] - 128 & 0xff) + ((pData[caret++] & 0xff) << 8); if (i > 32767) { i -= 0x10000; } return i; } public int readLEShort() { int i = (pData[caret++] & 0xff) + ((pData[caret++] & 0xff) << 8); if (i > 32767) { i -= 0x10000; } return i; } /** * Reads the next int from the payload. * * @return An int */ public int readInt() { return (pData[caret++] & 0xff) << 24 | (pData[caret++] & 0xff) << 16 | (pData[caret++] & 0xff) << 8 | pData[caret++] & 0xff; } public int readLEInt() { return pData[caret++] & 0xff | (pData[caret++] & 0xff) << 8 | (pData[caret++] & 0xff) << 16 | (pData[caret++] & 0xff) << 24; } /** * Reads the next long from the payload. * * @return A long */ public long readLong() { return (long) (pData[caret++] & 0xff) << 56 | (long) (pData[caret++] & 0xff) << 48 | (long) (pData[caret++] & 0xff) << 40 | (long) (pData[caret++] & 0xff) << 32 | (long) (pData[caret++] & 0xff) << 24 | (long) (pData[caret++] & 0xff) << 16 | (long) (pData[caret++] & 0xff) << 8 | pData[caret++] & 0xff; } /** * Reads the string which is formed by the unread portion of the payload. * * @return A String */ public String readString() { return readString(pLength - caret); } public String readRS2String() { int start = caret; while (pData[caret++] != 0) { ; } return new String(pData, start, caret - start - 1); } public void readBytes(byte[] buf, int off, int len) { for (int i = 0; i < len; i++) { buf[off + i] = pData[caret++]; } } /** * Reads a string of the specified length from the payload. * * @param length * The length of the string to be read * @return A String */ public String readString(int length) { String rv = new String(pData, caret, length); caret += length; return rv; } /** * Skips the specified number of bytes in the payload. * * @param x * The number of bytes to be skipped */ public void skip(int x) { caret += x; } public int remaining() { return pData.length - caret; } /** * Returns this packet in string form. * * @return A String representing this packet */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[id=" + pID + ",len=" + pLength + ",data=0x"); for (int x = 0; x < pLength; x++) { sb.append(byteToHex(pData[x], true)); } sb.append("]"); return sb.toString(); } private static String byteToHex(byte b, boolean forceLeadingZero) { StringBuilder out = new StringBuilder(); int ub = b & 0xff; if (ub / 16 > 0 || forceLeadingZero) { out.append(hex[ub / 16]); } out.append(hex[ub % 16]); return out.toString(); } private static final char[] hex = "0123456789ABCDEF".toCharArray(); public int readShortA() { caret += 2; return ((pData[caret - 2] & 0xFF) << 8) + (pData[caret - 1] - 128 & 0xFF); } public byte readByteC() { return (byte) -readByte(); } public byte readByteS() { return (byte) (128 - readByte()); } }