Skip to content

Commit 2ef07bb

Browse files
authored
Merge pull request #83 from bodgit/bra
Add various branch converters
2 parents d6abcbc + 59612b8 commit 2ef07bb

File tree

13 files changed

+351
-1
lines changed

13 files changed

+351
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Current status:
2020
* Handles archives split into multiple volumes, (`7za a -v100m test.7z ...`).
2121
* Handles self-extracting archives, (`7za a -sfx archive.exe ...`).
2222
* Validates CRC values as it parses the file.
23-
* Supports BCJ2, Brotli, Bzip2, Copy, Deflate, Delta, LZ4, LZMA, LZMA2 and Zstandard methods.
23+
* Supports ARM, BCJ, BCJ2, Brotli, Bzip2, Copy, Deflate, Delta, LZ4, LZMA, LZMA2, PPC, SPARC and Zstandard methods.
2424
* Implements the `fs.FS` interface so you can treat an opened 7-zip archive like a filesystem.
2525

2626
More examples of 7-zip archives are needed to test all of the different combinations/algorithms possible.

internal/bra/arm.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package bra
2+
3+
import (
4+
"encoding/binary"
5+
"io"
6+
)
7+
8+
const armAlignment = 4
9+
10+
type arm struct {
11+
ip uint32
12+
}
13+
14+
func (c *arm) Size() int { return armAlignment }
15+
16+
func (c *arm) Convert(b []byte, encoding bool) int {
17+
if len(b) < c.Size() {
18+
return 0
19+
}
20+
21+
if c.ip == 0 {
22+
c.ip += armAlignment
23+
}
24+
25+
var i int
26+
27+
for i = 0; i < len(b) & ^(armAlignment-1); i += armAlignment {
28+
v := binary.LittleEndian.Uint32(b[i:])
29+
30+
c.ip += uint32(armAlignment)
31+
32+
if b[i+3] == 0xeb {
33+
v <<= 2
34+
35+
if encoding {
36+
v += c.ip
37+
} else {
38+
v -= c.ip
39+
}
40+
41+
v >>= 2
42+
v &= 0x00ffffff
43+
v |= 0xeb000000
44+
}
45+
46+
binary.LittleEndian.PutUint32(b[i:], v)
47+
}
48+
49+
return i
50+
}
51+
52+
// NewARMReader returns a new ARM io.ReadCloser.
53+
func NewARMReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
54+
return newReader(readers, new(arm))
55+
}

internal/bra/bcj.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package bra
2+
3+
import (
4+
"encoding/binary"
5+
"io"
6+
)
7+
8+
const bcjLookAhead = 4
9+
10+
type bcj struct {
11+
ip, state uint32
12+
}
13+
14+
func (c *bcj) Size() int { return bcjLookAhead + 1 }
15+
16+
func test86MSByte(b byte) bool {
17+
return (b+1)&0xfe == 0
18+
}
19+
20+
//nolint:cyclop,funlen,gocognit
21+
func (c *bcj) Convert(b []byte, encoding bool) int {
22+
if len(b) < c.Size() {
23+
return 0
24+
}
25+
26+
var (
27+
pos int
28+
mask = c.state & 7
29+
)
30+
31+
for {
32+
p := pos
33+
for ; p < len(b)-bcjLookAhead; p++ {
34+
if b[p]&0xfe == 0xe8 {
35+
break
36+
}
37+
}
38+
39+
d := p - pos
40+
pos = p
41+
42+
if p >= len(b)-bcjLookAhead {
43+
if d > 2 {
44+
c.state = 0
45+
} else {
46+
c.state = mask >> d
47+
}
48+
49+
c.ip += uint32(pos)
50+
51+
return pos
52+
}
53+
54+
if d > 2 {
55+
mask = 0
56+
} else {
57+
mask >>= d
58+
if mask != 0 && (mask > 4 || mask == 3 || test86MSByte(b[p+int(mask>>1)+1])) {
59+
mask = (mask >> 1) | 4
60+
pos++
61+
62+
continue
63+
}
64+
}
65+
66+
//nolint:nestif
67+
if test86MSByte(b[p+4]) {
68+
v := binary.LittleEndian.Uint32(b[p+1:])
69+
cur := c.ip + uint32(c.Size()+pos)
70+
pos += c.Size()
71+
72+
if encoding {
73+
v += cur
74+
} else {
75+
v -= cur
76+
}
77+
78+
if mask != 0 {
79+
sh := mask & 6 << 2
80+
if test86MSByte(byte(v >> sh)) {
81+
v ^= (uint32(0x100) << sh) - 1
82+
if encoding {
83+
v += cur
84+
} else {
85+
v -= cur
86+
}
87+
}
88+
89+
mask = 0
90+
}
91+
92+
binary.LittleEndian.PutUint32(b[p+1:], v)
93+
b[p+4] = 0 - b[p+4]&1
94+
} else {
95+
mask = (mask >> 1) | 4
96+
pos++
97+
}
98+
}
99+
}
100+
101+
// NewBCJReader returns a new BCJ io.ReadCloser.
102+
func NewBCJReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
103+
return newReader(readers, new(bcj))
104+
}

