diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs b/src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs new file mode 100644 index 00000000..24f5ab17 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs @@ -0,0 +1,63 @@ +using System; +using System.Buffers.Binary; +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterARM64 : Filter +{ + private int _pos; + + public BCJFilterARM64(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 8) => _pos = 0; + + protected override int Transform(byte[] buffer, int offset, int count) + { + var end = offset + count - 4; + int i; + + for (i = offset; i <= end; i += 4) + { + uint pc = (uint)(_pos + i - offset); + uint instr = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i, 4) + ); + + if ((instr >> 26) == 0x25) + { + uint src = instr; + instr = 0x94000000; + + pc >>= 2; + if (!_isEncoder) + pc = 0U - pc; + + instr |= (src + pc) & 0x03FFFFFF; + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), instr); + } + else if ((instr & 0x9F000000) == 0x90000000) + { + uint src = ((instr >> 29) & 3) | ((instr >> 3) & 0x001FFFFC); + + if (((src + 0x00020000) & 0x001C0000) != 0) + continue; + + instr &= 0x9000001F; + + pc >>= 12; + if (!_isEncoder) + pc = 0U - pc; + + uint dest = src + pc; + instr |= (dest & 3) << 29; + instr |= (dest & 0x0003FFFC) << 3; + instr |= (0U - (dest & 0x00020000)) & 0x00E00000; + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), instr); + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs b/src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs new file mode 100644 index 00000000..67d9cb52 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs @@ -0,0 +1,210 @@ +using System; +using System.Buffers.Binary; +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterRISCV : Filter +{ + private int _pos; + + public BCJFilterRISCV(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 8) => _pos = 0; + + private int Decode(byte[] buffer, int offset, int count) + { + if (count < 8) + { + return 0; + } + + var end = offset + count - 8; + int i; + for (i = offset; i <= end; i += 2) + { + uint inst = buffer[i]; + if (inst == 0xEF) + { + uint b1 = buffer[i + 1]; + if ((b1 & 0x0D) != 0) + continue; + + uint b2 = buffer[i + 2]; + uint b3 = buffer[i + 3]; + uint pc = (uint)(_pos + i); + + uint addr = ((b1 & 0xF0) << 13) | (b2 << 9) | (b3 << 1); + + addr -= pc; + + buffer[i + 1] = (byte)((b1 & 0x0F) | ((addr >> 8) & 0xF0)); + + buffer[i + 2] = (byte)( + ((addr >> 16) & 0x0F) | ((addr >> 7) & 0x10) | ((addr << 4) & 0xE0) + ); + + buffer[i + 3] = (byte)(((addr >> 4) & 0x7F) | ((addr >> 13) & 0x80)); + + i += 4 - 2; + } + else if ((inst & 0x7F) == 0x17) + { + uint inst2 = 0; + inst |= (uint)buffer[i + 1] << 8; + inst |= (uint)buffer[i + 2] << 16; + inst |= (uint)buffer[i + 3] << 24; + + if ((inst & 0xE80) != 0) + { + inst2 = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + if (((((inst) << 8) ^ (inst2)) & 0xF8003) != 3) + { + i += 6 - 2; + continue; + } + uint addr = inst & 0xFFFFF000; + addr += inst2 >> 20; + + inst = 0x17 | (2 << 7) | (inst2 << 12); + inst2 = addr; + } + else + { + uint inst2_rs1 = inst >> 27; + if ((uint)(((inst) - 0x3117) << 18) >= ((inst2_rs1) & 0x1D)) + { + i += 4 - 2; + continue; + } + + uint addr = BinaryPrimitives.ReadUInt32BigEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + + addr -= (uint)(_pos + i); + + inst2 = (inst >> 12) | (addr << 20); + + inst = 0x17 | (inst2_rs1 << 7) | ((addr + 0x800) & 0xFFFFF000); + } + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), inst); + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i + 4, 4), inst2); + + i += 8 - 2; + } + } + i -= offset; + _pos += i; + return i; + } + + private int Encode(byte[] buffer, int offset, int count) + { + if (count < 8) + { + return 0; + } + + var end = offset + count - 8; + int i; + for (i = offset; i <= end; i += 2) + { + uint inst = buffer[i]; + if (inst == 0xEF) + { + uint b1 = buffer[i + 1]; + if ((b1 & 0x0D) != 0) + continue; + + uint b2 = buffer[i + 2]; + uint b3 = buffer[i + 3]; + uint pc = (uint)(_pos + i); + + uint addr = + ((b1 & 0xF0) << 8) + | ((b2 & 0x0F) << 16) + | ((b2 & 0x10) << 7) + | ((b2 & 0xE0) >> 4) + | ((b3 & 0x7F) << 4) + | ((b3 & 0x80) << 13); + + addr += pc; + + buffer[i + 1] = (byte)((b1 & 0x0F) | ((addr >> 13) & 0xF0)); + + buffer[i + 2] = (byte)(addr >> 9); + + buffer[i + 3] = (byte)(addr >> 1); + + i += 4 - 2; + } + else if ((inst & 0x7F) == 0x17) + { + inst |= (uint)buffer[i + 1] << 8; + inst |= (uint)buffer[i + 2] << 16; + inst |= (uint)buffer[i + 3] << 24; + + if ((inst & 0xE80) != 0) + { + uint inst2 = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + if (((((inst) << 8) ^ (inst2)) & 0xF8003) != 3) + { + i += 6 - 2; + continue; + } + uint addr = inst & 0xFFFFF000; + addr += (inst2 >> 20) - ((inst2 >> 19) & 0x1000); + + addr += (uint)(_pos + i); + inst = 0x17 | (2 << 7) | (inst2 << 12); + + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), inst); + BinaryPrimitives.WriteUInt32BigEndian(new Span(buffer, i + 4, 4), addr); + } + else + { + uint fake_rs1 = inst >> 27; + if ((uint)(((inst) - 0x3117) << 18) >= ((fake_rs1) & 0x1D)) + { + i += 4 - 2; + continue; + } + + uint fake_addr = BinaryPrimitives.ReadUInt32LittleEndian( + new ReadOnlySpan(buffer, i + 4, 4) + ); + + uint fake_inst2 = (inst >> 12) | (fake_addr << 20); + + inst = 0x17 | (fake_rs1 << 7) | (fake_addr & 0xFFFFF000); + + BinaryPrimitives.WriteUInt32LittleEndian(new Span(buffer, i, 4), inst); + BinaryPrimitives.WriteUInt32LittleEndian( + new Span(buffer, i + 4, 4), + fake_inst2 + ); + } + i += 8 - 2; + } + } + i -= offset; + _pos += i; + return i; + } + + protected override int Transform(byte[] buffer, int offset, int count) + { + if (_isEncoder) + { + return Encode(buffer, offset, count); + } + else + { + return Decode(buffer, offset, count); + } + } +} diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index f4cfde4b..eb3e3bdd 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -25,6 +25,8 @@ internal static class DecoderRegistry private const uint K_ARM = 0x03030501; private const uint K_ARMT = 0x03030701; private const uint K_SPARC = 0x03030805; + private const uint K_ARM64 = 0x0A; + private const uint K_RISCV = 0x0B; private const uint K_DEFLATE = 0x040108; private const uint K_B_ZIP2 = 0x040202; private const uint K_ZSTD = 0x4F71101; @@ -66,6 +68,10 @@ long limit return new BCJFilterARMT(false, inStreams.Single()); case K_SPARC: return new BCJFilterSPARC(false, inStreams.Single()); + case K_ARM64: + return new BCJFilterARM64(false, inStreams.Single()); + case K_RISCV: + return new BCJFilterRISCV(false, inStreams.Single()); case K_B_ZIP2: return new BZip2Stream(inStreams.Single(), CompressionMode.Decompress, true); case K_PPMD: diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index 226ba4d8..a0c9d8c9 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -162,6 +162,12 @@ public class SevenZipArchiveTests : ArchiveTests [Fact] public void SevenZipArchive_SPARC_FileRead() => ArchiveFileRead("7Zip.SPARC.7z"); + [Fact] + public void SevenZipArchive_ARM64_FileRead() => ArchiveFileRead("7Zip.ARM64.7z"); + + [Fact] + public void SevenZipArchive_RISCV_FileRead() => ArchiveFileRead("7Zip.RISCV.7z"); + [Fact] public void SevenZipArchive_Filters_FileRead() => ArchiveFileRead("7Zip.Filters.7z"); diff --git a/tests/TestArchives/Archives/7Zip.ARM64.7z b/tests/TestArchives/Archives/7Zip.ARM64.7z new file mode 100644 index 00000000..2e9d6293 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.ARM64.7z differ diff --git a/tests/TestArchives/Archives/7Zip.Filters.7z b/tests/TestArchives/Archives/7Zip.Filters.7z index ea15f3b2..d066779b 100644 Binary files a/tests/TestArchives/Archives/7Zip.Filters.7z and b/tests/TestArchives/Archives/7Zip.Filters.7z differ diff --git a/tests/TestArchives/Archives/7Zip.RISCV.7z b/tests/TestArchives/Archives/7Zip.RISCV.7z new file mode 100644 index 00000000..b65c517b Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.RISCV.7z differ