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, 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 | |