#define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include "MPQCryptography.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; uint16_t * mpq_extblocks = NULL; int i; /****TODO****/ /* Implement endianess a bit everywhere */ 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; } if (lseek64(fd, mpq_i->hash_table_offset, SEEK_SET) != mpq_i->hash_table_offset) read_error(); if (!read_data(mpq_a, mpq_hashs, mpq_i->hash_table_entries * sizeof(mpq_hash_t))) read_error(); if (lseek64(fd, mpq_i->block_table_offset, SEEK_SET) != mpq_i->block_table_offset) read_error(); if (!read_data(mpq_a, mpq_blocks, mpq_i->block_table_entries * sizeof(mpq_block_t))) read_error(); if (mpq_i->extended_block_table_offset) { if (lseek64(fd, mpq_i->block_table_offset, SEEK_SET)) read_error(); if (!(mpq_extblocks = (uint16_t *) malloc(mpq_i->block_table_entries * sizeof(uint16_t)))) { __mpqlib_errno = MPQLIB_ERROR_MEMORY; free_archive(mpq_a); free(mpq_hashs); free(mpq_blocks); return NULL; } if (!read_data(mpq_a, mpq_blocks, mpq_i->block_table_entries * sizeof(mpq_block_t))) { free(mpq_extblocks); read_error(); } } __mpqlib_decrypt(mpq_hashs, mpq_i->hash_table_entries * sizeof(mpq_block_t), __mpqlib_hash_cstring("(hash table)", 3), 1); __mpqlib_decrypt(mpq_blocks, mpq_i->block_table_entries * sizeof(mpq_block_t), __mpqlib_hash_cstring("(block table)", 3), 1); for (i = 0; i < mpq_i->hash_table_entries; i++) { /****TODO****/ /* Implement various checks of the hash table. */ mpq_i->hashs[i].file_path_hasha = mpq_hashs[i].file_path_hasha; mpq_i->hashs[i].file_path_hashb = mpq_hashs[i].file_path_hashb; mpq_i->hashs[i].language = mpq_hashs[i].language; mpq_i->hashs[i].platform = mpq_hashs[i].platform; mpq_i->hashs[i].file_block_index = mpq_hashs[i].file_block_index; } for (i = 0; i < mpq_i->block_table_entries; i++) { /****TODO****/ /* Implement various checks of the block table. */ mpq_i->blocks[i].block_offset = mpq_blocks[i].block_offset + (mpq_extblocks ? (((uint64_t) mpq_extblocks[i]) << 32): 0); mpq_i->blocks[i].block_size = mpq_blocks[i].block_size; mpq_i->blocks[i].file_size = mpq_blocks[i].file_size; mpq_i->blocks[i].flags = mpq_blocks[i].flags; } if (mpq_extblocks) free(mpq_extblocks); free(mpq_blocks); free(mpq_hashs); return mpq_a; }