summaryrefslogtreecommitdiff
path: root/mpq-fs.c
blob: 3a2a4b3631a944c4e006ae7f46391bc755c39f6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "lookupa.h"
#include "hashtab.h"
#include "mpq-fs.h"
#include "mpq-misc.h"

#define MAX_FNAME 2048

#define INITIAL_HASH_SIZE 10

typedef struct hash_entry_t {
    struct mpq_archive_t * mpq_a;
    int entry;
} hash_entry;

static char * strnormalize(char * _str) {
    char * str = _str;
    while (*str) {
        *str = toupper(*str);
        if (*str == '/')
            *str = '\\';
        str++;
    }
    return _str;
}

htab * hash_table = 0;

static void add_file(struct mpq_archive_t * mpq_a, char * fname) {
    int entry;
    char * nfname;

    if ((entry = mpqlib_find_hash_entry_by_name(mpq_a, fname, 0, 0)) < 0)
        return;
    
    if (!hash_table)
        hash_table = hcreate(INITIAL_HASH_SIZE);

    nfname = strnormalize(strdup(fname));    
    hadd(hash_table, (uint8_t *) nfname, strlen(nfname), NULL);
    if (!hstuff(hash_table)) {
        hstuff(hash_table) = malloc(sizeof(hash_entry));
    }
    ((hash_entry *) hstuff(hash_table))->mpq_a = mpq_a;
    ((hash_entry *) hstuff(hash_table))->entry = entry;
}

/*
 * Adds an opened archive file into the system, and try to automagically import the list file.
 */
void mpqlib_fs_add_archive(struct mpq_archive_t * mpq_a) {
    struct mpq_file_t * listfile;
    char * buffer;
    int s;
    
    if (!(listfile = mpqlib_open_filename(mpq_a, "(listfile)")))
        return;
    
    s = mpqlib_seek(listfile, 0, MPQLIB_SEEK_END);
    mpqlib_seek(listfile, 0, MPQLIB_SEEK_SET);

    if (!(buffer = (char *) malloc(s + 1))) {
        mpqlib_close(listfile);
        return;
    }
    
    buffer[s] = 0;
    mpqlib_read(listfile, buffer, s);
    mpqlib_fs_attach_listfile(mpq_a, buffer);
    
    free(buffer);
    mpqlib_close(listfile);
}

/*
 * Generalistic function to add an archive to the directory structure using a custom listfile.
 */
void mpqlib_fs_attach_listfile(struct mpq_archive_t * mpq_a, const char * listfile) {
    char fname[MAX_FNAME];
    const char * p;
    char * fnp;
    
    for (p = listfile, fnp = fname; *p; p++) {
        switch (*p) {
        /* Each entry in the listfile may be separated by CR, LF, and/or ';'. */
        case '\r':
        case '\n':
        case ';':
            *fnp = 0;
            if (fnp != fname)
                add_file(mpq_a, fname);
            fnp = fname;
            break;
        default:
            *(fnp++) = *p;
            break;
        }
    }
}

/*
 * Recursively find a file.
 */
static hash_entry * find_file(char * fname) {
    if (!hfind(hash_table, (uint8_t *) fname, strlen(fname)))
        return NULL;
    
    return (hash_entry *) hstuff(hash_table);
}

struct mpq_file_t * mpqlib_fs_open(const char * _fname) {
    char * fname = strnormalize(strdup(_fname));
    hash_entry * entry;
    
    entry = find_file(fname);
    
    free(fname);

    if (entry) {
        return mpqlib_open_file(entry->mpq_a, entry->entry);
    }
    
    return NULL;
}