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