Implement a stdio-based backend for the VFS (no archive support)
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Tue, 16 Nov 2010 22:00:52 +0000 (22:00 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Tue, 16 Nov 2010 22:00:52 +0000 (22:00 +0000)
git-svn-id: https://s.snth.net/svn/neverball/trunk@3365 78b8d119-cf0a-0410-b17c-f493084dd1d7

Makefile
share/common.c
share/common.h
share/dir.c
share/dir.h
share/fs_stdio.c [new file with mode: 0644]

index aae1186..08e22cc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -93,7 +93,12 @@ ALL_CPPFLAGS += $(CPPFLAGS)
 
 SDL_LIBS := $(shell sdl-config --libs)
 PNG_LIBS := $(shell libpng-config --libs)
+
+ifeq ($(ENABLE_FS),stdio)
+FS_LIBS :=
+else
 FS_LIBS := -lphysfs
+endif
 
 # The  non-conditionalised values  below  are specific  to the  native
 # system. The native system of this Makefile is Linux (or GNU+Linux if
@@ -170,6 +175,7 @@ MAPC_OBJS := \
        share/fs_jpg.o      \
        share/dir.o         \
        share/array.o       \
+       share/list.o        \
        share/mapc.o
 BALL_OBJS := \
        share/lang.o        \
@@ -285,9 +291,15 @@ PUTT_OBJS := \
 BALL_OBJS += share/solid_sim_sol.o
 PUTT_OBJS += share/solid_sim_sol.o
 
+ifeq ($(ENABLE_FS),stdio)
+BALL_OBJS += share/fs_stdio.o
+PUTT_OBJS += share/fs_stdio.o
+MAPC_OBJS += share/fs_stdio.o
+else
 BALL_OBJS += share/fs_physfs.o
 PUTT_OBJS += share/fs_physfs.o
 MAPC_OBJS += share/fs_physfs.o
+endif
 
 ifeq ($(ENABLE_TILT),wii)
 BALL_OBJS += share/tilt_wii.o
index ab86dc3..d5dcdc7 100644 (file)
@@ -208,6 +208,11 @@ int path_is_abs(const char *path)
     return 0;
 }
 
