You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.5 KiB
130 lines
3.5 KiB
package go7z
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/bzip2"
|
|
"compress/flate"
|
|
"encoding/binary"
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/saracen/go7z/filters"
|
|
"github.com/ulikunitz/xz/lzma"
|
|
)
|
|
|
|
// Decompressor is a handler function called when a registered decompressor is
|
|
// initialized.
|
|
type Decompressor func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error)
|
|
|
|
var (
|
|
decompressors sync.Map // map[uint32]Decompressor
|
|
)
|
|
|
|
func init() {
|
|
// copy
|
|
RegisterDecompressor(0x00, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
return r[0], nil
|
|
}))
|
|
|
|
// delta
|
|
RegisterDecompressor(0x03, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 || len(options) == 0 || len(options) > 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
|
|
return filters.NewDeltaDecoder(r[0], uint(options[0])+1, int64(unpackSize))
|
|
}))
|
|
|
|
// lzma
|
|
RegisterDecompressor(0x030101, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
|
|
// We can't set options in the lzma decoder library, so instead we add
|
|
// a fake header
|
|
header := bytes.NewBuffer(options)
|
|
binary.Write(header, binary.LittleEndian, unpackSize)
|
|
|
|
return lzma.NewReader(io.MultiReader(header, r[0]))
|
|
}))
|
|
|
|
// lzma2
|
|
RegisterDecompressor(0x21, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
|
|
config := lzma.Reader2Config{}
|
|
if len(options) > 0 {
|
|
config.DictCap = int(2 | (options[0] & 1))
|
|
config.DictCap <<= (options[0] >> 1) + 11
|
|
}
|
|
|
|
return config.NewReader2(r[0])
|
|
}))
|
|
|
|
// bcj2
|
|
RegisterDecompressor(0x303011b, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 4 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
return filters.NewBCJ2Decoder(r[0], r[1], r[2], r[3], int64(unpackSize))
|
|
}))
|
|
|
|
// deflate
|
|
RegisterDecompressor(0x40108, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
return flate.NewReader(r[0]), nil
|
|
}))
|
|
|
|
// bzip2
|
|
RegisterDecompressor(0x40202, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
|
|
return bzip2.NewReader(r[0]), nil
|
|
}))
|
|
|
|
// AES
|
|
RegisterDecompressor(0x6f10701, Decompressor(func(r []io.Reader, options []byte, unpackSize uint64, ro *ReaderOptions) (io.Reader, error) {
|
|
if len(r) != 1 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
if len(options) < 2 {
|
|
return nil, ErrNotSupported
|
|
}
|
|
|
|
saltSize := ((options[0] >> 7) & 1) + (options[1] >> 4)
|
|
ivSize := ((options[0] >> 6) & 1) + (options[1] & 0x0F)
|
|
power := int(options[0]) & 0x3f
|
|
|
|
options = options[2:]
|
|
salt := options[:saltSize]
|
|
iv := options[saltSize : saltSize+ivSize]
|
|
|
|
return filters.NewAESDecrypter(r[0], power, salt, iv, ro.Password())
|
|
}))
|
|
}
|
|
|
|
// RegisterDecompressor registers a decompressor.
|
|
func RegisterDecompressor(method uint32, dcomp Decompressor) {
|
|
if _, dup := decompressors.LoadOrStore(method, dcomp); dup {
|
|
panic("decompressor already registered")
|
|
}
|
|
}
|
|
|
|
func decompressor(method uint32) Decompressor {
|
|
di, ok := decompressors.Load(method)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return di.(Decompressor)
|
|
}
|