From e12fcb08a14de1f5738e0162bba50cdbf87dee47 Mon Sep 17 00:00:00 2001 From: pixel Date: Thu, 5 Jul 2007 18:05:43 +0000 Subject: First import... --- mpq-bios.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 mpq-bios.c (limited to 'mpq-bios.c') diff --git a/mpq-bios.c b/mpq-bios.c new file mode 100644 index 0000000..e37a462 --- /dev/null +++ b/mpq-bios.c @@ -0,0 +1,227 @@ +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include + +#include "mpq-bios.h" +#include "mpq-errors.h" +#include "errors.h" +#include "inttypes.h" + +typedef struct { + /* basic version of the header. */ + char magic[4]; + uint32_t header_size; + uint32_t archive_size; + uint16_t format_version; + uint16_t sector_size; + + uint32_t hash_table_offset; + uint32_t block_table_offset; + uint32_t hash_table_entries; + uint32_t block_table_entries; + + /* extended version of header - Burning Crusade. */ + uint64_t extended_block_table_offset; + uint16_t hash_table_offset_high; + uint16_t block_table_offset_high; +} __attribute__ ((packed)) mpq_header_t; + +typedef struct { + uint32_t file_path_hasha; + uint32_t file_path_hashb; + uint16_t language; + uint16_t platform; + uint32_t file_block_index; +} __attribute__ ((packed)) mpq_hash_t; + +typedef struct { + uint32_t block_offset; + uint32_t block_size; + uint32_t file_size; + uint32_t flags; +} __attribute__ ((packed)) mpq_block_t; + +typedef struct { + uint32_t file_path_hasha; + uint32_t file_path_hashb; + uint16_t language; + uint16_t platform; + uint32_t file_block_index; +} hash_t; + +typedef struct { + uint64_t block_offset; + uint32_t block_size; + uint32_t file_size; + uint32_t flags; +} block_t; + +struct mpq_internals_t { + int fd; + int closeit; + + uint32_t header_size; + uint32_t archive_size; + uint16_t format_version; + uint32_t sector_size; + + uint64_t hash_table_offset; + uint64_t block_table_offset; + uint32_t hash_table_entries; + uint32_t block_table_entries; + + uint64_t extended_block_table_offset; + + hash_t * hashs; + block_t * blocks; +}; + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +mpq_archive_t * mpqlib_open_archive(const char * fname) { + int fd; + mpq_archive_t * r; + + if ((fd = open(fname, O_RDONLY | O_BINARY)) == -1) { + __mpqlib_errno = MPQLIB_ERROR_OPEN; + return NULL; + } + + r = mpqlib_reopen_archive(fd); + if (r) { + r->mpq_i->closeit = 1; + } else { + close(fd); + } + + return r; +} + +#define STD_HEADER_SIZE 0x20 +#define EXT_HEADER_SIZE 0x0C + +#define no_mpq() { \ + __mpqlib_errno = MPQLIB_ERROR_NOT_MPQ_ARCHIVE; \ + free(mpq_i); \ + free(mpq_a); \ + return NULL; \ +} + +#define read_error() { \ + __mpqlib_errno = MPQLIB_ERROR_READ; \ + free_archive(mpq_a); \ + free(mpq_hashs); \ + free(mpq_blocks); \ + return NULL; \ +} + +static void free_archive(mpq_archive_t * mpq_a) { + free(mpq_a->mpq_i->hashs); + free(mpq_a->mpq_i->blocks); + free(mpq_a->mpq_i); + free(mpq_a); +} + +static int read_data(mpq_archive_t * mpq_a, void * buf, size_t l) { + if (read(mpq_a->mpq_i->fd, buf, l) != l) + return 0; + return 1; +} + +mpq_archive_t * mpqlib_reopen_archive(int fd) { + struct mpq_internals_t * mpq_i; + mpq_archive_t * mpq_a; + mpq_header_t mpq_h; + mpq_hash_t * mpq_hashs; + mpq_block_t * mpq_blocks; + + if (!(mpq_i = (struct mpq_internals_t *) malloc(sizeof(struct mpq_internals_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + return NULL; + } + + if (!(mpq_a = (mpq_archive_t *) malloc(sizeof(mpq_archive_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_i); + return NULL; + } + + mpq_i->closeit = 0; + mpq_i->fd = fd; + mpq_a->mpq_i = mpq_i; + + if (read(fd, &mpq_h, STD_HEADER_SIZE) != STD_HEADER_SIZE) + no_mpq(); + + if (strncmp(mpq_h.magic, "MPQ\x1a", 4)) + no_mpq(); + + if ((mpq_h.format_version | 1) != 1) + no_mpq(); + + if (mpq_h.format_version == 1) { + if (read(fd, ((char *)(&mpq_h)) + STD_HEADER_SIZE, EXT_HEADER_SIZE) != EXT_HEADER_SIZE) + no_mpq(); + } else { + mpq_h.extended_block_table_offset = 0; + mpq_h.hash_table_offset_high = 0; + mpq_h.block_table_offset_high = 0; + } + + mpq_i->header_size = mpq_h.header_size; + mpq_i->archive_size = mpq_h.archive_size; + mpq_i->format_version = mpq_h.format_version; + mpq_i->sector_size = 0x200 << mpq_h.sector_size; + + mpq_i->hash_table_offset = (uint64_t) mpq_h.hash_table_offset + (((uint64_t) mpq_h.hash_table_offset_high) << 32); + mpq_i->block_table_offset = (uint64_t) mpq_h.block_table_offset + (((uint64_t) mpq_h.block_table_offset_high) << 32); + mpq_i->hash_table_entries = mpq_h.hash_table_entries; + mpq_i->block_table_entries = mpq_h.block_table_entries; + mpq_i->extended_block_table_offset = mpq_h.extended_block_table_offset; + + if (!(mpq_i->hashs = (hash_t *) malloc(mpq_i->hash_table_entries * sizeof(hash_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_i); + free(mpq_a); + return NULL; + } + + if (!(mpq_i->blocks = (block_t *) malloc(mpq_i->block_table_entries * sizeof(block_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_i->hashs); + free(mpq_i); + free(mpq_a); + return NULL; + } + + if (!(mpq_hashs = (mpq_hash_t *) malloc(mpq_i->hash_table_entries * sizeof(mpq_hash_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free_archive(mpq_a); + return NULL; + } + + if (!(mpq_blocks = (mpq_block_t *) malloc(mpq_i->block_table_entries * sizeof(mpq_block_t)))) { + __mpqlib_errno = MPQLIB_ERROR_MEMORY; + free(mpq_hashs); + free_archive(mpq_a); + return NULL; + } + + lseek64(fd, mpq_i->hash_table_offset, SEEK_SET); + + if (!read_data(mpq_a, mpq_hashs, mpq_i->hash_table_entries * sizeof(mpq_hash_t))) + read_error(); + + if (!read_data(mpq_a, mpq_blocks, mpq_i->block_table_entries * sizeof(mpq_block_t))) + read_error(); + + // decrypt everything now. + + return mpq_a; +} -- cgit v1.2.3