Disabled skipping of item byte stream signature

ItemSerializer will process and validate item signature
Implemented far more robust logging to ItemSerializer
This commit is contained in:
Collin Smith 2020-08-02 11:11:59 -07:00
parent e8f359ffa2
commit a8e2402a19
5 changed files with 87 additions and 32 deletions

View File

@ -72,7 +72,6 @@ import com.riiablo.net.packet.d2gs.SwapStoreItem;
import com.riiablo.net.packet.d2gs.VelocityP;
import com.riiablo.net.packet.d2gs.WarpP;
import com.riiablo.save.CharData;
import com.riiablo.save.D2S;
import com.riiablo.util.ArrayUtils;
import com.riiablo.util.BufferUtils;
import com.riiablo.util.DebugUtils;
@ -314,7 +313,7 @@ public class ClientNetworkReceiver extends IntervalSystem {
PositionP position = findTable(sync, ComponentP.PositionP, new PositionP());
byte[] bytes = BufferUtils.readRemaining(item.dataAsByteBuffer());
BitStream bitStream = new BitStream(bytes);
bitStream.skip(D2S.ItemData.SECTION_HEADER_BITS);
// bitStream.skip(D2S.ItemData.SECTION_HEADER_BITS);
Item itemObj = com.riiablo.item.Item.loadFromStream(bitStream);
return factory.createItem(itemObj, position.x(), position.y());
}

View File

