Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * High-level PhysicsFS archiver for simple unpacked file formats.
3
 *
4
 * This is a framework that basic archivers build on top of. It's for simple
5
 *  formats that can just hand back a list of files and the offsets of their
6
 *  uncompressed data. There are an alarming number of formats like this.
7
 *
8
 * RULES: Archive entries must be uncompressed. Dirs and files allowed, but no
9
 *  symlinks, etc. We can relax some of these rules as necessary.
10
 *
11
 * Please see the file LICENSE.txt in the source's root directory.
12
 *
13
 *  This file written by Ryan C. Gordon.
14
 */
15
 
16
#define __PHYSICSFS_INTERNAL__
17
#include "physfs_internal.h"
18
 
19
typedef struct
20
{
21
    __PHYSFS_DirTree tree;
22
    PHYSFS_Io *io;
23
} UNPKinfo;
24
 
25
typedef struct
26
{
27
    __PHYSFS_DirTreeEntry tree;
28
    PHYSFS_uint64 startPos;
29
    PHYSFS_uint64 size;
30
    PHYSFS_sint64 ctime;
31
    PHYSFS_sint64 mtime;
32
} UNPKentry;
33
 
34
typedef struct
35
{
36
    PHYSFS_Io *io;
37
    UNPKentry *entry;
38
    PHYSFS_uint32 curPos;
39
} UNPKfileinfo;
40
 
41
 
42
void UNPK_closeArchive(void *opaque)
43
{
44
    UNPKinfo *info = ((UNPKinfo *) opaque);
45
    if (info)
46
    {
47
        __PHYSFS_DirTreeDeinit(&info->tree);
48
 
49
        if (info->io)
50
            info->io->destroy(info->io);
51
 
52
        allocator.Free(info);
53
    } /* if */
54
} /* UNPK_closeArchive */
55
 
56
void UNPK_abandonArchive(void *opaque)
57
{
58
    UNPKinfo *info = ((UNPKinfo *) opaque);
59
    if (info)
60
    {
61
        info->io = NULL;
62
        UNPK_closeArchive(info);
63
    } /* if */
64
} /* UNPK_abandonArchive */
65
 
66
static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
67
{
68
    UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
69
    const UNPKentry *entry = finfo->entry;
70
    const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
71
    PHYSFS_sint64 rc;
72
 
73
    if (bytesLeft < len)
74
        len = bytesLeft;
75
 
76
    rc = finfo->io->read(finfo->io, buffer, len);
77
    if (rc > 0)
78
        finfo->curPos += (PHYSFS_uint32) rc;
79
 
80
    return rc;
81
} /* UNPK_read */
82
 
83
 
84
static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
85
{
86
    BAIL(PHYSFS_ERR_READ_ONLY, -1);
87
} /* UNPK_write */
88
 
89
 
90
static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
91
{
92
    return ((UNPKfileinfo *) io->opaque)->curPos;
93
} /* UNPK_tell */
94
 
95
 
96
static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
97
{
98
    UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
99
    const UNPKentry *entry = finfo->entry;
100
    int rc;
101
 
102
    BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
103
    rc = finfo->io->seek(finfo->io, entry->startPos + offset);
104
    if (rc)
105
        finfo->curPos = (PHYSFS_uint32) offset;
106
 
107
    return rc;
108
} /* UNPK_seek */
109
 
110
 
111
static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io)
112
{
113
    const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
114
    return ((PHYSFS_sint64) finfo->entry->size);
115
} /* UNPK_length */
116
 
117
 
118
static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io)
119
{
120
    UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque;
121
    PHYSFS_Io *io = NULL;
122
    PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
123
    UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
124
    GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
125
    GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
126
 
127
    io = origfinfo->io->duplicate(origfinfo->io);
128
    if (!io) goto UNPK_duplicate_failed;
129
    finfo->io = io;
130
    finfo->entry = origfinfo->entry;
131
    finfo->curPos = 0;
132
    memcpy(retval, _io, sizeof (PHYSFS_Io));
133
    retval->opaque = finfo;
134
    return retval;
135
 
136
UNPK_duplicate_failed:
137
    if (finfo != NULL) allocator.Free(finfo);
138
    if (retval != NULL) allocator.Free(retval);
139
    if (io != NULL) io->destroy(io);
140
    return NULL;
141
} /* UNPK_duplicate */
142
 
143
static int UNPK_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
144
 
145
static void UNPK_destroy(PHYSFS_Io *io)
146
{
147
    UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
148
    finfo->io->destroy(finfo->io);
149
    allocator.Free(finfo);
150
    allocator.Free(io);
151
} /* UNPK_destroy */
152
 
153
 
