diff options
-rw-r--r-- | mpq-bios.c | 116 | ||||
-rw-r--r-- | mpq-bios.h | 1 | ||||
-rw-r--r-- | mpq-errors.c | 2 | ||||
-rw-r--r-- | mpq-errors.h | 2 |
4 files changed, 109 insertions, 12 deletions
@@ -29,6 +29,10 @@ #include "int-bios.h" #include "errors.h" +#ifndef MAX +#define MAX(x,y) (((x)<(y))?(y):(x)) +#endif + /* * MPQ header. */ @@ -112,6 +116,9 @@ typedef struct { struct mpq_archive_t { int fd; int closeit; + int for_write; + + uint64_t last_block; uint32_t header_size; uint32_t archive_size; @@ -149,6 +156,27 @@ struct mpq_archive_t * mpqlib_open_archive(const char * fname) { r = mpqlib_reopen_archive(fd); if (r) { r->closeit = 1; + r->for_write = 0; + } else { + close(fd); + } + + return r; +} + +struct mpq_archive_t * mpqlib_open_archive_for_writing(const char * fname) { + int fd; + struct mpq_archive_t * r; + + if ((fd = open(fname, O_RDWR | O_LARGEFILE | O_BINARY)) == -1) { + __mpqlib_errno = MPQLIB_ERROR_OPEN; + return NULL; + } + + r = mpqlib_reopen_archive(fd); + if (r) { + r->closeit = 1; + r->for_write = 1; } else { close(fd); } @@ -205,6 +233,10 @@ struct mpq_archive_t * mpqlib_reopen_archive(int fd) { mpq_block_t * mpq_blocks; uint16_t * mpq_extblocks = NULL; int i; + uint64_t last_block = 0; +#ifndef WIN32 + int flags; +#endif __mpqlib_errno = MPQLIB_ERROR_NO_ERROR; @@ -218,6 +250,13 @@ struct mpq_archive_t * mpqlib_reopen_archive(int fd) { mpq_a->closeit = 0; mpq_a->fd = fd; + mpq_a->for_write = 0; +#ifndef WIN32 + flags = fcntl(fd, F_GETFL); + if ((flags != -1) && (flags & O_RDWR)) { + mpq_a->for_write = 1; + } +#endif /* Reading the main header, and doing basic checks. */ if (read(fd, &mpq_h, STD_HEADER_SIZE) != STD_HEADER_SIZE) @@ -330,8 +369,11 @@ struct mpq_archive_t * mpqlib_reopen_archive(int fd) { mpq_a->blocks[i].block_size = mpq_blocks[i].block_size; mpq_a->blocks[i].file_size = mpq_blocks[i].file_size; mpq_a->blocks[i].flags = mpq_blocks[i].flags; + last_block = MAX(last_block, mpq_a->blocks[i].block_offset + mpq_a->blocks[i].block_size); } + mpq_a->last_block = last_block; + /* All done, let's clean up and exit. */ if (mpq_extblocks) free(mpq_extblocks); @@ -377,18 +419,31 @@ void mpqlib_printtables(struct mpq_archive_t * mpq_a) { } } -int mpqlib_find_hash_entry_by_name(struct mpq_archive_t * mpq_a, const char * name, uint32_t language, uint32_t platform) { - uint32_t h, hA, hB; - - h = mpqlib_hash_filename(name); - hA = mpqlib_hashA_filename(name); - hB = mpqlib_hashB_filename(name); +static hash_t * mpqlib_locate_hash_entry(struct mpq_archive_t * mpq_a, uint32_t h, uint32_t hA, uint32_t hB, uint32_t language, uint32_t platform) { + uint32_t i; + + /* The hash table is a true one, pre-built. We can access it as-it, speeding up the searches drastically. */ + for (i = h & (mpq_a->hash_table_entries - 1); i < mpq_a->hash_table_entries; i++) { + if ((mpq_a->hashs[i].file_path_hasha == hA) && + (mpq_a->hashs[i].file_path_hashb == hB) && + (mpq_a->hashs[i].language == language) && + (mpq_a->hashs[i].platform == platform)) { + return mpq_a->hashs + i; + } + if (mpq_a->hashs[i].file_block_index == 0xffffffff) + return NULL; + } - return mpqlib_find_hash_entry_by_hash(mpq_a, h, hA, hB, language, platform); + return NULL; } - -int mpqlib_find_hash_entry_by_hash(struct mpq_archive_t * mpq_a, uint32_t h, uint32_t hA, uint32_t hB, uint32_t language, uint32_t platform) { - int i; + +static hash_t * mpqlib_find_free_hash_entry_by_hash(struct mpq_archive_t * mpq_a, uint32_t h, uint32_t hA, uint32_t hB, uint32_t language, uint32_t platform) { + uint32_t i; + + if (!mpq_a->for_write) { + __mpqlib_errno = MPQLIB_ERROR_READONLY; + return NULL; + } /* The hash table is a true one, pre-built. We can access it as-it, speeding up the searches drastically. */ for (i = h & (mpq_a->hash_table_entries - 1); i < mpq_a->hash_table_entries; i++) { @@ -396,12 +451,49 @@ int mpqlib_find_hash_entry_by_hash(struct mpq_archive_t * mpq_a, uint32_t h, uin (mpq_a->hashs[i].file_path_hashb == hB) && (mpq_a->hashs[i].language == language) && (mpq_a->hashs[i].platform == platform)) { - return mpq_a->hashs[i].file_block_index; + /* Full collision ?! */ + __mpqlib_errno = MPQLIB_ERROR_UNKNOWN; + return NULL; } if (mpq_a->hashs[i].file_block_index == 0xffffffff) - break; + return mpq_a->hashs + i; } + __mpqlib_errno = MPQLIB_ERROR_TOO_MANY_FILES; + return NULL; +} + +static hash_t * mpqlib_find_free_hash_entry_by_name(struct mpq_archive_t * mpq_a, const char * name, uint32_t language, uint32_t platform) { + if (!mpq_a->for_write) { + __mpqlib_errno = MPQLIB_ERROR_READONLY; + return NULL; + } + + uint32_t h, hA, hB; + + h = mpqlib_hash_filename(name); + hA = mpqlib_hashA_filename(name); + hB = mpqlib_hashB_filename(name); + + return mpqlib_find_free_hash_entry_by_hash(mpq_a, h, hA, hB, language, platform); +} + +int mpqlib_find_hash_entry_by_name(struct mpq_archive_t * mpq_a, const char * name, uint32_t language, uint32_t platform) { + uint32_t h, hA, hB; + + h = mpqlib_hash_filename(name); + hA = mpqlib_hashA_filename(name); + hB = mpqlib_hashB_filename(name); + + return mpqlib_find_hash_entry_by_hash(mpq_a, h, hA, hB, language, platform); +} + +int mpqlib_find_hash_entry_by_hash(struct mpq_archive_t * mpq_a, uint32_t h, uint32_t hA, uint32_t hB, uint32_t language, uint32_t platform) { + hash_t * hash = mpqlib_locate_hash_entry(mpq_a, h, hA, hB, language, platform); + + if (hash) { + return hash->file_block_index; + } return -1; } @@ -30,6 +30,7 @@ extern "C" { void mpqlib_init(); struct mpq_archive_t * mpqlib_open_archive(const char * fname); +struct mpq_archive_t * mpqlib_open_archive_for_writing(const char * fname); struct mpq_archive_t * mpqlib_reopen_archive(int fd); void mpqlib_printtables(struct mpq_archive_t *); void mpqlib_close_archive(struct mpq_archive_t *); diff --git a/mpq-errors.c b/mpq-errors.c index 856ae12..b052e3b 100644 --- a/mpq-errors.c +++ b/mpq-errors.c @@ -15,6 +15,8 @@ static const char * error_strings[] = { "Imploded compression not supported yet.", "File isn't flagged as compressed - not supported yet.", "Compression error.", + "MPQ archive in read-only mode.", + "Too many files.", }; static const char * wrong_errno = "Invalid error number - internal error."; diff --git a/mpq-errors.h b/mpq-errors.h index d9918c4..ada05f1 100644 --- a/mpq-errors.h +++ b/mpq-errors.h @@ -26,6 +26,8 @@ enum { MPQLIB_ERROR_NOT_SUPPORTED, MPQLIB_ERROR_NOT_COMPRESSED, MPQLIB_ERROR_COMPRESSION, + MPQLIB_ERROR_READONLY, + MPQLIB_ERROR_TOO_MANY_FILES, MPQLIB_ERRORS_MAX }; |