package net.scapeemulator.cache.def; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import net.scapeemulator.cache.util.ByteBufferUtils; /** * A class that can decode and decode item definitions from the cache. * * @author Graham * @author `Discardedx2 * @author Davidi2 */ public final class ItemDefinition { private int id; private String name; private String examine; private int inventoryModelId; private int modelZoom; private int modelRotation1; private int modelRotation2; private int modelOffset1; private int modelOffset2; private boolean stackable; private int value; private boolean membersOnly; private Map<Integer, Object> scriptData; private int maleWearModel1; private int maleWearModel2; private int femaleWearModel1; private int femaleWearModel2; private int maleDialogueHat; private int femaleDialogueHat; private int maleDialogueModel; private int femaleDialogueModel; private int maleModel3; private int femaleModel3; private String[] groundOptions; private String[] inventoryOptions; private byte[] recolorPalette; private short[] originalModelColors; private short[] modifiedModelColors; private short[] originalTextureColors; private short[] modifiedTextureColors; private boolean unnoted; private int swapId; private int notedTemplateId; private int[] stackIds; private int[] stackAmountTriggers; private int teamId; private int lendId; private int weight; private int lendTemplateId; private int modelVerticesX; private int modelVerticesY; private int modelVerticesZ; private int modelLighting; /* unknowns */ private int anInt752; private int anInt756; private int anInt758; private int anInt760; private int anInt767; private int anInt768; private int anInt775; private int anInt777; private int anInt778; private int anInt788; private int anInt790; private int anInt800; private int anInt802; public ItemDefinition() { name = "null"; scriptData = new HashMap<Integer, Object>(); maleModel3 = -1; femaleModel3 = -1; maleDialogueHat = -1; notedTemplateId = -1; femaleDialogueModel = -1; modelVerticesY = 128; femaleDialogueHat = -1; anInt756 = -1; anInt767 = -1; anInt758 = -1; lendTemplateId = -1; lendId = -1; maleWearModel2 = -1; femaleWearModel1 = -1; stackable = false; swapId = -1; femaleWearModel2 = -1; anInt788 = -1; modelVerticesZ = 128; membersOnly = false; maleDialogueModel = -1; maleWearModel1 = -1; modelVerticesX = 128; groundOptions = new String[] { null, null, "take", null, null }; inventoryOptions = new String[] { null, null, null, null, "drop" }; modelZoom = 2000; unnoted = false; value = 1; } /** * @param buffer A {@link ByteBuffer} that contains information such as the items location. * @return a new ItemDefinition. */ public static ItemDefinition decode(int id, ByteBuffer buffer) { ItemDefinition def = new ItemDefinition(); def.id = id; def.swapId = id; while (true) { int opcode = buffer.get() & 0xFF; if (opcode == 0) break; if (opcode == 1) def.inventoryModelId = buffer.getShort() & 0xFFFFF; else if (opcode == 2) { def.name = ByteBufferUtils.getJagexString(buffer); def.examine = def.name + " " + id; } else if (opcode == 4) def.modelZoom = buffer.getShort() & 0xFFFFF; else if (opcode == 5) def.modelRotation1 = buffer.getShort() & 0xFFFFF; else if (opcode == 6) def.modelRotation2 = buffer.getShort() & 0xFFFFF; else if (opcode == 7) { def.modelOffset1 = buffer.getShort() & 0xFFFFF; if (def.modelOffset1 > 32767) def.modelOffset1 -= 65536; def.modelOffset1 <<= 0; } else if (opcode == 8) { def.modelOffset2 = buffer.getShort() & 0xFFFFF; if (def.modelOffset2 > 32767) def.modelOffset2 -= 65536; def.modelOffset2 <<= 0; } else if (opcode == 11) def.stackable = true; else if (opcode == 12) def.value = buffer.getInt(); else if (opcode == 16) def.membersOnly = true; else if (opcode == 18) buffer.getShort(); else if (opcode == 23) def.maleWearModel1 = buffer.getShort() & 0xFFFFF; else if (opcode == 24) def.femaleWearModel1 = buffer.getShort() & 0xFFFFF; else if (opcode == 25) def.maleWearModel2 = buffer.getShort() & 0xFFFFF; else if (opcode == 26) def.femaleWearModel2 = buffer.getShort() & 0xFFFFF; else if (opcode >= 30 && opcode < 35) def.groundOptions[opcode - 30] = ByteBufferUtils.getJagexString(buffer); else if (opcode >= 35 && opcode < 40) def.inventoryOptions[opcode - 35] = ByteBufferUtils.getJagexString(buffer); else if (opcode == 40) { int length = buffer.get() & 0xFF; def.originalModelColors = new short[length]; def.modifiedModelColors = new short[length]; for (int index = 0; index < length; index++) { def.originalModelColors[index] = (short) (buffer.getShort() & 0xFFFFF); def.modifiedModelColors[index] = (short) (buffer.getShort() & 0xFFFFF); } } else if (opcode == 41) { int length = buffer.get() & 0xFF; def.originalTextureColors = new short[length]; def.modifiedTextureColors = new short[length]; for (int index = 0; index < length; index++) { def.originalTextureColors[index] = (short) (buffer.getShort() & 0xFFFFF); def.modifiedTextureColors[index] = (short) (buffer.getShort() & 0xFFFFF); } } else if (opcode == 42) { int length = buffer.get() & 0xFF; def.recolorPalette = new byte[length]; for (int index = 0; index < length; index++) { def.recolorPalette[index] = buffer.get(); } } else if (opcode == 65) { def.unnoted = true; } else if (opcode == 78) { def.maleModel3 = buffer.getShort() & 0xFFFFF; } else if (opcode == 79) { def.femaleModel3 = buffer.getShort() & 0xFFFFF; } else if (opcode == 90) { def.maleDialogueModel = buffer.getShort() & 0xFFFFF; } else if (opcode == 91) { def.femaleDialogueModel = buffer.getShort() & 0xFFFFF; } else if (opcode == 92) { def.maleDialogueHat = buffer.getShort() & 0xFFFFF; } else if (opcode == 93) { def.femaleDialogueHat = buffer.getShort() & 0xFFFFF; } else if (opcode == 95) { def.anInt768 = buffer.getShort() & 0xFFFFF; } else if (opcode == 96) { def.anInt800 = buffer.get() & 0xFF; } else if (opcode == 97) { def.swapId = buffer.getShort() & 0xFFFFF; } else if (opcode == 98) { def.notedTemplateId = buffer.getShort() & 0xFFFFF; } else if (opcode >= 100 && opcode < 110) { if (def.stackIds == null) { def.stackIds = new int[10]; def.stackAmountTriggers = new int[10]; } def.stackIds[opcode - 100] = buffer.getShort() & 0xFFFFF; def.stackAmountTriggers[opcode - 100] = buffer.getShort() & 0xFFFFF; } else if (opcode == 110) { def.modelVerticesX = buffer.getShort() & 0xFFFFF; } else if (opcode == 111) { def.modelVerticesY = buffer.getShort() & 0xFFFFF; } else if (opcode == 112) { def.modelVerticesZ = buffer.getShort() & 0xFFFFF; } else if (opcode == 113) { def.modelLighting = buffer.get(); } else if (opcode == 114) { def.anInt790 = buffer.get() * 5; } else if (opcode == 115) { def.teamId = buffer.get() & 0xFF; } else if (opcode == 121) { def.lendId = buffer.getShort() & 0xFFFFF; } else if (opcode == 122) { def.lendTemplateId = buffer.getShort() & 0xFFFFF; } else if (opcode == 125) { def.anInt760 = buffer.get() << 0; def.anInt778 = buffer.get() << 0; def.anInt775 = buffer.get() << 0; } else if (opcode == 126) { def.anInt777 = buffer.get() << 0; def.anInt802 = buffer.get() << 0; def.anInt752 = buffer.get() << 0; } else if (opcode == 127) { def.anInt767 = buffer.get() & 0xFF; def.anInt758 = buffer.getShort() & 0xFFFFF; } else if (opcode == 128) { def.anInt788 = buffer.get() & 0xFF; def.anInt756 = buffer.getShort() & 0xFFFFF; } else if (opcode == 129) { buffer.get();// & 0xFF; buffer.getShort();// & 0xFFFFF; } else if (opcode == 130) { buffer.get();// & 0xFF; buffer.getShort();// & 0xFFFFF; } else if (opcode == 132) { int len = buffer.get() & 0xFF; for (int index = 0; index < len; index++) { buffer.getShort();// & 0xFFFFF; } } else if (opcode == 249) { int length = buffer.get() & 0xFF; for (int index = 0; index < length; index++) { boolean isString = buffer.get() == 1; int key = ByteBufferUtils.getTriByte(buffer); Object value = isString ? ByteBufferUtils.getJagexString(buffer) : buffer.getInt(); def.scriptData.put(key, value); } } } return def; } public ByteBuffer encode() throws IOException { try (ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream os = new DataOutputStream(bout)) { os.write(1); os.writeShort(inventoryModelId); if (!name.equals("null")) { os.write(2); ByteBufferUtils.putJagexString(os, name); } if (modelZoom != 2000) { os.write(4); os.writeShort(modelZoom); } if (modelRotation1 != 0) { os.write(5); os.writeShort(modelRotation1); } if (modelRotation2 != 0) { os.write(6); os.writeShort(modelRotation2); } if (modelOffset1 != 0) { os.write(7); os.writeShort(modelOffset1 < 0 ? modelOffset1 + 65536 : modelOffset1); } if (modelOffset2 != 0) { os.write(8); os.writeShort(modelOffset2 < 0 ? modelOffset2 + 65536 : modelOffset2); } if (stackable) { os.write(11); } if (value != 1) { os.write(12); os.writeInt(value); } if (membersOnly) { os.write(16); } if (maleWearModel1 != -1) { os.write(23); os.writeShort(maleWearModel1); } if (femaleWearModel1 != -1) { os.write(24); os.writeShort(femaleWearModel1); } if (maleWearModel2 != -1) { os.write(25); os.writeShort(maleWearModel2); } if (femaleWearModel2 != -1) { os.write(26); os.writeShort(femaleWearModel2); } for (int i = 0; i < groundOptions.length; i++) { String option = groundOptions[i]; if (option == null || (i == 2 && option.equals("take"))) { continue; } os.write(30 + i); ByteBufferUtils.putJagexString(os, option); } for (int i = 0; i < inventoryOptions.length; i++) { String option = inventoryOptions[i]; if (option == null || (i == 4 && option.equals("drop"))) { continue; } os.write(35 + i); ByteBufferUtils.putJagexString(os, option); } if (originalModelColors != null) { os.write(40); os.write(originalModelColors.length); for (int i = 0; i < originalModelColors.length; i++) { os.writeShort(originalModelColors[i]); os.writeShort(modifiedModelColors[i]); } } if (originalTextureColors != null) { os.write(41); os.write(originalTextureColors.length); for (int i = 0; i < originalTextureColors.length; i++) { os.writeShort(originalTextureColors[i]); os.writeShort(modifiedTextureColors[i]); } } if (recolorPalette != null) { os.write(42); os.write(recolorPalette.length); for (byte b : recolorPalette) { os.write(b); } } if (unnoted) { os.write(65); } if (maleModel3 != -1) { os.write(78); os.writeShort(maleModel3); } if (femaleModel3 != -1) { os.write(79); os.writeShort(femaleModel3); } if (maleDialogueModel != -1) { os.write(90); os.writeShort(maleDialogueModel); } if (femaleDialogueModel != -1) { os.write(91); os.writeShort(femaleDialogueModel); } if (maleDialogueHat != -1) { os.write(92); os.writeShort(maleDialogueHat); } if (femaleDialogueHat != -1) { os.write(93); os.writeShort(femaleDialogueHat); } if (anInt768 != 0) { os.write(95); os.writeShort(anInt768); } if (anInt800 != 0) { os.write(96); os.write(anInt800); } if (swapId != -1) { os.write(97); os.writeShort(swapId); } if (notedTemplateId != -1) { os.write(98); os.writeShort(notedTemplateId); } if (stackIds != null) { for (int i = 0; i < stackIds.length; i++) { if (stackIds[i] != 0 || stackAmountTriggers[i] != 0) { os.write(100 + i); os.writeShort(stackIds[i]); os.writeShort(stackAmountTriggers[i]); } } } if (modelVerticesX != 128) { os.write(110); os.writeShort(modelVerticesX); } if (modelVerticesY != 128) { os.write(111); os.writeShort(modelVerticesY); } if (modelVerticesZ != 128) { os.write(112); os.writeShort(modelVerticesZ); } if (modelLighting != 0) { os.write(113); os.write(modelLighting); } if (anInt790 != 0) { os.write(114); os.write(anInt790); } if (teamId != 0) { os.write(115); os.write(teamId); } if (lendId != -1) { os.write(121); os.writeShort(lendId); } if (lendTemplateId != -1) { os.write(122); os.writeShort(lendTemplateId); } if (anInt760 != 0 || anInt778 != 0 || anInt775 != 0) { os.write(125); os.write(anInt760); os.write(anInt778); os.write(anInt775); } if (anInt777 != 0 || anInt802 != 0 || anInt752 != 0) { os.write(126); os.write(anInt777); os.write(anInt802); os.write(anInt752); } if (anInt767 != -1 && anInt758 != -1) { os.write(127); os.write(anInt767); os.writeShort(anInt758); } if (anInt788 != -1 && anInt756 != -1) { os.write(128); os.write(anInt788); os.writeShort(anInt756); } if (scriptData.size() > 0) { os.write(249); os.write(scriptData.size()); for (Entry<Integer, Object> entry : scriptData.entrySet()) { Object value = entry.getValue(); boolean string = value instanceof String; os.write(string ? 1 : 0); os.write(entry.getKey() >> 16); os.write(entry.getKey() >> 8); os.write(entry.getKey()); if (string) { ByteBufferUtils.putJagexString(os, (String) value); } else { os.writeInt((int) value); } } } os.write(0); byte[] bytes = bout.toByteArray(); return ByteBuffer.wrap(bytes); } } public int getId() { return id; } public String getName() { return name; } public String getExamine() { return examine; } public int getInventoryModelId() { return inventoryModelId; } public int getModelZoom() { return modelZoom; } public int getModelRotation1() { return modelRotation1; } public int getModelRotation2() { return modelRotation2; } public int getModelOffset1() { return modelOffset1; } public int getModelOffset2() { return modelOffset2; } /** * Returns the raw boolean in the cache about whether this item is stackable. To get more * accurate information server sided, use isStackable() * * @return the raw boolean in the cache about whether this item is stackable */ public boolean getStackable() { return stackable; } /** * Uses a combination of item information to determine if this item should stack in a player * inventory. * * @return true if the item should stack in a player inventory, false otherwise */ public boolean isStackable() { return notedTemplateId == 799 || stackable; } public int getValue() { return value; } public int getHighAlchemyValue() { return (int) (value * 0.6); } public int getLowAlchemyValue() { return (int) (value * 0.4); } public boolean isMembersOnly() { return membersOnly; } public int getMaleWearModel1() { return maleWearModel1; } public int getMaleWearModel2() { return maleWearModel2; } public int getFemaleWearModel1() { return femaleWearModel1; } public int getFemaleWearModel2() { return femaleWearModel2; } public String[] getGroundOptions() { return groundOptions; } public String[] getInventoryOptions() { return inventoryOptions; } public short[] getOriginalModelColors() { return originalModelColors; } public short[] getModifiedModelColors() { return modifiedModelColors; } public short[] getTextureColour1() { return originalTextureColors; } public short[] getTextureColour2() { return modifiedTextureColors; } public int getMaleModel3() { return maleModel3; } public int getFemaleModel3() { return femaleModel3; } public int getSwapId() { return swapId; } public boolean canBank() { return true; } public boolean getUnnoted() { return unnoted; } public int getNotedItemId() { return notedTemplateId == 779 ? id : swapId; } public int getUnnotedItemId() { return unnoted ? id : swapId; } public int getNotedTemplateId() { return notedTemplateId; } public int[] getStackableIds() { return stackIds; } public int[] getStackableAmounts() { return stackAmountTriggers; } public int getTeamId() { return teamId; } public int getLendId() { return lendId; } public int getWeight() { return weight; } public int getLendTemplateId() { return lendTemplateId; } public void setName(String name) { this.name = name; } public void setExamine(String examine) { this.examine = examine; } public void setInventoryModelId(int inventoryModelId) { this.inventoryModelId = inventoryModelId; } public void setModelZoom(int modelZoom) { this.modelZoom = modelZoom; } public void setModelRotation1(int modelRotation1) { this.modelRotation1 = modelRotation1; } public void setModelRotation2(int modelRotation2) { this.modelRotation2 = modelRotation2; } public void setModelOffset1(int modelOffset1) { this.modelOffset1 = modelOffset1; } public void setModelOffset2(int modelOffset2) { this.modelOffset2 = modelOffset2; } public void setStackable(boolean stackable) { this.stackable = stackable; } public void setValue(int value) { this.value = value; } public void setMembersOnly(boolean membersOnly) { this.membersOnly = membersOnly; } public void setScriptData(Map<Integer, Object> scriptData) { this.scriptData = scriptData; } public void setMaleWearModel1(int maleWearModel1) { this.maleWearModel1 = maleWearModel1; } public void setMaleWearModel2(int maleWearModel2) { this.maleWearModel2 = maleWearModel2; } public void setFemaleWearModel1(int femaleWearModel1) { this.femaleWearModel1 = femaleWearModel1; } public void setFemaleWearModel2(int femaleWearModel2) { this.femaleWearModel2 = femaleWearModel2; } public void setMaleDialogueHat(int maleDialogueHat) { this.maleDialogueHat = maleDialogueHat; } public void setFemaleDialogueHat(int femaleDialogueHat) { this.femaleDialogueHat = femaleDialogueHat; } public void setMaleDialogueModel(int maleDialogueModel) { this.maleDialogueModel = maleDialogueModel; } public void setFemaleDialogueModel(int femaleDialogueModel) { this.femaleDialogueModel = femaleDialogueModel; } public void setMaleModel3(int maleModel3) { this.maleModel3 = maleModel3; } public void setFemaleModel3(int femaleModel3) { this.femaleModel3 = femaleModel3; } public void setGroundOptions(String[] groundOptions) { this.groundOptions = groundOptions; } public void setInventoryOptions(String[] inventoryOptions) { this.inventoryOptions = inventoryOptions; } public void setRecolorPalette(byte[] recolorPalette) { this.recolorPalette = recolorPalette; } public void setOriginalModelColors(short[] originalModelColors) { this.originalModelColors = originalModelColors; } public void setModifiedModelColors(short[] modifiedModelColors) { this.modifiedModelColors = modifiedModelColors; } public void setOriginalTextureColors(short[] originalTextureColors) { this.originalTextureColors = originalTextureColors; } public void setModifiedTextureColors(short[] modifiedTextureColors) { this.modifiedTextureColors = modifiedTextureColors; } public void setUnnoted(boolean unnoted) { this.unnoted = unnoted; } public void setSwapId(int swapId) { this.swapId = swapId; } public void setNotedTemplateId(int notedTemplateId) { this.notedTemplateId = notedTemplateId; } public void setStackIds(int[] stackIds) { this.stackIds = stackIds; } public void setStackAmountTriggers(int[] stackAmountTriggers) { this.stackAmountTriggers = stackAmountTriggers; } public void setTeamId(int teamId) { this.teamId = teamId; } public void setLendId(int lendId) { this.lendId = lendId; } public void setWeight(int weight) { this.weight = weight; } public void setLendTemplateId(int lendTemplateId) { this.lendTemplateId = lendTemplateId; } public void setModelVerticesY(int modelVerticesY) { this.modelVerticesY = modelVerticesY; } public void setModelLighting(int modelLighting) { this.modelLighting = modelLighting; } public void setModelVerticesZ(int modelVerticesZ) { this.modelVerticesZ = modelVerticesZ; } public void setModelVerticesX(int modelVerticesX) { this.modelVerticesX = modelVerticesX; } }