diff --git a/core/src/com/riiablo/attributes/StatListWriter.java b/core/src/com/riiablo/attributes/StatListWriter.java new file mode 100644 index 00000000..168b7630 --- /dev/null +++ b/core/src/com/riiablo/attributes/StatListWriter.java @@ -0,0 +1,56 @@ +package com.riiablo.attributes; + +import java.util.Iterator; + +import com.riiablo.codec.excel.ItemStatCost; +import com.riiablo.io.BitOutput; +import com.riiablo.logger.LogManager; +import com.riiablo.logger.Logger; +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) { + log.traceEntry("write(stats: {}, stat: {}, bits: {})", stats, stat, bits); + final ItemStatCost.Entry entry = stat.entry(); + if (entry.Saved) { + 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); + bits.write63u(stat.value(), entry.CSvBits); + } else { + if (log.traceEnabled()) log.trace("Writing stat {}", stat.debugString()); + bits.write63u(stat.param(), entry.Save_Param_Bits); + bits.write63u(stat.value() + entry.Save_Add, entry.Save_Bits); + } + } + + public void write(StatListGetter stats, BitOutput bits) { + for (Iterator it = stats.iterator(); it.hasNext();) { + final StatGetter stat = it.next(); + final short id = stat.id(); + try { + MDC.put("stat", id); + bits.write15u(id, Stat.BITS); + final byte numEncoded = Stat.getNumEncoded(id); + try { + if (numEncoded > 1) MDC.put("numEncoded", numEncoded); + write(stats, stat, bits); + 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); + } + } finally { + MDC.remove("numEncoded"); + } + } finally { + MDC.remove("stat"); + } + } + + bits.write15u(Stat.NONE, Stat.BITS); + } +} diff --git a/core/test/com/riiablo/attributes/StatListWriterTest.java b/core/test/com/riiablo/attributes/StatListWriterTest.java new file mode 100644 index 00000000..423054d6 --- /dev/null +++ b/core/test/com/riiablo/attributes/StatListWriterTest.java @@ -0,0 +1,69 @@ +package com.riiablo.attributes; + +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.badlogic.gdx.ApplicationAdapter; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.backends.headless.HeadlessApplication; + +import com.riiablo.Files; +import com.riiablo.Riiablo; +import com.riiablo.codec.StringTBLs; +import com.riiablo.io.BitInput; +import com.riiablo.io.BitOutput; +import com.riiablo.io.ByteInput; +import com.riiablo.io.ByteOutput; +import com.riiablo.logger.Level; +import com.riiablo.logger.LogManager; +import com.riiablo.mpq.MPQFileHandleResolver; + +public class StatListWriterTest { + @BeforeClass + public static void setup() { + Gdx.app = new HeadlessApplication(new ApplicationAdapter() {}); + Riiablo.home = Gdx.files.absolute("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Diablo II"); + Riiablo.mpqs = new MPQFileHandleResolver(); + Riiablo.string = new StringTBLs(Riiablo.mpqs); + Riiablo.files = new Files(); + } + + @AfterClass + public static void teardown() { + Gdx.app.exit(); + } + + @BeforeClass + public static void before() { + LogManager.setLevel("com.riiablo.attributes", Level.TRACE); + } + + private void testItem(byte[] data, long bitsToSkip, int length) { + 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); + 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); + bitOutput.flush(); + System.out.println(ByteBufUtil.prettyHexDump(out.buffer())); + + Assert.assertTrue(ByteBufUtil.equals(in.buffer(), 0, out.buffer(), 0, in.buffer().readerIndex())); + } + + @Test + public void read_item_grief_stats() { + testItem(Gdx.files.internal("test/Grief.d2i").readBytes(), 197, 0x12); + } +} \ No newline at end of file