2020-06-08 03:29:51 +03:00
|
|
|
package gz
|
|
|
|
|
|
|
|
// https://tools.ietf.org/html/rfc1952
|
|
|
|
// TODO: test name, comment etc
|
|
|
|
// TODO: verify isize?
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"compress/flate"
|
|
|
|
"hash/crc32"
|
|
|
|
"io"
|
2021-08-17 13:06:32 +03:00
|
|
|
|
|
|
|
"github.com/wader/fq/format"
|
|
|
|
"github.com/wader/fq/format/registry"
|
|
|
|
"github.com/wader/fq/pkg/bitio"
|
|
|
|
"github.com/wader/fq/pkg/decode"
|
2020-06-08 03:29:51 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var probeFormat []*decode.Format
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
registry.MustRegister(&decode.Format{
|
|
|
|
Name: format.GZIP,
|
|
|
|
Description: "gzip compression",
|
|
|
|
Groups: []string{format.PROBE},
|
|
|
|
DecodeFn: gzDecode,
|
|
|
|
Dependencies: []decode.Dependency{
|
|
|
|
{Names: []string{format.PROBE}, Formats: &probeFormat},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const delfateMethod = 8
|
|
|
|
|
2021-11-05 17:04:26 +03:00
|
|
|
var compressionMethodNames = decode.UToStr{
|
|
|
|
delfateMethod: "deflate",
|
|
|
|
}
|
|
|
|
|
|
|
|
var osNames = decode.UToStr{
|
2020-06-08 03:29:51 +03:00
|
|
|
0: "FAT filesystem (MS-DOS, OS/2, NT/Win32)",
|
|
|
|
1: "Amiga",
|
|
|
|
2: "VMS (or OpenVMS)",
|
|
|
|
3: "Unix",
|
|
|
|
4: "VM/CMS",
|
|
|
|
5: "Atari TOS",
|
|
|
|
6: "HPFS filesystem (OS/2, NT)",
|
|
|
|
7: "Macintosh",
|
|
|
|
8: "Z-System",
|
|
|
|
9: "CP/M",
|
|
|
|
10: " TOPS-20",
|
|
|
|
11: " NTFS filesystem (NT)",
|
|
|
|
12: " QDOS",
|
|
|
|
13: " Acorn RISCOS",
|
|
|
|
}
|
|
|
|
|
2021-11-05 17:04:26 +03:00
|
|
|
var deflateExtraFlagsNames = decode.UToStr{
|
|
|
|
2: "slow",
|
|
|
|
4: "fast",
|
|
|
|
}
|
|
|
|
|
2020-06-08 03:29:51 +03:00
|
|
|
func gzDecode(d *decode.D, in interface{}) interface{} {
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldRawLen("identification", 2*8, d.AssertRaw([]byte("\x1f\x8b")))
|
|
|
|
compressionMethod := d.FieldU8("compression_method", d.MapUToStr(compressionMethodNames))
|
2020-06-08 03:29:51 +03:00
|
|
|
hasHeaderCRC := false
|
|
|
|
hasExtra := false
|
|
|
|
hasName := false
|
|
|
|
hasComment := false
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldStruct("flags", func(d *decode.D) {
|
2020-06-08 03:29:51 +03:00
|
|
|
d.FieldBool("text")
|
|
|
|
hasHeaderCRC = d.FieldBool("header_crc")
|
|
|
|
hasExtra = d.FieldBool("extra")
|
|
|
|
hasName = d.FieldBool("name")
|
|
|
|
hasComment = d.FieldBool("comment")
|
|
|
|
d.FieldU3("reserved")
|
|
|
|
})
|
|
|
|
d.FieldU32LE("mtime") // TODO: unix time
|
|
|
|
switch compressionMethod {
|
|
|
|
case delfateMethod:
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldU8("extra_flags", d.MapUToStr(deflateExtraFlagsNames))
|
2020-06-08 03:29:51 +03:00
|
|
|
default:
|
|
|
|
d.FieldU8("extra_flags")
|
|
|
|
}
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldU8("os", d.MapUToStr(osNames))
|
2020-06-08 03:29:51 +03:00
|
|
|
if hasExtra {
|
|
|
|
// TODO:
|
|
|
|
xLen := d.FieldU16("xlen")
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldRawLen("extra_fields", int64(xLen*8))
|
2020-06-08 03:29:51 +03:00
|
|
|
}
|
|
|
|
if hasName {
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldUTF8NullTerminated("name")
|
2020-06-08 03:29:51 +03:00
|
|
|
}
|
|
|
|
if hasComment {
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldUTF8NullTerminated("comment")
|
2020-06-08 03:29:51 +03:00
|
|
|
}
|
|
|
|
if hasHeaderCRC {
|
2021-11-05 17:04:26 +03:00
|
|
|
// TODO: validate
|
|
|
|
d.FieldRawLen("header_crc", 16, d.RawHex)
|
2020-06-08 03:29:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
compressedLen := d.BitsLeft() - ((4 + 4) * 8) // len-(crc32+isize)
|
2021-11-05 17:04:26 +03:00
|
|
|
compressedBB := d.FieldRawLen("compressed", compressedLen)
|
|
|
|
crc32W := crc32.NewIEEE()
|
2020-06-08 03:29:51 +03:00
|
|
|
|
|
|
|
switch compressionMethod {
|
|
|
|
case delfateMethod:
|
|
|
|
deflateR := flate.NewReader(compressedBB)
|
|
|
|
uncompressed := &bytes.Buffer{}
|
2021-09-16 13:24:53 +03:00
|
|
|
if _, err := decode.Copy(d, io.MultiWriter(uncompressed, crc32W), deflateR); err != nil {
|
2020-06-08 03:29:51 +03:00
|
|
|
d.Invalid(err.Error())
|
|
|
|
}
|
|
|
|
uncompressedBB := bitio.NewBufferFromBytes(uncompressed.Bytes(), -1)
|
2021-09-16 16:29:11 +03:00
|
|
|
dv, _, _ := d.FieldTryFormatBitBuf("uncompressed", uncompressedBB, probeFormat, nil)
|
2020-06-08 03:29:51 +03:00
|
|
|
if dv == nil {
|
|
|
|
d.FieldRootBitBuf("uncompressed", uncompressedBB)
|
|
|
|
}
|
|
|
|
default:
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldRawLen("compressed", compressedLen)
|
2020-06-08 03:29:51 +03:00
|
|
|
}
|
|
|
|
|
2021-11-05 17:04:26 +03:00
|
|
|
d.FieldRawLen("crc32", 32, d.ValidateRaw(bitio.ReverseBytes(crc32W.Sum(nil))), d.RawHex)
|
2020-06-08 03:29:51 +03:00
|
|
|
d.FieldU32LE("isize")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|