2 * Copyright (C) 2003-2010 Neverball authors
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
27 * This file implements the virtual file system layer. Most file
28 * system and input/output operations are handled here. There are
29 * basically two groups of functions here: low-level functions
30 * implemented directly using the PhysicsFS 1.0 API and higher-level
31 * functions implemented using the former group.
34 /* -------------------------------------------------------------------------- */
41 int fs_init(const char *argv0)
43 if (PHYSFS_init(argv0))
45 PHYSFS_permitSymbolicLinks(1);
54 return PHYSFS_deinit();
57 const char *fs_error(void)
59 return PHYSFS_getLastError();
62 /* -------------------------------------------------------------------------- */
64 const char *fs_base_dir(void)
66 return PHYSFS_getBaseDir();
69 int fs_add_path(const char *path)
71 return PHYSFS_addToSearchPath(path, 0);
74 int fs_set_write_dir(const char *path)
76 return PHYSFS_setWriteDir(path);
79 const char *fs_get_write_dir(void)
81 return PHYSFS_getWriteDir();
84 /* -------------------------------------------------------------------------- */
86 Array fs_dir_scan(const char *path, int (*filter)(struct dir_item *))
88 return dir_scan(path, filter, PHYSFS_enumerateFiles, PHYSFS_freeList);
91 void fs_dir_free(Array items)
96 /* -------------------------------------------------------------------------- */
98 fs_file fs_open(const char *path, const char *mode)
102 assert((mode[0] == 'r' && !mode[1]) ||
103 (mode[0] == 'w' && (!mode[1] || mode[1] == '+')));
105 if ((fh = malloc(sizeof (*fh))))
110 fh->handle = PHYSFS_openRead(path);
114 fh->handle = (mode[1] == '+' ?
115 PHYSFS_openAppend(path) :
116 PHYSFS_openWrite(path));
122 PHYSFS_setBuffer(fh->handle, 0x2000);
134 int fs_close(fs_file fh)
136 if (PHYSFS_close(fh->handle))
144 /* -------------------------------------------------------------------------- */
146 int fs_mkdir(const char *path)
148 return PHYSFS_mkdir(path);
151 int fs_exists(const char *path)
153 return PHYSFS_exists(path);
156 int fs_remove(const char *path)
158 return PHYSFS_delete(path);
161 /* -------------------------------------------------------------------------- */
163 int fs_read(void *data, int size, int count, fs_file fh)
165 return PHYSFS_read(fh->handle, data, size, count);
168 int fs_write(const void *data, int size, int count, fs_file fh)
170 return PHYSFS_write(fh->handle, data, size, count);
173 int fs_flush(fs_file fh)
175 return PHYSFS_flush(fh->handle);
178 long fs_tell(fs_file fh)
180 return PHYSFS_tell(fh->handle);
183 int fs_seek(fs_file fh, long offset, int whence)
185 PHYSFS_uint64 pos = 0;
186 PHYSFS_sint64 cur = PHYSFS_tell(fh->handle);
187 PHYSFS_sint64 len = PHYSFS_fileLength(fh->handle);
208 return PHYSFS_seek(fh->handle, pos);
211 int fs_eof(fs_file fh)
213 return PHYSFS_eof(fh->handle);
216 int fs_length(fs_file fh)
218 return PHYSFS_fileLength(fh->handle);
221 /* -------------------------------------------------------------------------- */
224 * The code below does not use the PhysicsFS API.
227 /* -------------------------------------------------------------------------- */
229 static int cmp_dir_items(const void *A, const void *B)
231 const struct dir_item *a = A, *b = B;
232 return strcmp(a->path, b->path);
235 static int is_archive(struct dir_item *item)
237 return strcmp(item->path + strlen(item->path) - 4, ".zip") == 0;
240 static void add_archives(const char *path)
245 if ((archives = dir_scan(path, is_archive, NULL, NULL)))
247 array_sort(archives, cmp_dir_items);
249 for (i = 0; i < array_len(archives); i++)
250 fs_add_path(DIR_ITEM_GET(archives, i)->path);
256 int fs_add_path_with_archives(const char *path)
259 return fs_add_path(path);
262 /* -------------------------------------------------------------------------- */
264 int fs_rename(const char *src, const char *dst)
266 const char *write_dir;
267 char *real_src, *real_dst;
270 if ((write_dir = fs_get_write_dir()))
272 real_src = concat_string(write_dir, "/", src, NULL);
273 real_dst = concat_string(write_dir, "/", dst, NULL);
275 rc = file_rename(real_src, real_dst);
284 /* -------------------------------------------------------------------------- */
286 int fs_getc(fs_file fh)
290 if (fs_read(&c, 1, 1, fh) != 1)
296 int fs_putc(int c, fs_file fh)
298 unsigned char b = (unsigned char) c;
300 if (fs_write(&b, 1, 1, fh) != 1)
306 int fs_puts(const char *src, fs_file fh)
309 if (fs_putc(*src++, fh) < 0)
315 char *fs_gets(char *dst, int count, fs_file fh)
326 while (count > 1 && (c = fs_getc(fh)) >= 0)
332 /* Keep a newline and break. */
340 /* Ignore carriage returns. */
356 /* -------------------------------------------------------------------------- */
359 * Write out a multiline string to a file with appropriately converted
360 * linefeed characters.
362 static int write_lines(const char *start, int length, fs_file fh)
365 static const char crlf[] = "\r\n";
367 static const char crlf[] = "\n";
370 int total_written = 0;
376 while (total_written < length)
378 lf = strchr(start, '\n');
380 datalen = lf ? (int) (lf - start) : length - total_written;
381 written = fs_write(start, 1, datalen, fh);
386 total_written += written;
388 if (written < datalen)
393 if (fs_puts(crlf, fh) < 0)
401 return total_written;
404 /* -------------------------------------------------------------------------- */
407 * Trying to avoid defining a feature test macro for every platform by
408 * declaring vsnprintf with the C99 signature. This is probably bad.
414 extern int vsnprintf(char *, size_t, const char *, va_list);
417 int fs_printf(fs_file fh, const char *fmt, ...)
425 len = vsnprintf(NULL, 0, fmt, ap) + 1;
428 if ((buff = malloc(len)))
433 vsnprintf(buff, len, fmt, ap);
437 * HACK. This assumes fs_printf is always called with the
438 * intention of writing text, and not arbitrary data.
441 written = write_lines(buff, strlen(buff), fh);
451 /* -------------------------------------------------------------------------- */
453 void *fs_load(const char *path, int *datalen)
460 if ((fh = fs_open(path, "r")))
462 if ((*datalen = fs_length(fh)) > 0)
464 if ((data = malloc(*datalen)))
466 if (fs_read(data, *datalen, 1, fh) != 1)
481 /* -------------------------------------------------------------------------- */