Implement a Quake-like virtual file system layer
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Fri, 12 Jun 2009 02:37:22 +0000 (02:37 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Fri, 12 Jun 2009 02:37:22 +0000 (02:37 +0000)
This patch introduces a new module "share/fs" through which all file
system and input/output operations are handled.  The module internally
is supported by the PhysicsFS 1.0 API, so this change adds an external
dependency on that library.

How things work (for those not familiar with Quake's PK3):

Neverball now has a single, unified view of the file system, there's
only the "write directory" to which files are written and the "search
path" from which files are read.  The write directory is simply the
user directory (ie., ~/.neverball).  The search path consists of the
following components (items listed later take precedence):

 * archives found in the game data directory;
 * game data directory itself;
 * archives found in the user directory;
 * user directory itself.

Archives are sorted alphabetically (and similarly, archives later in
the alphabet take precedence).

A file in one component with the same name as another file in another
component of lower precedence overrides that file, and Neverball only
ever sees the file from the higher-precedence component.

This has not seen significant testing yet, and some things have
probably stopped working.  Keep your eyes open.

git-svn-id: https://s.snth.net/svn/neverball/trunk@2877 78b8d119-cf0a-0410-b17c-f493084dd1d7

47 files changed:
Makefile
ball/demo.c
ball/demo.h
ball/demo_dir.c
ball/game_client.c
ball/game_client.h
ball/game_server.c
ball/game_server.h
ball/level.c
ball/main.c
ball/set.c
ball/st_demo.c
ball/st_help.c
ball/st_title.c
putt/course.c
putt/game.c
putt/hole.c
putt/main.c
share/audio.c
share/ball.c
share/base_config.c
share/base_config.h
share/base_image.c
share/binary.c
share/binary.h
share/cmd.c
share/cmd.h
share/common.c
share/common.h
share/config.c
share/fs.c [new file with mode: 0644]
share/fs.h [new file with mode: 0644]
share/fs_jpg.c [new file with mode: 0644]
share/fs_jpg.h [new file with mode: 0644]
share/fs_ov.c [new file with mode: 0644]
share/fs_ov.h [new file with mode: 0644]
share/fs_png.c [new file with mode: 0644]
share/fs_png.h [new file with mode: 0644]
share/fs_rwops.c [new file with mode: 0644]
share/fs_rwops.h [new file with mode: 0644]
share/gui.c
share/image.c
share/item.c
share/lang.c
share/mapc.c
share/solid.c
share/syswm.c

index 715c2c8..c339d5d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -70,6 +70,7 @@ ALL_CPPFLAGS += $(CPPFLAGS)
 
 SDL_LIBS := $(shell sdl-config --libs)
 PNG_LIBS := $(shell libpng-config --libs)
+FS_LIBS := -lphysfs
 
 # The  non-conditionalised values  below  are specific  to the  native
 # system. The native system of this Makefile is Linux (or GNU+Linux if
@@ -102,7 +103,7 @@ ifdef DARWIN
     OGL_LIBS  := -framework OpenGL
 endif
 
-BASE_LIBS := -ljpeg $(PNG_LIBS)
+BASE_LIBS := -ljpeg $(PNG_LIBS) $(FS_LIBS)
 
 ifdef DARWIN
     BASE_LIBS += -L/opt/local/lib
@@ -137,6 +138,11 @@ MAPC_OBJS := \
        share/binary.o      \
        share/base_config.o \
        share/common.o      \
+       share/fs.o          \
+       share/fs_png.o      \
+       share/fs_jpg.o      \
+       share/dir.o         \
+       share/array.o       \
        share/mapc.o
 BALL_OBJS := \
        share/lang.o        \
@@ -170,6 +176,11 @@ BALL_OBJS := \
        share/cmd.o         \
        share/array.o       \
        share/dir.o         \
+       share/fs.o          \
+       share/fs_png.o      \
+       share/fs_jpg.o      \
+       share/fs_rwops.o    \
+       share/fs_ov.o       \
        ball/hud.o          \
        ball/game_common.o  \
        ball/game_client.o  \
@@ -225,6 +236,13 @@ PUTT_OBJS := \
        share/common.o      \
        share/syswm.o       \
        share/list.o        \
+       share/fs.o          \
+       share/fs_png.o      \
+       share/fs_jpg.o      \
+       share/fs_rwops.o    \
+       share/fs_ov.o       \
+       share/dir.o         \
+       share/array.o       \
        putt/hud.o          \
        putt/game.o         \
        putt/hole.o         \
index 0b1b885..f9d09de 100644 (file)
@@ -39,7 +39,7 @@
 
 #define DATELEN 20
 
-static FILE *demo_fp;
+static fs_file demo_fp;
 
 /*---------------------------------------------------------------------------*/
 
@@ -68,7 +68,7 @@ void demo_dump_info(const struct demo *d)
            d->time, d->goal, d->goal_e, d->score, d->balls, d->times);
 }
 
-static int demo_header_read(FILE *fp, struct demo *d)
+static int demo_header_read(fs_file fp, struct demo *d)
 {
     int magic;
     int version;
@@ -123,7 +123,7 @@ static int demo_header_read(FILE *fp, struct demo *d)
     return 0;
 }
 
-static void demo_header_write(FILE *fp, struct demo *d)
+static void demo_header_write(fs_file fp, struct demo *d)
 {
     int magic = MAGIC;
     int version = DEMO_VERSION;
@@ -158,12 +158,12 @@ static void demo_header_write(FILE *fp, struct demo *d)
 
 struct demo *demo_load(const char *path)
 {
-    FILE *fp;
+    fs_file fp;
     struct demo *d;
 
     d = NULL;
 
-    if ((fp = fopen(path, FMODE_RB)))
+    if ((fp = fs_open(path, "r")))
     {
         d = malloc(sizeof (struct demo));
 
@@ -179,7 +179,7 @@ struct demo *demo_load(const char *path)
             d = NULL;
         }
 
-        fclose(fp);
+        fs_close(fp);
     }
 
     return d;
@@ -196,10 +196,10 @@ int demo_exists(const char *name)
 {
     char buf[MAXSTR];
 
-    strcpy(buf, config_user(name));
+    strcpy(buf, name);
     strcat(buf, REPLAY_EXT);
 
-    return file_exists(buf);
+    return fs_exists(buf);
 }
 
 #define MAXSTRLEN(a) (sizeof ((a)) - 1)
@@ -303,7 +303,7 @@ int demo_play_init(const char *name, const struct level *level,
 
     memset(&demo, 0, sizeof (demo));
 
-    strncpy(demo.filename, config_user(name), MAXSTR);
+    strncpy(demo.filename, name, MAXSTR);
     strcat(demo.filename, REPLAY_EXT);
 
     demo.mode = mode;
@@ -321,7 +321,7 @@ int demo_play_init(const char *name, const struct level *level,
     demo.balls  = b;
     demo.times  = tt;
 
-    if ((demo_fp = fopen(demo.filename, FMODE_WB)))
+    if ((demo_fp = fs_open(demo.filename, "w")))
     {
         demo_header_write(demo_fp, &demo);
         audio_music_fade_to(2.0f, level->song);
@@ -346,15 +346,15 @@ void demo_play_stat(int status, int coins, int timer)
 {
     if (demo_fp)
     {
-        long pos = ftell(demo_fp);
+        long pos = fs_tell(demo_fp);
 
-        fseek(demo_fp, 8, SEEK_SET);
+        fs_seek(demo_fp, 8, SEEK_SET);
 
         put_index(demo_fp, &timer);
         put_index(demo_fp, &coins);
         put_index(demo_fp, &status);
 
-        fseek(demo_fp, pos, SEEK_SET);
+        fs_seek(demo_fp, pos, SEEK_SET);
     }
 }
 
@@ -362,7 +362,7 @@ void demo_play_stop(void)
 {
     if (demo_fp)
     {
-        fclose(demo_fp);
+        fs_close(demo_fp);
         demo_fp = NULL;
     }
 }
@@ -381,18 +381,19 @@ void demo_rename(const char *name)
         demo_exists(USER_REPLAY_FILE) &&
         strcmp(name, USER_REPLAY_FILE) != 0)
     {
-        strcpy(src, config_user(USER_REPLAY_FILE));
+        strcpy(src, USER_REPLAY_FILE);
         strcat(src, REPLAY_EXT);
 
-        strcpy(dst, config_user(name));
+        strcpy(dst, name);
         strcat(dst, REPLAY_EXT);
 
-        file_rename(src, dst);
+        fs_rename(src, dst);
     }
 }
 
 void demo_rename_player(const char *name, const char *player)
 {
+#if 0
     char filename[MAXSTR];
     FILE *old_fp, *new_fp;
     struct demo d;
@@ -459,6 +460,7 @@ void demo_rename_player(const char *name, const char *player)
         }
         fclose(old_fp);
     }
+#endif
 }
 
 /*---------------------------------------------------------------------------*/
@@ -473,7 +475,7 @@ const struct demo *curr_demo_replay(void)
 
 int demo_replay_init(const char *name, int *g, int *m, int *b, int *s, int *tt)
 {
-    demo_fp = fopen(name, FMODE_RB);
+    demo_fp = fs_open(name, "r");
 
     if (demo_fp && demo_header_read(demo_fp, &demo_replay))
     {
@@ -543,7 +545,7 @@ int demo_replay_step(float dt)
                 break;
         }
 
-        if (!feof(demo_fp))
+        if (!fs_eof(demo_fp))
         {
             game_client_step(NULL);
             return 1;
@@ -556,10 +558,10 @@ void demo_replay_stop(int d)
 {
     if (demo_fp)
     {
-        fclose(demo_fp);
+        fs_close(demo_fp);
         demo_fp = NULL;
 
-        if (d) remove(demo_replay.filename);
+        if (d) fs_remove(demo_replay.filename);
     }
 }
 
@@ -570,6 +572,6 @@ void demo_replay_dump_info(void)
 
 /*---------------------------------------------------------------------------*/
 
-FILE *demo_file(void) { return demo_fp; }
+fs_file demo_file(void) { return demo_fp; }
 
 /*---------------------------------------------------------------------------*/
index 623b2c4..283eb16 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdio.h>
 
 #include "level.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -67,7 +68,7 @@ const struct demo *curr_demo_replay(void);
 
 /*---------------------------------------------------------------------------*/
 
-FILE *demo_file(void);
+fs_file demo_file(void);
 
 /*---------------------------------------------------------------------------*/
 
index 7f984d0..6356a5f 100644 (file)
@@ -5,7 +5,7 @@
 #include "common.h"
 #include "demo.h"
 #include "demo_dir.h"
-#include "dir.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -16,7 +16,7 @@ static int scan_item(struct dir_item *item)
 
 Array demo_dir_scan(const char *path)
 {
-    return dir_scan(path, scan_item, NULL, NULL);
+    return fs_dir_scan(path, scan_item);
 }
 
 void demo_dir_free(Array items)
index 0ad26b1..eda03f7 100644 (file)
@@ -375,7 +375,7 @@ static void game_run_cmd(const union cmd *cmd)
     }
 }
 
