summaryrefslogtreecommitdiff
path: root/mpq-bios.c
diff options
context:
space:
mode:
authorpixel <pixel>2007-07-05 18:05:43 +0000
committerpixel <pixel>2007-07-05 18:05:43 +0000
commite12fcb08a14de1f5738e0162bba50cdbf87dee47 (patch)
tree48ed11b4e908a1760d0bd0ddce96c05d2a896e13 /mpq-bios.c
First import...
Diffstat (limited to 'mpq-bios.c')
-rw-r--r--mpq-bios.c227
1 files changed, 227 insertions, 0 deletions
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;
+}