package net.scapeemulator.game.net.game;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import net.scapeemulator.game.net.game.GameFrame.Type;
import net.scapeemulator.util.crypto.StreamCipher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class GameFrameDecoder extends ByteToMessageDecoder {
private static final Logger logger = LoggerFactory.getLogger(GameFrameDecoder.class);
private static final int[] SIZES = new int[256];
static {
SIZES[0] = 0;
SIZES[1] = -3;
SIZES[2] = -3;
SIZES[3] = 2; // Attack NPC
SIZES[4] = -3;
SIZES[5] = -3;
SIZES[6] = -3;
SIZES[7] = -3;
SIZES[8] = -3;
SIZES[9] = 6; // Interface option #9
SIZES[10] = 4; // Actionbuttons #2
SIZES[11] = -3;
SIZES[12] = -3;
SIZES[13] = -3;
SIZES[14] = -3;
SIZES[15] = -3;
SIZES[16] = -3;
SIZES[17] = -3;
SIZES[18] = -3;
SIZES[19] = -3;
SIZES[20] = 4; // Unknown
SIZES[21] = 4; // Camera
SIZES[22] = 1; // Focus
SIZES[23] = 4; // Enter amount
SIZES[24] = -3;
SIZES[25] = -3;
SIZES[26] = -3;
SIZES[27] = 16; // Item on item
SIZES[28] = -3;
SIZES[29] = -3;
SIZES[30] = 2; // Fourth click NPC (trade slayermaster).
SIZES[31] = -3;
SIZES[32] = -3;
SIZES[33] = 6; // Ground item option 4
SIZES[34] = 8; // Add ignore
SIZES[35] = -3;
SIZES[36] = -3;
SIZES[37] = -3;
SIZES[38] = -3;
SIZES[39] = -1; // Walk
SIZES[40] = -3;
SIZES[41] = -3;
SIZES[42] = -3;
SIZES[43] = -3;
SIZES[44] = -1; // Command
SIZES[45] = -3;
SIZES[46] = -3;
SIZES[47] = -3;
SIZES[48] = 6; // Ground item option 5
SIZES[49] = -3;
SIZES[50] = -3;
SIZES[51] = -3;
SIZES[52] = -3;
SIZES[53] = -3; // TODO double check
SIZES[54] = -3;
SIZES[55] = 8; // Inventory item option 2
SIZES[56] = -3;
SIZES[57] = 8; // Delete friend
SIZES[58] = -3;
SIZES[59] = -3;
SIZES[60] = -3;
SIZES[61] = -3;
SIZES[62] = -3;
SIZES[63] = -3;
SIZES[64] = 6; // Interface option #8
SIZES[65] = -3;
SIZES[66] = 6; // Pick up item
SIZES[67] = -3;
SIZES[68] = 2; // Attack player
SIZES[69] = -3;
SIZES[70] = -3;
SIZES[71] = 2; // Trade player
SIZES[72] = 2; // NPC Examine
SIZES[73] = -3;
SIZES[74] = -3;
SIZES[75] = 6; // Mouse click
SIZES[76] = -3;
SIZES[77] = -1; // Walk
SIZES[78] = 2; // Second click NPC
SIZES[79] = 12; // Swapping inventory places in shop, bank and duel
SIZES[80] = -3;
SIZES[81] = 8; // Unequip item
SIZES[82] = -3;
SIZES[83] = -3;
SIZES[84] = 6; // Object third click
SIZES[85] = -3;
SIZES[86] = -3;
SIZES[87] = -3;
SIZES[88] = -3;
SIZES[89] = -3;
SIZES[90] = -3;
SIZES[91] = -3;
SIZES[92] = 2; // Inventory item examine.
SIZES[93] = 0; // Ping
SIZES[94] = 2; // Object examine
SIZES[95] = -3;
SIZES[96] = -3;
SIZES[97] = -3;
SIZES[98] = 4; // Toggle sound setting
SIZES[99] = 10; // ?
SIZES[100] = -3;
SIZES[101] = 14; // Item on ground object
SIZES[102] = -3;
SIZES[103] = -3;
SIZES[104] = 8; // Join clan chat
SIZES[105] = -3;
SIZES[106] = 2; // Follow player
SIZES[107] = -3;
SIZES[108] = -3;
SIZES[109] = -3;
SIZES[110] = 0; // Region loading, size varies
SIZES[111] = 2; // Grand Exchange item search
SIZES[112] = -3;
SIZES[113] = -3;
SIZES[114] = -3;
SIZES[115] = 10; // Use item on npc
SIZES[116] = -3;
SIZES[117] = -3;
SIZES[118] = -3;
SIZES[119] = -3;
SIZES[120] = 8; // Add friend
SIZES[121] = -3;
SIZES[122] = -3;
SIZES[123] = -3;
SIZES[124] = 6; // Interface option #3
SIZES[125] = -3;
SIZES[126] = -3;
SIZES[127] = -3;
SIZES[128] = -3;
SIZES[129] = -3;
SIZES[130] = -3;
SIZES[131] = -3;
SIZES[132] = 6; // Actionbuttons #3
SIZES[133] = -3;
SIZES[134] = 14; // Item on object
SIZES[135] = 8; // Drop item
SIZES[136] = -3;
SIZES[137] = 4; // Unknown, nothing major
SIZES[138] = -3;
SIZES[139] = -3;
SIZES[140] = -3;
SIZES[141] = -3;
SIZES[142] = -3;
SIZES[143] = -3;
SIZES[144] = -3;
SIZES[145] = -3;
SIZES[146] = -3;
SIZES[147] = -3;
SIZES[148] = 2; // Third click NPC
SIZES[149] = -3;
SIZES[150] = -3;
SIZES[151] = -3;
SIZES[152] = -3;
SIZES[153] = 8; // Inventory item option 3 (check RC pouch)
SIZES[154] = -3;
SIZES[155] = 6; // Actionbutton
SIZES[156] = 8; // Inventory item option 1 (food etc)
SIZES[157] = 3; // Privacy options
SIZES[158] = -3;
SIZES[159] = -3;
SIZES[160] = -3;
SIZES[161] = 8; // Inventory item option 4 (rub/empty)
SIZES[162] = 8; // Clan chat kick
SIZES[163] = -3;
SIZES[164] = -3;
SIZES[165] = -3;
SIZES[166] = 6; // Interface option #7
SIZES[167] = -1;
SIZES[168] = 6; // Interface option #6
SIZES[169] = -3;
SIZES[170] = 6; // Object in construction, maybe something else?
SIZES[171] = -3;
SIZES[172] = -3;
SIZES[173] = -3;
SIZES[174] = -3;
SIZES[175] = -3;
SIZES[176] = -3;
SIZES[177] = 2; // Junk, no real purpose
SIZES[178] = -3;
SIZES[179] = -3;
SIZES[180] = 2; // Accept trade (chatbox)
SIZES[181] = -3;
SIZES[182] = -3;
SIZES[183] = -3;
SIZES[184] = 0; // Close interface
SIZES[185] = -3;
SIZES[186] = -3;
SIZES[187] = -3;
SIZES[188] = 9; // Clan ranks
SIZES[189] = -3;
SIZES[190] = -3;
SIZES[191] = -3;
SIZES[192] = -3;
SIZES[193] = -3;
SIZES[194] = 6; // Object second click
SIZES[195] = 8; // Magic on player
SIZES[196] = 6; // Interface option #2
SIZES[197] = -3;
SIZES[198] = -3;
SIZES[199] = 6; // Interface option #4
SIZES[200] = -3;
SIZES[201] = -1; // Send PM
SIZES[202] = -3;
SIZES[203] = -3;
SIZES[204] = -3;
SIZES[205] = -3;
SIZES[206] = 8; // Operate item
SIZES[207] = -3;
SIZES[208] = -3;
SIZES[209] = -3;
SIZES[210] = -3;
SIZES[211] = -3;
SIZES[212] = -3;
SIZES[213] = 8; // Delete ignore
SIZES[214] = -3;
SIZES[215] = -1; // Walk
SIZES[216] = -3;
SIZES[217] = -3;
SIZES[218] = 2; // Fifth click NPC
SIZES[219] = -3;
SIZES[220] = -3;
SIZES[221] = -3;
SIZES[222] = -3;
SIZES[223] = -3;
SIZES[224] = -3;
SIZES[225] = -3;
SIZES[226] = -3;
SIZES[227] = -3;
SIZES[228] = 6; // Ground item option 1
SIZES[229] = -3;
SIZES[230] = -3;
SIZES[231] = 9; // Swap item slot
SIZES[232] = -3;
SIZES[233] = -3;
SIZES[234] = 6; // Interface option #5
SIZES[235] = -3;
SIZES[236] = -3;
SIZES[237] = -1; // Public chat
SIZES[238] = -3;
SIZES[239] = 8; // Magic on NPC
SIZES[240] = -3;
SIZES[241] = -3;
SIZES[242] = -3;
SIZES[243] = 6; // Screen type (fullscreen, small HD etc)
SIZES[244] = 8; // Enter text
SIZES[245] = 0; // Idle logout
SIZES[246] = -3;
SIZES[247] = 6; // Object 4th option
SIZES[248] = -3;
SIZES[249] = -3;
SIZES[250] = -3;
SIZES[251] = -3;
SIZES[252] = -3;
SIZES[253] = 14; // Magic on item
SIZES[254] = 6; // First click object
SIZES[255] = -3;
}
private enum State {
READ_OPCODE, READ_SIZE, READ_PAYLOAD
}
private final StreamCipher cipher;
private State state = State.READ_OPCODE;
private boolean variable;
private int opcode, size;
public GameFrameDecoder(StreamCipher cipher) {
this.cipher = cipher;
}
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf buf, MessageBuf<Object> out) throws Exception {
if (state == State.READ_OPCODE) {
if (!buf.isReadable())
return;
opcode = (buf.readUnsignedByte() - cipher.nextInt()) & 0xFF;
size = SIZES[opcode];
if (size == -3) {
logger.info("invalid opcode: " + opcode);
ctx.close();
return;
}
variable = size == -1;
state = variable ? State.READ_SIZE : State.READ_PAYLOAD;
}
if (state == State.READ_SIZE) {
if (!buf.isReadable())
return;
size = buf.readUnsignedByte();
state = State.READ_PAYLOAD;
}
if (state == State.READ_PAYLOAD) {
if (buf.readableBytes() < size)
return;
ByteBuf payload = buf.readBytes(size);
state = State.READ_OPCODE;
out.add(new GameFrame(opcode, variable ? Type.VARIABLE_BYTE : Type.FIXED, payload));
}
}
}