internal/bra/bra.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package bra
2+
3+
type converter interface {
4+
Size() int
5+
Convert([]byte, bool) int
6+
}
7+
8+
func max(x, y int) int {
9+
if x > y {
10+
return x
11+
}
12+
13+
return y
14+
}

internal/bra/ppc.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package bra
2+
3+
import (
4+
"encoding/binary"
5+
"io"
6+
)
7+
8+
const ppcAlignment = 4
9+
10+
type ppc struct {
11+
ip uint32
12+
}
13+
14+
func (c *ppc) Size() int { return ppcAlignment }
15+
16+
func (c *ppc) Convert(b []byte, encoding bool) int {
17+
if len(b) < c.Size() {
18+
return 0
19+
}
20+
21+
var i int
22+
23+
for i = 0; i < len(b) & ^(ppcAlignment-1); i += ppcAlignment {
24+
v := binary.BigEndian.Uint32(b[i:])
25+
26+
if b[i+0]&0xfc == 0x48 && b[i+3]&3 == 1 {
27+
if encoding {
28+
v += c.ip
29+
} else {
30+
v -= c.ip
31+
}
32+
33+
v &= 0x03ffffff
34+
v |= 0x48000000
35+
}
36+
37+
c.ip += uint32(ppcAlignment)
38+
39+
binary.BigEndian.PutUint32(b[i:], v)
40+
}
41+
42+
return i
43+
}
44+
45+
// NewPPCReader returns a new PPC io.ReadCloser.
46+
func NewPPCReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
47+
return newReader(readers, new(ppc))
48+
}

internal/bra/reader.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package bra
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"io"
7+
)
8+
9+
type readCloser struct {
10+
rc io.ReadCloser
11+
buf bytes.Buffer
12+
conv converter
13+
}
14+
15+
func (rc *readCloser) Close() (err error) {
16+
if rc.rc != nil {
17+
err = rc.rc.Close()
18+
rc.rc = nil
19+
}
20+
21+
return
22+
}
23+
24+
func (rc *readCloser) Read(p []byte) (int, error) {
25+
if rc.rc == nil {
26+
return 0, errors.New("bra: Read after Close")
27+
}
28+
29+
if _, err := io.CopyN(&rc.buf, rc.rc, int64(max(len(p), rc.conv.Size())-rc.buf.Len())); err != nil {
30+
if !errors.Is(err, io.EOF) {
31+
return 0, err
32+
}
33+
}
34+
35+
if n := rc.conv.Convert(rc.buf.Bytes(), false); n > 0 {
36+
return rc.buf.Read(p[:n])
37+
}
38+
39+
return rc.buf.Read(p)
40+
}
41+
42+
func newReader(readers []io.ReadCloser, conv converter) (io.ReadCloser, error) {
43+
if len(readers) != 1 {
44+
return nil, errors.New("bra: need exactly one reader")
45+
}
46+
47+
return &readCloser{
48+
rc: readers[0],
49+
conv: conv,
50+
}, nil
51+
}