-void game_client_step(FILE *demo_fp)
+void game_client_step(fs_file demo_fp)
 {
     union cmd *cmdp;
 
@@ -408,7 +408,7 @@ int  game_client_init(const char *file_name)
     if (client_state)
         game_client_free();
 
-    if (!sol_load_gl(&file, config_data(file_name),
+    if (!sol_load_gl(&file, file_name,
                      config_get_d(CONFIG_TEXTURES),
                      config_get_d(CONFIG_SHADOW)))
         return (client_state = 0);
@@ -466,7 +466,7 @@ int  game_client_init(const char *file_name)
     first_update = 1;
 
     back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
-    sol_load_gl(&back, config_data(back_name),
+    sol_load_gl(&back, back_name,
                 config_get_d(CONFIG_TEXTURES), 0);
 
     return client_state;
index 9d855cd..f925059 100644 (file)
@@ -3,12 +3,13 @@
 
 #include <stdio.h>
 #include "solid.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
 int   game_client_init(const char *);
 void  game_client_free(void);
-void  game_client_step(FILE *);
+void  game_client_step(fs_file);
 
 int   curr_clock(void);
 int   curr_coins(void);
index a9f9857..f1cce5a 100644 (file)
@@ -149,7 +149,7 @@ static int input_get_c(void)
     return (int) input_current.c;
 }
 
-int input_put(FILE *fout)
+int input_put(fs_file fout)
 {
     if (server_state)
     {
@@ -163,7 +163,7 @@ int input_put(FILE *fout)
     return 0;
 }
 
-int input_get(FILE *fin)
+int input_get(fs_file fin)
 {
     if (server_state)
     {
@@ -172,7 +172,7 @@ int input_get(FILE *fin)
         get_short(fin, &input_current.r);
         get_short(fin, &input_current.c);
 
-        return (feof(fin) ? 0 : 1);
+        return (fs_eof(fin) ? 0 : 1);
     }
     return 0;
 }
@@ -495,7 +495,7 @@ int game_server_init(const char *file_name, int t, int e)
     if (server_state)
         game_server_free();
 
-    if (!sol_load_only_file(&file, config_data(file_name)))
+    if (!sol_load_only_file(&file, file_name))
         return (server_state = 0);
 
     server_state = 1;
index 5f3b52f..c9a9173 100644 (file)
@@ -2,6 +2,7 @@
 #define GAME_SERVER_H
 
 #include "solid.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -28,8 +29,8 @@ void  game_set_fly(float, const struct s_file *);
 
 /*---------------------------------------------------------------------------*/
 
-int input_put(FILE *);
-int input_get(FILE *);
+int input_put(fs_file);
+int input_get(fs_file);
 
 /*---------------------------------------------------------------------------*/
 
index a6364e6..778263f 100644 (file)
@@ -141,7 +141,7 @@ int level_load(const char *filename, struct level *level)
 #define default_error \
     L_("Not a valid level file")
 
-    if (!sol_load_only_head(&sol, config_data(filename)))
+    if (!sol_load_only_head(&sol, filename))
     {
         const char *error = errno ? strerror(errno) : default_error;
         fprintf(stderr, format, filename, error);
index ab9b75c..cf8fabb 100644 (file)
@@ -29,6 +29,8 @@
 #include "gui.h"
 #include "set.h"
 #include "tilt.h"
+#include "fs.h"
+#include "common.h"
 
 #include "st_conf.h"
 #include "st_title.h"
@@ -46,7 +48,7 @@ static void shot(void)
     static char filename[MAXSTR];
 
     sprintf(filename, "screen%05d.png", config_screenshot());
-    image_snap(config_user(filename));
+    image_snap(filename);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -346,23 +348,17 @@ int main(int argc, char *argv[])
     SDL_Joystick *joy = NULL;
     int t1, t0, uniform;
 
-    config_exec_path = argv[0];
+    if (!fs_init(argv[0]))
+    {
+        fputs("Failure to initialize virtual file system\n", stderr);
+        return 1;
+    }
 
     lang_init("neverball");
 
     parse_args(argc, argv);
 
-    if (!config_data_path(data_path, SET_FILE))
-    {
-        fputs(L_("Failure to establish game data directory\n"), stderr);
-        return 1;
-    }
-
-    if (!config_user_path(NULL))
-    {
-        fputs(L_("Failure to establish config directory\n"), stderr);
-        return 1;
-    }
+    config_paths(data_path);
 
     /* Initialize SDL system and subsystems */
 
@@ -379,9 +375,9 @@ int main(int argc, char *argv[])
 
     /* Dump replay information and exit. */
 
-    if (display_info)
+    if (display_info && fs_add_path(dir_name(demo_path)))
     {
-        if (!progress_replay(demo_path))
+        if (!progress_replay(base_name(demo_path, NULL)))
         {
             fprintf(stderr, L_("Replay file '%s': %s\n"), demo_path,
                     errno ?  strerror(errno) : L_("Not a replay file"));
@@ -414,7 +410,8 @@ int main(int argc, char *argv[])
 
     /* Initialise demo playback. */
 
-    if (replay_demo && progress_replay(demo_path))
+    if (replay_demo && fs_add_path(dir_name(demo_path)) &&
+        progress_replay(base_name(demo_path, NULL)))
     {
         demo_play_goto(1);
         goto_state(&st_demo_play);
index ac28e43..80c735f 100644 (file)
@@ -23,6 +23,7 @@
 #include "image.h"
 #include "set.h"
 #include "common.h"
+#include "fs.h"
 
 #include "game_server.h"
 #include "game_client.h"
@@ -60,23 +61,23 @@ static struct level level_v[MAXLVL];
 
 /*---------------------------------------------------------------------------*/
 
-static void put_score(FILE *fp, const struct score *s)
+static void put_score(fs_file fp, const struct score *s)
 {
     int j;
 
     for (j = 0; j < NSCORE; j++)
-        fprintf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
+        fs_printf(fp, "%d %d %s\n", s->timer[j], s->coins[j], s->player[j]);
 }
 
 void set_store_hs(void)
 {
     const struct set *s = &set_v[set];
-    FILE *fout;
+    fs_file fout;
     int i;
     const struct level *l;
     char states[MAXLVL + 1];
 
-    if ((fout = fopen(config_user(s->user_scores), "w")))
+    if ((fout = fs_open(s->user_scores, "w")))
     {
         for (i = 0; i < s->count; i++)
         {
@@ -88,7 +89,7 @@ void set_store_hs(void)
                 states[i] = 'O';
         }
         states[s->count] = '\0';
-        fprintf(fout, "%s\n",states);
+        fs_printf(fout, "%s\n",states);
 
         put_score(fout, &s->time_score);
         put_score(fout, &s->coin_score);
@@ -102,21 +103,23 @@ void set_store_hs(void)
             put_score(fout, &l->score.most_coins);
         }
 
-        fclose(fout);
+        fs_close(fout);
     }
 }
 
-static int get_score(FILE *fp, struct score *s)
+static int get_score(fs_file fp, struct score *s)
 {
     int j;
     int res = 1;
+    char line[MAXSTR];
 
     for (j = 0; j < NSCORE && res; j++)
     {
-        res = fscanf(fp, "%d %d %s\n",
-                     &s->timer[j],
-                     &s->coins[j],
-                     s->player[j]) == 3;
+        res = (fs_gets(line, sizeof (line), fp) &&
+               sscanf(line, "%d %d %s\n",
+                      &s->timer[j],
+                      &s->coins[j],
+                      s->player[j]) == 3);
     }
     return res;
 }
@@ -125,16 +128,17 @@ static int get_score(FILE *fp, struct score *s)
 static void set_load_hs(void)
 {
     struct set *s = &set_v[set];
-    FILE *fin;
+    fs_file fin;
     int i;
     int res = 0;
     struct level *l;
-    const char *fn = config_user(s->user_scores);
-    char states[MAXLVL + 1];
+    const char *fn = s->user_scores;
+    char states[MAXLVL + sizeof ("\n")];
 
-    if ((fin = fopen(fn, "r")))
+    if ((fin = fs_open(fn, "r")))
     {
-        res = fscanf(fin, "%s\n", states) == 1 && strlen(states) == s->count;
+        res = (fs_gets(states, sizeof (states), fin) &&
+               strlen(states) - 1 == s->count);
 
         for (i = 0; i < s->count && res; i++)
         {
@@ -172,7 +176,7 @@ static void set_load_hs(void)
                 get_score(fin, &l->score.most_coins);
         }
 
-        fclose(fin);
+        fs_close(fin);
     }
 
     if (!res && errno != ENOENT)
@@ -187,10 +191,10 @@ static void set_load_hs(void)
 
 static int set_load(struct set *s, const char *filename)
 {
-    FILE *fin;
+    fs_file fin;
     char *scores, *level_name;
 
-    fin = fopen(config_data(filename), "r");
+    fin = fs_open(filename, "r");
 
     if (!fin)
     {
@@ -235,7 +239,7 @@ static int set_load(struct set *s, const char *filename)
             s->count++;
         }
 
-        fclose(fin);
+        fs_close(fin);
 
         return 1;
     }
@@ -245,14 +249,14 @@ static int set_load(struct set *s, const char *filename)
     free(s->id);
     free(s->shot);
 
-    fclose(fin);
+    fs_close(fin);
 
     return 0;
 }
 
 int set_init()
 {
-    FILE *fin;
+    fs_file fin;
     char *name;
 
     if (set_state)
@@ -261,7 +265,7 @@ int set_init()
     set   = 0;
     count = 0;
 
-    if ((fin = fopen(config_data(SET_FILE), "r")))
+    if ((fin = fs_open(SET_FILE, "r")))
     {
         while (count < MAXSET && read_line(&name, fin))
         {
@@ -278,7 +282,7 @@ int set_init()
 
             free(name);
         }
-        fclose(fin);
+        fs_close(fin);
 
         set_state = 1;
     }
index b61a713..46ca3f8 100644 (file)
@@ -248,7 +248,7 @@ static int demo_enter(void)
     if (items)
         demo_dir_free(items);
 
-    items = demo_dir_scan(config_user(""));
+    items = demo_dir_scan("");
     total = array_len(items);
 
     id = gui_vstack(0);
index 3e582df..dbadca6 100644 (file)
@@ -52,14 +52,12 @@ static int help_action(int t)
         break;
 
     case HELP_DEMO_1:
-        if (demo_replay_init(config_data("gui/demo1.nbr"),
-                             NULL, NULL, NULL, NULL, NULL))
+        if (demo_replay_init("gui/demo1.nbr", NULL, NULL, NULL, NULL, NULL))
             return goto_state(&st_help_demo);
         break;
 
     case HELP_DEMO_2:
-        if (demo_replay_init(config_data("gui/demo2.nbr"),
-                             NULL, NULL, NULL, NULL, NULL))
+        if (demo_replay_init("gui/demo2.nbr", NULL, NULL, NULL, NULL, NULL))
             return goto_state(&st_help_demo);
         break;
 
index 8ea5720..b6180eb 100644 (file)
@@ -237,7 +237,7 @@ static void title_timer(int id, float dt)
         if (real_time > 1.0f)
         {
             if (!items)
-                items = demo_dir_scan(config_user(""));
+                items = demo_dir_scan("");
 
             if ((demo = pick_demo(items)))
             {
index 1b97073..663e029 100644 (file)
@@ -19,6 +19,7 @@
 #include "config.h"
 #include "course.h"
 #include "hole.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -41,19 +42,21 @@ static struct course course_v[MAXCRS];
 
 void course_init()
 {
-    FILE *fin;
+    fs_file fin;
+    char buff[MAXSTR];
 
     if (course_state)
         course_free();
 
     count = 0;
 
-    if ((fin = fopen(config_data(COURSE_FILE), "r")))
+    if ((fin = fs_open(COURSE_FILE, "r")))
     {
-        while (fscanf(fin, "%s %s\n",
+        while (fs_gets(buff, sizeof (buff), fin) &&
+               sscanf(buff, "%s %s\n",
                       course_v[count].holes,
                       course_v[count].shot) == 2 &&
-                fgets(course_v[count].desc, MAXSTR, fin))
+               fs_gets(course_v[count].desc, MAXSTR, fin))
         {
             char *q = course_v[count].desc + strlen(course_v[count].desc) - 1;
 
@@ -62,7 +65,7 @@ void course_init()
             count++;
         }
 
-        fclose(fin);
+        fs_close(fin);
 
         course_state = 1;
     }
index a86185b..e4fc8c7 100644 (file)
@@ -86,8 +86,9 @@ void game_init(const char *s)
     jump_b = 0;
 
     view_init();
-    sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
-                                    config_get_d(CONFIG_SHADOW));
+    sol_load_gl(&file, s,
+                config_get_d(CONFIG_TEXTURES),
+                config_get_d(CONFIG_SHADOW));
 }
 
 void game_free(void)
index 98eade3..6b8a389 100644 (file)
@@ -25,6 +25,7 @@
 #include "back.h"
 #include "audio.h"
 #include "config.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -52,7 +53,8 @@ static int        score_v[MAXHOL][MAXPLY];
 
 static void hole_init_rc(const char *filename)
 {
-    FILE *fin;
+    fs_file fin;
+    char buff[MAXSTR];
 
     hole   = 0;
     player = 0;
@@ -61,16 +63,17 @@ static void hole_init_rc(const char *filename)
 
     /* Load the holes list. */
 
-    if ((fin = fopen(config_data(filename), "r")))
+    if ((fin = fs_open(filename, "r")))
     {
-        while (fscanf(fin, "%s %s %d %s",
-                       hole_v[count].file,
-                       hole_v[count].back,
+        while (fs_gets(buff, sizeof (buff), fin) &&
+               sscanf(buff, "%s %s %d %s",
+                      hole_v[count].file,
+                      hole_v[count].back,
                       &hole_v[count].par,
-                       hole_v[count].song) == 4)
+                      hole_v[count].song) == 4)
             count++;
 
-        fclose(fin);
+        fs_close(fin);
     }
 }
 
index 55fa95a..f847bc6 100644 (file)
@@ -31,6 +31,7 @@
 #include "hole.h"
 #include "game.h"
 #include "gui.h"
+#include "fs.h"
 
 #include "st_conf.h"
 #include "st_all.h"
@@ -45,7 +46,7 @@ static int shot(void)
     static char filename[MAXSTR];
 
     sprintf(filename, "screen%05d.png", config_screenshot());
-    image_snap(config_user(filename));
+    image_snap(filename);
 
     return 1;
 }
@@ -193,78 +194,75 @@ int main(int argc, char *argv[])
     int camera = 0;
     SDL_Joystick *joy = NULL;
 
-    config_exec_path = argv[0];
+    if (!fs_init(argv[0]))
+    {
+        fputs("Failure to initialize virtual file system\n", stderr);
+        return 1;
+    }
 
     srand((int) time(NULL));
 
     lang_init("neverball");
+    config_paths(argc > 1 ? argv[1] : NULL);
 
-    if (config_data_path((argc > 1 ? argv[1] : NULL), COURSE_FILE))
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) == 0)
     {
-        if (config_user_path(NULL))
-        {
-            if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) == 0)
-            {
-                config_init();
-                config_load();
+        config_init();
+        config_load();
 
-                /* Cache Neverball's camera setting. */
+        /* Cache Neverball's camera setting. */
 
-                camera = config_get_d(CONFIG_CAMERA);
+        camera = config_get_d(CONFIG_CAMERA);
 
-                /* Initialize the joystick. */
+        /* Initialize the joystick. */
 
-                if (SDL_NumJoysticks() > 0)
-                {
-                    joy = SDL_JoystickOpen(config_get_d(CONFIG_JOYSTICK_DEVICE));
-                    if (joy)
-                    {
-                        SDL_JoystickEventState(SDL_ENABLE);
-                        set_joystick(joy);
-                    }
-                }
+        if (SDL_NumJoysticks() > 0)
+        {
+            joy = SDL_JoystickOpen(config_get_d(CONFIG_JOYSTICK_DEVICE));
+            if (joy)
+            {
+                SDL_JoystickEventState(SDL_ENABLE);
+                set_joystick(joy);
+            }
+        }
 
-                /* Initialize the audio. */
+        /* Initialize the audio. */
 
-                audio_init();
+        audio_init();
 
-                /* Initialize the video. */
+        /* Initialize the video. */
 
-                if (video_init(TITLE, ICON))
-                {
-                    int t1, t0 = SDL_GetTicks();
+        if (video_init(TITLE, ICON))
+        {
+            int t1, t0 = SDL_GetTicks();
 
-                    /* Run the main game loop. */
+            /* Run the main game loop. */
 
-                    init_state(&st_null);
-                    goto_state(&st_title);
+            init_state(&st_null);
+            goto_state(&st_title);
 
-                    while (loop())
-                        if ((t1 = SDL_GetTicks()) > t0)
-                        {
-                            st_timer((t1 - t0) / 1000.f);
-                            st_paint(0.001f * t1);
-                            SDL_GL_SwapBuffers();
+            while (loop())
+                if ((t1 = SDL_GetTicks()) > t0)
+                {
+                    st_timer((t1 - t0) / 1000.f);
+                    st_paint(0.001f * t1);
+                    SDL_GL_SwapBuffers();
 
-                            t0 = t1;
+                    t0 = t1;
 
-                            if (config_get_d(CONFIG_NICE))
-                                SDL_Delay(1);
-                        }
+                    if (config_get_d(CONFIG_NICE))
+                        SDL_Delay(1);
                 }
+        }
 
-                /* Restore Neverball's camera setting. */
+        /* Restore Neverball's camera setting. */
 
-                config_set_d(CONFIG_CAMERA, camera);
-                config_save();
+        config_set_d(CONFIG_CAMERA, camera);
+        config_save();
 
-                SDL_Quit();
-            }
-            else fprintf(stderr, "%s: %s\n", argv[0], SDL_GetError());
-        }
-        else fprintf(stderr, L_("Failure to establish config directory\n"));
+        SDL_Quit();
     }
-    else fprintf(stderr, L_("Failure to establish game data directory\n"));
+    else fprintf(stderr, "%s: %s\n", argv[0], SDL_GetError());
 
     return 0;
 }
index c2f8536..2eeebe7 100644 (file)
@@ -23,6 +23,8 @@
 #include "config.h"
 #include "audio.h"
 #include "common.h"
+#include "fs.h"
+#include "fs_ov.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -52,6 +54,10 @@ static struct voice *queue  = NULL;
 static struct voice *voices = NULL;
 static short        *buffer = NULL;
 
+static ov_callbacks callbacks = {
+    fs_ov_read, fs_ov_seek, fs_ov_close, fs_ov_tell
+};
+
 /*---------------------------------------------------------------------------*/
 
 #define MIX(d, s) {                           \
@@ -140,7 +146,7 @@ static int voice_step(struct voice *V, float volume, Uint8 *stream, int length)
 static struct voice *voice_init(const char *filename, float a)
 {
     struct voice *V;
-    FILE        *fp;
+    fs_file      fp;
 
     /* Allocate and initialize a new voice structure. */
 
@@ -152,9 +158,9 @@ static struct voice *voice_init(const char *filename, float a)
 
         /* Attempt to open the named Ogg stream. */
 
-        if ((fp = fopen(config_data(filename), FMODE_RB)))
+        if ((fp = fs_open(filename, "r")))
         {
-            if (ov_open(fp, &V->vf, NULL, 0) == 0)
+            if (ov_open_callbacks(fp, &V->vf, NULL, 0, callbacks) == 0)
             {
                 vorbis_info *info = ov_info(&V->vf, -1);
 
@@ -171,7 +177,7 @@ static struct voice *voice_init(const char *filename, float a)
 
                 /* The file will be closed when the Ogg is cleared. */
             }
-            else fclose(fp);
+            else fs_close(fp);
         }
     }
     return V;
index 63e7fc7..135b40e 100644 (file)
@@ -99,13 +99,13 @@ void ball_init(void)
     inner_alpha = 1.0f;
     outer_alpha = 1.0f;
 
-    if ((has_solid = sol_load_gl(&solid, config_data(solid_file), T, 0)))
+    if ((has_solid = sol_load_gl(&solid, solid_file, T, 0)))
         solid_flags = ball_opts(&solid, &solid_alpha);
 
-    if ((has_inner = sol_load_gl(&inner, config_data(inner_file), T, 0)))
+    if ((has_inner = sol_load_gl(&inner, inner_file, T, 0)))
         inner_flags = ball_opts(&inner, &inner_alpha);
 
-    if ((has_outer = sol_load_gl(&outer, config_data(outer_file), T, 0)))
+    if ((has_outer = sol_load_gl(&outer, outer_file, T, 0)))
         outer_flags = ball_opts(&outer, &outer_alpha);
 }
 
index 5dd5d03..bb555a0 100644 (file)
 #include "glext.h"
 #include "vec3.h"
 #include "common.h"
+#include "fs.h"
+#include "dir.h"
+#include "array.h"
 
 /*---------------------------------------------------------------------------*/
 
-/* Define the mkdir symbol. */
-
-#ifdef _WIN32
-#include <direct.h>
-#else
-#include <sys/stat.h>
-#endif
-
-/*---------------------------------------------------------------------------*/
-
-static char data_path[MAXSTR];
-static char user_path[MAXSTR];
-
-/*
- * Given  a path  and a  file name  relative to  that path,  create an
- * absolute path name and return a temporary pointer to it.
- */
-static const char *config_file(const char *path, const char *file)
+static const char *pick_data_path(const char *arg_data_path)
 {
-    static char absolute[MAXSTR];
+    static char dir[MAXSTR];
+    char *env;
 
-    size_t d = path ? strlen(path) : 0;
+    if (arg_data_path)
+        return arg_data_path;
 
-    strncpy(absolute, path ? path : "", MAXSTR - 1);
-    strncat(absolute, "/",  MAXSTR - d - 1);
-    strncat(absolute, file ? file : "", MAXSTR - d - 2);
+    if ((env = getenv("NEVERBALL_DATA")))
+        return env;
 
-    return absolute;
-}
+    if (path_is_abs(CONFIG_DATA))
+        return CONFIG_DATA;
 
-static int config_test(const char *path, const char *file)
-{
-    if (file)
-    {
-        FILE *fp;
-
-        if ((fp = fopen(config_file(path, file), "r")))
-        {
-            fclose(fp);
-            return 1;
-        }
-        return 0;
-    }
-    return 1;
-}
+    strncpy(dir, fs_base_dir(), sizeof (dir) - 1);
+    strncat(dir, "/",           sizeof (dir) - strlen(dir) - 1);
+    strncat(dir, CONFIG_DATA,   sizeof (dir) - strlen(dir) - 1);
 
-const char *config_data(const char *file)
-{
-    return config_file(data_path, file);
+    return dir;
 }
 
-const char *config_user(const char *file)
+static const char *pick_home_path(void)
 {
-    return config_file(user_path, file);
-}
+    const char *path;
 
-/*---------------------------------------------------------------------------*/
-
-const char *config_exec_path;
+#ifdef _WIN32
+    return (path = getenv("APPDATA")) ? path : fs_base_dir();
+#else
+    return (path = getenv("HOME")) ? path : fs_base_dir();
+#endif
+}
 
-/*
- * Attempt to find  the game data directory.  Search  the command line
- * parameter,  the environment,  and the  hard-coded default,  in that
- * order.  Confirm it by checking for presence of the named file.
- */
-int config_data_path(const char *path, const char *file)
+void config_paths(const char *arg_data_path)
 {
-    char *dir;
+    const char *data, *home, *user;
 
-    if (path && config_test(path, file))
-    {
-        strncpy(data_path, path, MAXSTR);
-        return 1;
-    }
+    /*
+     * Scan in turn the game data and user directories for archives,
+     * adding each archive to the search path.  Archives with names
+     * further down the alphabet take precedence.  After each scan,
+     * add the directory itself, taking precedence over archives added
+     * so far.
+     */
 
-    if ((dir = getenv("NEVERBALL_DATA")) && config_test(dir, file))
-    {
-        strncpy(data_path, dir, MAXSTR);
-        return 1;
-    }
-
-    dir = path_resolve(config_exec_path, CONFIG_DATA);
-
-    if (config_test(dir, file))
-    {
-        strncpy(data_path, dir, MAXSTR);
-        return 1;
-    }
+    /* Data directory. */
 
-    return 0;
-}
+    data = pick_data_path(arg_data_path);
 
-/*
- * Determine the location of  the user's home directory.  Ensure there
- * is a  directory there for  storing configuration, high  scores, and
- * replays.
- *
- * Under Windows check the APPDATA environment variable and if that's
- * not set, just assume the user has permission to write to the data
- * directory.
- */
-int config_user_path(const char *file)
-{
-#ifdef _WIN32
-    char *dir;
+    fs_add_path_with_archives(data);
 
-    if ((dir = getenv("APPDATA")) || (dir = data_path))
-    {
-        size_t d = strlen(dir);
+    /* User directory. */
 
-        strncpy(user_path, dir,         MAXSTR - 1);
-        strncat(user_path, "\\",        MAXSTR - d - 1);
-        strncat(user_path, CONFIG_USER, MAXSTR - d - 2);
-    }
+    home = pick_home_path();
+    user = concat_string(home, "/", CONFIG_USER, NULL);
 
-    if ((mkdir(user_path) == 0) || (errno == EEXIST))
-        if (config_test(user_path, file))
-            return 1;
-#else
-    char *dir;
+    /* Set up directory for writing, create if needed. */
 
-    if ((dir = getenv("HOME")))
+    if (!fs_set_write_dir(user))
     {
-        size_t d = strlen(dir);
-
-        strncpy(user_path, dir,         MAXSTR - 1);
-        strncat(user_path, "/",         MAXSTR - d - 1);
-        strncat(user_path, CONFIG_USER, MAXSTR - d - 2);
+        if (fs_set_write_dir(home) && fs_mkdir(CONFIG_USER))
+            fs_set_write_dir(user);
     }
 
-    if ((mkdir(user_path, 0777) == 0) || (errno == EEXIST))
-        if (config_test(user_path, file))
-            return 1;
-#endif
+    fs_add_path_with_archives(user);
 
-    return 0;
+    free((void *) user);
 }
 
 /*---------------------------------------------------------------------------*/
index 939e8cf..6adc3dc 100644 (file)
 
 /*---------------------------------------------------------------------------*/
 
-const char *config_data(const char *);
-const char *config_user(const char *);
-
-extern const char *config_exec_path;
-
-int  config_data_path(const char *, const char *);
-int  config_user_path(const char *);
+void config_paths(const char *);
 
 /*---------------------------------------------------------------------------*/
 
index 3942523..b3c5808 100644 (file)
 #include "base_config.h"
 #include "base_image.h"
 
+#include "fs.h"
+#include "fs_png.h"
+#include "fs_jpg.h"
+
 /*---------------------------------------------------------------------------*/
 
 void image_size(int *W, int *H, int w, int h)
@@ -40,7 +44,7 @@ static void *image_load_png(const char *filename, int *width,
                                                   int *height,
                                                   int *bytes)
 {
-    FILE *fp;
+    fs_file fh;
 
     png_structp readp = NULL;
     png_infop   infop = NULL;
@@ -49,7 +53,7 @@ static void *image_load_png(const char *filename, int *width,
 
     /* Initialize all PNG import data structures. */
 
-    if (!(fp = fopen(filename, FMODE_RB)))
+    if (!(fh = fs_open(filename, "r")))
         return NULL;
 
     if (!(readp = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
@@ -66,7 +70,7 @@ static void *image_load_png(const char *filename, int *width,
 
         /* Read the PNG header. */
 
-        png_init_io(readp, fp);
+        png_set_read_fn(readp, fh, fs_png_read);
         png_read_info(readp, infop);
 
         png_set_expand(readp);
@@ -115,7 +119,7 @@ static void *image_load_png(const char *filename, int *width,
     /* Free all resources. */
 
     png_destroy_read_struct(&readp, &infop, NULL);
-    fclose(fp);
+    fs_close(fh);
 
     return p;
 }
@@ -125,9 +129,9 @@ static void *image_load_jpg(const char *filename, int *width,
                                                   int *bytes)
 {
     GLubyte *p = NULL;
-    FILE   *fp;
+    fs_file fp;
 
-    if ((fp = fopen(filename, FMODE_RB)))
+    if ((fp = fs_open(filename, "r")))
     {
         struct jpeg_decompress_struct cinfo;
         struct jpeg_error_mgr         jerr;
@@ -138,7 +142,10 @@ static void *image_load_jpg(const char *filename, int *width,
 
         cinfo.err = jpeg_std_error(&jerr);
         jpeg_create_decompress(&cinfo);
-        jpeg_stdio_src(&cinfo, fp);
+
+        /* Set up a VFS source manager. */
+
+        fs_jpg_src(&cinfo, fp);
 
         /* Grab the JPG header info. */
 
@@ -167,7 +174,7 @@ static void *image_load_jpg(const char *filename, int *width,
         jpeg_finish_decompress(&cinfo);
         jpeg_destroy_decompress(&cinfo);
 
-        fclose(fp);
+        fs_close(fp);
     }
 
     return p;
index 3cd1de1..58ac1e7 100644 (file)
 
 #include <SDL_endian.h>
 
+#include "fs.h"
+
 /*---------------------------------------------------------------------------*/
 
-void put_float(FILE *fout, const float *f)
+void put_float(fs_file fout, const float *f)
 {
     const unsigned char *p = (const unsigned char *) f;
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    fputc((int) p[3], fout);
-    fputc((int) p[2], fout);
-    fputc((int) p[1], fout);
-    fputc((int) p[0], fout);
+    fs_putc((int) p[3], fout);
+    fs_putc((int) p[2], fout);
+    fs_putc((int) p[1], fout);
+    fs_putc((int) p[0], fout);
 #else
-    fputc((int) p[0], fout);
-    fputc((int) p[1], fout);
-    fputc((int) p[2], fout);
-    fputc((int) p[3], fout);
+    fs_putc((int) p[0], fout);
+    fs_putc((int) p[1], fout);
+    fs_putc((int) p[2], fout);
+    fs_putc((int) p[3], fout);
 #endif
 }
 
-void put_index(FILE *fout, const int *i)
+void put_index(fs_file fout, const int *i)
 {
     const unsigned char *p = (const unsigned char *) i;
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    fputc((int) p[3], fout);
-    fputc((int) p[2], fout);
-    fputc((int) p[1], fout);
-    fputc((int) p[0], fout);
+    fs_putc((int) p[3], fout);
+    fs_putc((int) p[2], fout);
+    fs_putc((int) p[1], fout);
+    fs_putc((int) p[0], fout);
 #else
-    fputc((int) p[0], fout);
-    fputc((int) p[1], fout);
-    fputc((int) p[2], fout);
-    fputc((int) p[3], fout);
+    fs_putc((int) p[0], fout);
+    fs_putc((int) p[1], fout);
+    fs_putc((int) p[2], fout);
+    fs_putc((int) p[3], fout);
 #endif
 }
 
-void put_short(FILE *fout, const short *s)
+void put_short(fs_file fout, const short *s)
 {
     const unsigned char *p = (const unsigned char *) s;
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    fputc((int) p[1], fout);
-    fputc((int) p[0], fout);
+    fs_putc((int) p[1], fout);
+    fs_putc((int) p[0], fout);
 #else
-    fputc((int) p[0], fout);
-    fputc((int) p[1], fout);
+    fs_putc((int) p[0], fout);
+    fs_putc((int) p[1], fout);
 #endif
 }
 
-void put_array(FILE *fout, const float *v, size_t n)
+void put_array(fs_file fout, const float *v, size_t n)
 {
     size_t i;
 
@@ -77,54 +79,54 @@ void put_array(FILE *fout, const float *v, size_t n)
 
 /*---------------------------------------------------------------------------*/
 
-void get_float(FILE *fin, float *f)
+void get_float(fs_file fin, float *f)
 {
     unsigned char *p = (unsigned char *) f;
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    p[3] = (unsigned char) fgetc(fin);
-    p[2] = (unsigned char) fgetc(fin);
-    p[1] = (unsigned char) fgetc(fin);
-    p[0] = (unsigned char) fgetc(fin);
+    p[3] = (unsigned char) fs_getc(fin);
+    p[2] = (unsigned char) fs_getc(fin);
+    p[1] = (unsigned char) fs_getc(fin);
+    p[0] = (unsigned char) fs_getc(fin);
 #else
-    p[0] = (unsigned char) fgetc(fin);
-    p[1] = (unsigned char) fgetc(fin);
-    p[2] = (unsigned char) fgetc(fin);
-    p[3] = (unsigned char) fgetc(fin);
+    p[0] = (unsigned char) fs_getc(fin);
+    p[1] = (unsigned char) fs_getc(fin);
+    p[2] = (unsigned char) fs_getc(fin);
+    p[3] = (unsigned char) fs_getc(fin);
 #endif
 }
 
-void get_index(FILE *fin, int *i)
+void get_index(fs_file fin, int *i)
 {
     unsigned char *p = (unsigned char *) i;
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    p[3] = (unsigned char) fgetc(fin);
-    p[2] = (unsigned char) fgetc(fin);
-    p[1] = (unsigned char) fgetc(fin);
-    p[0] = (unsigned char) fgetc(fin);
+    p[3] = (unsigned char) fs_getc(fin);
+    p[2] = (unsigned char) fs_getc(fin);
+    p[1] = (unsigned char) fs_getc(fin);
+    p[0] = (unsigned char) fs_getc(fin);
 #else
-    p[0] = (unsigned char) fgetc(fin);
-    p[1] = (unsigned char) fgetc(fin);
-    p[2] = (unsigned char) fgetc(fin);
-    p[3] = (unsigned char) fgetc(fin);
+    p[0] = (unsigned char) fs_getc(fin);
+    p[1] = (unsigned char) fs_getc(fin);
+    p[2] = (unsigned char) fs_getc(fin);
+    p[3] = (unsigned char) fs_getc(fin);
 #endif
 }
 
-void get_short(FILE *fin, short *s)
+void get_short(fs_file fin, short *s)
 {
     unsigned char *p = (unsigned char *) s;
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    p[1] = (unsigned char) fgetc(fin);
-    p[0] = (unsigned char) fgetc(fin);
+    p[1] = (unsigned char) fs_getc(fin);
+    p[0] = (unsigned char) fs_getc(fin);
 #else
-    p[0] = (unsigned char) fgetc(fin);
-    p[1] = (unsigned char) fgetc(fin);
+    p[0] = (unsigned char) fs_getc(fin);
+    p[1] = (unsigned char) fs_getc(fin);
 #endif
 }
 
-void get_array(FILE *fin, float *v, size_t n)
+void get_array(fs_file fin, float *v, size_t n)
 {
     size_t i;
 
@@ -134,20 +136,20 @@ void get_array(FILE *fin, float *v, size_t n)
 
 /*---------------------------------------------------------------------------*/
 
-void put_string(FILE *fout, const char *s)
+void put_string(fs_file fout, const char *s)
 {
-    fputs(s, fout);
-    fputc('\0', fout);
+    fs_puts(s, fout);
+    fs_putc('\0', fout);
 }
 
-void get_string(FILE *fin, char *s, int max)
+void get_string(fs_file fin, char *s, int max)
 {
     int c = -1;
 
     if (max == 0)
         return;
 
-    while (max && c && (c = fgetc(fin)) != EOF)
+    while (max && c && (c = fs_getc(fin)) >= 0)
     {
         *s++ = c;
         max--;
index 2d43ce4..fc0c0d5 100644 (file)
@@ -5,6 +5,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "fs.h"
+
 /*---------------------------------------------------------------------------*/
 
 #define FLOAT_BYTES     4
 #define ARRAY_BYTES(n)  (FLOAT_BYTES * (n))
 #define STRING_BYTES(s) (strlen(s) + 1)
 
-void put_float(FILE *, const float *);
-void put_index(FILE *, const int   *);
-void put_short(FILE *, const short *);
-void put_array(FILE *, const float *, size_t);
+void put_float(fs_file, const float *);
+void put_index(fs_file, const int   *);
+void put_short(fs_file, const short *);
+void put_array(fs_file, const float *, size_t);
 
-void get_float(FILE *, float *);
-void get_index(FILE *, int   *);
-void get_short(FILE *, short *);
-void get_array(FILE *, float *, size_t);
+void get_float(fs_file, float *);
+void get_index(fs_file, int   *);
+void get_short(fs_file, short *);
+void get_array(fs_file, float *, size_t);
 
-void put_string(FILE *fout, const char *);
-void get_string(FILE *fin, char *, int );
+void put_string(fs_file fout, const char *);
+void get_string(fs_file fin, char *, int );
 
 /*---------------------------------------------------------------------------*/
 
index 1752718..ce98413 100644 (file)
@@ -43,7 +43,7 @@ static int cmd_stats = 0;
  */
 
 #define PUT_FUNC(t)                                                     \
-    static void cmd_put_ ## t(FILE *fp, const union cmd *cmd) {         \
+    static void cmd_put_ ## t(fs_file fp, const union cmd *cmd) {       \
     const char *cmd_name = #t;                                          \
                                                                         \
     /* This is a write, so BYTES should be safe to eval already. */     \
@@ -56,7 +56,7 @@ static int cmd_stats = 0;
     if (cmd_stats) printf("put");                                       \
 
 #define GET_FUNC(t)                                             \
-    static void cmd_get_ ## t(FILE *fp, union cmd *cmd) {       \
+    static void cmd_get_ ## t(fs_file fp, union cmd *cmd) {     \
     const char *cmd_name = #t;                                  \
                                                                 \
     /* This is a read, so we'll have to eval BYTES later. */    \
@@ -575,14 +575,14 @@ END_FUNC;
 #define PUT_CASE(t) case t: cmd_put_ ## t(fp, cmd); break
 #define GET_CASE(t) case t: cmd_get_ ## t(fp, cmd); break
 
-int cmd_put(FILE *fp, const union cmd *cmd)
+int cmd_put(fs_file fp, const union cmd *cmd)
 {
     if (!fp || !cmd)
         return 0;
 
     assert(cmd->type > CMD_NONE && cmd->type < CMD_MAX);
 
-    fputc(cmd->type, fp);
+    fs_putc(cmd->type, fp);
 
     switch (cmd->type)
     {
@@ -623,10 +623,10 @@ int cmd_put(FILE *fp, const union cmd *cmd)
         break;
     }
 
-    return !feof(fp);
+    return !fs_eof(fp);
 }
 
-int cmd_get(FILE *fp, union cmd *cmd)
+int cmd_get(fs_file fp, union cmd *cmd)
 {
     int type;
     short size;
@@ -634,7 +634,7 @@ int cmd_get(FILE *fp, union cmd *cmd)
     if (!fp || !cmd)
         return 0;
 
-    if ((type = fgetc(fp)) != EOF)
+    if ((type = fs_getc(fp)) >= 0)
     {
         get_short(fp, &size);
 
@@ -642,7 +642,7 @@ int cmd_get(FILE *fp, union cmd *cmd)
 
         if (type >= CMD_MAX)
         {
-            fseek(fp, size, SEEK_CUR);
+            fs_seek(fp, size, SEEK_CUR);
             type = CMD_NONE;
         }
 
@@ -687,7 +687,7 @@ int cmd_get(FILE *fp, union cmd *cmd)
             break;
         }
 
-        return !feof(fp);
+        return !fs_eof(fp);
     }
     return 0;
 }
index af6fabf..09609cf 100644 (file)
@@ -308,9 +308,9 @@ union cmd
 /* No module should see this. */
 #undef HEADER
 
-#include <stdio.h>
+#include "fs.h"
 
-int cmd_put(FILE *, const union cmd *);
-int cmd_get(FILE *, union cmd *);
+int cmd_put(fs_file, const union cmd *);
+int cmd_get(fs_file, union cmd *);
 
 #endif
index 1aefe6a..b0ca104 100644 (file)
 #include <assert.h>
 
 #include "common.h"
+#include "fs.h"
 
 #define MAXSTR 256
 
 /*---------------------------------------------------------------------------*/
 
-int read_line(char **dst, FILE *fin)
+int read_line(char **dst, fs_file fin)
 {
     char buffer[MAXSTR] = "";
     int  buffer_size    = 0;
@@ -45,7 +46,7 @@ int read_line(char **dst, FILE *fin)
 
     while (!seen_newline)
     {
-        if (fgets(buffer, sizeof (buffer), fin) == NULL)
+        if (fs_gets(buffer, sizeof (buffer), fin) == NULL)
         {
             if (store_size > 0)
                 break;
index ce498d5..e292b09 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <time.h>
 #include <stdio.h>
+#include "fs.h"
 
 #ifdef __GNUC__
 #define NULL_TERMINATED __attribute__ ((__sentinel__))
@@ -34,7 +35,7 @@
 #define MIN(x, y) ((x) < (y) ? (x) : (y))
 #define MAX(x, y) ((x) > (y) ? (x) : (y))
 
-int   read_line(char **, FILE *);
+int   read_line(char **, fs_file);
 char *strip_newline(char *);
 
 char *dupe_string(const char *);
index 2425dc5..9046aef 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "config.h"
 #include "common.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -342,13 +343,13 @@ static int scan_key_and_value(char **dst_key, char **dst_val, char *line)
 
 void config_load(void)
 {
-    FILE *fp;
+    fs_file fh;
 
-    if ((fp = fopen(config_user(USER_CONFIG_FILE), "r")))
+    if ((fh = fs_open(USER_CONFIG_FILE, "r")))
     {
         char *line, *key, *val;
 
-        while (read_line(&line, fp))
+        while (read_line(&line, fh))
         {
             if (scan_key_and_value(&key, &val, line))
             {
@@ -410,7 +411,7 @@ void config_load(void)
             }
             free(line);
         }
-        fclose(fp);
+        fs_close(fh);
 
         dirty = 0;
     }
@@ -418,9 +419,9 @@ void config_load(void)
 
 void config_save(void)
 {
-    FILE *fp;
+    fs_file fh;
 
-    if (dirty && (fp = fopen(config_user(USER_CONFIG_FILE), "w")))
+    if (dirty && (fh = fs_open(USER_CONFIG_FILE, "w")))
     {
         int i;
 
@@ -465,9 +466,9 @@ void config_save(void)
             }
 
             if (s)
-                fprintf(fp, "%-25s %s\n", option_d[i].name, s);
+                fs_printf(fh, "%-25s %s\n", option_d[i].name, s);
             else
-                fprintf(fp, "%-25s %d\n", option_d[i].name, option_d[i].cur);
+                fs_printf(fh, "%-25s %d\n", option_d[i].name, option_d[i].cur);
         }
 
         /* Write out string options. */
@@ -475,10 +476,10 @@ void config_save(void)
         for (i = 0; i < ARRAYSIZE(option_s); i++)
         {
             if (option_s[i].cur && *option_s[i].cur)
-                fprintf(fp, "%-25s %s\n", option_s[i].name, option_s[i].cur);
+                fs_printf(fh, "%-25s %s\n", option_s[i].name, option_s[i].cur);
         }
 
-        fclose(fp);
+        fs_close(fh);
     }
 
     dirty = 0;
diff --git a/share/fs.c b/share/fs.c
new file mode 100644 (file)
index 0000000..1918f3c
--- /dev/null
@@ -0,0 +1,422 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <physfs.h>
+
+#include "fs.h"
+#include "dir.h"
+#include "array.h"
+
+/*
+ * This file implements the virtual file system layer.  Most file
+ * system and input/output operations are handled here.  There are
+ * basically two groups of functions here: low-level functions
+ * implemented directly using the PhysicsFS 1.0 API and higher-level
+ * functions implemented using the former group.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+struct fs_file
+{
+    PHYSFS_file *handle;
+};
+
+int fs_init(const char *argv0)
+{
+    return PHYSFS_init(argv0);
+}
+
+int fs_quit(void)
+{
+    return PHYSFS_deinit();
+}
+
+/* -------------------------------------------------------------------------- */
+
+const char *fs_base_dir(void)
+{
+    return PHYSFS_getBaseDir();
+}
+
+int fs_add_path(const char *path)
+{
+    return PHYSFS_addToSearchPath(path, 0);
+}
+
+int fs_set_write_dir(const char *path)
+{
+    return PHYSFS_setWriteDir(path);
+}
+
+/* -------------------------------------------------------------------------- */
+
+Array fs_dir_scan(const char *path, int (*filter)(struct dir_item *))
+{
+    return dir_scan(path, filter, PHYSFS_enumerateFiles, PHYSFS_freeList);
+}
+
+void fs_dir_free(Array items)
+{
+    dir_free(items);
+}
+
+/* -------------------------------------------------------------------------- */
+
+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))))
+    {
+        switch (mode[0])
+        {
+        case 'r':
+            fh->handle = PHYSFS_openRead(path);
+            break;
+
+        case 'w':
+            fh->handle = (mode[1] == '+' ?
+                          PHYSFS_openAppend(path) :
+                          PHYSFS_openWrite(path));
+            break;
+        }
+
+        if (fh->handle)
+        {
+            PHYSFS_setBuffer(fh->handle, 0x2000);
+        }
+        else
+        {
+            free(fh);
+            fh = NULL;
+        }
+    }
+
+    return fh;
+}
+
+int fs_close(fs_file fh)
+{
+    if (PHYSFS_close(fh->handle))
+    {
+        free(fh);
+        return 1;
+    }
+    return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+int fs_mkdir(const char *path)
+{
+    return PHYSFS_mkdir(path);
+}
+
+int fs_exists(const char *path)
+{
+    return PHYSFS_exists(path);
+}
+
+int fs_remove(const char *path)
+{
+    return PHYSFS_delete(path);
+}
+
+/* -------------------------------------------------------------------------- */
+
+int fs_read(void *data, int size, int count, fs_file fh)
+{
+    return PHYSFS_read(fh->handle, data, size, count);
+}
+
+int fs_write(const void *data, int size, int count, fs_file fh)
+{
+    return PHYSFS_write(fh->handle, data, size, count);
+}
+
+int fs_flush(fs_file fh)
+{
+    return PHYSFS_flush(fh->handle);
+}
+
+long fs_tell(fs_file fh)
+{
+    return PHYSFS_tell(fh->handle);
+}
+
+int fs_seek(fs_file fh, long offset, int whence)
+{
+    PHYSFS_uint64 pos = 0;
+    PHYSFS_sint64 cur = PHYSFS_tell(fh->handle);
+    PHYSFS_sint64 len = PHYSFS_fileLength(fh->handle);
+
+    switch (whence)
+    {
+    case SEEK_SET:
+        pos = offset;
+        break;
+
+    case SEEK_CUR:
+        if (cur < 0)
+            return -1;
+        pos = cur + offset;
+        break;
+
+    case SEEK_END:
+        if (len < 0)
+            return -1;
+        pos = len + offset;
+        break;
+    }
+
+    return PHYSFS_seek(fh->handle, pos);
+}
+
+int fs_eof(fs_file fh)
+{
+    return PHYSFS_eof(fh->handle);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * The code below does not use the PhysicsFS API.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+static int cmp_dir_items(const void *A, const void *B)
+{
+    const struct dir_item *a = A, *b = B;
+    return strcmp(a->path, b->path);
+}
+
+static int is_archive(struct dir_item *item)
+{
+    return strcmp(item->path + strlen(item->path) - 4, ".zip") == 0;
+}
+
+static void add_archives(const char *path)
+{
+    Array archives;
+    int i;
+
+    if ((archives = dir_scan(path, is_archive, NULL, NULL)))
+    {
+        array_sort(archives, cmp_dir_items);
+
+        for (i = 0; i < array_len(archives); i++)
+            fs_add_path(DIR_ITEM_GET(archives, i)->path);
+
+        dir_free(archives);
+    }
+}
+
+int fs_add_path_with_archives(const char *path)
+{
+    add_archives(path);
+    return fs_add_path(path);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void copy_file(fs_file src, fs_file dst)
+{
+    char buff[256];
+    int  size;
+
+    while ((size = fs_read(buff, 1, sizeof (buff), src)) > 0)
+        fs_write(buff, 1, size, dst);
+}
+
+int fs_rename(const char *src, const char *dst)
+{
+    fs_file fin, fout;
+    int copied = 0;
+
+    if ((fin = fs_open(src, "r")))
+    {
+        if ((fout = fs_open(dst, "w")))
+        {
+            copy_file(fin, fout);
+            copied = 1;
+            fs_close(fout);
+        }
+        fs_close(fin);
+    }
+
+    return copied && fs_remove(src);
+}
+
+/* -------------------------------------------------------------------------- */
+
+int fs_getc(fs_file fh)
+{
+    unsigned char c;
+
+    if (fs_read(&c, 1, 1, fh) != 1)
+        return -1;
+
+    return (int) c;
+}
+
+int fs_putc(int c, fs_file fh)
+{
+    unsigned char b = (unsigned char) c;
+
+    if (fs_write(&b, 1, 1, fh) != 1)
+        return -1;
+
+    return b;
+}
+
+int fs_puts(const char *src, fs_file fh)
+{
+    while (*src)
+        if (fs_putc(*src++, fh) < 0)
+            return -1;
+
+    return 0;
+}
+
+char *fs_gets(char *dst, int count, fs_file fh)
+{
+    char *s = dst;
+    char *cr = NULL;
+    int c = 0;
+
+    if (fs_eof(fh))
+        return NULL;
+
+    while (count > 1 && (c = fs_getc(fh)) >= 0)
+    {
+        count--;
+
+        *s = c;
+
+        /* Normalize possible CRLF and break. */
+
+        if (*s == '\n')
+        {
+            if (cr + 1 == s)
+                *cr = '\n';
+            else
+                s++;
+
+            break;
+        }
+
+        /* Note carriage return. */
+
+        if (*s == '\r')
+            cr = s;
+
+        s++;
+    }
+
+    if (count > 0)
+        *s = '\0';
+
+    return c < 0 ? NULL : dst;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Write out a multiline string to a file with appropriately converted
+ * linefeed characters.
+ */
+static int write_lines(const char *start, int length, fs_file fh)
+{
+#ifdef _WIN32
+    static const char crlf[] = "\r\n";
+#else
+    static const char crlf[] = "\n";
+#endif
+
+    int total_written = 0;
+
+    int datalen;
+    int written;
+    char *lf;
+
+    while (total_written < length)
+    {
+        lf = strchr(start, '\n');
+
+        datalen = lf ? (int) (lf - start) : length - total_written;
+        written = fs_write(start, 1, datalen, fh);
+
+        if (written < 0)
+            break;
+
+        total_written += written;
+
+        if (written < datalen)
+            break;
+
+        if (lf)
+        {
+            if (fs_puts(crlf, fh) < 0)
+                break;
+
+            total_written += 1;
+            start = lf + 1;
+        }
+    }
+
+    return total_written;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Trying to avoid defining a feature test macro for every platform by
+ * declaring vsnprintf with the C99 signature.  This is probably bad.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+extern int vsnprintf(char *, size_t, const char *, va_list);
+
+int fs_printf(fs_file fh, const char *fmt, ...)
+{
+    char *buff;
+    int len;
+
+    va_list ap;
+
+    va_start(ap, fmt);
+    len = vsnprintf(NULL, 0, fmt, ap) + 1;
+    va_end(ap);
+
+    if ((buff = malloc(len)))
+    {
+        int written;
+
+        va_start(ap, fmt);
+        vsnprintf(buff, len, fmt, ap);
+        va_end(ap);
+
+        /*
+         * HACK.  This assumes fs_printf is always called with the
+         * intention of writing text, and not arbitrary data.
+         */
+
+        written = write_lines(buff, strlen(buff), fh);
+
+        free(buff);
+
+        return written;
+    }
+
+    return 0;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/share/fs.h b/share/fs.h
new file mode 100644 (file)
index 0000000..d48415c
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef FS_H
+#define FS_H
+
+typedef struct fs_file *fs_file;
+
+int fs_init(const char *argv0);
+int fs_quit(void);
+
+const char *fs_error(void);
+
+const char *fs_base_dir(void);
+int         fs_add_path(const char *);
+int         fs_add_path_with_archives(const char *);
+int         fs_set_write_dir(const char *);
+
+int fs_exists(const char *);
+int fs_remove(const char *);
+int fs_rename(const char *, const char *);
+
+fs_file fs_open(const char *path, const char *mode);
+int     fs_close(fs_file);
+
+int  fs_read(void *data, int size, int count, fs_file);
+int  fs_write(const void *data, int size, int count, fs_file);
+int  fs_flush(fs_file);
+long fs_tell(fs_file);
+int  fs_seek(fs_file, long offset, int whence);
+int  fs_eof(fs_file);
+
+int   fs_getc(fs_file);
+char *fs_gets(char *dst, int count, fs_file fh);
+int   fs_putc(int c, fs_file);
+int   fs_puts(const char *src, fs_file);
+
+int fs_mkdir(const char *);
+
+#include <stdarg.h>
+
+int fs_printf(fs_file, const char *fmt, ...);
+
+#include "dir.h"
+#include "array.h"
+
+Array fs_dir_scan(const char *, int (*filter)(struct dir_item *));
+void  fs_dir_free(Array);
+
+#endif
diff --git a/share/fs_jpg.c b/share/fs_jpg.c
new file mode 100644 (file)
index 0000000..1de144f
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * jdatasrc.c
+ *
+ * Copyright (C) 1994-1996, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains decompression data source routines for the case of
+ * reading JPEG data from a file (or any stdio stream).  While these routines
+ * are sufficient for most applications, some will want to use a different
+ * source manager.
+ * IMPORTANT: we assume that fread() will correctly transcribe an array of
+ * JOCTETs from 8-bit-wide elements on external storage.  If char is wider
+ * than 8 bits on your machine, you may need to do some tweaking.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include "fs.h"
+#include "fs_jpg.h"
+
+/* -------------------------------------------------------------------------- */
+
+/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
+#include "jpeglib.h"
+#include "jerror.h"
+
+/* Expanded data source object for stdio input */
+
+typedef struct {
+  struct jpeg_source_mgr pub;  /* public fields */
+
+  fs_file infile;              /* source stream */
+  JOCTET * buffer;             /* start of buffer */
+  boolean start_of_file;       /* have we gotten any data yet? */
+} my_source_mgr;
+
+typedef my_source_mgr * my_src_ptr;
+
+#define INPUT_BUF_SIZE  4096   /* choose an efficiently fread'able size */
+
+
+/*
+ * Initialize source --- called by jpeg_read_header
+ * before any data is actually read.
+ */
+
+METHODDEF(void)
+init_source (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* We reset the empty-input-file flag for each image,
+   * but we don't clear the input buffer.
+   * This is correct behavior for reading a series of images from one source.
+   */
+  src->start_of_file = TRUE;
+}
+
+
+/*
+ * Fill the input buffer --- called whenever buffer is emptied.
+ *
+ * In typical applications, this should read fresh data into the buffer
+ * (ignoring the current state of next_input_byte & bytes_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been reloaded.  It is not necessary to
+ * fill the buffer entirely, only to obtain at least one more byte.
+ *
+ * There is no such thing as an EOF return.  If the end of the file has been
+ * reached, the routine has a choice of ERREXIT() or inserting fake data into
+ * the buffer.  In most cases, generating a warning message and inserting a
+ * fake EOI marker is the best course of action --- this will allow the
+ * decompressor to output however much of the image is there.  However,
+ * the resulting error message is misleading if the real problem is an empty
+ * input file, so we handle that case specially.
+ *
+ * In applications that need to be able to suspend compression due to input
+ * not being available yet, a FALSE return indicates that no more data can be
+ * obtained right now, but more may be forthcoming later.  In this situation,
+ * the decompressor will return to its caller (with an indication of the
+ * number of scanlines it has read, if any).  The application should resume
+ * decompression after it has loaded more data into the input buffer.  Note
+ * that there are substantial restrictions on the use of suspension --- see
+ * the documentation.
+ *
+ * When suspending, the decompressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point must be rescanned after resumption, so move it to
+ * the front of the buffer rather than discarding it.
+ */
+
+METHODDEF(boolean)
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+  size_t nbytes;
+
+  nbytes = fs_read(src->buffer, 1, INPUT_BUF_SIZE, src->infile);
+
+  if (nbytes <= 0) {
+    if (src->start_of_file)    /* Treat empty input file as fatal error */
+      ERREXIT(cinfo, JERR_INPUT_EMPTY);
+    WARNMS(cinfo, JWRN_JPEG_EOF);
+    /* Insert a fake EOI marker */
+    src->buffer[0] = (JOCTET) 0xFF;
+    src->buffer[1] = (JOCTET) JPEG_EOI;
+    nbytes = 2;
+  }
+
+  src->pub.next_input_byte = src->buffer;
+  src->pub.bytes_in_buffer = nbytes;
+  src->start_of_file = FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Skip data --- used to skip over a potentially large amount of
+ * uninteresting data (such as an APPn marker).
+ *
+ * Writers of suspendable-input applications must note that skip_input_data
+ * is not granted the right to give a suspension return.  If the skip extends
+ * beyond the data currently in the buffer, the buffer can be marked empty so
+ * that the next read will cause a fill_input_buffer call that can suspend.
+ * Arranging for additional bytes to be discarded before reloading the input
+ * buffer is the application writer's problem.
+ */
+
+METHODDEF(void)
+skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* Just a dumb implementation for now.  Could use fseek() except
+   * it doesn't work on pipes.  Not clear that being smart is worth
+   * any trouble anyway --- large skips are infrequent.
+   */
+  if (num_bytes > 0) {
+    while (num_bytes > (long) src->pub.bytes_in_buffer) {
+      num_bytes -= (long) src->pub.bytes_in_buffer;
+      (void) fill_input_buffer(cinfo);
+      /* note we assume that fill_input_buffer will never return FALSE,
+       * so suspension need not be handled.
+       */
+    }
+    src->pub.next_input_byte += (size_t) num_bytes;
+    src->pub.bytes_in_buffer -= (size_t) num_bytes;
+  }
+}
+
+
+/*
+ * An additional method that can be provided by data source modules is the
+ * resync_to_restart method for error recovery in the presence of RST markers.
+ * For the moment, this source module just uses the default resync method
+ * provided by the JPEG library.  That method assumes that no backtracking
+ * is possible.
+ */
+
+
+/*
+ * Terminate source --- called by jpeg_finish_decompress
+ * after all data has been read.  Often a no-op.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+METHODDEF(void)
+term_source (j_decompress_ptr cinfo)
+{
+  /* no work necessary here */
+}
+
+
+/*
+ * Prepare for input from a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing decompression.
+ */
+
+GLOBAL(void)
+fs_jpg_src (j_decompress_ptr cinfo, fs_file infile)
+{
+  my_src_ptr src;
+
+  /* The source object and input buffer are made permanent so that a series
+   * of JPEG images can be read from the same file by calling jpeg_stdio_src
+   * only before the first one.  (If we discarded the buffer at the end of
+   * one image, we'd likely lose the start of the next one.)
+   * This makes it unsafe to use this manager and a different source
+   * manager serially with the same JPEG object.  Caveat programmer.
+   */
+  if (cinfo->src == NULL) {    /* first time for this JPEG object? */
+    cinfo->src = (struct jpeg_source_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+                                 sizeof (my_source_mgr));
+    src = (my_src_ptr) cinfo->src;
+    src->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+                                 INPUT_BUF_SIZE * sizeof (JOCTET));
+  }
+
+  src = (my_src_ptr) cinfo->src;
+  src->pub.init_source = init_source;
+  src->pub.fill_input_buffer = fill_input_buffer;
+  src->pub.skip_input_data = skip_input_data;
+  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+  src->pub.term_source = term_source;
+  src->infile = infile;
+  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+  src->pub.next_input_byte = NULL; /* until buffer loaded */
+}
diff --git a/share/fs_jpg.h b/share/fs_jpg.h
new file mode 100644 (file)
index 0000000..35fabf7
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef FS_JPG
+#define FS_JPG
+
+/*
+ * jpeglib.h triggers errors due to missing size_t and FILE type
+ * definitions.  Include these two headers as a workaround.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <jpeglib.h>
+
+#include "fs.h"
+
+void fs_jpg_src(j_decompress_ptr cinfo, fs_file infile);
+
+#endif
diff --git a/share/fs_ov.c b/share/fs_ov.c
new file mode 100644 (file)
index 0000000..cdb60ad
--- /dev/null
@@ -0,0 +1,22 @@
+#include "fs.h"
+#include "fs_ov.h"
+
+size_t fs_ov_read(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+    return fs_read(ptr, size, nmemb, datasource);
+}
+
+int fs_ov_seek(void *datasource, ogg_int64_t offset, int whence)
+{
+    return fs_seek(datasource, offset, whence);
+}
+
+int fs_ov_close(void *datasource)
+{
+    return fs_close(datasource);
+}
+
+long fs_ov_tell(void *datasource)
+{
+    return fs_tell(datasource);
+}
diff --git a/share/fs_ov.h b/share/fs_ov.h
new file mode 100644 (file)
index 0000000..db4fcb7
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef FS_OV
+#define FS_OV
+
+#include <vorbis/vorbisfile.h>
+
+size_t fs_ov_read(void *ptr, size_t size, size_t nmemb, void *datasource);
+int    fs_ov_seek(void *datasource, ogg_int64_t offset, int whence);
+int    fs_ov_close(void *datasource);
+long   fs_ov_tell(void *datasource);
+
+#endif
diff --git a/share/fs_png.c b/share/fs_png.c
new file mode 100644 (file)
index 0000000..1575a66
--- /dev/null
@@ -0,0 +1,26 @@
+#include <png.h>
+#include <string.h>
+#include "fs_png.h"
+#include "fs.h"
+
+/*---------------------------------------------------------------------------*/
+
+void fs_png_read(png_structp readp, png_bytep data, png_size_t length)
+{
+    int read = fs_read(data, 1, length, png_get_io_ptr(readp));
+
+    if (read < length)
+        memset(data + read, 0, length - read);
+}
+
+void fs_png_write(png_structp writep, png_bytep data, png_size_t length)
+{
+    fs_write(data, 1, length, png_get_io_ptr(writep));
+}
+
+void fs_png_flush(png_structp writep)
+{
+    fs_flush(png_get_io_ptr(writep));
+}
+
+/*---------------------------------------------------------------------------*/
diff --git a/share/fs_png.h b/share/fs_png.h
new file mode 100644 (file)
index 0000000..7c5730d
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef FS_PNG
+#define FS_PNG
+
+#include <png.h>
+
+void fs_png_read (png_structp readp,  png_bytep data, png_size_t length);
+void fs_png_write(png_structp writep, png_bytep data, png_size_t length);
+void fs_png_flush(png_structp writep);
+
+#endif
diff --git a/share/fs_rwops.c b/share/fs_rwops.c
new file mode 100644 (file)
index 0000000..c3d49d7
--- /dev/null
@@ -0,0 +1,55 @@
+#include "fs_rwops.h"
+
+static int rwops_seek(SDL_RWops *ctx, int offset, int whence)
+{
+    fs_file fh = ctx->hidden.unknown.data1;
+    return fs_seek(fh, offset, whence) ? fs_tell(fh) : -1;
+}
+
+static int rwops_read(SDL_RWops *ctx, void *ptr, int size, int maxnum)
+{
+    return fs_read(ptr, size, maxnum, ctx->hidden.unknown.data1);
+}
+
+static int rwops_write(SDL_RWops *ctx, const void *ptr, int size, int num)
+{
+    return fs_write(ptr, size, num, ctx->hidden.unknown.data1);
+}
+
+static int rwops_close(SDL_RWops *ctx)
+{
+    fs_file fh = ctx->hidden.unknown.data1;
+
+    if (!fs_close(fh))
+        return -1;
+
+    SDL_FreeRW(ctx);
+    return 0;
+}
+
+SDL_RWops *fs_rwops_make(fs_file fh)
+{
+    SDL_RWops *ctx;
+
+    if ((ctx = SDL_AllocRW()))
+    {
+        ctx->seek  = rwops_seek;
+        ctx->read  = rwops_read;
+        ctx->write = rwops_write;
+        ctx->close = rwops_close;
+
+        ctx->hidden.unknown.data1 = fh;
+    }
+
+    return ctx;
+}
+
+SDL_RWops *fs_rwops_open(const char *path, const char *mode)
+{
+    fs_file fh;
+
+    if ((fh = fs_open(path, mode)))
+        return fs_rwops_make(fh);
+
+    return NULL;
+}
diff --git a/share/fs_rwops.h b/share/fs_rwops.h
new file mode 100644 (file)
index 0000000..bb87bfe
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef FS_RWOPS_H
+#define FS_RWOPS_H
+
+#include <SDL_rwops.h>
+#include "fs.h"
+
+SDL_RWops *fs_rwops_make(fs_file);
+SDL_RWops *fs_rwops_open(const char *path, const char *mode);
+
+#endif
index cf4c764..0defc67 100644 (file)
@@ -24,6 +24,9 @@
 #include "gui.h"
 #include "common.h"
 
+#include "fs.h"
+#include "fs_rwops.h"
+
 /*---------------------------------------------------------------------------*/
 
 #define MAXWIDGET 256
@@ -82,6 +85,7 @@ static struct widget widget[MAXWIDGET];
 static int           active;
 static int           radius;
 static TTF_Font     *font[3] = { NULL, NULL, NULL };
+static SDL_RWops    *fontrwops;
 
 static GLuint digit_text[3][11];
 static GLuint digit_list[3][11];
@@ -217,14 +221,14 @@ static const char *pick_font_path(void)
 {
     const char *path;
 
-    path = config_data(_(GUI_FACE));
+    path = _(GUI_FACE);
 
-    if (!file_exists(path))
+    if (!fs_exists(path))
     {
         fprintf(stderr, _("Font \"%s\" doesn't exist, trying default font.\n"),
                 path);
 
-        path = config_data(GUI_FACE);
+        path = GUI_FACE;
     }
 
     return path;
@@ -253,9 +257,23 @@ void gui_init(void)
 
         /* Load small, medium, and large typefaces. */
 
-        font[GUI_SML] = TTF_OpenFont(fontpath, s0);
-        font[GUI_MED] = TTF_OpenFont(fontpath, s1);
-        font[GUI_LRG] = TTF_OpenFont(fontpath, s2);
+        if (!(fontrwops = fs_rwops_open(fontpath, "r")))
+        {
+            fprintf(stderr, _("Could not open font %s.\n"), fontpath);
+            /* Return or no return, we'll probably crash now. */
+            return;
+        }
+
+        font[GUI_SML] = TTF_OpenFontRW(fontrwops, 0, s0);
+
+        SDL_RWseek(fontrwops, 0, SEEK_SET);
+        font[GUI_MED] = TTF_OpenFontRW(fontrwops, 0, s1);
+
+        SDL_RWseek(fontrwops, 0, SEEK_SET);
+        font[GUI_LRG] = TTF_OpenFontRW(fontrwops, 0, s2);
+
+        /* fontrwops remains open. */
+
         radius = s / 60;
 
         /* Initialize digit glyphs and lists for counters and clocks. */
@@ -339,6 +357,8 @@ void gui_free(void)
     if (font[GUI_MED]) TTF_CloseFont(font[GUI_MED]);
     if (font[GUI_SML]) TTF_CloseFont(font[GUI_SML]);
 
+    if (fontrwops) SDL_RWclose(fontrwops);
+
     TTF_Quit();
 }
 
index 76a54cc..4623547 100644 (file)
 #include "base_image.h"
 #include "config.h"
 
+#include "fs.h"
+#include "fs_png.h"
+
 /*---------------------------------------------------------------------------*/
 
 void image_snap(const char *filename)
 {
-    FILE       *filep  = NULL;
+    fs_file     filep  = NULL;
     png_structp writep = NULL;
     png_infop   infop  = NULL;
     png_bytep  *bytep  = NULL;
@@ -41,7 +44,7 @@ void image_snap(const char *filename)
 
     /* Initialize all PNG export data structures. */
 
-    if (!(filep = fopen(filename, FMODE_WB)))
+    if (!(filep = fs_open(filename, "w")))
         return;
     if (!(writep = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)))
         return;
@@ -54,7 +57,7 @@ void image_snap(const char *filename)
     {
         /* Initialize the PNG header. */
 
-        png_init_io (writep, filep);
+        png_set_write_fn(writep, filep, fs_png_write, fs_png_flush);
         png_set_IHDR(writep, infop, w, h, 8,
                      PNG_COLOR_TYPE_RGB,
                      PNG_INTERLACE_NONE,
@@ -90,7 +93,7 @@ void image_snap(const char *filename)
     /* Release all resources. */
 
     png_destroy_write_struct(&writep, &infop);
-    fclose(filep);
+    fs_close(filep);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -175,7 +178,7 @@ GLuint make_image_from_file(const char *filename)
 
     /* Load the image. */
 
-    if ((p = image_load(config_data(filename), &w, &h, &b)))
+    if ((p = image_load(filename, &w, &h, &b)))
     {
         o = make_texture(p, w, h, b);
         free(p);
@@ -298,7 +301,7 @@ SDL_Surface *load_surface(const char *filename)
     amask = 0xFF000000;
 #endif
 
-    if ((p = image_load(config_data(filename), &w, &h, &b)))
+    if ((p = image_load(filename, &w, &h, &b)))
     {
         void *q;
 
index 77baba6..8bf63d8 100644 (file)
@@ -82,9 +82,9 @@ void item_init(void)
 {
     int T = config_get_d(CONFIG_TEXTURES);
 
-    sol_load_gl(&item_coin_file, config_data("item/coin/coin.sol"), T, 0);
-    sol_load_gl(&item_grow_file, config_data("item/grow/grow.sol"), T, 0);
-    sol_load_gl(&item_shrink_file, config_data("item/shrink/shrink.sol"), T, 0);
+    sol_load_gl(&item_coin_file,   "item/coin/coin.sol",     T, 0);
+    sol_load_gl(&item_grow_file,   "item/grow/grow.sol",     T, 0);
+    sol_load_gl(&item_shrink_file, "item/shrink/shrink.sol", T, 0);
 }
 
 void item_free(void)
index e2968a4..8e85a95 100644 (file)
@@ -22,6 +22,7 @@
 #include "lang.h"
 #include "common.h"
 #include "base_config.h"
+#include "fs.h"
 
 /*---------------------------------------------------------------------------*/
 
 void lang_init(const char *domain)
 {
 #if ENABLE_NLS
-    char *dir = getenv("NEVERBALL_LOCALE");
+    char *dir = strdup(getenv("NEVERBALL_LOCALE"));
 
     if (!dir)
-        dir = path_resolve(config_exec_path, CONFIG_LOCALE);
+        dir = concat_string(fs_base_dir(), "/", CONFIG_LOCALE, NULL);
 
     errno = 0;
 
@@ -52,6 +53,8 @@ void lang_init(const char *domain)
     bindtextdomain(domain, dir);
     bind_textdomain_codeset(domain, DEFAULT_CODESET);
     textdomain(domain);
+
+    free(dir);
 #else
     return;
 #endif
index c448cd1..f217a41 100644 (file)
@@ -23,6 +23,8 @@
 #include "solid.h"
 #include "base_image.h"
 #include "base_config.h"
+#include "fs.h"
+#include "common.h"
 
 #define MAXSTR 256
 #define MAXKEY 16
@@ -353,8 +355,8 @@ static void size_image(const char *name, int *w, int *h)
     strcpy(jpg, name); strcat(jpg, ".jpg");
     strcpy(png, name); strcat(png, ".png");
 
-    if (size_load(config_data(png), w, h) ||
-        size_load(config_data(jpg), w, h))
+    if (size_load(png, w, h) ||
+        size_load(jpg, w, h))
     {
 
         if (image_n + 1 >= image_alloc)
@@ -388,10 +390,15 @@ static void size_image(const char *name, int *w, int *h)
 
 /* Read the given material file, adding a new material to the solid.  */
 
+#define scan_vec4(f, s, v)                                              \
+    if (fs_gets((s), sizeof (s), (f)))                                  \
+        sscanf((s), "%f %f %f %f", (v), (v) + 1, (v) + 2, (v) + 3)
+
 static int read_mtrl(struct s_file *fp, const char *name)
 {
+    static char line[MAXSTR];
     struct s_mtrl *mp;
-    FILE *fin;
+    fs_file fin;
     int mi;
 
     for (mi = 0; mi < fp->mc; mi++)
@@ -411,25 +418,30 @@ static int read_mtrl(struct s_file *fp, const char *name)
     mp->fl   = 0;
     mp->angle = 45.0f;
 
-    if ((fin = fopen(config_data(name), "r")))
+    if ((fin = fs_open(name, "r")))
     {
-        fscanf(fin,
-               "%f %f %f %f "
-               "%f %f %f %f "
-               "%f %f %f %f "
-               "%f %f %f %f "
-               "%f %d %f",
-               mp->d, mp->d + 1, mp->d + 2, mp->d + 3,
-               mp->a, mp->a + 1, mp->a + 2, mp->a + 3,
-               mp->s, mp->s + 1, mp->s + 2, mp->s + 3,
-               mp->e, mp->e + 1, mp->e + 2, mp->e + 3,
-               mp->h, &mp->fl, &mp->angle);
-        fclose(fin);
+        scan_vec4(fin, line, mp->d);
+        scan_vec4(fin, line, mp->a);
+        scan_vec4(fin, line, mp->s);
+        scan_vec4(fin, line, mp->e);
+
+        if (fs_gets(line, sizeof (line), fin))
+            mp->h[0] = strtod(line, NULL);
+
+        if (fs_gets(line, sizeof (line), fin))
+            mp->fl = strtol(line, NULL, 10);
+
+        if (fs_gets(line, sizeof (line), fin))
+            mp->angle = strtod(line, NULL);
+
+        fs_close(fin);
     }
 
     return mi;
 }
 
+#undef scan_vec4
+
 /*---------------------------------------------------------------------------*/
 
 /*
@@ -564,15 +576,15 @@ static void read_obj(struct s_file *fp, const char *name, int mi)
 {
     char line[MAXSTR];
     char mtrl[MAXSTR];
-    FILE *fin;
+    fs_file fin;
 
     int v0 = fp->vc;
     int t0 = fp->tc;
     int s0 = fp->sc;
 
-    if ((fin = fopen(config_data(name), "r")))
+    if ((fin = fs_open(name, "r")))
     {
-        while (fgets(line, MAXSTR, fin))
+        while (fs_gets(line, MAXSTR, fin))
         {
             if (strncmp(line, "usemtl", 6) == 0)
             {
@@ -590,7 +602,7 @@ static void read_obj(struct s_file *fp, const char *name, int mi)
             else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
             else if (strncmp(line, "v",  1) == 0) read_v (fp, line + 1);
         }
-        fclose(fin);
+        fs_close(fin);
     }
 }
 
@@ -688,11 +700,11 @@ static void make_plane(int   pi, float x0, float y0, float      z0,
 #define T_END 4
 #define T_NOP 5
 
-static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
+static int map_token(fs_file fin, int pi, char key[MAXSTR], char val[MAXSTR])
 {
     char buf[MAXSTR];
 
-    if (fgets(buf, MAXSTR, fin))
+    if (fs_gets(buf, MAXSTR, fin))
     {
         char c;
         float x0, y0, z0;
@@ -748,7 +760,7 @@ static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
 
 /* Parse a lump from the given file and add it to the solid. */
 
-static void read_lump(struct s_file *fp, FILE *fin)
+static void read_lump(struct s_file *fp, fs_file fin)
 {
     char k[MAXSTR];
     char v[MAXSTR];
@@ -1232,7 +1244,7 @@ static void make_ball(struct s_file *fp,
 
 /*---------------------------------------------------------------------------*/
 
-static void read_ent(struct s_file *fp, FILE *fin)
+static void read_ent(struct s_file *fp, fs_file fin)
 {
     char k[MAXKEY][MAXSTR];
     char v[MAXKEY][MAXSTR];
@@ -1272,7 +1284,7 @@ static void read_ent(struct s_file *fp, FILE *fin)
     if (!strcmp(v[i], "misc_model"))               make_body(fp, k, v, c, l0);
 }
 
-static void read_map(struct s_file *fp, FILE *fin)
+static void read_map(struct s_file *fp, fs_file fin)
 {
     char k[MAXSTR];
     char v[MAXSTR];
@@ -2438,49 +2450,60 @@ int main(int argc, char *argv[])
     char src[MAXSTR];
     char dst[MAXSTR];
     struct s_file f;
-    FILE *fin;
+    fs_file fin;
 
-    config_exec_path = argv[0];
+    if (!fs_init(argv[0]))
+    {
+        fprintf(stderr, "Failure to initialize virtual file system\n");
+        return 1;
+    }
 
     if (argc > 2)
     {
         if (argc > 3 && strcmp(argv[3], "--debug") == 0)
             debug_output = 1;
 
-        if (config_data_path(argv[2], NULL))
-        {
-            strncpy(src,  argv[1], MAXSTR);
-            strncpy(dst,  argv[1], MAXSTR);
+        fs_add_path     (dir_name(argv[1]));
+        fs_set_write_dir(dir_name(argv[1]));
 
-            if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
-                strcpy(dst + strlen(dst) - 4, ".sol");
-            else
-                strcat(dst, ".sol");
+        strncpy(src,  base_name(argv[1], NULL), MAXSTR);
+        strncpy(dst,  src,                      MAXSTR);
+
+        if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
+            strcpy(dst + strlen(dst) - 4, ".sol");
+        else
+            strcat(dst, ".sol");
 
-            if ((fin = fopen(src, "r")))
+        if ((fin = fs_open(src, "r")))
+        {
+            if (!fs_add_path_with_archives(argv[2]))
             {
-                init_file(&f);
-                read_map(&f, fin);
+                fprintf(stderr, "Failure to establish data directory\n");
+                fs_close(fin);
+                fs_quit();
+                return 1;
+            }
 
-                resolve();
-                targets(&f);
+            init_file(&f);
+            read_map(&f, fin);
 
-                clip_file(&f);
-                move_file(&f);
-                uniq_file(&f);
-                smth_file(&f);
-                sort_file(&f);
-                node_file(&f);
-                dump_file(&f, dst);
+            resolve();
+            targets(&f);
 
-                sol_stor(&f, dst);
+            clip_file(&f);
+            move_file(&f);
+            uniq_file(&f);
+            smth_file(&f);
+            sort_file(&f);
+            node_file(&f);
+            dump_file(&f, dst);
 
-                fclose(fin);
+            sol_stor(&f, dst);
 
-                free_imagedata();
-            }
+            fs_close(fin);
+
+            free_imagedata();
         }
-        else fprintf(stderr, "Failure to establish data directory\n");
     }
     else fprintf(stderr, "Usage: %s <map> <data> [--debug]\n", argv[0]);
 
index b2e277b..9a8e170 100644 (file)
 #include "solid.h"
 #include "base_config.h"
 #include "binary.h"
+#include "fs.h"
 
 #define MAGIC       0x4c4f53af
 #define SOL_VERSION 6
 
 /*---------------------------------------------------------------------------*/
 
-static void sol_load_mtrl(FILE *fin, struct s_mtrl *mp)
+static void sol_load_mtrl(fs_file fin, struct s_mtrl *mp)
 {
     get_array(fin,  mp->d, 4);
     get_array(fin,  mp->a, 4);
@@ -34,32 +35,32 @@ static void sol_load_mtrl(FILE *fin, struct s_mtrl *mp)
     get_array(fin,  mp->h, 1);
     get_index(fin, &mp->fl);
 
-    fread(mp->f, 1, PATHMAX, fin);
+    fs_read(mp->f, 1, PATHMAX, fin);
 }
 
-static void sol_load_vert(FILE *fin, struct s_vert *vp)
+static void sol_load_vert(fs_file fin, struct s_vert *vp)
 {
     get_array(fin,  vp->p, 3);
 }
 
-static void sol_load_edge(FILE *fin, struct s_edge *ep)
+static void sol_load_edge(fs_file fin, struct s_edge *ep)
 {
     get_index(fin, &ep->vi);
     get_index(fin, &ep->vj);
 }
 
-static void sol_load_side(FILE *fin, struct s_side *sp)
+static void sol_load_side(fs_file fin, struct s_side *sp)
 {
     get_array(fin,  sp->n, 3);
     get_float(fin, &sp->d);
 }
 
-static void sol_load_texc(FILE *fin, struct s_texc *tp)
+static void sol_load_texc(fs_file fin, struct s_texc *tp)
 {
     get_array(fin,  tp->u, 2);
 }
 
-static void sol_load_geom(FILE *fin, struct s_geom *gp)
+static void sol_load_geom(fs_file fin, struct s_geom *gp)
 {
     get_index(fin, &gp->mi);
     get_index(fin, &gp->ti);
@@ -73,7 +74,7 @@ static void sol_load_geom(FILE *fin, struct s_geom *gp)
     get_index(fin, &gp->vk);
 }
 
-static void sol_load_lump(FILE *fin, struct s_lump *lp)
+static void sol_load_lump(fs_file fin, struct s_lump *lp)
 {
     get_index(fin, &lp->fl);
     get_index(fin, &lp->v0);
@@ -86,7 +87,7 @@ static void sol_load_lump(FILE *fin, struct s_lump *lp)
     get_index(fin, &lp->sc);
 }
 
-static void sol_load_node(FILE *fin, struct s_node *np)
+static void sol_load_node(fs_file fin, struct s_node *np)
 {
     get_index(fin, &np->si);
     get_index(fin, &np->ni);
@@ -95,7 +96,7 @@ static void sol_load_node(FILE *fin, struct s_node *np)
     get_index(fin, &np->lc);
 }
 
-static void sol_load_path(FILE *fin, struct s_path *pp)
+static void sol_load_path(fs_file fin, struct s_path *pp)
 {
     get_array(fin,  pp->p, 3);
     get_float(fin, &pp->t);
@@ -104,7 +105,7 @@ static void sol_load_path(FILE *fin, struct s_path *pp)
     get_index(fin, &pp->s);
 }
 
-static void sol_load_body(FILE *fin, struct s_body *bp)
+static void sol_load_body(fs_file fin, struct s_body *bp)
 {
     get_index(fin, &bp->pi);
     get_index(fin, &bp->ni);
@@ -114,20 +115,20 @@ static void sol_load_body(FILE *fin, struct s_body *bp)
     get_index(fin, &bp->gc);
 }
 
-static void sol_load_item(FILE *fin, struct s_item *hp)
+static void sol_load_item(fs_file fin, struct s_item *hp)
 {
     get_array(fin,  hp->p, 3);
     get_index(fin, &hp->t);
     get_index(fin, &hp->n);
 }
 
-static void sol_load_goal(FILE *fin, struct s_goal *zp)
+static void sol_load_goal(fs_file fin, struct s_goal *zp)
 {
     get_array(fin,  zp->p, 3);
     get_float(fin, &zp->r);
 }
 
-static void sol_load_swch(FILE *fin, struct s_swch *xp)
+static void sol_load_swch(fs_file fin, struct s_swch *xp)
 {
     get_array(fin,  xp->p, 3);
     get_float(fin, &xp->r);
@@ -139,7 +140,7 @@ static void sol_load_swch(FILE *fin, struct s_swch *xp)
     get_index(fin, &xp->i);
 }
 
-static void sol_load_bill(FILE *fin, struct s_bill *rp)
+static void sol_load_bill(fs_file fin, struct s_bill *rp)
 {
     get_index(fin, &rp->fl);
     get_index(fin, &rp->mi);
@@ -153,14 +154,14 @@ static void sol_load_bill(FILE *fin, struct s_bill *rp)
     get_array(fin,  rp->p,  3);
 }
 
-static void sol_load_jump(FILE *fin, struct s_jump *jp)
+static void sol_load_jump(fs_file fin, struct s_jump *jp)
 {
     get_array(fin,  jp->p, 3);
     get_array(fin,  jp->q, 3);
     get_float(fin, &jp->r);
 }
 
-static void sol_load_ball(FILE *fin, struct s_ball *bp)
+static void sol_load_ball(fs_file fin, struct s_ball *bp)
 {
     get_array(fin,  bp->p, 3);
     get_float(fin, &bp->r);
@@ -178,19 +179,19 @@ static void sol_load_ball(FILE *fin, struct s_ball *bp)
     bp->e[2][2] = bp->E[2][2] = 1.0f;
 }
 
-static void sol_load_view(FILE *fin, struct s_view *wp)
+static void sol_load_view(fs_file fin, struct s_view *wp)
 {
     get_array(fin,  wp->p, 3);
     get_array(fin,  wp->q, 3);
 }
 
-static void sol_load_dict(FILE *fin, struct s_dict *dp)
+static void sol_load_dict(fs_file fin, struct s_dict *dp)
 {
     get_index(fin, &dp->ai);
     get_index(fin, &dp->aj);
 }
 
-static int sol_load_file(FILE *fin, struct s_file *fp)
+static int sol_load_file(fs_file fin, struct s_file *fp)
 {
     int i;
     int magic;
@@ -265,7 +266,7 @@ static int sol_load_file(FILE *fin, struct s_file *fp)
         fp->iv = (int           *) calloc(fp->ic, sizeof (int));
 
     if (fp->ac)
-        fread(fp->av, 1, fp->ac, fin);
+        fs_read(fp->av, 1, fp->ac, fin);
 
     for (i = 0; i < fp->dc; i++) sol_load_dict(fin, fp->dv + i);
     for (i = 0; i < fp->mc; i++) sol_load_mtrl(fin, fp->mv + i);
@@ -290,7 +291,7 @@ static int sol_load_file(FILE *fin, struct s_file *fp)
     return 1;
 }
 
-static int sol_load_head(FILE *fin, struct s_file *fp)
+static int sol_load_head(fs_file fin, struct s_file *fp)
 {
     int magic;
     int version;
@@ -324,12 +325,12 @@ static int sol_load_head(FILE *fin, struct s_file *fp)
     get_index(fin, &fp->wc);
     get_index(fin, &fp->ic);
 #endif
-    fseek(fin, 18 * 4, SEEK_CUR);
+    fs_seek(fin, 18 * 4, SEEK_CUR);
 
     if (fp->ac)
     {
         fp->av = (char *) calloc(fp->ac, sizeof (char));
-        fread(fp->av, 1, fp->ac, fin);
+        fs_read(fp->av, 1, fp->ac, fin);
     }
 
     if (fp->dc)
@@ -347,33 +348,33 @@ static int sol_load_head(FILE *fin, struct s_file *fp)
 
 int sol_load_only_file(struct s_file *fp, const char *filename)
 {
-    FILE *fin;
+    fs_file fin;
     int res = 0;
 
-    if ((fin = fopen(filename, FMODE_RB)))
+    if ((fin = fs_open(filename, "r")))
     {
         res = sol_load_file(fin, fp);
-        fclose(fin);
+        fs_close(fin);
     }
     return res;
 }
 
 int sol_load_only_head(struct s_file *fp, const char *filename)
 {
-    FILE *fin;
+    fs_file fin;
     int res = 0;
 
-    if ((fin = fopen(filename, FMODE_RB)))
+    if ((fin = fs_open(filename, "r")))
     {
         res = sol_load_head(fin, fp);
-        fclose(fin);
+        fs_close(fin);
     }
     return res;
 }
 
 /*---------------------------------------------------------------------------*/
 
-static void sol_stor_mtrl(FILE *fout, struct s_mtrl *mp)
+static void sol_stor_mtrl(fs_file fout, struct s_mtrl *mp)
 {
     put_array(fout,  mp->d, 4);
     put_array(fout,  mp->a, 4);
@@ -382,32 +383,32 @@ static void sol_stor_mtrl(FILE *fout, struct s_mtrl *mp)
     put_array(fout,  mp->h, 1);
     put_index(fout, &mp->fl);
 
-    fwrite(mp->f, 1, PATHMAX, fout);
+    fs_write(mp->f, 1, PATHMAX, fout);
 }
 
-static void sol_stor_vert(FILE *fout, struct s_vert *vp)
+static void sol_stor_vert(fs_file fout, struct s_vert *vp)
 {
     put_array(fout,  vp->p, 3);
 }
 
-static void sol_stor_edge(FILE *fout, struct s_edge *ep)
+static void sol_stor_edge(fs_file fout, struct s_edge *ep)
 {
     put_index(fout, &ep->vi);
     put_index(fout, &ep->vj);
 }
 
-static void sol_stor_side(FILE *fout, struct s_side *sp)
+static void sol_stor_side(fs_file fout, struct s_side *sp)
 {
     put_array(fout,  sp->n, 3);
     put_float(fout, &sp->d);
 }
 
-static void sol_stor_texc(FILE *fout, struct s_texc *tp)
+static void sol_stor_texc(fs_file fout, struct s_texc *tp)
 {
     put_array(fout,  tp->u, 2);
 }
 
-static void sol_stor_geom(FILE *fout, struct s_geom *gp)
+static void sol_stor_geom(fs_file fout, struct s_geom *gp)
 {
     put_index(fout, &gp->mi);
     put_index(fout, &gp->ti);
@@ -421,7 +422,7 @@ static void sol_stor_geom(FILE *fout, struct s_geom *gp)
     put_index(fout, &gp->vk);
 }
 
-static void sol_stor_lump(FILE *fout, struct s_lump *lp)
+static void sol_stor_lump(fs_file fout, struct s_lump *lp)
 {
     put_index(fout, &lp->fl);
     put_index(fout, &lp->v0);
@@ -434,7 +435,7 @@ static void sol_stor_lump(FILE *fout, struct s_lump *lp)
     put_index(fout, &lp->sc);
 }
 
-static void sol_stor_node(FILE *fout, struct s_node *np)
+static void sol_stor_node(fs_file fout, struct s_node *np)
 {
     put_index(fout, &np->si);
     put_index(fout, &np->ni);
@@ -443,7 +444,7 @@ static void sol_stor_node(FILE *fout, struct s_node *np)
     put_index(fout, &np->lc);
 }
 
-static void sol_stor_path(FILE *fout, struct s_path *pp)
+static void sol_stor_path(fs_file fout, struct s_path *pp)
 {
     put_array(fout,  pp->p, 3);
     put_float(fout, &pp->t);
@@ -452,7 +453,7 @@ static void sol_stor_path(FILE *fout, struct s_path *pp)
     put_index(fout, &pp->s);
 }
 
-static void sol_stor_body(FILE *fout, struct s_body *bp)
+static void sol_stor_body(fs_file fout, struct s_body *bp)
 {
     put_index(fout, &bp->pi);
     put_index(fout, &bp->ni);
@@ -462,20 +463,20 @@ static void sol_stor_body(FILE *fout, struct s_body *bp)
     put_index(fout, &bp->gc);
 }
 
-static void sol_stor_item(FILE *fout, struct s_item *hp)
+static void sol_stor_item(fs_file fout, struct s_item *hp)
 {
     put_array(fout,  hp->p, 3);
     put_index(fout, &hp->t);
     put_index(fout, &hp->n);
 }
 
-static void sol_stor_goal(FILE *fout, struct s_goal *zp)
+static void sol_stor_goal(fs_file fout, struct s_goal *zp)
 {
     put_array(fout,  zp->p, 3);
     put_float(fout, &zp->r);
 }
 
-static void sol_stor_swch(FILE *fout, struct s_swch *xp)
+static void sol_stor_swch(fs_file fout, struct s_swch *xp)
 {
     put_array(fout,  xp->p, 3);
     put_float(fout, &xp->r);
@@ -487,7 +488,7 @@ static void sol_stor_swch(FILE *fout, struct s_swch *xp)
     put_index(fout, &xp->i);
 }
 
-static void sol_stor_bill(FILE *fout, struct s_bill *rp)
+static void sol_stor_bill(fs_file fout, struct s_bill *rp)
 {
     put_index(fout, &rp->fl);
     put_index(fout, &rp->mi);
@@ -501,32 +502,32 @@ static void sol_stor_bill(FILE *fout, struct s_bill *rp)
     put_array(fout,  rp->p,  3);
 }
 
-static void sol_stor_jump(FILE *fout, struct s_jump *jp)
+static void sol_stor_jump(fs_file fout, struct s_jump *jp)
 {
     put_array(fout,  jp->p, 3);
     put_array(fout,  jp->q, 3);
     put_float(fout, &jp->r);
 }
 
-static void sol_stor_ball(FILE *fout, struct s_ball *bp)
+static void sol_stor_ball(fs_file fout, struct s_ball *bp)
 {
     put_array(fout,  bp->p, 3);
     put_float(fout, &bp->r);
 }
 
-static void sol_stor_view(FILE *fout, struct s_view *wp)
+static void sol_stor_view(fs_file fout, struct s_view *wp)
 {
     put_array(fout,  wp->p, 3);
     put_array(fout,  wp->q, 3);
 }
 
-static void sol_stor_dict(FILE *fout, struct s_dict *dp)
+static void sol_stor_dict(fs_file fout, struct s_dict *dp)
 {
     put_index(fout, &dp->ai);
     put_index(fout, &dp->aj);
 }
 
-static void sol_stor_file(FILE *fout, struct s_file *fp)
+static void sol_stor_file(fs_file fout, struct s_file *fp)
 {
     int i;
     int magic   = MAGIC;
@@ -556,7 +557,7 @@ static void sol_stor_file(FILE *fout, struct s_file *fp)
     put_index(fout, &fp->wc);
     put_index(fout, &fp->ic);
 
-    fwrite(fp->av, 1, fp->ac, fout);
+    fs_write(fp->av, 1, fp->ac, fout);
 
     for (i = 0; i < fp->dc; i++) sol_stor_dict(fout, fp->dv + i);
     for (i = 0; i < fp->mc; i++) sol_stor_mtrl(fout, fp->mv + i);
@@ -583,12 +584,12 @@ static void sol_stor_file(FILE *fout, struct s_file *fp)
 
 int sol_stor(struct s_file *fp, const char *filename)
 {
-    FILE *fout;
+    fs_file fout;
 
-    if ((fout = fopen(filename, FMODE_WB)))
+    if ((fout = fs_open(filename, "w")))
     {
         sol_stor_file(fout, fp);
-        fclose(fout);
+        fs_close(fout);
 
         return 1;
     }
index d27a666..6fbec8b 100644 (file)
@@ -62,7 +62,7 @@ void set_EWMH_icon(const char *filename)
      * [*] http://standards.freedesktop.org/wm-spec/latest/
      */
 
-    if ((p = image_load(config_data(filename), &w, &h, &b)))
+    if ((p = image_load(filename, &w, &h, &b)))
     {
         long *data = NULL;