154
static const PHYSFS_Io UNPK_Io =
155
{
156
    CURRENT_PHYSFS_IO_API_VERSION, NULL,
157
    UNPK_read,
158
    UNPK_write,
159
    UNPK_seek,
160
    UNPK_tell,
161
    UNPK_length,
162
    UNPK_duplicate,
163
    UNPK_flush,
164
    UNPK_destroy
165
};
166
 
167
 
168
static inline UNPKentry *findEntry(UNPKinfo *info, const char *path)
169
{
170
    return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path);
171
} /* findEntry */
172
 
173
 
174
PHYSFS_Io *UNPK_openRead(void *opaque, const char *name)
175
{
176
    PHYSFS_Io *retval = NULL;
177
    UNPKinfo *info = (UNPKinfo *) opaque;
178
    UNPKfileinfo *finfo = NULL;
179
    UNPKentry *entry = findEntry(info, name);
180
 
181
    BAIL_IF_ERRPASS(!entry, NULL);
182
    BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
183
 
184
    retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
185
    GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
186
 
187
    finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
188
    GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
189
 
190
    finfo->io = info->io->duplicate(info->io);
191
    GOTO_IF_ERRPASS(!finfo->io, UNPK_openRead_failed);
192
 
193
    if (!finfo->io->seek(finfo->io, entry->startPos))
194
        goto UNPK_openRead_failed;
195
 
196
    finfo->curPos = 0;
197
    finfo->entry = entry;
198
 
199
    memcpy(retval, &UNPK_Io, sizeof (*retval));
200
    retval->opaque = finfo;
201
    return retval;
202
 
203
UNPK_openRead_failed:
204
    if (finfo != NULL)
205
    {
206
        if (finfo->io != NULL)
207
            finfo->io->destroy(finfo->io);
208
        allocator.Free(finfo);
209
    } /* if */
210
 
211
    if (retval != NULL)
212
        allocator.Free(retval);
213
 
214
    return NULL;
215
} /* UNPK_openRead */
216
 
217
 
218
PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name)
219
{
220
    BAIL(PHYSFS_ERR_READ_ONLY, NULL);
221
} /* UNPK_openWrite */
222
 
223
 
224
PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name)
225
{
226
    BAIL(PHYSFS_ERR_READ_ONLY, NULL);
227
} /* UNPK_openAppend */
228
 
229
 
230
int UNPK_remove(void *opaque, const char *name)
231
{
232
    BAIL(PHYSFS_ERR_READ_ONLY, 0);
233
} /* UNPK_remove */
234
 
235
 
236
int UNPK_mkdir(void *opaque, const char *name)
237
{
238
    BAIL(PHYSFS_ERR_READ_ONLY, 0);
239
} /* UNPK_mkdir */
240
 
241
 
242
int UNPK_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
243
{
244
    UNPKinfo *info = (UNPKinfo *) opaque;
245
    const UNPKentry *entry = findEntry(info, path);
246
 
247
    BAIL_IF_ERRPASS(!entry, 0);
248
 
249
    if (entry->tree.isdir)
250
    {
251
        stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
252
        stat->filesize = 0;
253
    } /* if */
254
    else
255
    {
256
        stat->filetype = PHYSFS_FILETYPE_REGULAR;
257
        stat->filesize = entry->size;
258
    } /* else */
259
 
260
    stat->modtime = entry->mtime;
261
    stat->createtime = entry->ctime;
262
    stat->accesstime = -1;
263
    stat->readonly = 1;
264
 
265
    return 1;
266
} /* UNPK_stat */
267
 
268
 
269
void *UNPK_addEntry(void *opaque, char *name, const int isdir,
270
                    const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime,
271
                    const PHYSFS_uint64 pos, const PHYSFS_uint64 len)
272
{
273
    UNPKinfo *info = (UNPKinfo *) opaque;
274
    UNPKentry *entry;
275
 
276
    entry = (UNPKentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir);
277
    BAIL_IF_ERRPASS(!entry, NULL);
278
 
279
    entry->startPos = isdir ? 0 : pos;
280
    entry->size = isdir ? 0 : len;
281
    entry->ctime = ctime;
282
    entry->mtime = mtime;
283
 
284
    return entry;
285
} /* UNPK_addEntry */
286
 
287
 
288
void *UNPK_openArchive(PHYSFS_Io *io)
289
{
290
    UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
291
    BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
292
 
293
    if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry)))
294
    {
295
        allocator.Free(info);
296
        return NULL;
297
    } /* if */
298
 
299
    info->io = io;
300
 
301
    return info;
302
} /* UNPK_openArchive */
303
 
304
/* end of physfs_archiver_unpacked.c ... */
305