internal/bra/sparc.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package bra
2+
3+
import (
4+
"encoding/binary"
5+
"io"
6+
)
7+
8+
const sparcAlignment = 4
9+
10+
type sparc struct {
11+
ip uint32
12+
}
13+
14+
func (c *sparc) Size() int { return sparcAlignment }
15+
16+
func (c *sparc) Convert(b []byte, encoding bool) int {
17+
if len(b) < c.Size() {
18+
return 0
19+
}
20+
21+
var i int
22+
23+
for i = 0; i < len(b) & ^(sparcAlignment-1); i += sparcAlignment {
24+
v := binary.BigEndian.Uint32(b[i:])
25+
26+
if (b[i+0] == 0x40 && b[i+1]&0xc0 == 0) || (b[i+0] == 0x7f && b[i+1] >= 0xc0) {
27+
v <<= 2
28+
29+
if encoding {
30+
v += c.ip
31+
} else {
32+
v -= c.ip
33+
}
34+
35+
v &= 0x01ffffff
36+
v -= uint32(1) << 24
37+
v ^= 0xff000000
38+
v >>= 2
39+
v |= 0x40000000
40+
}
41+
42+
c.ip += uint32(sparcAlignment)
43+
44+
binary.BigEndian.PutUint32(b[i:], v)
45+
}
46+
47+
return i
48+
}
49+
50+
// NewSPARCReader returns a new SPARC io.ReadCloser.
51+
func NewSPARCReader(_ []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) {
52+
return newReader(readers, new(sparc))
53+
}

reader_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ func TestOpenReader(t *testing.T) {
120120
name: "sfx",
121121
file: "sfx.exe",
122122
},
123+
{
124+
name: "bcj",
125+
file: "bcj.7z",
126+
},
127+
{
128+
name: "ppc",
129+
file: "ppc.7z",
130+
},
131+
{
132+
name: "arm",
133+
file: "arm.7z",
134+
},
135+
{
136+
name: "sparc",
137+
file: "sparc.7z",
138+
},
123139
}
124140

125141
for _, table := range tables {

register.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/bodgit/sevenzip/internal/aes7z"
99
"github.com/bodgit/sevenzip/internal/bcj2"
10+
"github.com/bodgit/sevenzip/internal/bra"
1011
"github.com/bodgit/sevenzip/internal/brotli"
1112
"github.com/bodgit/sevenzip/internal/bzip2"
1213
"github.com/bodgit/sevenzip/internal/deflate"
@@ -42,8 +43,16 @@ func init() {
4243
RegisterDecompressor([]byte{0x03}, Decompressor(delta.NewReader))
4344
// LZMA
4445
RegisterDecompressor([]byte{0x03, 0x01, 0x01}, Decompressor(lzma.NewReader))
46+
// BCJ
47+
RegisterDecompressor([]byte{0x03, 0x03, 0x01, 0x03}, Decompressor(bra.NewBCJReader))
4548
// BCJ2
4649
RegisterDecompressor([]byte{0x03, 0x03, 0x01, 0x1b}, Decompressor(bcj2.NewReader))
50+
// PPC
51+
RegisterDecompressor([]byte{0x03, 0x03, 0x02, 0x05}, Decompressor(bra.NewPPCReader))
52+
// ARM
53+
RegisterDecompressor([]byte{0x03, 0x03, 0x05, 0x01}, Decompressor(bra.NewARMReader))
54+
// SPARC
55+
RegisterDecompressor([]byte{0x03, 0x03, 0x08, 0x05}, Decompressor(bra.NewSPARCReader))
4756
// Deflate
4857
RegisterDecompressor([]byte{0x04, 0x01, 0x08}, Decompressor(deflate.NewReader))
4958
// Bzip2

testdata/arm.7z

1.96 KB
Binary file not shown.

0 commit comments

Comments
 (0)