+char *path_join(const char *head, const char *tail)
+{
+    return *head ? concat_string(head, "/", tail, NULL) : strdup(tail);
+}
+
 static const char *path_last_sep(const char *path)
 {
     const char *sep;
index eba3b59..44a6818 100644 (file)
@@ -65,6 +65,8 @@ void file_copy(FILE *fin, FILE *fout);
 int path_is_sep(int);
 int path_is_abs(const char *);
 
+char *path_join(const char *, const char *);
+
 const char *base_name(const char *name);
 const char *base_name_sans(const char *name, const char *suffix);
 const char *dir_name(const char *name);
index 345fdab..96b534d 100644 (file)
@@ -63,7 +63,7 @@ static struct dir_item *add_item(Array items, const char *dir, const char *name)
 {
     struct dir_item *item = array_add(items);
 
-    item->path = *dir ? concat_string(dir, "/", name, NULL) : strdup(name);
+    item->path = path_join(dir, name);
     item->data = NULL;
 
     return item;
@@ -119,3 +119,15 @@ void dir_free(Array items)
 
     array_free(items);
 }
+
+int dir_exists(const char *path)
+{
+    DIR *dir;
+
+    if ((dir = opendir(path)))
+    {
+        closedir(dir);
+        return 1;
+    }
+    return 0;
+}
index 7e3bc08..360e6a9 100644 (file)
@@ -26,4 +26,14 @@ void  dir_free(Array);
 List dir_list_files(const char *);
 void dir_list_free (List);
 
+int dir_exists(const char *);
+
+#ifdef _WIN32
+#include <direct.h>
+#define dir_make(path) _mkdir(path)
+#else
+#include <sys/stat.h>
+#define dir_make(path) mkdir(path, 0777)
+#endif
+
 #endif
diff --git a/share/fs_stdio.c b/share/fs_stdio.c
new file mode 100644 (file)
index 0000000..2aa87bf
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2003-2010 Neverball authors
+ *
+ * NEVERBALL is  free software; you can redistribute  it and/or modify
+ * it under the  terms of the GNU General  Public License as published
+ * by the Free  Software Foundation; either version 2  of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
+ * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include "fs.h"
+#include "dir.h"
+#include "array.h"
+#include "list.h"
+#include "common.h"
+
+/*
+ * This file implements the low-level virtual file system routines
+ * using stdio.
+ */
+
+/*---------------------------------------------------------------------------*/
+
+struct fs_file
+{
+    FILE *handle;
+};
+
+static char *fs_dir_base;
+static char *fs_dir_write;
+static List  fs_path;
+
+int fs_init(const char *argv0)
+{
+    fs_dir_base  = strdup(dir_name(argv0));
+    fs_dir_write = NULL;
+    fs_path      = NULL;
+
+    return 1;
+}
+
+int fs_quit(void)
+{
+    if (fs_dir_base)
+    {
+        free(fs_dir_base);
+        fs_dir_base = NULL;
+    }
+
+    if (fs_dir_write)
+    {
+        free(fs_dir_write);
+        fs_dir_write = NULL;
+    }
+
+    while (fs_path)
+    {
+        free(fs_path->data);
+        fs_path = list_rest(fs_path);
+    }
+
+    return 1;
+}
+
+const char *fs_error(void)
+{
+    return strerror(errno);
+}
+
+/*---------------------------------------------------------------------------*/
+
+const char *fs_base_dir(void)
+{
+    return fs_dir_base;
+}
+
+int fs_add_path(const char *path)
+{
+    /* TODO: ZIP archive support. */
+
+    if (dir_exists(path))
+    {
+        fs_path = list_cons(strdup(path), fs_path);
+        return 1;
+    }
+    return 0;
+}
+
+int fs_set_write_dir(const char *path)
+{
+    if (dir_exists(path))
+    {
+        if (fs_dir_write)
+        {
+            free(fs_dir_write);
+            fs_dir_write = NULL;
+        }
+
+        fs_dir_write = strdup(path);
+        return 1;
+    }
+    return 0;
+}
+
+const char *fs_get_write_dir(void)
+{
+    return fs_dir_write;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void add_files(List *items, const char *real)
+{
+    List files, file;
+
+    if ((files = dir_list_files(real)))
+    {
+        for (file = files; file; file = file->next)
+        {
+            int skip = 0;
+            List p, l;
+
+            /* "Inspired" by PhysicsFS file enumeration code. */
+
+            for (p = NULL, l = *items; l; p = l, l = l->next)
+            {
+                int cmp;
+
+                if ((cmp = strcmp(l->data, file->data)) >= 0)
+                {
+                    skip = (cmp == 0);
+                    break;
+                }
+            }
+
+            if (!skip)
+            {
+                if (p)
+                    p->next = list_cons(file->data, p->next);
+                else
+                    *items = list_cons(file->data, *items);
+
+                /* Take over memory management duties. */
+
+                file->data = NULL;
+            }
+        }
+
+        dir_list_free(files);
+    }
+}
+
+static List list_files(const char *path)
+{
+    List files = NULL;
+    List p;
+
+    for (p = fs_path; p; p = p->next)
+    {
+        char *real = path_join(p->data, path);
+        add_files(&files, real);
+        free(real);
+    }
+
+    return files;
+}
+
+static void free_files(List files)
+{
+    while (files)
+    {
+        free(files->data);
+        files = list_rest(files);
+    }
+}
+
+Array fs_dir_scan(const char *path, int (*filter)(struct dir_item *))
+{
+    return dir_scan(path, filter, list_files, free_files);
+}
+
+void fs_dir_free(Array items)
+{
+    dir_free(items);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static char *real_path(const char *path)
+{
+    char *real = NULL;
+    List p;
+
+    for (p = fs_path; p; p = p->next)
+    {
+        real = path_join(p->data, path);
+
+        if (file_exists(real))
+            break;
+
+        free(real);
+        real = NULL;
+    }
+
+    return real;
+}
+
+/*---------------------------------------------------------------------------*/
+
+fs_file fs_open(const char *path, const char *mode)
+{
+    fs_file fh;
+
+    assert((mode[0] == 'r' && !mode[1]) ||
+           (mode[0] == 'w' && (!mode[1] || mode[1] == '+')));
+
+    if ((fh = malloc(sizeof (*fh))))
+    {
+        char *real;
+
+        fh->handle = NULL;
+
+        switch (mode[0])
+        {
+        case 'r':
+            if ((real = real_path(path)))
+            {
+                fh->handle = fopen(real, "rb");
+                free(real);
+            }
+
+            break;
+
+        case 'w':
+            if (fs_dir_write)
+            {
+                real = path_join(fs_dir_write, path);
+
+                fh->handle = (mode[1] == '+' ?
+                              fopen(real, "wb") :
+                              fopen(real, "wb+"));
+
+                free(real);
+            }
+            break;
+        }
+
+        if (!fh->handle)
+        {
+            free(fh);
+            fh = NULL;
+        }
+    }
+
+    return fh;
+}
+
+int fs_close(fs_file fh)
+{
+    if (fclose(fh->handle))
+    {
+        free(fh);
+        return 1;
+    }
+    return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int fs_mkdir(const char *path)
+{
+    char *real;
+    int rc;
+
+    real = path_join(fs_dir_write, path);
+    rc = dir_make(real);
+    free((void *) real);
+
+    return rc == 0;
+}
+
+int fs_exists(const char *path)
+{
+    char *real;
+
+    if ((real = real_path(path)))
+    {
+        free(real);
+        return 1;
+    }
+    return 0;
+}
+
+int fs_remove(const char *path)
+{
+    char *real;
+    int rc;
+
+    real = path_join(fs_dir_write, path);
+    rc = (remove(real) == 0);
+    free(real);
+
+    return rc;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int fs_read(void *data, int size, int count, fs_file fh)
+{
+    return fread(data, size, count, fh->handle);
+}
+
+int fs_write(const void *data, int size, int count, fs_file fh)
+{
+    return fwrite(data, size, count, fh->handle);
+}
+
+int fs_flush(fs_file fh)
+{
+    return fflush(fh->handle);
+}
+
+long fs_tell(fs_file fh)
+{
+    return ftell(fh->handle);
+}
+
+int fs_seek(fs_file fh, long offset, int whence)
+{
+    return fseek(fh->handle, offset, whence);
+}
+
+int fs_eof(fs_file fh)
+{
+    /*
+     * Unlike PhysicsFS, stdio does not register EOF unless we have
+     * actually attempted to read past the end of the file.  Nothing
+     * is done to mitigate this: instead, code that relies on
+     * PhysicsFS behavior should be fixed not to.
+     */
+    return feof(fh->handle);
+}
+
+int fs_length(fs_file fh)
+{
+    long len, cur = ftell(fh->handle);
+
+    fseek(fh->handle, 0, SEEK_END);
+    len = ftell(fh->handle);
+    fseek(fh->handle, cur, SEEK_SET);
+
+    return len;
+}
+
+/*---------------------------------------------------------------------------*/