diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b6d62..d950a3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ API between minor releases. ## [Unreleased] +### Fixed +- Packed 16-bit and 32-bit integers in their full-width form are read little-endian. They were read with their bytes + reversed, so entity records that store a colour, extended data size or reactor count in that form were decoded from + the wrong bit position and failed or gave NaN or absurd coordinates (#7). + ## [0.4.0] - 2026-11-11 ### Added diff --git a/src/DwgIO/Internal/Container/BitReaderMsb.cs b/src/DwgIO/Internal/Container/BitReaderMsb.cs index 2c6166a..d489e4c 100644 --- a/src/DwgIO/Internal/Container/BitReaderMsb.cs +++ b/src/DwgIO/Internal/Container/BitReaderMsb.cs @@ -75,7 +75,7 @@ public uint ReadPackedInt() uint tag = ReadTwoBits(); return tag switch { - 0 => (uint)ReadBits(32), + 0 => ReadRawUInt32(), 1 => (uint)ReadBits(8), _ => 0, }; @@ -89,7 +89,7 @@ public uint ReadPackedShort() uint tag = ReadTwoBits(); return tag switch { - 0 => (uint)ReadBits(16), + 0 => ReadRawByte() | (uint)ReadRawByte() << 8, // operands run left to right: low byte first 1 => (uint)ReadBits(8), 2 => 0, _ => 256, diff --git a/tests/DwgIO.Tests/BitReaderTests.cs b/tests/DwgIO.Tests/BitReaderTests.cs index 4e6b810..0143289 100644 --- a/tests/DwgIO.Tests/BitReaderTests.cs +++ b/tests/DwgIO.Tests/BitReaderTests.cs @@ -24,6 +24,52 @@ public void ReadRawUInt32_LittleEndian() Assert.Equal(0x12345678u, reader.ReadRawUInt32()); } + [Fact] + public void ReadPackedShort_FullWidth_IsLittleEndian() + { + // 6 padding bits, then tag 00 and the bytes 0x34 0x12. + var reader = new BitReaderMsb([0x00, 0x34, 0x12]); + reader.ReadBits(6); + Assert.Equal(0x1234u, reader.ReadPackedShort()); + } + + [Fact] + public void ReadPackedInt_FullWidth_IsLittleEndian() + { + var reader = new BitReaderMsb([0x00, 0x78, 0x56, 0x34, 0x12]); + reader.ReadBits(6); + Assert.Equal(0x12345678u, reader.ReadPackedInt()); + } + + [Fact] + public void ReadPackedShort_FullWidth_Unaligned() + { + var reader = new BitReaderMsb([0x0D, 0x04, 0x80]); + Assert.Equal(0x1234u, reader.ReadPackedShort()); + } + + [Fact] + public void ReadPackedShortAndInt_ShortForms_AreUnchanged() + { + // Tags 01 (one byte 0xAB), 10 and 11. + var shorts = new BitReaderMsb([0x6A, 0xEC]); + Assert.Equal(0xABu, shorts.ReadPackedShort()); + Assert.Equal(0u, shorts.ReadPackedShort()); + Assert.Equal(256u, shorts.ReadPackedShort()); + + var ints = new BitReaderMsb([0x6A, 0xEC]); + Assert.Equal(0xABu, ints.ReadPackedInt()); + Assert.Equal(0u, ints.ReadPackedInt()); + Assert.Equal(0u, ints.ReadPackedInt()); + } + + [Fact] + public void ReadObjectType_BeforeR2010_FullWidthIsLittleEndian() + { + var reader = new BitReaderMsb([0x3D, 0x00, 0x40]); + Assert.Equal(0x1F4, reader.ReadObjectType(r2010Plus: false)); + } + [Fact] public void ReadVarUInt_MultiByteContinuation() {