// // gzip.c // // All of the gzip-related additions to deflate (both encoder and decoder) are in this file // #include #include #include #include "deflate.h" #include "inflate.h" #include "infmacro.h" #include "defgzip.h" #include "infgzip.h" #include "crc32.h" #define GZIP_FLG_FTEXT 1 #define GZIP_FLG_CRC 2 #define GZIP_FLG_FEXTRA 4 #define GZIP_FLG_FNAME 8 #define GZIP_FLG_FCOMMENT 16 typedef enum { // GZIP header GZIP_HDR_STATE_READING_ID1, GZIP_HDR_STATE_READING_ID2, GZIP_HDR_STATE_READING_CM, GZIP_HDR_STATE_READING_FLG, GZIP_HDR_STATE_READING_MMTIME, // iterates 4 times GZIP_HDR_STATE_READING_XFL, GZIP_HDR_STATE_READING_OS, GZIP_HDR_STATE_READING_XLEN1, GZIP_HDR_STATE_READING_XLEN2, GZIP_HDR_STATE_READING_XLEN_DATA, GZIP_HDR_STATE_READING_FILENAME, GZIP_HDR_STATE_READING_COMMENT, GZIP_HDR_STATE_READING_CRC16_PART1, GZIP_HDR_STATE_READING_CRC16_PART2, GZIP_HDR_STATE_DONE, // done reading GZIP header // GZIP footer GZIP_FTR_STATE_READING_CRC, // iterates 4 times GZIP_FTR_STATE_READING_FILE_SIZE // iterates 4 times } t_gzip_state; void EncoderInitGzipVariables(t_encoder_context *context) { context->gzip_crc32 = 0; context->gzip_input_stream_size = 0; context->gzip_fOutputGzipHeader = FALSE; } void DecoderInitGzipVariables(t_decoder_context *context) { context->gzip_crc32 = 0; context->gzip_output_stream_size = 0; } void WriteGzipHeader(t_encoder_context *context, int compression_level) { BYTE *output_curpos = context->output_curpos; // only need 11 bytes _ASSERT(context->output_curpos + 16 < context->output_endpos); #ifndef TESTING // the proper code path *output_curpos++ = 0x1F; // ID1 *output_curpos++ = 0x8B; // ID2 *output_curpos++ = 8; // CM = deflate *output_curpos++ = 0; // FLG, no text, no crc, no extra, no name, no comment *output_curpos++ = 0; // MTIME (Modification Time) - no time available *output_curpos++ = 0; *output_curpos++ = 0; *output_curpos++ = 0; // XFL // 2 = compressor used max compression, slowest algorithm // 4 = compressor used fastest algorithm if (compression_level == 10) *output_curpos++ = 2; else *output_curpos++ = 4; *output_curpos++ = 0; // OS: 0 = FAT filesystem (MS-DOS, OS/2, NT/Win32) #else /* TESTING */ // this code is for code path testing only // it uses all of the headers to ensure that the decoder can handle them correctly *output_curpos++ = 0x1F; // ID1 *output_curpos++ = 0x8B; // ID2 *output_curpos++ = 8; // CM = deflate *output_curpos++ = (GZIP_FLG_CRC|GZIP_FLG_FEXTRA|GZIP_FLG_FNAME|GZIP_FLG_FCOMMENT); // FLG *output_curpos++ = 0; // MTIME (Modification Time) - no time available *output_curpos++ = 0; *output_curpos++ = 0; *output_curpos++ = 0; *output_curpos++ = 2; // XFL *output_curpos++ = 0; // OS: 0 = FAT filesystem (MS-DOS, OS/2, NT/Win32) // FEXTRA *output_curpos++ = 3; // LSB *output_curpos++ = 0; // MSB output_curpos += 3; // 3 bytes of data // FNAME, null terminated filename output_curpos += strlen(strcpy(output_curpos, "my filename"))+1; // FCOMMENT, null terminated comment output_curpos += strlen(strcpy(output_curpos, "my comment"))+1; // CRC16 *output_curpos++ = 0x12; *output_curpos++ = 0x34; #endif context->output_curpos = output_curpos; } void WriteGzipFooter(t_encoder_context *context) { BYTE *output_curpos = context->output_curpos; *output_curpos++ = (BYTE) (context->gzip_crc32 & 255); *output_curpos++ = (BYTE) ((context->gzip_crc32 >> 8) & 255); *output_curpos++ = (BYTE) ((context->gzip_crc32 >> 16) & 255); *output_curpos++ = (BYTE) ((context->gzip_crc32 >> 24) & 255); *output_curpos++ = (BYTE) (context->gzip_input_stream_size & 255); *output_curpos++ = (BYTE) ((context->gzip_input_stream_size >> 8) & 255); *output_curpos++ = (BYTE) ((context->gzip_input_stream_size >> 16) & 255); *output_curpos++ = (BYTE) ((context->gzip_input_stream_size >> 24) & 255); context->output_curpos = output_curpos; } BOOL ReadGzipFooter(t_decoder_context *context) { if (context->state == STATE_START_READING_GZIP_FOOTER) { context->state = STATE_READING_GZIP_FOOTER; context->gzip_footer_substate = GZIP_FTR_STATE_READING_CRC; context->gzip_footer_loop_counter = 0; } _ASSERT(context->state == STATE_READING_GZIP_FOOTER); if (INPUT_EOF()) return TRUE; if (context->gzip_footer_substate == GZIP_FTR_STATE_READING_CRC) { if (context->gzip_footer_loop_counter == 0) context->gzip_footer_crc32 = 0; while (context->gzip_footer_loop_counter < 4) { context->gzip_footer_crc32 |= ((*context->input_curpos++) << (8*context->gzip_footer_loop_counter)); context->gzip_footer_loop_counter++; if (INPUT_EOF()) break; } if (context->gzip_footer_loop_counter >= 4) { context->gzip_footer_substate = GZIP_FTR_STATE_READING_FILE_SIZE; context->gzip_footer_loop_counter = 0; } if (INPUT_EOF()) return TRUE; } if (context->gzip_footer_substate == GZIP_FTR_STATE_READING_FILE_SIZE) { if (context->gzip_footer_loop_counter == 0) context->gzip_footer_output_stream_size = 0; while (context->gzip_footer_loop_counter < 4) { context->gzip_footer_output_stream_size |= ((*context->input_curpos++) << (8*context->gzip_footer_loop_counter)); context->gzip_footer_loop_counter++; if (INPUT_EOF()) break; } if (context->gzip_footer_loop_counter >= 4) context->state = STATE_VERIFYING_GZIP_FOOTER; } return TRUE; } BOOL ReadGzipHeader(t_decoder_context *context) { if (context->state != STATE_READING_GZIP_HEADER) { context->state = STATE_READING_GZIP_HEADER; context->gzip_header_substate = GZIP_HDR_STATE_READING_ID1; } if (INPUT_EOF()) return TRUE; if (context->gzip_header_substate == GZIP_HDR_STATE_READING_ID1) { if (*context->input_curpos++ != 0x1F) return FALSE; context->gzip_header_substate = GZIP_HDR_STATE_READING_ID2; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_ID2) { if (*context->input_curpos++ != 0x8B) return FALSE; context->gzip_header_substate = GZIP_HDR_STATE_READING_CM; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_CM) { // compression mode must be 8 (deflate) if (*context->input_curpos++ != 8) return FALSE; context->gzip_header_substate = GZIP_HDR_STATE_READING_FLG; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_FLG) { context->gzip_header_flag = *context->input_curpos++; context->gzip_header_substate = GZIP_HDR_STATE_READING_MMTIME; context->gzip_header_loop_counter = 0; // 4 MMTIME bytes if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_MMTIME) { // MTIME while (context->gzip_header_loop_counter < 4) { context->input_curpos++; context->gzip_header_loop_counter++; if (INPUT_EOF()) return TRUE; } context->gzip_header_substate = GZIP_HDR_STATE_READING_XFL; context->gzip_header_loop_counter = 0; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_XFL) { context->input_curpos++; // ignore XFL context->gzip_header_substate = GZIP_HDR_STATE_READING_OS; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_OS) { context->input_curpos++; // ignore OS context->gzip_header_substate = GZIP_HDR_STATE_READING_XLEN1; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_XLEN1) { // skip over some states if there's no "extra" data if ((context->gzip_header_flag & GZIP_FLG_FEXTRA) == 0) { context->gzip_header_substate = GZIP_HDR_STATE_READING_FILENAME; goto gzip_state_reading_fname; } context->gzip_header_xlen1_byte = *context->input_curpos++; context->gzip_header_substate = GZIP_HDR_STATE_READING_XLEN2; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_XLEN2) { BYTE xlen2 = *context->input_curpos++; context->gzip_header_xlen = context->gzip_header_xlen1_byte | (xlen2 << 8); context->gzip_header_substate = GZIP_HDR_STATE_READING_XLEN_DATA; context->gzip_header_loop_counter = 0; // 0 bytes of XLEN data read so far if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_XLEN_DATA) { while (context->gzip_header_loop_counter < context->gzip_header_xlen) { context->input_curpos++; context->gzip_header_loop_counter++; if (INPUT_EOF()) break; } if (context->gzip_header_loop_counter >= context->gzip_header_xlen) context->gzip_header_substate = GZIP_HDR_STATE_READING_FILENAME; if (INPUT_EOF()) return TRUE; } gzip_state_reading_fname: if (context->gzip_header_substate == GZIP_HDR_STATE_READING_FILENAME) { // skip over this state if there's no filename if ((context->gzip_header_flag & GZIP_FLG_FNAME) == 0) { context->gzip_header_substate = GZIP_HDR_STATE_READING_COMMENT; goto gzip_state_reading_comment; } do { if (*context->input_curpos++ == 0) { // filename null terminator found context->gzip_header_substate = GZIP_HDR_STATE_READING_COMMENT; break; } } while (!INPUT_EOF()); if (INPUT_EOF()) return TRUE; } gzip_state_reading_comment: if (context->gzip_header_substate == GZIP_HDR_STATE_READING_COMMENT) { // skip over this state if there's no filename if ((context->gzip_header_flag & GZIP_FLG_FCOMMENT) == 0) { context->gzip_header_substate = GZIP_HDR_STATE_READING_CRC16_PART1; goto gzip_state_reading_crc16; } do { if (*context->input_curpos++ == 0) { // filename null terminator found context->gzip_header_substate = GZIP_HDR_STATE_READING_CRC16_PART1; break; } } while (!INPUT_EOF()); if (INPUT_EOF()) return TRUE; } gzip_state_reading_crc16: if (context->gzip_header_substate == GZIP_HDR_STATE_READING_CRC16_PART1) { // skip over these states if there's no crc16 if ((context->gzip_header_flag & GZIP_FLG_CRC) == 0) { context->gzip_header_substate = GZIP_HDR_STATE_DONE; goto gzip_state_done; } context->input_curpos++; // ignore crc context->gzip_header_substate = GZIP_HDR_STATE_READING_CRC16_PART2; if (INPUT_EOF()) return TRUE; } if (context->gzip_header_substate == GZIP_HDR_STATE_READING_CRC16_PART2) { context->input_curpos++; // ignore crc context->gzip_header_substate = GZIP_HDR_STATE_DONE; if (INPUT_EOF()) return TRUE; } gzip_state_done: if (context->gzip_header_substate == GZIP_HDR_STATE_DONE) context->state = STATE_READING_BFINAL_NEED_TO_INIT_BITBUF; return TRUE; } #define DO1(buf) crc = g_CrcTable[((ULONG)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); #define DO2(buf) DO1(buf); DO1(buf); #define DO4(buf) DO2(buf); DO2(buf); #define DO8(buf) DO4(buf); DO4(buf); ULONG GzipCRC32(ULONG crc, const BYTE *buf, ULONG len) { crc = crc ^ 0xffffffffUL; while (len >= 8) { DO8(buf); len -= 8; } if (len) { do { DO1(buf); } while (--len); } return crc ^ 0xffffffffUL; } // // Works just like memcpy() except that we update context->crc32 and context->input_stream_size // at the same time. // // BUGBUG Could possibly improve the perf by copying 4 or 8 bytes at a time as above // void GzipCRCmemcpy(t_encoder_context *context, BYTE *dest, const BYTE *src, ULONG count) { ULONG crc = context->gzip_crc32 ^ 0xffffffffUL; context->gzip_input_stream_size += count; while (count-- > 0) { *dest++ = *src; DO1(src); // increments src } context->gzip_crc32 = crc ^ 0xffffffffUL; }