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
19typedef struct
20{
21 __PHYSFS_DirTree tree;
22 PHYSFS_Io *io;
23} UNPKinfo;
24
25typedef 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
34typedef struct
35{
36 PHYSFS_Io *io;
37 UNPKentry *entry;
38 PHYSFS_uint32 curPos;
39} UNPKfileinfo;
40
41
42void 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
56void 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
66static 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
84static 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
90static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
91{
92 return ((UNPKfileinfo *) io->opaque)->curPos;
93} /* UNPK_tell */
94
95
96static 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
111static 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
118static 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
136UNPK_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
143static int UNPK_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
144
145static 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
154static 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
168static inline UNPKentry *findEntry(UNPKinfo *info, const char *path)
169{
170 return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path);
171} /* findEntry */
172
173
174PHYSFS_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
203UNPK_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
218PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name)
219{
220 BAIL(PHYSFS_ERR_READ_ONLY, NULL);
221} /* UNPK_openWrite */
222
223
224PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name)
225{
226 BAIL(PHYSFS_ERR_READ_ONLY, NULL);
227} /* UNPK_openAppend */
228
229
230int UNPK_remove(void *opaque, const char *name)
231{
232 BAIL(PHYSFS_ERR_READ_ONLY, 0);
233} /* UNPK_remove */
234
235
236int UNPK_mkdir(void *opaque, const char *name)
237{
238 BAIL(PHYSFS_ERR_READ_ONLY, 0);
239} /* UNPK_mkdir */
240
241
242int 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
269void *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
288void *UNPK_openArchive(PHYSFS_Io *io, const int case_sensitive, const int only_usascii)
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), case_sensitive, only_usascii))
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
306