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  }; | 
