Added boolean parameter to read/write StatList functions

entry.Saved was not appropriate for determining this feature
Implemented prop list flags into StatListWriterTest tests
Added additional logging to help with organizing encoded stats
This commit is contained in:
Collin Smith
2020-09-02 19:47:05 -07:00
parent 57c12bd781
commit d3cbca6b6f
4 changed files with 124 additions and 21 deletions

View File

@ -9,11 +9,11 @@ import com.riiablo.logger.MDC;
public class StatListReader {
private static final Logger log = LogManager.getLogger(StatListReader.class);
public StatGetter read(StatListBuilder stats, short stat, BitInput bits) {
public StatGetter read(StatListBuilder stats, short stat, BitInput bits, boolean cs) {
log.traceEntry("read(stats: {}, stat: {}, bits: {})", stats, stat, bits);
final ItemStatCost.Entry entry = Stat.entry(stat);
final int param, value;
if (entry.Saved) {
if (cs) {
log.trace("Reading character save stat...");
assert !entry.CSvSigned : "entry.CSvSigned(" + entry.CSvSigned + ") unsupported";
param = (int) bits.read63u(entry.CSvParam);
@ -26,18 +26,23 @@ public class StatListReader {
return stats.put(stat, param, value).last();
}
public StatListGetter read(StatListBuilder stats, BitInput bits) {
public StatListGetter read(StatListBuilder stats, BitInput bits, boolean cs) {
log.traceEntry("read(stats: {}, bits: {})", stats, bits);
for (short stat; (stat = bits.read15u(Stat.BITS)) != Stat.NONE;) {
try {
MDC.put("stat", stat);
final byte numEncoded = Stat.getNumEncoded(stat);
try {
if (numEncoded > 1) MDC.put("numEncoded", numEncoded);
if (numEncoded > 1) {
MDC.put("numEncoded", numEncoded);
MDC.put("encodedStat", stat);
}
for (short j = stat, s = (short) (stat + numEncoded); j < s; j++) {
read(stats, j, bits);
if (j > stat) MDC.put("encodedStat", j);
read(stats, j, bits, cs);
}
} finally {
MDC.remove("encodedStat");
MDC.remove("numEncoded");
}
} finally {
@ -51,10 +56,10 @@ public class StatListReader {
/**
* Reads a single property list into {@link Attributes#base()}
*/
public StatListGetter read(Attributes attrs, BitInput bits) {
public StatListGetter read(Attributes attrs, BitInput bits, boolean cs) {
final StatList stats = attrs.base().clear();
final StatListBuilder builder = stats.buildList();
return read(builder, bits);
return read(builder, bits, cs);
}
/**
@ -67,7 +72,7 @@ public class StatListReader {
if (((flags >> i) & 1) == 1) {
try {
MDC.put("propList", StatListFlags.itemToString(i));
read(builder, bits);
read(builder, bits, false);
} finally {
MDC.remove("propList");
}

View File

@ -11,10 +11,10 @@ import com.riiablo.logger.MDC;
public class StatListWriter {
private static final Logger log = LogManager.getLogger(StatListWriter.class);
public void write(StatListGetter stats, StatGetter stat, BitOutput bits) {
public void write(StatListGetter stats, StatGetter stat, BitOutput bits, boolean cs) {
log.traceEntry("write(stats: {}, stat: {}, bits: {})", stats, stat, bits);
final ItemStatCost.Entry entry = stat.entry();
if (entry.Saved) {
if (cs) {
if (log.traceEnabled()) log.trace("Writing character save stat {}", stat.debugString());
assert !entry.CSvSigned : "entry.CSvSigned(" + entry.CSvSigned + ") unsupported";
bits.write63u(stat.param(), entry.CSvParam);
@ -26,7 +26,7 @@ public class StatListWriter {
}
}
public void write(StatListGetter stats, BitOutput bits) {
public void write(StatListGetter stats, BitOutput bits, boolean cs) {
for (Iterator<StatGetter> it = stats.iterator(); it.hasNext();) {
final StatGetter stat = it.next();
final short id = stat.id();
@ -35,15 +35,20 @@ public class StatListWriter {
bits.write15u(id, Stat.BITS);
final byte numEncoded = Stat.getNumEncoded(id);
try {
if (numEncoded > 1) MDC.put("numEncoded", numEncoded);
write(stats, stat, bits);
if (numEncoded > 1) {
MDC.put("numEncoded", numEncoded);
MDC.put("encodedStat", id);
}
write(stats, stat, bits, cs);
for (short j = 1; j < numEncoded; j++) {
final StatGetter next = it.next();
assert next.id() == id + j : String.format(
"it.next(%s) != %d : getNumEncoded(%s)[%d..%d]", next, id + j, id + j, id, id + numEncoded - 1);
write(stats, stat, bits);
MDC.put("encodedStat", next.id());
write(stats, stat, bits, cs);
}
} finally {
MDC.remove("encodedStat");
MDC.remove("numEncoded");
}
} finally {
@ -53,4 +58,22 @@ public class StatListWriter {
bits.write15u(Stat.NONE, Stat.BITS);
}
public void write(Attributes attrs, BitOutput bits) {
throw new UnsupportedOperationException(); // TODO: character saves
}
public void write(Attributes attrs, BitOutput bits, int flags, int maxLists) {
final StatList stats = attrs.list();
for (int i = 0; i < maxLists; i++) {
if (((flags >> i) & 1) == 1) {
try {
MDC.put("propList", StatListFlags.itemToString(i));
write(stats.get(i), bits, false);
} finally {
MDC.remove("propList");
}
}
}
}
}

View File

@ -55,7 +55,7 @@ public class StatListReaderTest {
final BitInput bits = ByteInput.wrap(bytes).unalign();
final Attributes attrs = Attributes.aggregateAttributes();
final StatListReader reader = new StatListReader();
reader.read(attrs.base().buildList(), bits);
reader.read(attrs.base().buildList(), bits, true);
}
@Test
@ -89,7 +89,7 @@ public class StatListReaderTest {
final BitInput bits = ByteInput.wrap(bytes).unalign().skipBits(197);
final Attributes attrs = Attributes.aggregateAttributes();
final StatListReader reader = new StatListReader();
final StatListGetter stats = reader.read(attrs.base().buildList(), bits);
final StatListGetter stats = reader.read(attrs.base().buildList(), bits, false);
Assert.assertTrue(stats.contains(Stat.item_healafterkill));
Assert.assertEquals(11, stats.get(Stat.item_healafterkill).value1());

View File

@ -22,6 +22,10 @@ import com.riiablo.logger.Level;
import com.riiablo.logger.LogManager;
import com.riiablo.mpq.MPQFileHandleResolver;
import static com.riiablo.attributes.StatListFlags.FLAG_MAGIC;
import static com.riiablo.attributes.StatListFlags.FLAG_RUNE;
import static com.riiablo.attributes.StatListFlags.NUM_ITEM_LISTS;
public class StatListWriterTest {
@BeforeClass
public static void setup() {
@ -42,20 +46,21 @@ public class StatListWriterTest {
LogManager.setLevel("com.riiablo.attributes", Level.TRACE);
}
private void testItem(byte[] data, long bitsToSkip, int length) {
private void testItem(byte[] data, long bitsToSkip, int length, int flags) {
final int offset = (int) (bitsToSkip >> 3);
final int bitOffset = (int) (bitsToSkip & 0x7);
ByteInput in = ByteInput.wrap(Unpooled.wrappedBuffer(data, offset, length));
BitInput bitInput = in.unalign().skipBits(bitOffset);
StatListReader reader = new StatListReader();
final StatListGetter stats = reader.read(StatList.obtain().buildList(), bitInput);
final Attributes attrs = Attributes.aggregateAttributes();
reader.read(attrs, bitInput, flags, NUM_ITEM_LISTS);
System.out.println(ByteBufUtil.prettyHexDump(in.buffer(), 0, in.buffer().readerIndex()));
ByteOutput out = ByteOutput.wrap(Unpooled.buffer(length, length));
BitOutput bitOutput = out.unalign().writeRaw(data[offset], bitOffset);
System.out.println(ByteBufUtil.prettyHexDump(out.buffer()));
StatListWriter writer = new StatListWriter();
writer.write(stats, bitOutput);
writer.write(attrs, bitOutput, flags, NUM_ITEM_LISTS);
bitOutput.flush();
System.out.println(ByteBufUtil.prettyHexDump(out.buffer()));
@ -63,7 +68,77 @@ public class StatListWriterTest {
}
@Test
public void read_item_grief_stats() {
testItem(Gdx.files.internal("test/Grief.d2i").readBytes(), 197, 0x12);
public void Spirit() {
testItem(Gdx.files.internal("test/Spirit.d2i").readBytes(), 216, 0x19, FLAG_MAGIC | FLAG_RUNE);
}
// @Test
// public void Annihilus() {
// testItem(Gdx.files.internal("test/Annihilus.d2i").readBytes());
// }
//
// @Test
// public void Hunters_Bow_of_Blight() {
// testItem(Gdx.files.internal("test/Hunter's Bow of Blight.d2i").readBytes());
// }
//
// @Test
// public void Horadric_Malus() {
// testItem(Gdx.files.internal("test/Horadric Malus.d2i").readBytes());
// }
//
// @Test
// public void Wirts_Leg() {
// testItem(Gdx.files.internal("test/Wirt's Leg.d2i").readBytes());
// }
@Test
public void Grief() {
testItem(Gdx.files.internal("test/Grief.d2i").readBytes(), 197, 0x12, FLAG_RUNE);
}
// @Test
// public void Horadric_Cube() {
// testItem(Gdx.files.internal("test/Horadric Cube.d2i").readBytes());
// }
//
// @Test
// public void Flawed_Ruby() {
// testItem(Gdx.files.internal("test/Flawed Ruby.d2i").readBytes());
// }
//
// @Test
// public void Thul_Rune() {
// testItem(Gdx.files.internal("test/Thul Rune.d2i").readBytes());
// }
//
// @Test
// public void Tome_of_Town_Portal() {
// testItem(Gdx.files.internal("test/Tome of Town Portal.d2i").readBytes());
// }
//
// @Test
// public void Tome_of_Identify() {
// testItem(Gdx.files.internal("test/Tome of Identify.d2i").readBytes());
// }
//
// @Test
// public void Rugged_Small_Charm_of_Vita() {
// testItem(Gdx.files.internal("test/Rugged Small Charm of Vita.d2i").readBytes());
// }
//
// @Test
// public void Aldurs_Advance() {
// testItem(Gdx.files.internal("test/Aldur's Advance.d2i").readBytes());
// }
//
// @Test
// public void Blood_Eye() {
// testItem(Gdx.files.internal("test/Blood Eye.d2i").readBytes());
// }
//
// @Test
// public void Vampire_Gaze() {
// testItem(Gdx.files.internal("test/Vampire Gaze.d2i").readBytes());
// }
}