From e12fcb08a14de1f5738e0162bba50cdbf87dee47 Mon Sep 17 00:00:00 2001 From: pixel Date: Thu, 5 Jul 2007 18:05:43 +0000 Subject: First import... --- MPQCryptography.c | 249 +++++++ MPQCryptography.h | 48 ++ Makefile | 32 + errors.c | 4 + errors.h | 6 + extract.c | 2007 +++++++++++++++++++++++++++++++++++++++++++++++++++++ extract.h | 36 + inttypes.h | 14 + mpq-bios.c | 227 ++++++ mpq-bios.h | 23 + mpq-errors.c | 22 + mpq-errors.h | 25 + 12 files changed, 2693 insertions(+) create mode 100644 MPQCryptography.c create mode 100644 MPQCryptography.h create mode 100644 Makefile create mode 100644 errors.c create mode 100644 errors.h create mode 100644 extract.c create mode 100644 extract.h create mode 100644 inttypes.h create mode 100644 mpq-bios.c create mode 100644 mpq-bios.h create mode 100644 mpq-errors.c create mode 100644 mpq-errors.h diff --git a/MPQCryptography.c b/MPQCryptography.c new file mode 100644 index 0000000..5cd29d3 --- /dev/null +++ b/MPQCryptography.c @@ -0,0 +1,249 @@ +/* + * MPQCryptography.c + * MPQKit + * + * Created by Jean-Francois Roy on Sat Oct 05 2002. + * Copyright (c) 2002-2007 MacStorm. All rights reserved. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "MPQCryptography.h" +#include "inttypes.h" + +static int crypt_table_initialized = 0; +static uint32_t crypt_table[0x500]; + +static void memrev(unsigned char *buf, size_t count) +{ + unsigned char *r; + + for (r = buf + count - 1; buf < r; buf++, r--) { + *buf ^= *r; + *r ^= *buf; + *buf ^= *r; + } +} + +const uint32_t *__mpqlib_get_cryptography_table() +{ + assert(crypt_table_initialized); + return crypt_table; +} + +void __mpqlib_init_cryptography() +{ + // Prepare crypt_table + uint32_t seed = 0x00100001; + uint32_t index1 = 0; + uint32_t index2 = 0; + int32_t i; + + if (!crypt_table_initialized) { + crypt_table_initialized = 1; + + for (index1 = 0; index1 < 0x100; index1++) { + for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100) { + uint32_t temp1, temp2; + + seed = (seed * 125 + 3) % 0x2AAAAB; + temp1 = (seed & 0xFFFF) << 0x10; + + seed = (seed * 125 + 3) % 0x2AAAAB; + temp2 = (seed & 0xFFFF); + + crypt_table[index2] = (temp1 | temp2); + } + } + } + // Load up OpenSSL + OpenSSL_add_all_digests(); + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + ERR_load_crypto_strings(); +} + +void __mpqlib_encrypt(char *data, uint32_t length, uint32_t key, bool disable_input_swapping) +{ + assert(crypt_table_initialized); + assert(data); + + uint32_t *buffer32 = (uint32_t *) data; + uint32_t seed = 0xEEEEEEEE; + uint32_t ch; + + // Round to 4 bytes + length = length / 4; + + // We duplicate the loop to avoid costly branches + if (disable_input_swapping) { + while (length-- > 0) { + seed += crypt_table[0x400 + (key & 0xFF)]; + ch = *buffer32 ^ (key + seed); + + key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); + seed = *buffer32 + seed + (seed << 5) + 3; + + *buffer32++ = ch; + } + } else { + while (length-- > 0) { + seed += crypt_table[0x400 + (key & 0xFF)]; + ch = *buffer32 ^ (key + seed); + + key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); + seed = *buffer32 + seed + (seed << 5) + 3; + + *buffer32++ = ch; + } + } +} + +void __mpqlib_decrypt(char *data, uint32_t length, uint32_t key, bool disable_output_swapping) +{ + assert(crypt_table_initialized); + assert(data); + + uint32_t *buffer32 = (uint32_t *) data; + uint32_t seed = 0xEEEEEEEE; + uint32_t ch; + + // Round to 4 bytes + length = length / 4; + + if (disable_output_swapping) { + while (length-- > 0) { + ch = *buffer32; + + seed += crypt_table[0x400 + (key & 0xFF)]; + ch = ch ^ (key + seed); + + key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); + seed = ch + seed + (seed << 5) + 3; + + *buffer32++ = ch; + } + + } else { + while (length-- > 0) { + ch = *buffer32; + + seed += crypt_table[0x400 + (key & 0xFF)]; + ch = ch ^ (key + seed); + + key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); + seed = ch + seed + (seed << 5) + 3; + + *buffer32++ = ch; + } + } +} + +uint32_t __mpqlib_hash_cstring(const char *string, uint32_t type) +{ + assert(crypt_table_initialized); + assert(string); + + uint32_t seed1 = 0x7FED7FED; + uint32_t seed2 = 0xEEEEEEEE; + uint32_t shifted_type = (type << 8); + int32_t ch; + + while (*string != 0) { + ch = *string++; + if (ch > 0x60 && ch < 0x7b) + ch -= 0x20; + + seed1 = crypt_table[shifted_type + ch] ^ (seed1 + seed2); + seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3; + } + + return seed1; +} + +uint32_t __mpqlib_hash_data(const char *data, uint32_t length, uint32_t type) +{ + assert(crypt_table_initialized); + assert(data); + + uint32_t seed1 = 0x7FED7FED; + uint32_t seed2 = 0xEEEEEEEE; + uint32_t shifted_type = (type << 8); + int32_t ch; + + while (length > 0) { + ch = *data++; + + seed1 = crypt_table[shifted_type + ch] ^ (seed1 + seed2); + seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3; + length--; + } + + return seed1; +} + +void __mpqlib_crc32(const unsigned char *buffer, uint32_t length, uint32_t * crc, uint32_t flags) +{ + uint32_t local_crc = 0; + const uint32_t *crc_table = (uint32_t *) get_crc_table(); + const unsigned char *buffer_end = buffer + length; + + if (crc) + local_crc = *crc; + if (flags & __MPQLIB_CRC_INIT) + local_crc = 0xFFFFFFFF; + + if (flags & __MPQLIB_CRC_UPDATE) { + while (buffer < buffer_end) { + local_crc = ((local_crc >> 8) & 0x00FFFFFF) ^ crc_table[(local_crc ^ *buffer) & 0xFF]; + buffer++; + } + } + + if (flags & __MPQLIB_CRC_FINALIZE) + local_crc = local_crc ^ 0xFFFFFFFF; + if (crc) + *crc = local_crc; +} + +int __mpqlib_verify_weak_signature(RSA * public_key, const unsigned char *signature, const unsigned char *digest) +{ + unsigned char reversed_signature[__MPQLIB_WEAK_SIGNATURE_SIZE]; + + memcpy(reversed_signature, signature + 8, __MPQLIB_WEAK_SIGNATURE_SIZE); + memrev(reversed_signature, __MPQLIB_WEAK_SIGNATURE_SIZE); + + return RSA_verify(NID_md5, digest, MD5_DIGEST_LENGTH, reversed_signature, __MPQLIB_WEAK_SIGNATURE_SIZE, public_key); +} + +int __mpqlib_verify_strong_signature(RSA * public_key, const unsigned char *signature, const unsigned char *digest) +{ + unsigned char reversed_signature[__MPQLIB_STRONG_SIGNATURE_SIZE]; + + memcpy(reversed_signature, signature + 4, __MPQLIB_STRONG_SIGNATURE_SIZE); + memrev(reversed_signature, __MPQLIB_STRONG_SIGNATURE_SIZE); + + unsigned char real_digest[__MPQLIB_STRONG_SIGNATURE_SIZE]; + + memset(real_digest, 0xbb, sizeof(real_digest)); + real_digest[0] = 0x0b; + + uint32_t digest_offset = sizeof(real_digest) - SHA_DIGEST_LENGTH; + + memcpy(real_digest + digest_offset, digest, SHA_DIGEST_LENGTH); + memrev(real_digest + digest_offset, SHA_DIGEST_LENGTH); + + RSA_public_decrypt(__MPQLIB_STRONG_SIGNATURE_SIZE, reversed_signature, reversed_signature, public_key, RSA_NO_PADDING); + unsigned long error = ERR_get_error(); + + return (!error && memcmp(reversed_signature, real_digest, __MPQLIB_STRONG_SIGNATURE_SIZE) == 0); +} diff --git a/MPQCryptography.h b/MPQCryptography.h new file mode 100644 index 0000000..e8af48e --- /dev/null +++ b/MPQCryptography.h @@ -0,0 +1,48 @@ +/* + * MPQCryptography.h + * MPQKit + * + * Created by Jean-Francois Roy on Sat Oct 05 2002. + * Copyright (c) 2002-2007 MacStorm. All rights reserved. + * + */ + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !defined(__MPQLIB_WEAK_SIGNATURE_SIZE) +#define __MPQLIB_WEAK_SIGNATURE_SIZE 64 +#endif + +#if !defined(__MPQLIB_STRONG_SIGNATURE_SIZE) +#define __MPQLIB_STRONG_SIGNATURE_SIZE 256 +#endif + + void __mpqlib_init_cryptography(void); + + const uint32_t *__mpqlib_get_cryptography_table(void); + + void __mpqlib_encrypt(char *data, uint32_t length, uint32_t key, + bool disable_input_swapping); + void __mpqlib_decrypt(char *data, uint32_t length, uint32_t key, + bool disable_output_swapping); + + uint32_t __mpqlib_hash_cstring(const char *string, uint32_t type); + uint32_t __mpqlib_hash_data(const char *data, uint32_t length, uint32_t type); + +#define __MPQLIB_CRC_INIT 0x1 +#define __MPQLIB_CRC_UPDATE 0x2 +#define __MPQLIB_CRC_FINALIZE 0x4 + void __mpqlib_crc32(const unsigned char *buffer, uint32_t length, uint32_t * crc, uint32_t flags); + + int __mpqlib_verify_weak_signature(RSA * public_key, const unsigned char *signature, const unsigned char *digest); + int __mpqlib_verify_strong_signature(RSA * public_key, const unsigned char *signature, const unsigned char *digest); + +#if defined(__cplusplus) +} +#endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..69a8e33 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +CC = gcc +AR = ar rcs + +CPPFLAGS = -g -Wall -Werror + +SOURCE_LIST = \ +errors.c \ +extract.c \ +mpq-bios.c \ +MPQCryptography.c \ +mpq-errors.c \ + +TARGET = mpqlib.a + + +OBJ_LIST = $(addsuffix .o, $(notdir $(basename $(SOURCE_LIST)))) +DEP_LIST = $(addsuffix .dep, $(notdir $(basename $(SOURCE_LIST)))) + +all: dep $(TARGET) + +$(TARGET): $(OBJ_LIST) + $(AR) $@ $(OBJ_LIST) + +clean: + rm -f *.o *.dep $(TARGET) + +dep: $(ALL_DEPS) + +%.dep : %.c + $(CC) $(CPPFLAGS) -M -MF $@ $< + +-include $(ALL_DEPS) diff --git a/errors.c b/errors.c new file mode 100644 index 0000000..86ff239 --- /dev/null +++ b/errors.c @@ -0,0 +1,4 @@ +#include "errors.h" + +int __mpqlib_errno = 0; + diff --git a/errors.h b/errors.h new file mode 100644 index 0000000..12f766b --- /dev/null +++ b/errors.h @@ -0,0 +1,6 @@ +#ifndef __ERRORS_H__ +#define __ERRORS_H__ + +extern int __mpqlib_errno; + +#endif diff --git a/extract.c b/extract.c new file mode 100644 index 0000000..2bd47e9 --- /dev/null +++ b/extract.c @@ -0,0 +1,2007 @@ +/* + * extract.c -- global extracting function for all known file compressions + * in a MPQ archive. + * + * Copyright (C) 2003 Maik Broemme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "extract.h" + +#define FALSE 0 +#define TRUE !FALSE + +typedef int (*DECOMPRESS) (char *, int *, const char *, int); +typedef struct { + unsigned long mask; /* Decompression bit */ + DECOMPRESS decompress; /* Decompression function */ +} decompress_table; + +static int pkzip_decompress(char *out_buf, int *out_length, const char *in_buf, int in_length); +static int zlib_decompress(char *out_buf, int *out_length, const char *in_buf, int in_length); +static int huff_decompress(char *out_buf, int *out_length, const char *in_buf, int in_length); +static int adpcm_decompress_mono(char *out_buf, int *out_length, const char *in_buf, int in_length); +static int adpcm_decompress_stereo(char *out_buf, int *out_length, const char *in_buf, int in_length); + +static decompress_table dcmp_table[] = { + {0x08, pkzip_decompress}, /* Decompression with Pkware Data Compression Library */ + {0x02, zlib_decompress}, /* Decompression with the "zlib" library */ + {0x01, huff_decompress}, /* Huffmann decompression */ + {0x80, adpcm_decompress_stereo}, /* adpcm decompression for stereo adpcms */ + {0x40, adpcm_decompress_mono} /* adpcm decompression for mono adpcms */ +}; + +/* + * ADPCM decompression + */ + +typedef union { + unsigned short *pw; + unsigned char *pb; +} byte_and_short; + +/* + * Tables necessary for decompression + */ +static unsigned long adpcm_step_changes[] = { + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006, + 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, + 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, + 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008 +}; + +static unsigned long adpcm_steps[] = { + 0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E, + 0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F, + 0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042, + 0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F, + 0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133, + 0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292, + 0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583, + 0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0, + 0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954, + 0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B, + 0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462, + 0x00007FFF +}; + +/* + * Decompress a adpcm file, mono or stereo + * + * Offset: 1500F230 + */ +static int adpcm_decompress(unsigned char *out_buf, int out_length, unsigned char *in_buf, + int in_length, int channels) +{ + byte_and_short out; + byte_and_short in; + unsigned char *in_end = in_buf + in_length; /* End on input buffer */ + unsigned long index; + long nr_array1[2]; + long nr_array2[2]; + int count = 0; + + out.pb = out_buf; + in.pb = in_buf; + nr_array1[0] = 0x2C; + nr_array1[1] = 0x2C; + in.pw++; + + /* + * 15007AD7 + */ + for (count = 0; count < channels; count++) { + long temp; + + temp = *(short *) in.pw++; + nr_array2[count] = temp; + if (out_length < 2) { + return out.pb - out_buf; + } + *out.pw++ = (unsigned short) temp; + out_length -= 2; + } + index = channels - 1; + while (in.pb < in_end) { + unsigned char one_byte = *in.pb++; + + if (channels == 2) { + index = (index == 0) ? 1 : 0; + } + + /* + * Get one byte from input buffer + * 15007B25 + */ + if (one_byte & 0x80) { + /* + * 15007B32 + */ + switch (one_byte & 0x7F) { + case 0: /* 15007B8E */ + if (nr_array1[index] != 0) { + nr_array1[index]--; + } + if (out_length < 2) { + break; + } + *out.pw++ = (unsigned short) nr_array2[index]; + out_length -= 2; + continue; + case 1: /* 15007B72 */ + nr_array1[index] += 8; /* EBX also */ + if (nr_array1[index] > 0x58) { + nr_array1[index] = 0x58; + } + if (channels == 2) { + index = (index == 0) ? 1 : 0; + } + continue; + case 2: + continue; + default: + nr_array1[index] -= 8; + if (nr_array1[index] < 0) { + nr_array1[index] = 0; + } + if (channels != 2) { + continue; + } + index = (index == 0) ? 1 : 0; + continue; + } + } else { + unsigned long temp1 = adpcm_steps[nr_array1[index]]; /* EDI */ + unsigned long temp2 = temp1 >> in_buf[1]; /* ESI */ + long temp3 = nr_array2[index]; /* ECX */ + + if (one_byte & 0x01) { /* EBX = one_byte */ + temp2 += (temp1 >> 0); + } + if (one_byte & 0x02) { + temp2 += (temp1 >> 1); + } + if (one_byte & 0x04) { + temp2 += (temp1 >> 2); + } + if (one_byte & 0x08) { + temp2 += (temp1 >> 3); + } + if (one_byte & 0x10) { + temp2 += (temp1 >> 4); + } + if (one_byte & 0x20) { + temp2 += (temp1 >> 5); + } + if (one_byte & 0x40) { + temp3 -= temp2; + if (temp3 <= (long) 0xFFFF8000) { + temp3 = (long) 0xFFFF8000; + } + } else { + temp3 += temp2; + if (temp3 >= 0x7FFF) { + temp3 = 0x7FFF; + } + } + nr_array2[index] = temp3; + if (out_length < 2) { + break; + } + + temp2 = nr_array1[index]; + one_byte &= 0x1F; + *out.pw++ = (unsigned short) temp3; + out_length -= 2; + temp2 += adpcm_step_changes[one_byte]; + nr_array1[index] = temp2; + + if (nr_array1[index] < 0) { + nr_array1[index] = 0; + } else { + if (nr_array1[index] > 0x58) { + nr_array1[index] = 0x58; + } + } + } + } + return (out.pb - out_buf); +} + +/* + * Huffman decompression + */ + +#define PTR_NOT(ptr) (struct huffman_tree_item *)(~(unsigned long)(ptr)) +#define PTR_PTR(ptr) ((struct huffman_tree_item *)(ptr)) +#define PTR_INT(ptr) (long)(ptr) + +#define INSERT_ITEM 1 +#define SWITCH_ITEMS 2 /* Switch the item1 and item2 */ + +/* + * Input stream for Huffmann decompression + */ +struct huffman_input_stream { + unsigned char *in_buf; /* 00 - Input data */ + unsigned long bit_buf; /* 04 - Input bit buffer */ + unsigned int bits; /* 08 - Number of bits remaining in 'byte' */ +}; + +/* + * Huffmann tree item. + */ +struct huffman_tree_item { + struct huffman_tree_item *next; /* 00 - Pointer to next huffman_tree_item */ + struct huffman_tree_item *prev; /* 04 - Pointer to prev huffman_tree_item (< 0 if none) */ + unsigned long dcmp_byte; /* 08 - Index of this item in item pointer array, decompressed byte + * value */ + unsigned long byte_value; /* 0C - Some byte value */ + struct huffman_tree_item *parent; /* 10 - Pointer to parent huffman_tree_item (NULL if none) */ + struct huffman_tree_item *child; /* 14 - Pointer to child huffman_tree_item */ +}; + +/* + * Structure used for quick decompress. The 'bits' contains + * number of bits and dcmp_byte contains result decompressed byte + * value. After each walk through Huffman tree are filled all entries + * which are multiplies of number of bits loaded from input stream. + * These entries contain number of bits and result value. At the next + * 7 bits is tested this structure first. If corresponding entry found, + * decompression routine will not walk through Huffman tree and + * directly stores output byte to output stream. + */ +struct huffman_decompress { + unsigned long offs00; /* 00 - 1 if resolved */ + unsigned long bits; /* 04 - Bit count */ + union { + unsigned long dcmp_byte; /* 08 - Byte value for decompress (if bitCount <= 7) */ + struct huffman_tree_item *p_item; /* 08 - THTreeItem (if number of bits is greater + * than 7 */ + }; +}; + +/* + * Structure for Huffman tree. + */ +struct huffman_tree { + unsigned long cmp0; /* 0000 - 1 if compression type 0 */ + unsigned long offs0004; /* 0004 - Some flag */ + + struct huffman_tree_item items0008[0x203]; /* 0008 - huffman tree items */ + + /* + * Sometimes used as huffman tree item + */ + struct huffman_tree_item *item3050; /* 3050 - Always NULL (?) */ + struct huffman_tree_item *item3054; /* 3054 - Pointer to huffman_tree_item */ + struct huffman_tree_item *item3058; /* 3058 - Pointer to huffman_tree_item (< 0 if invalid) */ + + /* + * Sometimes used as huffman tree item + */ + struct huffman_tree_item *item305C; /* 305C - Usually NULL */ + struct huffman_tree_item *first; /* 3060 - Pointer to top (first) Huffman tree item */ + struct huffman_tree_item *last; /* 3064 - Pointer to bottom (last) Huffman tree item (< 0 + * if invalid) */ + unsigned long items; /* 3068 - Number of used huffman tree items */ + + struct huffman_tree_item *items306C[0x102]; /* 306C - huffman_tree_item pointer array */ + struct huffman_decompress qd3474[0x80]; /* 3474 - Array for quick decompression */ + + unsigned char table1502A630[]; /* Some table to make struct size flexible */ +}; + +static unsigned char table1502A630[] = { + + /* + * Data for compression type 0x00 + */ + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, + + /* + * Data for compression type 0x01 + */ + 0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05, + 0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, + 0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, + 0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, + 0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, + 0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03, + 0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, + 0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B, + 0x00, 0x00, + + /* + * Data for compression type 0x02 + */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04, + 0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, + 0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A, + 0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + /* + * Data for compression type 0x03 + */ + 0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03, + 0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, + 0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, + 0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03, + 0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01, + 0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, + 0x00, 0x00, + + /* + * Data for compression type 0x04 + */ + 0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + /* + * Data for compression type 0x05 + */ + 0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82, + 0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37, + 0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D, + 0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + /* + * Data for compression type 0x06 + */ + 0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + /* + * Data for compression type 0x07 + */ + 0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + /* + * Data for compression type 0x08 + */ + 0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10, + 0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11, + 0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +/* + * Gets previous Huffman tree item (?) + */ +static struct huffman_tree_item *huff_get_prev_item(struct huffman_tree_item *hi, long value) +{ + if (PTR_INT(hi->prev) < 0) { + return PTR_NOT(hi->prev); + } + if (value < 0) { + value = hi - hi->next->prev; + } + return hi->prev + value; +} + +/* + * 1500BC90 + */ +static void huff_remove_item(struct huffman_tree_item *hi) +{ + struct huffman_tree_item *temp; /* EDX */ + + if (hi->next != NULL) { + temp = hi->prev; + if (PTR_INT(temp) <= 0) { + temp = PTR_NOT(temp); + } else { + temp += (hi - hi->next->prev); + } + temp->next = hi->next; + hi->next->prev = hi->prev; + hi->next = hi->prev = NULL; + } +} + +static void huff_insert_item(struct huffman_tree_item **p_item, + struct huffman_tree_item *item, unsigned long where, + struct huffman_tree_item *item2) +{ + struct huffman_tree_item *next = item->next; /* EDI - next to the first item */ + struct huffman_tree_item *prev = item->prev; /* ESI - prev to the first item */ + struct huffman_tree_item *prev2; /* Pointer to previous item */ + long next2; /* Pointer to the next item */ + + /* + * The same code like in mpq_huff_remove_item(); + */ + if (next != 0) { /* If the first item already has next one */ + if (PTR_INT(prev) < 0) { + prev = PTR_NOT(prev); + } else { + prev += (item - next->prev); + } + + /* + * 150083C1 + * Remove the item from the tree + */ + prev->next = next; + next->prev = prev; + + /* + * Invalidate 'prev' and 'next' pointer + */ + item->next = 0; + item->prev = 0; + } + + if (item2 == NULL) { /* EDX - If the second item is not entered, */ + item2 = PTR_PTR(&p_item[1]); /* take the first tree item */ + } + + switch (where) { + case SWITCH_ITEMS: /* Switch the two items */ + item->next = item2->next; /* item2->next (Pointer to pointer to first) */ + item->prev = item2->next->prev; + item2->next->prev = item; + item2->next = item; /* Set the first item */ + return; + case INSERT_ITEM: /* Insert as the last item */ + item->next = item2; /* Set next item (or pointer to pointer to first item) */ + item->prev = item2->prev; /* Set prev item (or last item in the tree) */ + next2 = PTR_INT(p_item[0]); /* Usually NULL */ + prev2 = item2->prev; /* Prev item to the second (or last tree item) */ + if (PTR_INT(prev2) < 0) { + prev2 = PTR_NOT(prev); + prev2->next = item; + item2->prev = item; /* Next after last item */ + return; + } + if (next2 < 0) { + next2 = item2 - item2->next->prev; + } + prev2 += next2; + prev2->next = item; + item2->prev = item; /* Set the next/last item */ + return; + default: + return; + } +} + +/* + * Builds Huffman tree. Called with the first 8 bits loaded from input stream. + */ +static void huff_build_tree(struct huffman_tree *ht, unsigned int cmp_type) +{ + unsigned long max_byte; /* [ESP+10] - The greatest character found in table */ + unsigned char *byte_array; /* [ESP+1C] - Pointer to unsigned char in table1502A630 */ + unsigned long i; /* egcs in linux doesn't like multiple for loops without an + * explicit i */ + unsigned int found; /* Thats needed to replace the goto stuff from original source :) */ + struct huffman_tree_item **p_item; /* [ESP+14] - Pointer to Huffman tree item pointer array */ + struct huffman_tree_item *child1; + + /* + * Loop while pointer has a negative value. + */ + while (PTR_INT(ht->last) > 0) { /* ESI - Last entry */ + struct huffman_tree_item *temp; /* EAX */ + + if (ht->last->next != NULL) { /* ESI->next */ + huff_remove_item(ht->last); + } + ht->item3058 = PTR_PTR(&ht->item3054); /* [EDI+4] */ + ht->last->prev = ht->item3058; /* EAX */ + temp = huff_get_prev_item(PTR_PTR(&ht->item3054), PTR_INT(&ht->item3050)); + temp->next = ht->last; + ht->item3054 = ht->last; + } + + /* + * Clear all pointers in huffman tree item array. + */ + memset(ht->items306C, 0, sizeof(ht->items306C)); + + max_byte = 0; /* Greatest character found init to zero. */ + p_item = (struct huffman_tree_item **) &ht->items306C; /* Pointer to current entry in + * huffman tree item pointer array */ + + /* + * Ensure we have low 8 bits only + */ + cmp_type &= 0xFF; + byte_array = table1502A630 + cmp_type * 258; /* EDI also */ + + for (i = 0; i < 0x100; i++, p_item++) { + struct huffman_tree_item *item = ht->item3058; /* Item to be created */ + struct huffman_tree_item *p_item3 = ht->item3058; + unsigned char one_byte = byte_array[i]; + + /* + * Skip all the bytes which are zero. + */ + if (byte_array[i] == 0) { + continue; + } + + /* + * If not valid pointer, take the first available item in the array. + */ + if (PTR_INT(item) <= 0) { + item = &ht->items0008[ht->items++]; + } + + /* + * Insert this item as the top of the tree. + */ + huff_insert_item(&ht->item305C, item, SWITCH_ITEMS, NULL); + + item->parent = NULL; /* Invalidate child and parent */ + item->child = NULL; + *p_item = item; /* Store pointer into pointer array */ + + item->dcmp_byte = i; /* Store counter */ + item->byte_value = one_byte; /* Store byte value */ + if (one_byte >= max_byte) { + max_byte = one_byte; + continue; + } + + /* + * Find the first item which has byte value greater than current one byte + */ + found = 0; + if (PTR_INT((p_item3 = ht->last)) > 0) { /* EDI - Pointer to the last item */ + + /* + * 15006AF7 + */ + if (p_item3 != NULL) { + do { /* 15006AFB */ + if (p_item3->byte_value >= one_byte) { + found = 1; + break; + } + p_item3 = p_item3->prev; + } while (PTR_INT(p_item3) > 0); + } + } + + if (found == 0) { + p_item3 = NULL; + } + + /* + * 15006B09 + */ + if (item->next != NULL) { + huff_remove_item(item); + } + + /* + * 15006B15 + */ + if (p_item3 == NULL) { + p_item3 = PTR_PTR(&ht->first); + } + + /* + * 15006B1F + */ + item->next = p_item3->next; + item->prev = p_item3->next->prev; + p_item3->next->prev = item; + p_item3->next = item; + } + + /* + * 15006B4A + */ + for (; i < 0x102; i++) { + struct huffman_tree_item **p_item2 = &ht->items306C[i]; /* EDI */ + + /* + * 15006B59 + */ + struct huffman_tree_item *item2 = ht->item3058; /* ESI */ + + if (PTR_INT(item2) <= 0) { + item2 = &ht->items0008[ht->items++]; + } + huff_insert_item(&ht->item305C, item2, INSERT_ITEM, NULL); + + /* + * 15006B89 + */ + item2->dcmp_byte = i; + item2->byte_value = 1; + item2->parent = NULL; + item2->child = NULL; + *p_item2++ = item2; + } + + /* + * 15006BAA + */ + if (PTR_INT((child1 = ht->last)) > 0) { /* EDI - last item (first child to item */ + struct huffman_tree_item *child2; /* EBP */ + struct huffman_tree_item *item; /* ESI */ + + /* + * 15006BB8 + */ + while (PTR_INT((child2 = child1->prev)) > 0) { + if (PTR_INT((item = ht->item3058)) <= 0) { + item = &ht->items0008[ht->items++]; + } + /* + * 15006BE3 + */ + huff_insert_item(&ht->item305C, item, SWITCH_ITEMS, NULL); + + /* + * 15006BF3 + */ + item->parent = NULL; + item->child = NULL; + + /* + * EDX = child2->byte_value + child1->byte_value; + * EAX = child1->byte_value; + * ECX = max_byte; The greatest character (0xFF usually) + */ + item->byte_value = child1->byte_value + child2->byte_value; /* 0x02 */ + item->child = child1; /* Prev item in the */ + child1->parent = item; + child2->parent = item; + + /* + * EAX = item->byte_value; + */ + if (item->byte_value >= max_byte) { + max_byte = item->byte_value; + } else { + struct huffman_tree_item *p_item2 = child2->prev; /* EDI */ + + found = 0; + if (PTR_INT(p_item2) > 0) { + + /* + * 15006C2D + */ + do { + if (p_item2->byte_value >= item->byte_value) { + found = 1; + break; + } + p_item2 = p_item2->prev; + } while (PTR_INT(p_item2) > 0); + } + if (found == 0) { + p_item2 = NULL; + } + if (item->next != 0) { + struct huffman_tree_item *temp4 = huff_get_prev_item(item, -1); + + temp4->next = item->next; /* The first item changed */ + item->next->prev = item->prev; /* First->prev changed to negative value */ + item->next = NULL; + item->prev = NULL; + } + + /* + * 15006C62 + */ + if (p_item2 == NULL) { + p_item2 = PTR_PTR(&ht->first); + } + item->next = p_item2->next; /* Set item with 0x100 byte value */ + item->prev = p_item2->next->prev; /* Set item with 0x17 byte value */ + p_item2->next->prev = item; /* Changed prev of item with */ + p_item2->next = item; + } + + /* + * 15006C7B + */ + if (PTR_INT((child1 = child2->prev)) <= 0) { + break; + } + } + } + + /* + * 15006C88 + */ + ht->offs0004 = 1; +} + +/* + * Gets the whole byte from the input stream. + */ +static unsigned long huff_get_8bits(struct huffman_input_stream *is) +{ + unsigned long one_byte; + + if (is->bits <= 8) { + is->bit_buf |= *(unsigned short *) is->in_buf << is->bits; + is->in_buf += sizeof(unsigned short); + is->bits += 16; + } + + one_byte = (is->bit_buf & 0xFF); + is->bit_buf >>= 8; + is->bits -= 8; + + return one_byte; +} + +/* + * Gets 7 bits from the stream. + */ +static unsigned long huff_get_7bits(struct huffman_input_stream *is) +{ + if (is->bits <= 7) { + is->bit_buf |= *(unsigned short *) is->in_buf << is->bits; + is->in_buf += sizeof(unsigned short); + is->bits += 16; + } + + /* + * Get 7 bits from input stream. + */ + return (is->bit_buf & 0x7F); +} + +/* + * Gets one bit from input stream. + */ +static unsigned long huff_get_bit(struct huffman_input_stream *is) +{ + unsigned long bit = (is->bit_buf & 1); + + is->bit_buf >>= 1; + if (--is->bits == 0) { + is->bit_buf = *(unsigned long *) is->in_buf; + is->in_buf += sizeof(unsigned long); + is->bits = 32; + } + return bit; +} + +static struct huffman_tree_item *huff_call1500E740(struct huffman_tree *ht, unsigned int value) +{ + struct huffman_tree_item *p_item1 = ht->item3058; /* EDX */ + struct huffman_tree_item *p_item2; /* EAX */ + struct huffman_tree_item *p_next; + struct huffman_tree_item *p_prev; + struct huffman_tree_item **pp_item; + + if (PTR_INT(p_item1) <= 0 || (p_item2 = p_item1) == NULL) { + if ((p_item2 = &ht->items0008[ht->items++]) != NULL) { + p_item1 = p_item2; + } else { + p_item1 = ht->first; + } + } else { + p_item1 = p_item2; + } + + p_next = p_item1->next; + if (p_next != NULL) { + p_prev = p_item1->prev; + if (PTR_INT(p_prev) <= 0) { + p_prev = PTR_NOT(p_prev); + } else { + p_prev += (p_item1 - p_item1->next->prev); + } + + p_prev->next = p_next; + p_next->prev = p_prev; + p_item1->next = NULL; + p_item1->prev = NULL; + } + pp_item = &ht->first; /* ESI */ + if (value > 1) { + + /* + * ECX = ht->first->next; + */ + p_item1->next = *pp_item; + p_item1->prev = (*pp_item)->prev; + + (*pp_item)->prev = p_item2; + *pp_item = p_item1; + + p_item2->parent = NULL; + p_item2->child = NULL; + } else { + p_item1->next = (struct huffman_tree_item *) pp_item; + p_item1->prev = pp_item[1]; + /* + * EDI = ht->item305C; + */ + p_prev = pp_item[1]; /* ECX */ + if (p_prev <= 0) { + p_prev = PTR_NOT(p_prev); + p_prev->next = p_item1; + p_prev->prev = p_item2; + + p_item2->parent = NULL; + p_item2->child = NULL; + } else { + if (PTR_INT(ht->item305C) < 0) { + p_prev += (struct huffman_tree_item *) pp_item - (*pp_item)->prev; + } else { + p_prev += PTR_INT(ht->item305C); + } + + p_prev->next = p_item1; + pp_item[1] = p_item2; + p_item2->parent = NULL; + p_item2->child = NULL; + } + } + return p_item2; +} + +static void huff_call1500E820(struct huffman_tree *ht, struct huffman_tree_item *p_item) +{ + struct huffman_tree_item *p_item1; /* EDI */ + struct huffman_tree_item *p_item2 = NULL; /* EAX */ + struct huffman_tree_item *p_item3; /* EDX */ + struct huffman_tree_item *p_prev; /* EBX */ + + for (; p_item != NULL; p_item = p_item->parent) { + p_item->byte_value++; + + for (p_item1 = p_item;; p_item1 = p_prev) { + p_prev = p_item1->prev; + if (PTR_INT(p_prev) <= 0) { + p_prev = NULL; + break; + } + if (p_prev->byte_value >= p_item->byte_value) { + break; + } + } + + if (p_item1 == p_item) { + continue; + } + + if (p_item1->next != NULL) { + p_item2 = huff_get_prev_item(p_item1, -1); + p_item2->next = p_item1->next; + p_item1->next->prev = p_item1->prev; + p_item1->next = NULL; + p_item1->prev = NULL; + } + p_item2 = p_item->next; + p_item1->next = p_item2; + p_item1->prev = p_item2->prev; + p_item2->prev = p_item1; + p_item->next = p_item1; + if ((p_item2 = p_item1) != NULL) { + p_item2 = huff_get_prev_item(p_item, -1); + p_item2->next = p_item->next; + p_item->next->prev = p_item->prev; + p_item->next = NULL; + p_item->prev = NULL; + } + + if (p_prev == NULL) { + p_prev = PTR_PTR(&ht->first); + } + p_item2 = p_prev->next; + p_item->next = p_item2; + p_item->prev = p_item2->prev; + p_item2->prev = p_item; + p_prev->next = p_item; + + p_item3 = p_item1->parent->child; + p_item2 = p_item->parent; + if (p_item2->child == p_item) { + p_item2->child = p_item1; + } + + if (p_item3 == p_item1) { + p_item1->parent->child = p_item; + } + + p_item2 = p_item->parent; + p_item->parent = p_item1->parent; + p_item1->parent = p_item2; + ht->offs0004++; + } +} + +static int huff_do_decompress(struct huffman_tree *ht, struct huffman_input_stream *is, + unsigned char *out_buf, unsigned int out_length) +{ + unsigned int n8bits; /* 8 bits loaded from input stream */ + unsigned int n7bits; /* 7 bits loaded from input stream */ + unsigned int found; /* Thats needed to replace the goto stuff from original source :) */ + unsigned int dcmp_byte = 0; + unsigned long bit_count; + struct huffman_decompress *qd; + unsigned int has_qd; /* Can we use quick decompression? */ + struct huffman_tree_item *p_item1; + struct huffman_tree_item *p_item2; + unsigned char *out_pos = out_buf; + + /* + * Test the output length. Must not be non zero. + */ + if (out_length == 0) { + return 0; + } + + /* + * Get the compression type from the input stream. + */ + n8bits = huff_get_8bits(is); + + /* + * Build the Huffman tree + */ + huff_build_tree(ht, n8bits); + ht->cmp0 = (n8bits == 0) ? TRUE : FALSE; + + for (;;) { + n7bits = huff_get_7bits(is); /* Get 7 bits from input stream */ + + /* + * Try to use quick decompression. Check huffman_decompress array for corresponding item. + * If found, use the result byte instead. + */ + qd = &ht->qd3474[n7bits]; + + /* + * If there is a quick-pass possible (ebx) + */ + has_qd = (qd->offs00 >= ht->offs0004) ? TRUE : FALSE; + + /* + * If we can use quick decompress, use it. + */ + if (has_qd) { + found = 0; + if (qd->bits > 7) { + is->bit_buf >>= 7; + is->bits -= 7; + p_item1 = qd->p_item; + found = 1; + } + if (found == 0) { + is->bit_buf >>= qd->bits; + is->bits -= qd->bits; + dcmp_byte = qd->dcmp_byte; + } + } else { + found = 1; + p_item1 = ht->first->next->prev; + if (PTR_INT(p_item1) <= 0) { + p_item1 = NULL; + } + } + + if (found == 1) { + bit_count = 0; + p_item2 = NULL; + do { + p_item1 = p_item1->child; /* Move down by one level */ + if (huff_get_bit(is)) { /* If current bit is set, move to previous */ + p_item1 = p_item1->prev; + } + if (++bit_count == 7) { /* If we are at 7th bit, save current huffman tree item. */ + p_item2 = p_item1; + } + } while (p_item1->child != NULL); /* Walk until tree has no deeper level */ + + if (has_qd == FALSE) { + if (bit_count > 7) { + qd->offs00 = ht->offs0004; + qd->bits = bit_count; + qd->p_item = p_item2; + } else { + unsigned long index = n7bits & (0xFFFFFFFF >> (32 - bit_count)); + unsigned long add = (1 << bit_count); + + for (qd = &ht->qd3474[index]; index <= 0x7F; index += add, qd += add) { + qd->offs00 = ht->offs0004; + qd->bits = bit_count; + qd->dcmp_byte = p_item1->dcmp_byte; + } + } + } + dcmp_byte = p_item1->dcmp_byte; + } + + if (dcmp_byte == 0x101) { /* Huffman tree needs to be modified */ + n8bits = huff_get_8bits(is); + p_item1 = (ht->last <= 0) ? NULL : ht->last; + + p_item2 = huff_call1500E740(ht, 1); + p_item2->parent = p_item1; + p_item2->dcmp_byte = p_item1->dcmp_byte; + p_item2->byte_value = p_item1->byte_value; + ht->items306C[p_item2->dcmp_byte] = p_item2; + + p_item2 = huff_call1500E740(ht, 1); + p_item2->parent = p_item1; + p_item2->dcmp_byte = n8bits; + p_item2->byte_value = 0; + ht->items306C[p_item2->dcmp_byte] = p_item2; + + p_item1->child = p_item2; + huff_call1500E820(ht, p_item2); + if (ht->cmp0 == 0) { + huff_call1500E820(ht, ht->items306C[n8bits]); + } + dcmp_byte = n8bits; + } + + if (dcmp_byte == 0x100) { + break; + } + + *out_pos++ = (unsigned char) dcmp_byte; + if (--out_length == 0) { + break; + } + if (ht->cmp0) { + huff_call1500E820(ht, ht->items306C[dcmp_byte]); + } + } + return (out_pos - out_buf); +} + +static void huff_init_tree(struct huffman_tree *ht, struct huffman_tree_item *hi) +{ + int count; + + /* + * Clear links for all the items in the tree + */ + for (hi = ht->items0008, count = 0x203; count != 0; hi++, count--) { + hi->next = hi->prev = NULL; + } + + ht->item3050 = NULL; + ht->item3054 = PTR_PTR(&ht->item3054); + ht->item3058 = PTR_NOT(ht->item3054); + + ht->item305C = NULL; + ht->first = PTR_PTR(&ht->first); + ht->last = PTR_NOT(ht->first); + + ht->offs0004 = 1; + ht->items = 0; + + /* + * Clear all huffman_decompress items. Do this only if preparing for decompression + */ + for (count = 0; count < sizeof(ht->qd3474) / sizeof(struct huffman_decompress); count++) { + ht->qd3474[count].offs00 = 0; + } +} + +/* + * PKWARE's compression. + */ + +#define PKZIP_EXP_BUFFER_SIZE 12596 /* Size of decompress buffer */ +#define PKZIP_CMP_BINARY 0 /* Binary compression */ +#define PKZIP_CMP_ASCII 1 /* Ascii compression */ +#define PKZIP_CMP_NO_ERROR 0 +#define PKZIP_CMP_INV_DICTSIZE 1 +#define PKZIP_CMP_INV_MODE 2 +#define PKZIP_CMP_BAD_DATA 3 +#define PKZIP_CMP_ABORT 4 + +/* + * Compression structure (size: 12596 bytes on x86-32) + */ +typedef struct { + unsigned long offs0000; /* 0000 */ + unsigned long cmp_type; /* 0004 - Compression type (PZIP_CMP_BINARY or + * PKZIP_CMP_ASCII) */ + unsigned long out_pos; /* 0008 - Position in output buffer */ + unsigned long dsize_bits; /* 000C - Dict size (4, 5, 6 for 0x400, 0x800, 0x1000) */ + unsigned long dsize_mask; /* 0010 - Dict size bitmask (0x0F, 0x1F, 0x3F for 0x400, 0x800, + * 0x1000) */ + unsigned long bit_buf; /* 0014 - 16-bit buffer for processing input data */ + unsigned long extra_bits; /* 0018 - Number of extra (above 8) bits in bit buffer */ + unsigned int in_pos; /* 001C - Position in in_buf */ + unsigned long in_bytes; /* 0020 - Number of bytes in input buffer */ + void *param; /* 0024 - Custom parameter */ + unsigned int (*read_buf) (char *buf, unsigned int *size, void *param); /* 0028 */ + void (*write_buf) (const char *buf, unsigned int *size, void *param); /* 002C */ + unsigned char out_buf[0x2000]; /* 0030 - Output circle buffer. Starting position is 0x1000 + */ + unsigned char offs_2030[0x204]; /* 2030 - ??? */ + unsigned char in_buf[0x800]; /* 2234 - Buffer for data to be decompressed */ + unsigned char pos1[0x100]; /* 2A34 - Positions in buffers */ + unsigned char pos2[0x100]; /* 2B34 - Positions in buffers */ + unsigned char offs_2c34[0x100]; /* 2C34 - Buffer for */ + unsigned char offs_2d34[0x100]; /* 2D34 - Buffer for */ + unsigned char offs_2e34[0x80]; /* 2EB4 - Buffer for */ + unsigned char offs_2eb4[0x100]; /* 2EB4 - Buffer for */ + unsigned char bits_asc[0x100]; /* 2FB4 - Buffer for */ + unsigned char dist_bits[0x40]; /* 30B4 - Numbers of bytes to skip copied block length */ + unsigned char slen_bits[0x10]; /* 30F4 - Numbers of bits for skip copied block length */ + unsigned char clen_bits[0x10]; /* 3104 - Number of valid bits for copied block */ + unsigned short len_base[0x10]; /* 3114 - Buffer for */ +} __attribute__ ((packed)) pkzip_data_cmp; + +typedef struct { + const char *in_buf; /* Pointer to input data buffer */ + unsigned int in_pos; /* Current offset in input data buffer */ + int in_bytes; /* Number of bytes in the input buffer */ + char *out_buf; /* Pointer to output data buffer */ + unsigned int out_pos; /* Position in the output buffer */ + int max_out; /* Maximum number of bytes in the output buffer */ +} pkzip_data; +/* + * Tables + */ +static unsigned char pkzip_dist_bits[] = { + 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +static unsigned char pkzip_dist_code[] = { + 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, + 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, + 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, + 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 +}; + +static unsigned char pkzip_clen_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 +}; + +static unsigned short pkzip_len_base[] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106 +}; + +static unsigned char pkzip_slen_bits[] = { + 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 +}; + +static unsigned char pkzip_len_code[] = { + 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 +}; + +static unsigned char pkzip_bits_asc[] = { + 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, + 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, + 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, + 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, + 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, + 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, + 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D +}; + +static unsigned short pkzip_code_asc[] = { + 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, + 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, + 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, + 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, + 0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, + 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, + 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, + 0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, + 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, + 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, + 0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, + 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, + 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, + 0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, + 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, + 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, + 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, + 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, + 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, + 0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, + 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, + 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, + 0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, + 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, + 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, + 0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, + 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, + 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, + 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, + 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, + 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, + 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 +}; + +/* + * Local functions + */ +static void pkzip_gen_decode_tabs(long count, unsigned char *bits, unsigned char *code, unsigned char *buf2) +{ + long i; + + for (i = count - 1; i >= 0; i--) { /* EBX - count */ + unsigned long idx1 = code[i]; + unsigned long idx2 = 1 << bits[i]; + + do { + buf2[idx1] = (unsigned char) i; + idx1 += idx2; + } while (idx1 < 0x100); + } +} + +static void pkzip_gen_asc_tabs(pkzip_data_cmp * mpq_pkzip) +{ + unsigned short *code_asc = &pkzip_code_asc[0xFF]; + unsigned long acc, add; + unsigned short count; + + for (count = 0x00FF; code_asc >= pkzip_code_asc; code_asc--, count--) { + unsigned char *bits_asc = mpq_pkzip->bits_asc + count; + unsigned char bits_tmp = *bits_asc; + + if (bits_tmp <= 8) { + add = (1 << bits_tmp); + acc = *code_asc; + do { + mpq_pkzip->offs_2c34[acc] = (unsigned char) count; + acc += add; + } while (acc < 0x100); + } else { + if ((acc = (*code_asc & 0xFF)) != 0) { + mpq_pkzip->offs_2c34[acc] = 0xFF; + if (*code_asc & 0x3F) { + bits_tmp -= 4; + *bits_asc = bits_tmp; + add = (1 << bits_tmp); + acc = *code_asc >> 4; + do { + mpq_pkzip->offs_2d34[acc] = (unsigned char) count; + acc += add; + } while (acc < 0x100); + } else { + bits_tmp -= 6; + *bits_asc = bits_tmp; + add = (1 << bits_tmp); + acc = *code_asc >> 6; + do { + mpq_pkzip->offs_2e34[acc] = (unsigned char) count; + acc += add; + } while (acc < 0x80); + } + } else { + bits_tmp -= 8; + *bits_asc = bits_tmp; + add = (1 << bits_tmp); + acc = *code_asc >> 8; + do { + mpq_pkzip->offs_2eb4[acc] = (unsigned char) count; + acc += add; + } while (acc < 0x100); + } + } + } +} + +/* + * Skips given number of bits in bit buffer. Result is stored in mpq_pkzip->bit_buf + * If no data in input buffer, returns true + */ +static int pkzip_skip_bits(pkzip_data_cmp * mpq_pkzip, unsigned long bits) +{ + /* + * If number of bits required is less than number of (bits in the buffer) ? + */ + if (bits <= mpq_pkzip->extra_bits) { + mpq_pkzip->extra_bits -= bits; + mpq_pkzip->bit_buf >>= bits; + return 0; + } + + /* + * Load input buffer if necessary + */ + mpq_pkzip->bit_buf >>= mpq_pkzip->extra_bits; + if (mpq_pkzip->in_pos == mpq_pkzip->in_bytes) { + mpq_pkzip->in_pos = sizeof(mpq_pkzip->in_buf); + if ((mpq_pkzip->in_bytes = + mpq_pkzip->read_buf((char *) mpq_pkzip->in_buf, &mpq_pkzip->in_pos, + mpq_pkzip->param)) == 0) { + return 1; + } + mpq_pkzip->in_pos = 0; + } + + /* + * Update bit buffer + */ + mpq_pkzip->bit_buf |= (mpq_pkzip->in_buf[mpq_pkzip->in_pos++] << 8); + mpq_pkzip->bit_buf >>= (bits - mpq_pkzip->extra_bits); + mpq_pkzip->extra_bits = (mpq_pkzip->extra_bits - bits) + 8; + return 0; +} + +/* + * Decompress the imploded data using coded literals. + * Returns: 0x000 - 0x0FF : One byte from compressed file. + * 0x100 - 0x305 : Copy previous block (0x100 = 1 byte) + * 0x306 : Out of buffer (?) + */ +static unsigned long pkzip_explode_lit(pkzip_data_cmp * mpq_pkzip) +{ + unsigned long bits; /* Number of bits to skip */ + unsigned long value; /* Position in buffers */ + + /* + * Test the current bit in byte buffer. If is not set, simply return the next byte. + */ + if (mpq_pkzip->bit_buf & 1) { + + /* + * Skip current bit in the buffer. + */ + if (pkzip_skip_bits(mpq_pkzip, 1)) { + return 0x306; + } + + /* + * The next bits are position in buffers. + */ + value = mpq_pkzip->pos2[(mpq_pkzip->bit_buf & 0xFF)]; + + /* + * Get number of bits to skip + */ + if (pkzip_skip_bits(mpq_pkzip, mpq_pkzip->slen_bits[value])) { + return 0x306; + } + if ((bits = mpq_pkzip->clen_bits[value]) != 0) { + unsigned long val2 = mpq_pkzip->bit_buf & ((1 << bits) - 1); + + if (pkzip_skip_bits(mpq_pkzip, bits)) { + if ((value + val2) != 0x10E) { + return 0x306; + } + } + value = mpq_pkzip->len_base[value] + val2; + } + return value + 0x100; /* Return number of bytes to repeat */ + } + + /* + * Skip one bit + */ + if (pkzip_skip_bits(mpq_pkzip, 1)) { + return 0x306; + } + + /* + * If the binary compression type, read 8 bits and return them as one byte. + */ + if (mpq_pkzip->cmp_type == PKZIP_CMP_BINARY) { + value = mpq_pkzip->bit_buf & 0xFF; + if (pkzip_skip_bits(mpq_pkzip, 8)) { + return 0x306; + } + return value; + } + + /* + * When ASCII compression ... + */ + if (mpq_pkzip->bit_buf & 0xFF) { + value = mpq_pkzip->offs_2c34[mpq_pkzip->bit_buf & 0xFF]; + if (value == 0xFF) { + if (mpq_pkzip->bit_buf & 0x3F) { + if (pkzip_skip_bits(mpq_pkzip, 4)) { + return 0x306; + } + value = mpq_pkzip->offs_2d34[mpq_pkzip->bit_buf & 0xFF]; + } else { + if (pkzip_skip_bits(mpq_pkzip, 6)) { + return 0x306; + } + value = mpq_pkzip->offs_2e34[mpq_pkzip->bit_buf & 0x7F]; + } + } + } else { + if (pkzip_skip_bits(mpq_pkzip, 8)) { + return 0x306; + } + value = mpq_pkzip->offs_2eb4[mpq_pkzip->bit_buf & 0xFF]; + } + return pkzip_skip_bits(mpq_pkzip, mpq_pkzip->bits_asc[value]) ? 0x306 : value; +} + +/* + * Retrieves the number of bytes to move back. + */ +static unsigned long pkzip_explode_dist(pkzip_data_cmp * mpq_pkzip, unsigned long length) +{ + unsigned long pos = mpq_pkzip->pos1[(mpq_pkzip->bit_buf & 0xFF)]; + unsigned long skip = mpq_pkzip->dist_bits[pos]; /* Number of bits to skip */ + + /* + * Skip the appropriate number of bits + */ + if (pkzip_skip_bits(mpq_pkzip, skip) == 1) { + return 0; + } + if (length == 2) { + pos = (pos << 2) | (mpq_pkzip->bit_buf & 0x03); + if (pkzip_skip_bits(mpq_pkzip, 2) == 1) { + return 0; + } + } else { + pos = (pos << mpq_pkzip->dsize_bits) | (mpq_pkzip->bit_buf & mpq_pkzip->dsize_mask); + + /* + * Skip the bits + */ + if (pkzip_skip_bits(mpq_pkzip, mpq_pkzip->dsize_bits) == 1) { + return 0; + } + } + return pos + 1; +} + +static unsigned long pkzip_expand(pkzip_data_cmp * mpq_pkzip) +{ + unsigned int copy_bytes; /* Number of bytes to copy */ + unsigned long one_byte; /* One byte from compressed file */ + unsigned long result; + + mpq_pkzip->out_pos = 0x1000; /* Initialize output buffer position */ + + /* + * If end of data or error, terminate decompress + */ + while ((result = one_byte = pkzip_explode_lit(mpq_pkzip)) < 0x305) { + + /* + * If one byte is greater than 0x100, means "Repeat n - 0xFE bytes" + */ + if (one_byte >= 0x100) { + unsigned char *source; /* ECX */ + unsigned char *target; /* EDX */ + unsigned long copy_length = one_byte - 0xFE; + unsigned long move_back; + + /* + * Get length of data to copy + */ + if ((move_back = pkzip_explode_dist(mpq_pkzip, copy_length)) == 0) { + result = 0x306; + break; + } + + /* + * Target and source pointer + */ + target = &mpq_pkzip->out_buf[mpq_pkzip->out_pos]; + source = target - move_back; + mpq_pkzip->out_pos += copy_length; + while (copy_length-- > 0) { + *target++ = *source++; + } + } else { + mpq_pkzip->out_buf[mpq_pkzip->out_pos++] = (unsigned char) one_byte; + } + + /* + * If number of extracted bytes has reached 1/2 of output buffer, + * flush output buffer. + */ + if (mpq_pkzip->out_pos >= 0x2000) { + + /* + * Copy decompressed data into user buffer. + */ + copy_bytes = 0x1000; + mpq_pkzip->write_buf((char *) &mpq_pkzip->out_buf[0x1000], ©_bytes, + mpq_pkzip->param); + + /* + * If there are some data left, keep them alive + */ + memcpy(mpq_pkzip->out_buf, &mpq_pkzip->out_buf[0x1000], mpq_pkzip->out_pos - 0x1000); + mpq_pkzip->out_pos -= 0x1000; + } + } + copy_bytes = mpq_pkzip->out_pos - 0x1000; + mpq_pkzip->write_buf((char *) &mpq_pkzip->out_buf[0x1000], ©_bytes, mpq_pkzip->param); + return result; +} + +/* + * Main exploding function. + */ +static unsigned int pkzip_explode(unsigned int (*read_buf) (char *buf, unsigned int *size, void *param), + void (*write_buf) (const char *buf, unsigned int *size, void *param), + char *work_buf, void *param) +{ + + pkzip_data_cmp *mpq_pkzip = (pkzip_data_cmp *) work_buf; + + /* + * Set the whole work buffer to zeros + */ + memset(mpq_pkzip, 0, sizeof(pkzip_data_cmp)); + + /* + * Initialize work struct and load compressed data + */ + mpq_pkzip->read_buf = read_buf; + mpq_pkzip->write_buf = write_buf; + mpq_pkzip->param = param; + mpq_pkzip->in_pos = sizeof(mpq_pkzip->in_buf); + mpq_pkzip->in_bytes = mpq_pkzip->read_buf((char *) mpq_pkzip->in_buf, &mpq_pkzip->in_pos, mpq_pkzip->param); + if (mpq_pkzip->in_bytes <= 4) { + return PKZIP_CMP_BAD_DATA; + } + mpq_pkzip->cmp_type = mpq_pkzip->in_buf[0]; /* Get the compression type */ + mpq_pkzip->dsize_bits = mpq_pkzip->in_buf[1]; /* Get the dictionary size */ + mpq_pkzip->bit_buf = mpq_pkzip->in_buf[2]; /* Initialize 16-bit bit buffer */ + mpq_pkzip->extra_bits = 0; /* Extra (over 8) bits */ + mpq_pkzip->in_pos = 3; /* Position in input buffer */ + + /* + * Test for the valid dictionary size + */ + if (4 > mpq_pkzip->dsize_bits || mpq_pkzip->dsize_bits > 6) { + return PKZIP_CMP_INV_DICTSIZE; + } + mpq_pkzip->dsize_mask = 0xFFFF >> (0x10 - mpq_pkzip->dsize_bits); /* Shifted by 'sar' + * instruction */ + if (mpq_pkzip->cmp_type != PKZIP_CMP_BINARY) { + if (mpq_pkzip->cmp_type != PKZIP_CMP_ASCII) { + return PKZIP_CMP_INV_MODE; + } + memcpy(mpq_pkzip->bits_asc, pkzip_bits_asc, sizeof(mpq_pkzip->bits_asc)); + pkzip_gen_asc_tabs(mpq_pkzip); + } + memcpy(mpq_pkzip->slen_bits, pkzip_slen_bits, sizeof(mpq_pkzip->slen_bits)); + pkzip_gen_decode_tabs(0x10, mpq_pkzip->slen_bits, pkzip_len_code, mpq_pkzip->pos2); + memcpy(mpq_pkzip->clen_bits, pkzip_clen_bits, sizeof(mpq_pkzip->clen_bits)); + memcpy(mpq_pkzip->len_base, pkzip_len_base, sizeof(mpq_pkzip->len_base)); + memcpy(mpq_pkzip->dist_bits, pkzip_dist_bits, sizeof(mpq_pkzip->dist_bits)); + pkzip_gen_decode_tabs(0x40, mpq_pkzip->dist_bits, pkzip_dist_code, mpq_pkzip->pos1); + if (pkzip_expand(mpq_pkzip) != 0x306) { + return PKZIP_CMP_NO_ERROR; + } + return PKZIP_CMP_ABORT; +} + + +/* + * Support functions for PKWARE data compression library. + * + * Function loads data from the input buffer. Used by mpq_pkzip + * "implode" and "explode" function as user-defined callback. + * Returns number of bytes loaded. + * + * char * buf - Pointer to a buffer where to store loaded data + * unsigned int * size - Max. number of bytes to read + * void * param - Custom pointer, parameter of implode/explode + */ +static unsigned int pkzip_read_input_data(char *buf, unsigned int *size, void *param) +{ + pkzip_data *info = (pkzip_data *) param; + unsigned int max_avail = (info->in_bytes - info->in_pos); + unsigned int to_read = *size; + + /* + * Check the case when not enough data available + */ + if (to_read > max_avail) { + to_read = max_avail; + } + + /* + * Load data and increment offsets + */ + memcpy(buf, info->in_buf + info->in_pos, to_read); + info->in_pos += to_read; + + return to_read; +} + +/* + * Support functions for PKWARE data compression library. + * + * Function for store output data. Used by mpq_pkzip "implode" and + * "explode" as user-defined callback. + * + * char * buf - Pointer to data to be written + * unsigned int * size - Number of bytes to write + * void * param - Custom pointer, parameter of implode/explode + */ +static void pkzip_write_output_data(const char *buf, unsigned int *size, void *param) +{ + pkzip_data *info = (pkzip_data *) param; + unsigned int max_write = (info->max_out - info->out_pos); + unsigned int to_write = *size; + + /* + * Check the case when not enough space in the output buffer + */ + if (to_write > max_write) { + to_write = max_write; + } + + /* + * Write output data and increments offsets + */ + memcpy(info->out_buf + info->out_pos, buf, to_write); + info->out_pos += to_write; +} + +static int pkzip_decompress(char *out_buf, int *out_length, const char *in_buf, int in_length) +{ + pkzip_data info; /* Data information */ + char *work_buf = malloc(PKZIP_EXP_BUFFER_SIZE); /* mpq_pkzip work buffer */ + + /* + * Fill data information structure + */ + info.in_buf = in_buf; + info.in_pos = 0; + info.in_bytes = in_length; + info.out_buf = out_buf; + info.out_pos = 0; + info.max_out = *out_length; + + /* + * Do the decompression + */ + pkzip_explode(pkzip_read_input_data, pkzip_write_output_data, work_buf, &info); + *out_length = info.out_pos; + free(work_buf); + return 0; +} + +static int adpcm_decompress_mono(char *out_buf, int *out_length, const char *in_buf, int in_length) +{ + *out_length = adpcm_decompress((unsigned char *) out_buf, *out_length, (unsigned char *) in_buf, in_length, 1); + return 1; +} + +static int adpcm_decompress_stereo(char *out_buf, int *out_length, const char *in_buf, int in_length) +{ + *out_length = adpcm_decompress((unsigned char *) out_buf, *out_length, (unsigned char *) in_buf, in_length, 2); + return 1; +} + +static int zlib_decompress(char *out_buf, int *out_length, const char *in_buf, int in_length) +{ + z_stream z; /* Stream information for zlib */ + int result; + + /* + * Fill the stream structure for zlib + */ + z.next_in = (Bytef *) in_buf; + z.avail_in = (uInt) in_length; + z.total_in = in_length; + z.next_out = (Bytef *) out_buf; + z.avail_out = *out_length; + z.total_out = 0; + z.zalloc = NULL; + z.zfree = NULL; + + /* + * Initialize the decompression structure. Storm.dll uses zlib version 1.1.3 + */ + if ((result = inflateInit(&z)) == 0) { + + /* + * Call zlib to decompress the data + */ + result = inflate(&z, Z_FINISH); + *out_length = z.total_out; + inflateEnd(&z); + } + return result; +} + +/* + * Huffmann decompression routine. The in_length parameter is not used, but needs + * to be specified due to compatibility reasons. + * + * 1500F5F0 + */ +static int huff_decompress(char *out_buf, int *out_length, const char *in_buf, int in_length) +{ + struct huffman_tree *ht = malloc(sizeof(struct huffman_tree)); + struct huffman_input_stream *is = malloc(sizeof(struct huffman_input_stream)); + struct huffman_tree_item *hi = malloc(sizeof(struct huffman_tree_item)); + memset(ht, 0, sizeof(struct huffman_tree)); + memset(is, 0, sizeof(struct huffman_input_stream)); + memset(hi, 0, sizeof(struct huffman_tree_item)); + + /* + * Initialize input stream + */ + is->bit_buf = *(unsigned long *) in_buf; + in_buf += sizeof(unsigned long); + is->in_buf = (unsigned char *) in_buf; + is->bits = 32; + + /* + * Initialize the Huffmann tree for decompression + */ + huff_init_tree(ht, hi); + + *out_length = huff_do_decompress(ht, is, (unsigned char *) out_buf, *out_length); + + free(hi); + free(is); + free(ht); + return 0; +} + +int __mpqlib_multi_decompress(char *out_buf, int *pout_length, const char *in_buf, int in_length) +{ + char *temp_buf = NULL; /* Temporary storage for decompressed data */ + char *work_buf = NULL; /* Where to store decompressed data */ + int out_length = *pout_length; /* For storage number of output bytes */ + unsigned fDecompressions1; /* Decompressions applied to the block */ + unsigned fDecompressions2; /* Just another copy of decompressions applied to the block */ + int count = 0; /* Counter for every use */ + int entries = (sizeof(dcmp_table) / sizeof(decompress_table)); + int i; + + /* + * If the input length is the same as output, do nothing. + */ + if (in_length == out_length) { + if (in_buf == out_buf) { + return 1; + } + memcpy(out_buf, in_buf, in_length); + return 1; + } + + /* + * Get applied compression types and decrement data length + */ + fDecompressions1 = fDecompressions2 = (unsigned char) *in_buf++; + in_length--; + + /* + * Search decompression table type and get all types of compression + */ + for (i = 0; i < entries; i++) { + /* + * We have to apply this decompression? + */ + if (fDecompressions1 & dcmp_table[i].mask) { + count++; + } + + /* + * Clear this flag from temporary variable. + */ + fDecompressions2 &= ~dcmp_table[i].mask; + } + + /* + * Check if there is some method unhandled + * (E.g. compressed by future versions) + */ + if (fDecompressions2 != 0) { + printf("Unknown Compression - bitfield = %08x\n", fDecompressions2); + return 0; + } + + /* + * If there is more than only one compression, we have to allocate extra buffer + */ + if (count >= 2) { + temp_buf = malloc(out_length); + } + + /* + * Apply all decompressions + */ + for (i = 0, count = 0; i < entries; i++) { + + /* + * If not used this kind of compression, skip the loop + */ + if (fDecompressions1 & dcmp_table[i].mask) { + + /* + * If odd case, use target buffer for output, otherwise use allocated tempbuf + */ + work_buf = (count++ & 1) ? temp_buf : out_buf; + out_length = *pout_length; + + /* + * Decompress buffer using corresponding function + */ + dcmp_table[i].decompress(work_buf, &out_length, in_buf, in_length); + + /* + * Move output length to src length for next compression + */ + in_length = out_length; + in_buf = work_buf; + } + } + + /* + * If output buffer is not the same like target buffer, we have to copy data + */ + if (work_buf != out_buf) { + memcpy(out_buf, in_buf, out_length); + } + *pout_length = out_length; + + /* + * Delete temporary buffer, if necessary + */ + if (temp_buf != NULL) { + free(temp_buf); + } + return 1; +} + +int __mpqlib_pkzip_decompress(char *out_buf, int *pout_length, const char *in_buf, int in_length) +{ + return pkzip_decompress(out_buf, pout_length, in_buf, in_length); +} diff --git a/extract.h b/extract.h new file mode 100644 index 0000000..d9fd0fc --- /dev/null +++ b/extract.h @@ -0,0 +1,36 @@ +/* + * mpq.h -- some default types and defines. + * + * Copyright (C) 2003 Maik Broemme + * + * This source was adepted from the C++ version of StormLib.h and + * StormPort.h included in stormlib. The C++ version belongs to + * the following authors, + * + * Ladislav Zezula + * Marko Friedemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id: extract.h,v 1.1 2007-07-05 18:05:43 pixel Exp $ + */ + +#ifndef __EXTRACT_H__ +#define __EXTRACT_H__ + +int __mpqlib_multi_decompress(char *out_buf, int *pout_length, const char *in_buf, int in_length); +int __mpqlib_pkzip_decompress(char *out_buf, int *pout_length, const char *in_buf, int in_length); + +#endif diff --git a/inttypes.h b/inttypes.h new file mode 100644 index 0000000..09944ee --- /dev/null +++ b/inttypes.h @@ -0,0 +1,14 @@ +#ifndef __INTTYPES_H__ +#define __INTTYPES_H__ + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +#endif diff --git a/mpq-bios.c b/mpq-bios.c new file mode 100644 index 0000000..e37a462 --- /dev/null +++ b/mpq-bios.c @@ -0,0 +1,227 @@ +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include + +#include "mpq-bios.h" +#include "mpq-errors.h" +#include "errors.h" +#include "inttypes.h" + +typedef struct { + /* basic version of the header. */ + char magic[4]; + uint32_t header_size; + uint32_t archive_size; + uint16_t format_version; + uint16_t sector_size; + + uint32_t hash_table_offset; + uint32_t block_table_offset; + uint32_t hash_table_entries; + uint32_t block_table_entries; + + /* extended version of header - Burning Crusade. */ + uint64_t extended_block_table_offset; + uint16_t hash_table_offset_high; + uint16_t block_table_offset_high; +} __attribute__ ((packed)) mpq_header_t; + +typedef struct { + uint32_t file_path_hasha; + uint32_t file_path_hashb; + uint16_t language; + uint16_t platform; + uint32_t file_block_index; +} __attribute__ ((packed)) mpq_hash_t; + +typedef struct { + uint32_t block_offset; + uint32_t block_size; + uint32_t file_size; + uint32_t flags; +} __attribute__ ((packed)) mpq_block_t; + +typedef struct { + uint32_t file_path_hasha; + uint32_t file_path_hashb; + uint16_t language; + uint16_t platform; + uint32_t file_block_index; +} hash_t; + +typedef struct { + uint64_t block_offset; + uint32_t block_size; + uint32_t file_size; + uint32_t flags; +} block_t; + +struct mpq_internals_t { + int fd; + int closeit; + + uint32_t header_size; + uint32_t archive_size; + uint16_t format_version; + uint32_t sector_size; + + uint64_t hash_table_offset; + uint64_t block_table_offset; + uint32_t hash_table_entries; + uint32_t block_table_entries; + + uint64_t extended_block_table_offset; + + hash_t * hashs; + block_t * blocks; +}; + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +mpq_archive_t * mpqlib_open_archive(const char * fname) { + int fd; + mpq_archive_t * r; + + if ((fd = open(fname, O_RDONLY | O_BINARY)) == -1) { + __mpqlib_errno = MPQLIB_ERROR_OPEN; + return NULL; + } + + r = mpqlib_reopen_archive(fd); + if (r) { + r->mpq_i->closeit = 1; + } else { + close(fd); + } + + return r; +} + +#define STD_HEADER_SIZE 0x20 +#define EXT_HEADER_SIZE 0x0C + +#define no_mpq() { \ + __mpqlib_errno = MPQLIB_ERROR_NOT_MPQ_ARCHIVE; \ + free(mpq_i); \ + free(mpq_a); \ + return NULL; \ +} + +#define read_error() { \ + __mpqlib_errno = MPQLIB_ERROR_READ; \ + free_archive(mpq_a); \ + free(mpq_hashs); \ + free(mpq_blocks); \ + return NULL; \ +} + +static void free_archive(mpq_archive_t * mpq_a) { + free(mpq_a->mpq_i->hashs); + free(mpq_a->mpq_i->blocks); + free(mpq_a->mpq_i); + free(mpq_a); +} + +static int read_data(mpq_archive_t * mpq_a, void * buf, size_t l) { + if (read(mpq_a->mpq_i->fd, buf, l) != l) + return 0; + return 1; +} + +mpq_archive_t * mpqlib_reopen_archive(int fd) { + struct mpq_internals_t * mpq_i; + mpq_archive_t * mpq_a; + mpq_header_t mpq_h; + mpq_hash_t * mpq_hashs; + mpq_block_t * mpq_blocks; + + if (!(mpq_i = (struct mpq_internals_t *) malloc(sizeof(struct mpq_internals_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + return NULL; + } + + if (!(mpq_a = (mpq_archive_t *) malloc(sizeof(mpq_archive_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_i); + return NULL; + } + + mpq_i->closeit = 0; + mpq_i->fd = fd; + mpq_a->mpq_i = mpq_i; + + if (read(fd, &mpq_h, STD_HEADER_SIZE) != STD_HEADER_SIZE) + no_mpq(); + + if (strncmp(mpq_h.magic, "MPQ\x1a", 4)) + no_mpq(); + + if ((mpq_h.format_version | 1) != 1) + no_mpq(); + + if (mpq_h.format_version == 1) { + if (read(fd, ((char *)(&mpq_h)) + STD_HEADER_SIZE, EXT_HEADER_SIZE) != EXT_HEADER_SIZE) + no_mpq(); + } else { + mpq_h.extended_block_table_offset = 0; + mpq_h.hash_table_offset_high = 0; + mpq_h.block_table_offset_high = 0; + } + + mpq_i->header_size = mpq_h.header_size; + mpq_i->archive_size = mpq_h.archive_size; + mpq_i->format_version = mpq_h.format_version; + mpq_i->sector_size = 0x200 << mpq_h.sector_size; + + mpq_i->hash_table_offset = (uint64_t) mpq_h.hash_table_offset + (((uint64_t) mpq_h.hash_table_offset_high) << 32); + mpq_i->block_table_offset = (uint64_t) mpq_h.block_table_offset + (((uint64_t) mpq_h.block_table_offset_high) << 32); + mpq_i->hash_table_entries = mpq_h.hash_table_entries; + mpq_i->block_table_entries = mpq_h.block_table_entries; + mpq_i->extended_block_table_offset = mpq_h.extended_block_table_offset; + + if (!(mpq_i->hashs = (hash_t *) malloc(mpq_i->hash_table_entries * sizeof(hash_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_i); + free(mpq_a); + return NULL; + } + + if (!(mpq_i->blocks = (block_t *) malloc(mpq_i->block_table_entries * sizeof(block_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_i->hashs); + free(mpq_i); + free(mpq_a); + return NULL; + } + + if (!(mpq_hashs = (mpq_hash_t *) malloc(mpq_i->hash_table_entries * sizeof(mpq_hash_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free_archive(mpq_a); + return NULL; + } + + if (!(mpq_blocks = (mpq_block_t *) malloc(mpq_i->block_table_entries * sizeof(mpq_block_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_hashs); + free_archive(mpq_a); + return NULL; + } + + lseek64(fd, mpq_i->hash_table_offset, SEEK_SET); + + if (!read_data(mpq_a, mpq_hashs, mpq_i->hash_table_entries * sizeof(mpq_hash_t))) + read_error(); + + if (!read_data(mpq_a, mpq_blocks, mpq_i->block_table_entries * sizeof(mpq_block_t))) + read_error(); + + // decrypt everything now. + + return mpq_a; +} diff --git a/mpq-bios.h b/mpq-bios.h new file mode 100644 index 0000000..dd3c634 --- /dev/null +++ b/mpq-bios.h @@ -0,0 +1,23 @@ +#ifndef __MPQ_BIOS_H__ +#define __MPQ_BIOS_H__ + +struct mpq_internals_t; + +typedef struct { + char * name; + + struct mpq_internals_t * mpq_i; +} mpq_archive_t; + +#ifdef __cplusplus +extern "C" { +#endif + +mpq_archive_t * mpqlib_open_archive(const char * fname); +mpq_archive_t * mpqlib_reopen_archive(int fd); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mpq-errors.c b/mpq-errors.c new file mode 100644 index 0000000..e0b8655 --- /dev/null +++ b/mpq-errors.c @@ -0,0 +1,22 @@ +#include "mpq-errors.h" +#include "errors.h" + +static const char * error_strings[] = { + "No error.", + "Error opening file.", + "File specified isn't an MPQ archive.", + "Not enough memory.", + "File read error.", +}; + +static const char * wrong_errno = "Invalid error number - internal error."; + +int mpqlib_errno() { + return __mpqlib_errno; +} + +const char * mpqlib_error() { + if ((__mpqlib_errno < 0) || (__mpqlib_errno >= MPQLIB_ERRORS_MAX)) + return wrong_errno; + return error_strings[__mpqlib_errno]; +} diff --git a/mpq-errors.h b/mpq-errors.h new file mode 100644 index 0000000..e7da789 --- /dev/null +++ b/mpq-errors.h @@ -0,0 +1,25 @@ +#ifndef __MPQ_ERRORS_H__ +#define __MPQ_ERRORS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +int mpqlib_errno(); +const char * mpqlib_error(); + +#ifdef __cplusplus +} +#endif + +enum { + MPQLIB_ERROR_NO_ERROR = 0, + MPQLIB_ERROR_OPEN, + MPQLIB_ERROR_NOT_MPQ_ARCHIVE, + MPQLIB_ERROR_MEMORY, + MPQLIB_ERROR_READ, + + MPQLIB_ERRORS_MAX +}; + +#endif -- cgit v1.2.3