part of archive; | |
/** | |
* Decompress data with the gzip format decoder. | |
*/ | |
class GZipDecoder { | |
static const int SIGNATURE = 0x8b1f; | |
static const int DEFLATE = 8; | |
static const int FLAG_TEXT = 0x01; | |
static const int FLAG_HCRC = 0x02; | |
static const int FLAG_EXTRA = 0x04; | |
static const int FLAG_NAME = 0x08; | |
static const int FLAG_COMMENT = 0x10; | |
List<int> decodeBytes(List<int> data, {bool verify: false}) { | |
return decodeBuffer(new InputStream(data), verify: verify); | |
} | |
void decodeStream(dynamic input, dynamic output) { | |
_readHeader(input); | |
new Inflate.stream(input, output); | |
} | |
List<int> decodeBuffer(dynamic input, {bool verify: false}) { | |
_readHeader(input); | |
// Inflate | |
List<int> buffer = new Inflate.buffer(input).getBytes(); | |
if (verify) { | |
int crc = input.readUint32(); | |
int computedCrc = getCrc32(buffer); | |
if (crc != computedCrc) { | |
throw new ArchiveException('Invalid CRC checksum'); | |
} | |
int size = input.readUint32(); | |
if (size != buffer.length) { | |
throw new ArchiveException('Size of decompressed file not correct'); | |
} | |
} | |
return buffer; | |
} | |
void _readHeader(dynamic input) { | |
// The GZip format has the following structure: | |
// Offset Length Contents | |
// 0 2 bytes magic header 0x1f, 0x8b (\037 \213) | |
// 2 1 byte compression method | |
// 0: store (copied) | |
// 1: compress | |
// 2: pack | |
// 3: lzh | |
// 4..7: reserved | |
// 8: deflate | |
// 3 1 byte flags | |
// bit 0 set: file probably ascii text | |
// bit 1 set: continuation of multi-part gzip file, part number present | |
// bit 2 set: extra field present | |
// bit 3 set: original file name present | |
// bit 4 set: file comment present | |
// bit 5 set: file is encrypted, encryption header present | |
// bit 6,7: reserved | |
// 4 4 bytes file modification time in Unix format | |
// 8 1 byte extra flags (depend on compression method) | |
// 9 1 byte OS type | |
// [ | |
// 2 bytes optional part number (second part=1) | |
// ]? | |
// [ | |
// 2 bytes optional extra field length (e) | |
// (e)bytes optional extra field | |
// ]? | |
// [ | |
// bytes optional original file name, zero terminated | |
// ]? | |
// [ | |
// bytes optional file comment, zero terminated | |
// ]? | |
// [ | |
// 12 bytes optional encryption header | |
// ]? | |
// bytes compressed data | |
// 4 bytes crc32 | |
// 4 bytes uncompressed input size modulo 2^32 | |
int signature = input.readUint16(); | |
if (signature != SIGNATURE) { | |
throw new ArchiveException('Invalid GZip Signature'); | |
} | |
int compressionMethod = input.readByte(); | |
if (compressionMethod != DEFLATE) { | |
throw new ArchiveException('Invalid GZip Compression Methos'); | |
} | |
int flags = input.readByte(); | |
/*int fileModTime =*/ input.readUint32(); | |
/*int extraFlags =*/ input.readByte(); | |
/*int osType =*/ input.readByte(); | |
if (flags & FLAG_EXTRA != 0) { | |
int t = input.readUint16(); | |
input.readBytes(t); | |
} | |
if (flags & FLAG_NAME != 0) { | |
input.readString(); | |
} | |
if (flags & FLAG_COMMENT != 0) { | |
input.readString(); | |
} | |
// just throw away for now | |
if (flags & FLAG_HCRC != 0) { | |
input.readUint16(); | |
} | |
} | |
} |