@ -7,7 +7,6 @@ import com.riiablo.engine.server.component.Item;
import com.riiablo.net.packet.d2gs.ComponentP;
import com.riiablo.net.packet.d2gs.EntitySync;
import com.riiablo.net.packet.d2gs.ItemP;
import com.riiablo.save.D2S;
import com.riiablo.util.BufferUtils;
public class ItemSerializer implements FlatBuffersSerializer<Item, ItemP> {
@ -35,7 +34,7 @@ public class ItemSerializer implements FlatBuffersSerializer<Item, ItemP> {
getTable(sync, j);
byte[] bytes = BufferUtils.readRemaining(table.dataAsByteBuffer());
BitStream bitStream = new BitStream(bytes);
bitStream.skip(D2S.ItemData.SECTION_HEADER_BITS);
// bitStream.skip(D2S.ItemData.SECTION_HEADER_BITS);
c.item = com.riiablo.item.Item.loadFromStream(bitStream);
return c;
}

View File

@ -33,7 +33,7 @@ public class Item {
private static final ItemLabeler DEFAULT_LABELER = new ItemLabeler();
public static Item loadFromStream(BitStream bitStream) {
return DEFAULT_SERIALIZER.read(bitStream);
return DEFAULT_SERIALIZER.readSingleItem(bitStream);
}
public static final float ETHEREAL_ALPHA = 2 / 3f;
@ -277,6 +277,10 @@ public class Item {
return (flags & flag) == flag;
}
public boolean isCompact() {
return hasFlag(ITEMFLAG_COMPACT);
}
public boolean isIdentified() {
return hasFlag(ITEMFLAG_IDENTIFIED);
}
@ -476,7 +480,7 @@ public class Item {
return base.usesound;
}
private String getFlagsString() {
String getFlagsString() {
StringBuilder builder = new StringBuilder();
if (ONLY_KNOWN_FLAGS && (flags & ITEMFLAG__RELOAD ) == ITEMFLAG__RELOAD ) builder.append("ITEMFLAG__RELOAD" ).append('|');
if (ONLY_KNOWN_FLAGS && (flags & ITEMFLAG__BOUGHT ) == ITEMFLAG__BOUGHT ) builder.append("ITEMFLAG__BOUGHT" ).append('|');

View File

@ -1,43 +1,82 @@
package com.riiablo.item;
import com.badlogic.gdx.Gdx;
import java.util.Arrays;
import org.apache.logging.log4j.Logger;
import com.badlogic.gdx.utils.Array;
import com.riiablo.Riiablo;
import com.riiablo.codec.excel.Gems;
import com.riiablo.codec.util.BitStream;
import com.riiablo.log.Log;
import com.riiablo.log.LogManager;
import com.riiablo.save.InvalidFormat;
import com.riiablo.util.DebugUtils;
public class ItemSerializer {
private static final String TAG = "ItemSerializer";
private static final Logger log = LogManager.getLogger(ItemSerializer.class);
private static final boolean DEBUG = true;
private static final byte[] SIGNATURE = {0x4A, 0x4D};
public Item read(BitStream bitStream) {
private static boolean readSignature(BitStream bitStream) {
log.trace("Validating item signature");
byte[] signature = bitStream.readFully(SIGNATURE.length);
boolean matched = Arrays.equals(signature, SIGNATURE);
if (!matched) {
throw new InvalidFormat(
String.format("Item signature doesn't match expected signature: %s, expected %s",
DebugUtils.toByteArray(signature),
DebugUtils.toByteArray(SIGNATURE)));
}
return matched;
}
public Item readItem(BitStream bitStream) {
Item item = readSingleItem(bitStream);
if (item.socketsFilled > 0) log.trace("Reading {} sockets...", item.socketsFilled);
for (int i = 0; i < item.socketsFilled; i++) {
try {
Log.put("socket", String.valueOf(i));
bitStream.alignToByte();
item.sockets.add(readSingleItem(bitStream));
} finally {
Log.remove("socket");
}
}
return item;
}
public Item readSingleItem(BitStream bitStream) {
log.trace("Reading item...");
readSignature(bitStream);
Item item = new Item();
item.reset();
item.flags = (int) bitStream.readUnsigned(Integer.SIZE);
item.version = bitStream.readUnsigned8OrLess(Byte.SIZE);
item.flags = (int) bitStream.readUnsigned(Integer.SIZE);
Log.tracef(log, "flags: 0x%08X [%s]", item.flags, item.getFlagsString());
item.version = bitStream.readUnsigned8OrLess(Byte.SIZE);
log.trace("version: {}", item.version);
bitStream.skip(2); // Unknown use -- safe to skip
item.location = Location.valueOf(bitStream.readUnsigned7OrLess(3));
item.bodyLoc = BodyLoc.valueOf(bitStream.readUnsigned7OrLess(4));
item.gridX = bitStream.readUnsigned7OrLess(4);
item.gridY = bitStream.readUnsigned7OrLess(4);
item.bodyLoc = BodyLoc.valueOf(bitStream.readUnsigned7OrLess(4));
item.gridX = bitStream.readUnsigned7OrLess(4);
item.gridY = bitStream.readUnsigned7OrLess(4);
item.storeLoc = StoreLoc.valueOf(bitStream.readUnsigned7OrLess(3));
if ((item.flags & Item.ITEMFLAG_BODYPART) == Item.ITEMFLAG_BODYPART) {
int charClass = bitStream.readUnsigned7OrLess(3);
int charLevel = bitStream.readUnsigned7OrLess(7);
String charName = bitStream.readString2(Riiablo.MAX_NAME_LENGTH + 1, 7);
int charClass = bitStream.readUnsigned7OrLess(3);
int charLevel = bitStream.readUnsigned7OrLess(7);
String charName = bitStream.readString2(Riiablo.MAX_NAME_LENGTH + 1, 7);
item.setEar(charClass, charLevel, charName);
} else {
item.setBase(bitStream.readString(4).trim());
item.socketsFilled = bitStream.readUnsigned7OrLess(3);
}
log.trace("code: {}", item.code);
if ((item.flags & Item.ITEMFLAG_COMPACT) == Item.ITEMFLAG_COMPACT) {
readCompact(item);
} else {
read(bitStream, item);
readStandard(bitStream, item);
}
return item;
@ -52,12 +91,13 @@ public class ItemSerializer {
}
}
private static void read(BitStream bitStream, Item item) {
item.data = bitStream.getBufferView(); // TODO: remove when serialization implemented
item.id = (int) bitStream.readUnsigned(Integer.SIZE);
item.ilvl = bitStream.readUnsigned7OrLess(7);
item.quality = Quality.valueOf(bitStream.readUnsigned7OrLess(4));
item.pictureId = bitStream.readBoolean() ? bitStream.readUnsigned7OrLess(3) : Item.NO_PICTURE_ID;
private static void readStandard(BitStream bitStream, Item item) {
item.data = bitStream.getBufferView(); // TODO: remove when serialization implemented
item.id = (int) bitStream.readUnsigned(Integer.SIZE);
Log.tracef(log, "id: 0x%08X", item.id);
item.ilvl = bitStream.readUnsigned7OrLess(7);
item.quality = Quality.valueOf(bitStream.readUnsigned7OrLess(4));
item.pictureId = bitStream.readBoolean() ? bitStream.readUnsigned7OrLess(3) : Item.NO_PICTURE_ID;
item.classOnly = bitStream.readBoolean() ? bitStream.readUnsigned15OrLess(11) : Item.NO_CLASS_ONLY;
readQualityData(bitStream, item);
@ -86,10 +126,12 @@ public class ItemSerializer {
}
private static boolean readQualityData(BitStream bitStream, Item item) {
log.trace("quality: {}", item.quality);
switch (item.quality) {
case LOW:
case HIGH:
item.qualityId = bitStream.readUnsigned31OrLess(3);
log.trace("qualityId: {}", item.qualityId);
return true;
case NORMAL:
@ -98,28 +140,39 @@ public class ItemSerializer {
case SET:
item.qualityId = bitStream.readUnsigned31OrLess(Item.SET_ID_SIZE);
log.trace("qualityId: {}", item.qualityId);
item.qualityData = Riiablo.files.SetItems.get(item.qualityId);
log.trace("qualityData: {}", item.qualityData);
if (item.qualityId == (1 << Item.SET_ID_SIZE) - 1) {
Gdx.app.error(TAG, String.format("Unknown set id: 0x%03x", item.qualityId));
log.error("Unknown set item id: {}", item.qualityId);
// This is unexpected -- all set items should reference a set id
// TODO: throw item format exception
}
return true;
case UNIQUE:
item.qualityId = bitStream.readUnsigned31OrLess(Item.UNIQUE_ID_SIZE);
log.trace("qualityId: {}", item.qualityId);
item.qualityData = Riiablo.files.UniqueItems.get(item.qualityId);
log.trace("qualityData: {}", item.qualityData);
if (item.qualityId == (1 << Item.UNIQUE_ID_SIZE) - 1) {
Gdx.app.error(TAG, String.format("Unknown unique id: 0x%03x", item.qualityId));
log.warn("Unknown unique item id: {}", item.qualityId);
// This is expected for hdm and possibly others
// TODO: ensure item can be gracefully handled, else throw item format exception
}
return true;
case MAGIC:
item.qualityId = bitStream.readUnsigned31OrLess(2 * Item.MAGIC_AFFIX_SIZE); // 11 for prefix, 11 for suffix
log.trace("qualityId: {}", item.qualityId);
return true;
case RARE:
case CRAFTED:
item.qualityId = bitStream.readUnsigned31OrLess(2 * Item.RARE_AFFIX_SIZE); // 8 for prefix, 8 for suffix
log.trace("qualityId: {}", item.qualityId);
item.qualityData = new RareQualityData(bitStream);
log.trace("qualityData: {}", item.qualityData);
return true;
default:

View File

@ -763,7 +763,7 @@ public class D2S {
public static class ItemData {
static final byte[] SECTION_HEADER = {0x4A, 0x4D};
static final byte[] SECTION_FOOTER = ArrayUtils.addAll(SECTION_HEADER, new byte[] {0x00, 0x00});
public static final int SECTION_HEADER_BITS = SECTION_HEADER.length * Byte.SIZE;
// public static final int SECTION_HEADER_BITS = SECTION_HEADER.length * Byte.SIZE;
public byte header[];
public short size;
@ -784,7 +784,7 @@ public class D2S {
//else System.out.println(i + " = " + slice.remaining());
byte[] bytes = BufferUtils.readRemaining(slice);
BitStream bitStream = new BitStream(bytes);
bitStream.skip(SECTION_HEADER_BITS);
// bitStream.skip(SECTION_HEADER_BITS);
Item item = Item.loadFromStream(bitStream);
items.add(item);
@ -794,7 +794,7 @@ public class D2S {
//else System.out.println(i + " = " + slice.remaining());
bytes = BufferUtils.readRemaining(slice);
bitStream = new BitStream(bytes);
bitStream.skip(SECTION_HEADER_BITS);
// bitStream.skip(SECTION_HEADER_BITS);
Item socket = Item.loadFromStream(bitStream);
item.sockets.add(socket);
assert socket.location == Location.SOCKET;
@ -838,7 +838,7 @@ public class D2S {
if (slice.remaining() <= 0) return this;
byte[] bytes = BufferUtils.readRemaining(buffer);
BitStream bitStream = new BitStream(bytes);
bitStream.skip(ItemData.SECTION_HEADER_BITS);
// bitStream.skip(ItemData.SECTION_HEADER_BITS);
item = Item.loadFromStream(bitStream);
for (int j = 0; j < item.socketsFilled; j++) {
slice = BufferUtils.slice(buffer, ItemData.SECTION_HEADER, true);
@ -846,7 +846,7 @@ public class D2S {
//else System.out.println(i + " = " + slice.remaining());
bytes = BufferUtils.readRemaining(slice);
bitStream = new BitStream(bytes);
bitStream.skip(ItemData.SECTION_HEADER_BITS);
// bitStream.skip(ItemData.SECTION_HEADER_BITS);
Item socket = Item.loadFromStream(bitStream);
item.sockets.add(socket);
assert socket.location == Location.SOCKET;