summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MPQCryptography.c249
-rw-r--r--MPQCryptography.h48
-rw-r--r--Makefile32
-rw-r--r--errors.c4
-rw-r--r--errors.h6
-rw-r--r--extract.c2007
-rw-r--r--extract.h36
-rw-r--r--inttypes.h14
-rw-r--r--mpq-bios.c227
-rw-r--r--mpq-bios.h23
-rw-r--r--mpq-errors.c22
-rw-r--r--mpq-errors.h25
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], &copy_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], &copy_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