diff options
author | pixel <pixel> | 2007-07-05 18:05:43 +0000 |
---|---|---|
committer | pixel <pixel> | 2007-07-05 18:05:43 +0000 |
commit | e12fcb08a14de1f5738e0162bba50cdbf87dee47 (patch) | |
tree | 48ed11b4e908a1760d0bd0ddce96c05d2a896e13 |
First import...
-rw-r--r-- | MPQCryptography.c | 249 | ||||
-rw-r--r-- | MPQCryptography.h | 48 | ||||
-rw-r--r-- | Makefile | 32 | ||||
-rw-r--r-- | errors.c | 4 | ||||
-rw-r--r-- | errors.h | 6 | ||||
-rw-r--r-- | extract.c | 2007 | ||||
-rw-r--r-- | extract.h | 36 | ||||
-rw-r--r-- | inttypes.h | 14 | ||||
-rw-r--r-- | mpq-bios.c | 227 | ||||
-rw-r--r-- | mpq-bios.h | 23 | ||||
-rw-r--r-- | mpq-errors.c | 22 | ||||
-rw-r--r-- | mpq-errors.h | 25 |
12 files changed, 2693 insertions, 0 deletions
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 <assert.h> +#include <string.h> +#include <zlib.h> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/md5.h> +#include <openssl/obj_mac.h> +#include <openssl/sha.h> + +#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 <stdbool.h> +#include <stdint.h> +#include <openssl/rsa.h> + +#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 <mbroemme@plusserver.de> + * + * 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 <stdlib.h> +#include <stdio.h> +#include <zlib.h> +#include <memory.h> + +#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 <mbroemme@plusserver.de> + * + * 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 <ladik.zezula.net> + * Marko Friedemann <marko.friedemann@bmx-chemnitz.de> + * + * 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 <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#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 |