diff --git a/README.md b/README.md index c2058fb..6c16475 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Current status: * Handles archives split into multiple volumes, (`7za a -v100m test.7z ...`). * Handles self-extracting archives, (`7za a -sfx archive.exe ...`). * Validates CRC values as it parses the file. -* Supports ARM, BCJ, BCJ2, Brotli, Bzip2, Copy, Deflate, Delta, LZ4, LZMA, LZMA2, PPC, SPARC and Zstandard methods. +* Supports ARM, ARM64, BCJ, BCJ2, Brotli, Bzip2, Copy, Deflate, Delta, LZ4, LZMA, LZMA2, PPC, SPARC and Zstandard methods. * Implements the `fs.FS` interface so you can treat an opened 7-zip archive like a filesystem. More examples of 7-zip archives are needed to test all of the different combinations/algorithms possible. diff --git a/internal/bra/arm.go b/internal/bra/arm.go index 3916a0c..de8676f 100644 --- a/internal/bra/arm.go +++ b/internal/bra/arm.go @@ -19,7 +19,7 @@ func (c *arm) Convert(b []byte, encoding bool) int { } if c.ip == 0 { - c.ip += armAlignment + c.ip = armAlignment } var i int @@ -27,18 +27,15 @@ func (c *arm) Convert(b []byte, encoding bool) int { for i = 0; i < len(b) & ^(armAlignment-1); i += armAlignment { v := binary.LittleEndian.Uint32(b[i:]) - c.ip += uint32(armAlignment) + c.ip += armAlignment if b[i+3] == 0xeb { - v <<= 2 - if encoding { - v += c.ip + v += c.ip >> 2 } else { - v -= c.ip + v -= c.ip >> 2 } - v >>= 2 v &= 0x00ffffff v |= 0xeb000000 } diff --git a/internal/bra/arm64.go b/internal/bra/arm64.go new file mode 100644 index 0000000..128155c --- /dev/null +++ b/internal/bra/arm64.go @@ -0,0 +1,78 @@ +package bra + +import ( + "encoding/binary" + "io" +) + +const arm64Alignment = 4 + +type arm64 struct { + ip uint32 +} + +func (c *arm64) Size() int { return arm64Alignment } + +func (c *arm64) Convert(b []byte, encoding bool) int { + if len(b) < c.Size() { + return 0 + } + + var i int + + for i = 0; i < len(b) & ^(arm64Alignment-1); i, c.ip = i+arm64Alignment, c.ip+arm64Alignment { + v := binary.LittleEndian.Uint32(b[i:]) + + if (v-0x94000000)&0xfc000000 == 0 { + if encoding { + v += c.ip >> 2 + } else { + v -= c.ip >> 2 + } + + v &= 0x03ffffff + v |= 0x94000000 + + binary.LittleEndian.PutUint32(b[i:], v) + + continue + } + + v -= 0x90000000 + + if v&0x9f000000 == 0 { + const ( + flag = uint32(1) << (24 - 4) + mask = uint32(1)<<24 - flag<<1 + ) + + v += flag + + if v&mask > 0 { + continue + } + + z, ip := v&0xffffffe0|v>>26, (c.ip>>(12-3)) & ^uint32(7) + + if encoding { + z += ip + } else { + z -= ip + } + + v &= 0x1f + v |= 0x90000000 + v |= z << 26 + v |= 0x00ffffe0 & ((z & (flag<<1 - 1)) - flag) + + binary.LittleEndian.PutUint32(b[i:], v) + } + } + + return i +} + +// NewARM64Reader returns a new ARM64 io.ReadCloser. +func NewARM64Reader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) { + return newReader(readers, new(arm64)) +} diff --git a/internal/bra/ppc.go b/internal/bra/ppc.go index 9d38243..48197a3 100644 --- a/internal/bra/ppc.go +++ b/internal/bra/ppc.go @@ -20,7 +20,7 @@ func (c *ppc) Convert(b []byte, encoding bool) int { var i int - for i = 0; i < len(b) & ^(ppcAlignment-1); i += ppcAlignment { + for i = 0; i < len(b) & ^(ppcAlignment-1); i, c.ip = i+ppcAlignment, c.ip+ppcAlignment { v := binary.BigEndian.Uint32(b[i:]) if b[i+0]&0xfc == 0x48 && b[i+3]&3 == 1 { @@ -34,8 +34,6 @@ func (c *ppc) Convert(b []byte, encoding bool) int { v |= 0x48000000 } - c.ip += uint32(ppcAlignment) - binary.BigEndian.PutUint32(b[i:], v) } diff --git a/internal/bra/sparc.go b/internal/bra/sparc.go index 8aa4553..a39f3a2 100644 --- a/internal/bra/sparc.go +++ b/internal/bra/sparc.go @@ -20,10 +20,12 @@ func (c *sparc) Convert(b []byte, encoding bool) int { var i int - for i = 0; i < len(b) & ^(sparcAlignment-1); i += sparcAlignment { + for i = 0; i < len(b) & ^(sparcAlignment-1); i, c.ip = i+sparcAlignment, c.ip+sparcAlignment { v := binary.BigEndian.Uint32(b[i:]) if (b[i+0] == 0x40 && b[i+1]&0xc0 == 0) || (b[i+0] == 0x7f && b[i+1] >= 0xc0) { + const flag = uint32(1) << 24 + v <<= 2 if encoding { @@ -33,14 +35,12 @@ func (c *sparc) Convert(b []byte, encoding bool) int { } v &= 0x01ffffff - v -= uint32(1) << 24 + v -= flag v ^= 0xff000000 v >>= 2 v |= 0x40000000 } - c.ip += uint32(sparcAlignment) - binary.BigEndian.PutUint32(b[i:], v) } diff --git a/reader_test.go b/reader_test.go index a9d0c16..833e0db 100644 --- a/reader_test.go +++ b/reader_test.go @@ -197,6 +197,10 @@ func TestOpenReader(t *testing.T) { file: "COMPRESS-492.7z", err: sevenzip.ErrMissingUnpackInfo, }, + { + name: "arm64", + file: "arm64.7z", + }, } for _, table := range tables { @@ -657,3 +661,7 @@ func BenchmarkARM(b *testing.B) { func BenchmarkSPARC(b *testing.B) { benchmarkArchive(b, "sparc.7z", "", true) } + +func BenchmarkARM64(b *testing.B) { + benchmarkArchive(b, "arm64.7z", "", true) +} diff --git a/register.go b/register.go index 7f713d6..cac1bc2 100644 --- a/register.go +++ b/register.go @@ -69,6 +69,8 @@ func init() { RegisterDecompressor([]byte{0x04, 0xf7, 0x11, 0x04}, Decompressor(lz4.NewReader)) // AES-CBC-256 & SHA-256 RegisterDecompressor([]byte{0x06, 0xf1, 0x07, 0x01}, Decompressor(aes7z.NewReader)) + // ARM64 + RegisterDecompressor([]byte{0x0a}, Decompressor(bra.NewARM64Reader)) // LZMA2 RegisterDecompressor([]byte{0x21}, Decompressor(lzma2.NewReader)) } diff --git a/testdata/arm64.7z b/testdata/arm64.7z new file mode 100644 index 0000000..089f369 Binary files /dev/null and b/testdata/arm64.7z differ