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 |