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 |