diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs b/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs new file mode 100644 index 00000000..86e90b10 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterARM.cs @@ -0,0 +1,49 @@ +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterARM : Filter +{ + private int _pos; + + public BCJFilterARM(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 8) => _pos = 8; + + protected override int Transform(byte[] buffer, int offset, int count) + { + var end = offset + count - 4; + int i; + + for (i = offset; i <= end; i += 4) + { + if ((buffer[i + 3] & 0xFF) == 0xEB) + { + int src = + ((buffer[i + 2] & 0xFF) << 16) + | ((buffer[i + 1] & 0xFF) << 8) + | (buffer[i] & 0xFF); + + src <<= 2; + + int dest; + if (_isEncoder) + { + dest = src + (_pos + i - offset); + } + else + { + dest = src - (_pos + i - offset); + } + + dest >>>= 2; + buffer[i + 2] = (byte)(dest >>> 16); + buffer[i + 1] = (byte)(dest >>> 8); + buffer[i] = (byte)dest; + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs b/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs new file mode 100644 index 00000000..d2ad1bbf --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterARMT.cs @@ -0,0 +1,47 @@ +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterARMT : Filter +{ + private int _pos; + + public BCJFilterARMT(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 4) => _pos = 4; + + protected override int Transform(byte[] buffer, int offset, int count) + { + var end = offset + count - 4; + int i; + + for (i = offset; i <= end; i += 2) + { + if ((buffer[i + 1] & 0xF8) == 0xF0 && (buffer[i + 3] & 0xF8) == 0xF8) + { + int src = + ((buffer[i + 1] & 0x07) << 19) + | ((buffer[i] & 0xFF) << 11) + | ((buffer[i + 3] & 0x07) << 8) + | (buffer[i + 2] & 0xFF); + src <<= 1; + + int dest; + if (_isEncoder) + dest = src + (_pos + i - offset); + else + dest = src - (_pos + i - offset); + + dest >>>= 1; + buffer[i + 1] = (byte)(0xF0 | ((dest >>> 19) & 0x07)); + buffer[i] = (byte)(dest >>> 11); + buffer[i + 3] = (byte)(0xF8 | ((dest >>> 8) & 0x07)); + buffer[i + 2] = (byte)dest; + i += 2; + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs b/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs new file mode 100644 index 00000000..4d89ba39 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterIA64.cs @@ -0,0 +1,107 @@ +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterIA64 : Filter +{ + private int _pos; + + private static readonly int[] BRANCH_TABLE = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 0, + 0, + 7, + 7, + 4, + 4, + 0, + 0, + 4, + 4, + 0, + 0 + }; + + public BCJFilterIA64(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 16) => _pos = 0; + + protected override int Transform(byte[] buffer, int offset, int count) + { + var end = offset + count - 16; + int i; + + for (i = offset; i <= end; i += 16) + { + int instrTemplate = buffer[i] & 0x1F; + int mask = BRANCH_TABLE[instrTemplate]; + + for (int slot = 0, bitPos = 5; slot < 3; ++slot, bitPos += 41) + { + if (((mask >>> slot) & 1) == 0) + continue; + + int bytePos = bitPos >>> 3; + int bitRes = bitPos & 7; + + long instr = 0; + for (int j = 0; j < 6; ++j) + { + instr |= (buffer[i + bytePos + j] & 0xFFL) << (8 * j); + } + + long instrNorm = instr >>> bitRes; + + if (((instrNorm >>> 37) & 0x0F) != 0x05 || ((instrNorm >>> 9) & 0x07) != 0x00) + continue; + + int src = (int)((instrNorm >>> 13) & 0x0FFFFF); + src |= ((int)(instrNorm >>> 36) & 1) << 20; + src <<= 4; + + int dest; + if (_isEncoder) + dest = src + (_pos + i - offset); + else + dest = src - (_pos + i - offset); + + dest >>>= 4; + + instrNorm &= ~(0x8FFFFFL << 13); + instrNorm |= (dest & 0x0FFFFFL) << 13; + instrNorm |= (dest & 0x100000L) << (36 - 20); + + instr &= (1 << bitRes) - 1; + instr |= instrNorm << bitRes; + + for (int j = 0; j < 6; ++j) + { + buffer[i + bytePos + j] = (byte)(instr >>> (8 * j)); + } + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs b/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs new file mode 100644 index 00000000..11ed61a1 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterPPC.cs @@ -0,0 +1,48 @@ +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterPPC : Filter +{ + private int _pos; + + public BCJFilterPPC(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 4) => _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) + { + if ((buffer[i] & 0xFC) == 0x48 && (buffer[i + 3] & 0x03) == 0x01) + { + int src = + ((buffer[i] & 0x03) << 24) + | ((buffer[i + 1] & 0xFF) << 16) + | ((buffer[i + 2] & 0xFF) << 8) + | (buffer[i + 3] & 0xFC); + + int dest; + if (_isEncoder) + { + dest = src + (_pos + i - offset); + } + else + { + dest = src - (_pos + i - offset); + } + + buffer[i] = (byte)(0x48 | ((dest >>> 24) & 0x03)); + buffer[i + 1] = (byte)(dest >>> 16); + buffer[i + 2] = (byte)(dest >>> 8); + buffer[i + 3] = (byte)((buffer[i + 3] & 0x03) | dest); + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs b/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs new file mode 100644 index 00000000..67756d34 --- /dev/null +++ b/src/SharpCompress/Compressors/Filters/BCJFilterSPARC.cs @@ -0,0 +1,58 @@ +using System.IO; + +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilterSPARC : Filter +{ + private int _pos; + + public BCJFilterSPARC(bool isEncoder, Stream baseStream) + : base(isEncoder, baseStream, 4) => _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) + { + if ( + (buffer[i] == 0x40 && (buffer[i + 1] & 0xC0) == 0x00) + || (buffer[i] == 0x7F && (buffer[i + 1] & 0xC0) == 0xC0) + ) + { + int src = + ((buffer[i] & 0xFF) << 24) + | ((buffer[i + 1] & 0xFF) << 16) + | ((buffer[i + 2] & 0xFF) << 8) + | (buffer[i + 3] & 0xFF); + src <<= 2; + + int dest; + if (_isEncoder) + { + dest = src + (_pos + i - offset); + } + else + { + dest = src - (_pos + i - offset); + } + + dest >>>= 2; + dest = + (((0 - ((dest >>> 22) & 1)) << 22) & 0x3FFFFFFF) + | (dest & 0x3FFFFF) + | 0x40000000; + + buffer[i] = (byte)(dest >>> 24); + buffer[i + 1] = (byte)(dest >>> 16); + buffer[i + 2] = (byte)(dest >>> 8); + buffer[i + 3] = (byte)dest; + } + } + + i -= offset; + _pos += i; + return i; + } +} diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index 1fb20cf1..f4cfde4b 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -20,6 +20,11 @@ internal static class DecoderRegistry private const uint K_PPMD = 0x030401; private const uint K_BCJ = 0x03030103; private const uint K_BCJ2 = 0x0303011B; + private const uint K_PPC = 0x03030205; + private const uint K_IA64 = 0x03030401; + private const uint K_ARM = 0x03030501; + private const uint K_ARMT = 0x03030701; + private const uint K_SPARC = 0x03030805; private const uint K_DEFLATE = 0x040108; private const uint K_B_ZIP2 = 0x040202; private const uint K_ZSTD = 0x4F71101; @@ -51,6 +56,16 @@ internal static class DecoderRegistry return new BCJFilter(false, inStreams.Single()); case K_BCJ2: return new Bcj2DecoderStream(inStreams, info, limit); + case K_PPC: + return new BCJFilterPPC(false, inStreams.Single()); + case K_IA64: + return new BCJFilterIA64(false, inStreams.Single()); + case K_ARM: + return new BCJFilterARM(false, inStreams.Single()); + case K_ARMT: + return new BCJFilterARMT(false, inStreams.Single()); + case K_SPARC: + return new BCJFilterSPARC(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 773538a1..8937ce05 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using SharpCompress.Common; using SharpCompress.Readers; using Xunit; @@ -132,8 +133,33 @@ public class SevenZipArchiveTests : ArchiveTests ) ); + [Fact] public void SevenZipArchive_Delta_FileRead() => ArchiveFileRead("7Zip.delta.7z"); + [Fact] + public void SevenZipArchive_ARM_FileRead() => ArchiveFileRead("7Zip.ARM.7z"); + + [Fact] + public void SevenZipArchive_ARMT_FileRead() => ArchiveFileRead("7Zip.ARMT.7z"); + + [Fact] + public void SevenZipArchive_BCJ_FileRead() => ArchiveFileRead("7Zip.BCJ.7z"); + + [Fact] + public void SevenZipArchive_BCJ2_FileRead() => ArchiveFileRead("7Zip.BCJ2.7z"); + + [Fact] + public void SevenZipArchive_IA64_FileRead() => ArchiveFileRead("7Zip.IA64.7z"); + + [Fact] + public void SevenZipArchive_PPC_FileRead() => ArchiveFileRead("7Zip.PPC.7z"); + + [Fact] + public void SevenZipArchive_SPARC_FileRead() => ArchiveFileRead("7Zip.SPARC.7z"); + + [Fact] + public void SevenZipArchive_Filters_FileRead() => ArchiveFileRead("7Zip.Filters.7z"); + [Fact] public void SevenZipArchive_Delta_Distance() => ArchiveDeltaDistanceRead("7Zip.delta.distance.7z"); diff --git a/tests/TestArchives/Archives/7Zip.ARM.7z b/tests/TestArchives/Archives/7Zip.ARM.7z new file mode 100644 index 00000000..92fa8873 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.ARM.7z differ diff --git a/tests/TestArchives/Archives/7Zip.ARMT.7z b/tests/TestArchives/Archives/7Zip.ARMT.7z new file mode 100644 index 00000000..8f57b199 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.ARMT.7z differ diff --git a/tests/TestArchives/Archives/7Zip.BCJ.7z b/tests/TestArchives/Archives/7Zip.BCJ.7z new file mode 100644 index 00000000..0e4e8c55 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.BCJ.7z differ diff --git a/tests/TestArchives/Archives/7Zip.BCJ2.7z b/tests/TestArchives/Archives/7Zip.BCJ2.7z new file mode 100644 index 00000000..0a7ba3c3 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.BCJ2.7z differ diff --git a/tests/TestArchives/Archives/7Zip.Filters.7z b/tests/TestArchives/Archives/7Zip.Filters.7z new file mode 100644 index 00000000..ea15f3b2 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.Filters.7z differ diff --git a/tests/TestArchives/Archives/7Zip.IA64.7z b/tests/TestArchives/Archives/7Zip.IA64.7z new file mode 100644 index 00000000..eac667ce Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.IA64.7z differ diff --git a/tests/TestArchives/Archives/7Zip.PPC.7z b/tests/TestArchives/Archives/7Zip.PPC.7z new file mode 100644 index 00000000..20b5263f Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.PPC.7z differ diff --git a/tests/TestArchives/Archives/7Zip.SPARC.7z b/tests/TestArchives/Archives/7Zip.SPARC.7z new file mode 100644 index 00000000..4c896ad3 Binary files /dev/null and b/tests/TestArchives/Archives/7Zip.SPARC.7z differ