supertux is using physfs now, this simplifies the code and generalises file handling
authorMatthias Braun <matze@braunis.de>
Tue, 7 Jun 2005 15:59:27 +0000 (15:59 +0000)
committerMatthias Braun <matze@braunis.de>
Tue, 7 Jun 2005 15:59:27 +0000 (15:59 +0000)
SVN-Revision: 2575

37 files changed:
configure.ac
data/levels/test/script.stl
mk/jam/np_findlib.m4 [new file with mode: 0644]
mk/jam/np_lang_program.m4 [new file with mode: 0644]
src/Jamfile
src/audio/sound_manager.cpp
src/file_system.cpp
src/file_system.h
src/game_session.cpp
src/gameconfig.cpp
src/level.cpp
src/level_subset.cpp
src/lisp/list_iterator.cpp
src/lisp/parser.cpp
src/lisp/writer.cpp
src/lisp/writer.h
src/main.cpp
src/object/background.cpp
src/object/particlesystem.cpp
src/object/particlesystem_interactive.cpp
src/resources.cpp
src/resources.h
src/scripting/functions.cpp
src/scripting/functions.h
src/scripting/script_interpreter.cpp
src/scripting/script_interpreter.h
src/scripting/wrapper.cpp
src/sector.cpp
src/sprite/sprite_data.cpp
src/textscroller.cpp
src/tile.cpp
src/tile_manager.cpp
src/tinygettext/tinygettext.cpp
src/tinygettext/tinygettext.h
src/title.cpp
src/video/surface.cpp
src/worldmap.cpp

index c2dc0e8..a504fd2 100644 (file)
@@ -97,6 +97,18 @@ AM_ICONV
 AC_SUBST([ICONV_LIBS], [$LIBICONV])
 
 dnl ===========================================================================
+dnl Check for OpenGL
+if test "$enable_opengl" = "yes"; then
+  AX_CHECK_GL
+fi
+if test "$no_gl" = "yes" -o "$enable_opengl" = "no"; then
+  GL_AVAILABLE="no"
+  AC_DEFINE([NOOPENGL],, [Define if opengl should not be used])
+else
+  GL_AVAILABLE="yes"
+fi
+AC_SUBST([GL_AVAILABLE])
+
 dnl Check for SDL
 SDL_VERSION=1.2.4
 AM_PATH_SDL($SDL_VERSION,
@@ -126,16 +138,14 @@ NP_FINDLIB([SDLIMAGE], [SDL_image], [SDL_image >= 1.2],
         [AC_MSG_ERROR([Please install SDLImage >= 1.2.1])],
         [$SDL_CFLAGS], [$SDL_LIBS])
 
-if test "$enable_opengl" = "yes"; then
-  AX_CHECK_GL
-fi
-if test "$no_gl" = "yes" -o "$enable_opengl" = "no"; then
-  GL_AVAILABLE="no"
-  AC_DEFINE([NOOPENGL],, [Define if opengl should not be used])
-else
-  GL_AVAILABLE="yes"
-fi
-AC_SUBST([GL_AVAILABLE])
+NP_FINDLIB([PHYSFS], [physfs], [physfs >= 1.0.0],
+        NP_LANG_PROGRAM([#include <physfs.h>
+#if PHYSFS_VER_MAJOR < 1
+# error PHYSFS is too old
+#endif]),
+        [], [-lphysfs],
+        [],
+        [AC_MSG_ERROR([Please install physfs >= 1.0])])
 
 dnl Checks for library functions.
 AC_CHECK_FUNCS(mkdir strdup strstr)
index fad86a3..2f9bd84 100644 (file)
          (solid #f)
        )
        (init-script "
-function wait(time) {
-    set_wakeup_time(time);
-    suspend();
-}
 Text.set_text(translate(\"The Crazy Nolok Dance\"));
 Text.fade_in(2);
 TUX.set_animation(\"jump\");
diff --git a/mk/jam/np_findlib.m4 b/mk/jam/np_findlib.m4
new file mode 100644 (file)
index 0000000..f2c2d2f
--- /dev/null
@@ -0,0 +1,51 @@
+#  NP_FINDLIB(VARNAME, NAME, STRING, TESTAPP, CFLAGS, LIBS, ACTION_IF_FOUND,
+#             ACTION_IF_NOT_FOUND, EXTRACFLAGS, EXTRALIBS)
+AC_DEFUN([NP_FINDLIB], [
+    AC_ARG_WITH([lib$2], [AC_HELP_STRING([--with-lib$2=dir],
+        [specify location of lib$2 if not detected automatically; uses
+        dir, dir/include and dir/lib])])
+            
+    save_CFLAGS="$CFLAGS"
+    save_CPPFLAGS="$CPPFLAGS"
+    save_LIBS="$LIBS"
+
+    RESCFLAGS="$5"
+    RESLIBS="$6"
+    CFLAGS="$CFLAGS $5 $9"
+    CPPFLAGS="$CPPFLAGS $5 $9"
+    LIBS="$LIBS $6 $10"
+
+    AS_IF([test -n "$with_lib$2"], [
+        CFLAGS="-I$with_lib$2/include $CFLAGS"
+        CPPFLAGS="-I$with_lib$2/include $CPPFLAGS"
+        LIBS="-L$with_lib$2/lib $LIBS"
+        RESCFLAGS="-I$with_lib$2/include $RESCFLAGS"
+        RESLIBS="-L$with_lib$2/lib $RESLIBS"
+    ])
+
+    AC_MSG_CHECKING([for $3])
+
+    AC_LINK_IFELSE([$4], [buildok=yes], [buildok=no])
+
+    LIBS=$save_LIBS
+    CPPFLAGS=$save_CPPFLAGS
+    CFLAGS=$save_CFLAGS
+
+    AS_IF([test $buildok = yes],
+       [AC_MSG_RESULT([found])
+        $1_AVAILABLE=yes
+        $1_CFLAGS="$RESCFLAGS"
+        $1_LIBS="$RESLIBS"],
+       [AC_MSG_RESULT([not found])
+        $1_AVAILABLE=no
+        $1_CFLAGS=""
+        $1_LIBS=""])
+    
+    AC_SUBST([$1_AVAILABLE])
+    AC_SUBST([$1_CFLAGS])
+    AC_SUBST([$1_LIBS])
+
+    AS_IF([test $buildok = yes],
+       [ifelse([$7], , :, [$7])],
+       [ifelse([$8], , :, [$8])])
+])
diff --git a/mk/jam/np_lang_program.m4 b/mk/jam/np_lang_program.m4
new file mode 100644 (file)
index 0000000..e6559d5
--- /dev/null
@@ -0,0 +1,13 @@
+# NP_LANG_PROGRAM, custom version of AC_LANG_PROGRAM (because SDL on win32
+# NEEDS main(int argc, char** argv)
+AC_DEFUN([NP_LANG_PROGRAM],
+[$1
+m4_ifdef([_AC_LANG_PROGRAM_C_F77_HOOKS], [_AC_LANG_PROGRAM_C_F77_HOOKS])[]dnl
+int
+main(int argc, char** argv)
+{
+$2
+  ;
+  return 0;
+}
+])
index aeb443c..ba6b375 100644 (file)
@@ -5,24 +5,25 @@ SubInclude TOP src scripting ;
 
 sources = 
     [ Wildcard *.cpp *.h ]
-    [ Wildcard math : *.cpp *.h ]
-    [ Wildcard video : *.cpp *.h ]
     [ Wildcard audio : *.cpp *.h ]
+    [ Wildcard badguy : *.cpp *.h ]
+    [ Wildcard control : *.cpp *.h ]
     [ Wildcard gui : *.cpp *.h ]
     [ Wildcard lisp : *.cpp *.h ]
+    [ Wildcard math : *.cpp *.h ]
     [ Wildcard object : *.cpp *.h ]
-    [ Wildcard badguy : *.cpp *.h ]
+    [ Wildcard physfs : *.cpp *.h ]
     [ Wildcard sprite : *.cpp *.h ]
-    [ Wildcard trigger : *.cpp *.h ]
     [ Wildcard tinygettext : *.cpp *.h ]
-    [ Wildcard control : *.cpp *.h ]
+    [ Wildcard trigger : *.cpp *.h ]
+    [ Wildcard video : *.cpp *.h ]
 ;
 TRANSLATABLE_SOURCES += [ SearchSource $(sources) ] ;
 
 Application supertux : $(sources) $(wrapper_objects) ;
-C++Flags supertux : -DDATA_PREFIX='\"$(appdatadir)\"' ;
+C++Flags supertux : -DAPPDATADIR='\"$(appdatadir)\"' ;
 LinkWith supertux : squirrel ;
-ExternalLibs supertux : SDL SDLMIXER SDLIMAGE GL ICONV ;
+ExternalLibs supertux : SDL SDLMIXER SDLIMAGE GL ICONV PHYSFS BINRELOC ;
 Help supertux : "Build the supertux executable" ;
 IncludeDir supertux : squirrel/include ;
 
index 329190e..bb26ed2 100644 (file)
 #include <iostream>
 #include <stdexcept>
 #include <sstream>
+#include <physfs.h>
 
 #include "audio/sound_manager.h"
 
 #include "audio/musicref.h"
+#include "physfs/physfs_sdl.h"
 #include "moving_object.h"
 #include "resources.h"
 
@@ -122,25 +124,28 @@ SoundManager::load_music(const std::string& file)
 }
 
 bool
-SoundManager::exists_music(const std::string& file)
+SoundManager::exists_music(const std::string& filename)
 {
   if(!audio_device)
     return true;
   
   // song already loaded?
-  std::map<std::string, MusicResource>::iterator i = musics.find(file);
+  std::map<std::string, MusicResource>::iterator i = musics.find(filename);
   if(i != musics.end()) {
     return true;                                      
   }
-  
-  Mix_Music* song = Mix_LoadMUS(file.c_str());
+  const char* dir = PHYSFS_getRealDir(filename.c_str());
+  if(dir == 0)
+    return false;
+  Mix_Music* song = Mix_LoadMUS( (std::string(dir) + "/" + filename).c_str() );
   if(song == 0)
     return false;
 
   // insert into music list
   std::pair<std::map<std::string, MusicResource>::iterator, bool> result = 
     musics.insert(
-        std::make_pair<std::string, MusicResource> (file, MusicResource()));
+        std::make_pair<std::string, MusicResource> (filename, MusicResource()));
   MusicResource& resource = result.first->second;
   resource.manager = this;
   resource.music = song;
@@ -236,9 +241,8 @@ Mix_Chunk* SoundManager::preload_sound(const std::string& name)
   std::string filename = "sounds/";
   filename += name;
   filename += ".wav";
-  filename = get_resource_filename(filename);
   
-  Mix_Chunk* chunk = Mix_LoadWAV(filename.c_str());
+  Mix_Chunk* chunk = Mix_LoadWAV_RW(get_physfs_SDLRWops(filename), true);
   if(chunk != 0) {
     sounds.insert(std::make_pair(name, chunk));
   }
index af243f8..8d8b811 100644 (file)
 #include <config.h>
 
 #include "file_system.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.h>
-#ifndef WIN32
-#include <libgen.h>
-#endif
 
-#include "resources.h"
-
-#ifdef WIN32
-#define mkdir(dir, mode)    mkdir(dir)
-#endif
+#include <string>
 
 namespace FileSystem
 {
 
-/* Does the given file exist and is it accessible? */
-bool faccessible(const std::string& filename)
-{
-  FILE* f = fopen(filename.c_str(), "r");
-  if(f == 0)
-    return false;
-
-  fclose(f);
-  return true;
-}
-
-/* Can we write to this location? */
-bool fwriteable(const std::string& filename)
-{
-  FILE* f = fopen(filename.c_str(), "wa");
-  if (f == 0)
-    return false;
-  
-  fclose(f);
-  return true;
-}
-
-/* Makes sure a directory is created in either the SuperTux home directory or the SuperTux base directory.*/
-bool fcreatedir(const std::string& relative_dir)
-{
-  std::string path = user_dir + "/" + relative_dir + "/";
-  if(mkdir(path.c_str(),0755) == 0)
-    return true;
-  
-  path = datadir + "/" + relative_dir + "/";
-  if(mkdir(path.c_str(),0755) == 0)
-    return true;
-    
-  return false;
-}
-
-/* Get all names of sub-directories in a certain directory. */
-/* Returns the number of sub-directories found. */
-/* Note: The user has to free the allocated space. */
-std::set<std::string> dsubdirs(const std::string &rel_path,
-    const std::string& expected_file)
-{
-  DIR *dirStructP;
-  struct dirent *direntp;
-  std::set<std::string> sdirs;
-  std::string filename;
-  std::string path = user_dir + "/" + rel_path;
-
-  if((dirStructP = opendir(path.c_str())) != NULL)
-    {
-      while((direntp = readdir(dirStructP)) != NULL)
-        {
-          std::string absolute_filename;
-          struct stat buf;
-
-          absolute_filename = path + "/" + direntp->d_name;
-
-          if (stat(absolute_filename.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode))
-            {
-              if(!expected_file.empty())
-                {
-                  filename = path + "/" + direntp->d_name + "/" + expected_file;
-                  if(!faccessible(filename))
-                    continue;
-                }
-
-             sdirs.insert(direntp->d_name);
-            }
-        }
-      closedir(dirStructP);
-    }
-
-  path = datadir + "/" + rel_path;
-  if((dirStructP = opendir(path.c_str())) != NULL)
-    {
-      while((direntp = readdir(dirStructP)) != NULL)
-        {
-          std::string absolute_filename;
-          struct stat buf;
-
-          absolute_filename = path + "/" + direntp->d_name;
-
-          if (stat(absolute_filename.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode))
-            {
-              if(!expected_file.empty())
-                {
-                  filename = path + "/" + direntp->d_name + "/" + expected_file;
-                  if(!faccessible(filename.c_str()))
-                    {
-                      continue;
-                    }
-                  else
-                    {
-                      filename = user_dir + "/" + rel_path + "/" + direntp->d_name + "/" + expected_file;
-                      if(faccessible(filename.c_str()))
-                        continue;
-                    }
-                }
-
-             sdirs.insert(direntp->d_name);
-            }
-        }
-      closedir(dirStructP);
-    }
-
-  return sdirs;
-}
-
-std::set<std::string> dfiles(const std::string& rel_path,
-    const std::string& glob, const std::string& exception_str)
-{
-  DIR *dirStructP;
-  struct dirent *direntp;
-  std::set<std::string> sdirs;
-  std::string path = user_dir + "/" + rel_path;
-
-  if((dirStructP = opendir(path.c_str())) != NULL)
-    {
-      while((direntp = readdir(dirStructP)) != NULL)
-        {
-          std::string absolute_filename;
-          struct stat buf;
-
-          absolute_filename = path + "/" + direntp->d_name;
-
-          if (stat(absolute_filename.c_str(), &buf) == 0 && S_ISREG(buf.st_mode))
-            {
-              if(!exception_str.empty())
-                {
-                  if(strstr(direntp->d_name,exception_str.c_str()) != NULL)
-                    continue;
-                }
-              if(!glob.empty())
-                if(strstr(direntp->d_name,glob.c_str()) == NULL)
-                  continue;
-
-             sdirs.insert(direntp->d_name);
-            }
-        }
-      closedir(dirStructP);
-    }
-
-  path = datadir + "/" + rel_path;
-  if((dirStructP = opendir(path.c_str())) != NULL)
-    {
-      while((direntp = readdir(dirStructP)) != NULL)
-        {
-          std::string absolute_filename;
-          struct stat buf;
-
-          absolute_filename = path + "/" + direntp->d_name;
-
-          if (stat(absolute_filename.c_str(), &buf) == 0 && S_ISREG(buf.st_mode))
-            {
-              if(!exception_str.empty())
-                {
-                  if(strstr(direntp->d_name,exception_str.c_str()) != NULL)
-                    continue;
-                }
-              if(!glob.empty())
-                if(strstr(direntp->d_name,glob.c_str()) == NULL)
-                  continue;
-
-             sdirs.insert(direntp->d_name);
-            }
-        }
-      closedir(dirStructP);
-    }
-
-  return sdirs;
-}
-
 std::string dirname(const std::string& filename)
 {
   std::string::size_type p = filename.find_last_of('/');
@@ -198,24 +16,13 @@ std::string dirname(const std::string& filename)
   return filename.substr(0, p+1);
 }
 
-std::set<std::string> read_directory(const std::string& pathname)
+std::string basename(const std::string& filename)
 {
-  std::set<std::string> dirnames;
-  
-  DIR* dir = opendir(pathname.c_str());
-  if (dir)
-    {
-      struct dirent *direntp;
-      
-      while((direntp = readdir(dir)))
-        {
-          dirnames.insert(direntp->d_name);
-        }
-      
-      closedir(dir);
-    }
+  std::string::size_type p = filename.find_last_of('/');
+  if(p == std::string::npos)
+    return filename;
 
-  return dirnames;
+  return filename.substr(p, filename.size()-p);
 }
 
 }
index 341e1fc..47f86f4 100644 (file)
@@ -6,15 +6,8 @@
 
 namespace FileSystem
 {
-  bool faccessible(const std::string& filename);
-  bool fcreatedir(const std::string& relative_dir);
-  bool fwriteable(const std::string& filename);
-  std::set<std::string> read_directory(const std::string& pathname);
-  std::set<std::string> dsubdirs(const std::string& rel_path,
-          const std::string& expected_file);
-  std::set<std::string> dfiles(const std::string& rel_path,
-          const std::string& glob, const std::string& exception_str);
   std::string dirname(const std::string& filename);
+  std::string basename(const std::string& filename);
 }
 
 #endif
index 2928ed1..4f5495c 100644 (file)
@@ -765,7 +765,7 @@ std::string slotinfo(int slot)
   std::string title;
   std::stringstream stream;
   stream << slot;
-  slotfile = user_dir + "/save/slot" + stream.str() + ".stsg";
+  slotfile = "save/slot" + stream.str() + ".stsg";
 
   try {
     lisp::Parser parser;
@@ -796,7 +796,7 @@ bool process_load_game_menu()
   
   std::stringstream stream;
   stream << slot;
-  std::string slotfile = user_dir + "/save/slot" + stream.str() + ".stsg";
+  std::string slotfile = "save/slot" + stream.str() + ".stsg";
 
   fadeout(256);
   DrawingContext context;
index 2387c8c..94fa2ac 100644 (file)
@@ -61,7 +61,7 @@ void
 Config::load()
 {
   lisp::Parser parser;
-  std::auto_ptr<lisp::Lisp> root (parser.parse(user_dir + "/config"));
+  std::auto_ptr<lisp::Lisp> root (parser.parse("config"));
 
   const lisp::Lisp* config_lisp = root->get_lisp("supertux-config");
   if(!config_lisp)
@@ -96,14 +96,7 @@ Config::load()
 void
 Config::save()
 {
-  std::string configfile = user_dir + "/config";
-  std::ofstream file( (user_dir + "/config").c_str() );
-  if(!file.good()) {
-    std::stringstream msg;
-    msg << "Couldn't write config file '" << configfile << "'";
-    throw std::runtime_error(msg.str());
-  }
-  lisp::Writer writer(file);
+  lisp::Writer writer("config");
 
   writer.start_list("supertux-config");
 
index e9f8ccd..d6cd3c7 100644 (file)
@@ -70,13 +70,6 @@ Level::load(const std::string& filepath)
     level->get("version", version);
     if(version == 1) {
       load_old_format(*level);
-
-#if 0
-      // test for now
-      FlipLevelTransformer* transformer = new FlipLevelTransformer();  
-      transformer->transform(this);
-#endif
-     
       return;
     }
 
@@ -123,12 +116,7 @@ Level::load_old_format(const lisp::Lisp& reader)
 void
 Level::save(const std::string& filename)
 {
-  std::string filepath = "levels/" + filename;
-  int last_slash = filepath.find_last_of('/');
-  FileSystem::fcreatedir(filepath.substr(0,last_slash).c_str());
-  filepath = user_dir + "/" + filepath;
-  ofstream file(filepath.c_str(), ios::out);
-  lisp::Writer* writer = new lisp::Writer(file);
+  lisp::Writer* writer = new lisp::Writer(filename);
 
   writer->write_comment("Level made using SuperTux's built-in Level Editor");
 
@@ -150,7 +138,6 @@ Level::save(const std::string& filename)
   writer->end_list("supertux-level");
 
   delete writer;
-  file.close();
 }
 
 Level::~Level()
index 2366f8c..960f7dc 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdexcept>
 #include <assert.h>
 #include <unistd.h>
+#include <physfs.h>
 #include "level.h"
 #include "resources.h"
 #include "file_system.h"
@@ -30,6 +31,7 @@
 #include "level_subset.h"
 #include "lisp/parser.h"
 #include "lisp/lisp.h"
+#include "lisp/writer.h"
 
 static bool has_suffix(const std::string& data, const std::string& suffix)
 {
@@ -80,90 +82,55 @@ void LevelSubset::load(const std::string& subset)
 {
   name = subset;
   
-  // Check in which directory our subset is located (ie. ~/.supertux/
-  // or SUPERTUX_DATADIR)
-  std::string filename = get_resource_filename(
-      std::string("levels/") + subset + "/info");
-  if(filename == "") {
-    std::stringstream msg;
-    msg << "Couldn't find level subset '" << subset << "'.";
-    throw new std::runtime_error(msg.str());
-  }
+  std::string infofile = subset + "/info";
   try {
-    read_info_file(filename);
+    read_info_file(infofile);
   } catch(std::exception& e) {
     std::stringstream msg;
-    msg << "Couldn't parse info file '" << filename << "': " << e.what();
-    throw new std::runtime_error(msg.str());
+    msg << "Couldn't parse info file '" << infofile << "': " << e.what();
+    throw std::runtime_error(msg.str());
   }
 
   // test is a worldmap exists
   has_worldmap = false;
-  std::string worldmap = get_resource_filename(
-      std::string("levels/") + subset + "/worldmap.stwm");
-  if(worldmap != "") {
+  std::string worldmap = subset + "/worldmap.stwm";
+  if(PHYSFS_exists(worldmap.c_str())) {
     has_worldmap = true;
   }
 
-  if (levels.empty())
-    { // Level info file doesn't define any levels, so read the
-      // directory to see what we can find
-      std::set<std::string> files;
-  
-      filename = datadir + "/levels/" + subset + "/";
-      files = FileSystem::read_directory(filename);
+  if (levels.empty()) { 
+    // Level info file doesn't define any levels, so read the
+    // directory to see what we can find
+      
+    std::string path = subset + "/";
+    char** files = PHYSFS_enumerateFiles(path.c_str());
+    if(!files) {
+      std::cerr << "Warning: Couldn't read subset dir '" 
+                << path << "'.\n";
+      return;
+    }
 
-      filename = user_dir + "/levels/" + subset + "/";
-      std::set<std::string> user_files = FileSystem::read_directory(filename);
-      files.insert(user_files.begin(), user_files.end());
-  
-      for(std::set<std::string>::iterator i = files.begin(); i != files.end(); ++i)
-        {
-          if (has_suffix(*i, ".stl"))
-            levels.push_back(get_resource_filename(
-                  std::string("levels/" + subset+ "/" + *i)));
-        }
+    for(const char* const* filename = files; *filename != 0; ++filename) {
+      if(has_suffix(*filename, ".stl")) {
+        levels.push_back(path + *filename);
+      }
     }
+    PHYSFS_freeList(files);
+  }
 }
 
 void
 LevelSubset::save()
 {
-  FILE* fi;
-  std::string filename;
-
   /* Save data file: */
-  filename = "/levels/" + name + "/";
-
-  FileSystem::fcreatedir(filename.c_str());
-  filename = std::string(user_dir) + "/levels/" + name + "/info";
-  if(!FileSystem::fwriteable(filename.c_str()))
-    filename = datadir + "/levels/" + name + "/info";
-  if(FileSystem::fwriteable(filename.c_str()))
-    {
-      fi = fopen(filename.c_str(), "w");
-      if (fi == NULL)
-        {
-          perror(filename.c_str());
-        }
-
-      /* Write header: */
-      fprintf(fi,";; SuperTux-Level-Subset\n");
-      fprintf(fi,"(supertux-level-subset\n");
-
-      /* Save title info: */
-      fprintf(fi,"  (title \"%s\")\n", title.c_str());
-
-      /* Save the description: */
-      fprintf(fi,"  (description \"%s\")\n", description.c_str());
-
-      /* Save the hide from Contrbis menu boolean: */
-      fprintf(fi,"  (hide-from-contribs %s)\n", hide_from_contribs ? "#t" : "#f");
-
-      fprintf( fi,")");
-      fclose(fi);
-    }
+  std::string filename = name + "/info";
+  lisp::Writer writer(filename);
+
+  writer.start_list("supertux-level-subset");
+  writer.write_string("title", title);
+  writer.write_string("description", description);
+  writer.write_bool("hide-from-contribs", hide_from_contribs);
+  writer.end_list("supertux-level-subset");
 }
 
 void
@@ -182,7 +149,7 @@ LevelSubset::get_level_filename(unsigned int num)
 std::string
 LevelSubset::get_worldmap_filename()
 {
-  return std::string("/levels/" + name + "/worldmap.stwm");
+  return std::string(name + "/worldmap.stwm");
 }
 
 int
index 1850113..e03e0ca 100644 (file)
@@ -39,12 +39,12 @@ ListIterator::next()
 
   const lisp::Lisp* child = cur->get_car();
   if(!child)
-    throw new std::runtime_error("child is 0 in list entry");
+    throw std::runtime_error("child is 0 in list entry");
   if(child->get_type() != lisp::Lisp::TYPE_CONS)
-    throw new std::runtime_error("Expected CONS");
+    throw std::runtime_error("Expected CONS");
   const lisp::Lisp* name = child->get_car();
   if(!name || name->get_type() != lisp::Lisp::TYPE_SYMBOL)
-    throw new std::runtime_error("Expected symbol");
+    throw std::runtime_error("Expected symbol");
   name->get(current_item);
   current_lisp = child->get_cdr();
 
index 224ee3e..565dcc9 100644 (file)
@@ -26,6 +26,8 @@
 #include <iostream>
 
 #include "tinygettext/tinygettext.h"
+#include "physfs/physfs_stream.h"
+#include "resources.h"
 #include "parser.h"
 #include "lisp.h"
 #include "file_system.h"
@@ -51,7 +53,7 @@ Parser::~Parser()
 Lisp*
 Parser::parse(const std::string& filename)
 {
-  std::ifstream in(filename.c_str());
+  IFileStream in(filename);
   if(!in.good()) {
     std::stringstream msg;
     msg << "Parser problem: Couldn't open file '" << filename << "'.";
@@ -115,7 +117,7 @@ Parser::read()
         // evaluate translation function (_ str) in place here
         token = lexer->getNextToken();
         if(token != Lexer::TOKEN_STRING)
-          throw new std::runtime_error("Expected string after '(_'");
+          throw std::runtime_error("Expected string after '(_'");
         
         result = new Lisp(Lisp::TYPE_STRING);
         if(dictionary) {
@@ -129,7 +131,7 @@ Parser::read()
         }
         token = lexer->getNextToken();
         if(token != Lexer::TOKEN_CLOSE_PAREN)
-          throw new std::runtime_error("Expected ')' after '(_ string'");
+          throw std::runtime_error("Expected ')' after '(_ string'");
         break;
       }
 
index e4a810a..ff70ce1 100644 (file)
 #include <iostream>
 
 #include "writer.h"
+#include "physfs/physfs_stream.h"
 
 namespace lisp
 {
 
-Writer::Writer(std::ostream& newout)
-  : out(newout), indent_depth(0)
+Writer::Writer(const std::string& filename)
 {
+  out = new OFileStream(filename);
+  out_owned = true;
+  indent_depth = 0;
+}
+  
+Writer::Writer(std::ostream* newout)
+{
+  out = newout;
+  out_owned = false;
+  indent_depth = 0;
 }
 
 Writer::~Writer()
@@ -36,19 +46,21 @@ Writer::~Writer()
   if(lists.size() > 0) {
     std::cerr << "Warning: Not all sections closed in lispwriter!\n";
   }
+  if(out_owned)
+    delete out;
 }
 
 void
 Writer::write_comment(const std::string& comment)
 {
-  out << "; " << comment << "\n";
+  *out << "; " << comment << "\n";
 }
 
 void
 Writer::start_list(const std::string& listname)
 {
   indent();
-  out << '(' << listname << '\n';
+  *out << '(' << listname << '\n';
   indent_depth += 2;
 
   lists.push_back(listname);
@@ -71,21 +83,21 @@ Writer::end_list(const std::string& listname)
   
   indent_depth -= 2;
   indent();
-  out << ")\n";
+  *out << ")\n";
 }
 
 void
 Writer::write_int(const std::string& name, int value)
 {
   indent();
-  out << '(' << name << ' ' << value << ")\n";
+  *out << '(' << name << ' ' << value << ")\n";
 }
 
 void
 Writer::write_float(const std::string& name, float value)
 {
   indent();
-  out << '(' << name << ' ' << value << ")\n";
+  *out << '(' << name << ' ' << value << ")\n";
 }
 
 void
@@ -93,11 +105,11 @@ Writer::write_string(const std::string& name, const std::string& value,
     bool translatable)
 {
   indent();
-  out << '(' << name;
+  *out << '(' << name;
   if(translatable) {
-    out << " (_ \"" << value << "\"))\n";
+    *out << " (_ \"" << value << "\"))\n";
   } else {
-    out << " \"" << value << "\")\n";
+    *out << " \"" << value << "\")\n";
   }
 }
 
@@ -105,7 +117,7 @@ void
 Writer::write_bool(const std::string& name, bool value)
 {
   indent();
-  out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n";
+  *out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n";
 }
 
 void
@@ -113,10 +125,10 @@ Writer::write_int_vector(const std::string& name,
     const std::vector<int>& value)
 {
   indent();
-  out << '(' << name;
+  *out << '(' << name;
   for(std::vector<int>::const_iterator i = value.begin(); i != value.end(); ++i)
-    out << " " << *i;
-  out << ")\n";
+    *out << " " << *i;
+  *out << ")\n";
 }
 
 void
@@ -124,17 +136,17 @@ Writer::write_int_vector(const std::string& name,
     const std::vector<unsigned int>& value)
 {
   indent();
-  out << '(' << name;
+  *out << '(' << name;
   for(std::vector<unsigned int>::const_iterator i = value.begin(); i != value.end(); ++i)
-    out << " " << *i;
-  out << ")\n";
+    *out << " " << *i;
+  *out << ")\n";
 }
 
 void
 Writer::indent()
 {
   for(int i = 0; i<indent_depth; ++i)
-    out << ' ';
+    *out << ' ';
 }
 
 } // end of namespace lisp
index ad2ffea..90baf12 100644 (file)
@@ -28,34 +28,36 @@ namespace lisp
 {
 
   class Writer
-    {
-    public:
-      Writer(std::ostream& out);
-      ~Writer();
+  {
+  public:
+    Writer(const std::string& filename);
+    Writer(std::ostream* out);
+    ~Writer();
 
-      void write_comment(const std::string& comment);
+    void write_comment(const std::string& comment);
 
-      void start_list(const std::string& listname);
+    void start_list(const std::string& listname);
 
-      void write_int(const std::string& name, int value);
-      void write_float(const std::string& name, float value);
-      void write_string(const std::string& name, const std::string& value,
-          bool translatable = false);
-      void write_bool(const std::string& name, bool value);
-      void write_int_vector(const std::string& name, const std::vector<int>& value);
-      void write_int_vector(const std::string& name, const std::vector<unsigned int>& value);
-      // add more write-functions when needed...
+    void write_int(const std::string& name, int value);
+    void write_float(const std::string& name, float value);
+    void write_string(const std::string& name, const std::string& value,
+        bool translatable = false);
+    void write_bool(const std::string& name, bool value);
+    void write_int_vector(const std::string& name, const std::vector<int>& value);
+    void write_int_vector(const std::string& name, const std::vector<unsigned int>& value);
+    // add more write-functions when needed...
 
-      void end_list(const std::string& listname);
+    void end_list(const std::string& listname);
 
-    private:
-      void indent();
-
-      std::ostream& out;
-      int indent_depth;
-      std::vector<std::string> lists;
-    };
+  private:
+    void indent();
 
+    std::ostream* out;
+    bool out_owned;
+    int indent_depth;
+    std::vector<std::string> lists;
+  };
+  
 } //namespace lisp
 
 #endif //SUPERTUX_LISPWRITER_H
index 950da90..acf4465 100644 (file)
@@ -32,9 +32,7 @@
 #include <dirent.h>
 #include <unistd.h>
 #include <assert.h>
-#ifndef WIN32
-#include <libgen.h>
-#endif
+#include <physfs.h>
 #include <SDL.h>
 #include <SDL_mixer.h>
 #include <SDL_image.h>
 #include "title.h"
 #include "game_session.h"
 #include "file_system.h"
-
-#ifdef WIN32
-#define mkdir(dir, mode)    mkdir(dir)
-#endif
+#include "physfs/physfs_sdl.h"
 
 SDL_Surface* screen = 0;
 JoystickKeyboardController* main_controller = 0;
@@ -71,73 +66,110 @@ static void init_config()
   }
 }
 
-static void find_directories()
+static void init_tinygettext()
 {
-  const char* home = getenv("HOME");
-  if(home == 0) {
-#ifdef DEBUG
-    std::cerr << "Couldn't find home directory.\n";
-#endif
-    home = ".";
-  }
-
-  user_dir = home;
-  user_dir += "/.supertux";
-
-  // create directories
-  std::string savedir = user_dir + "/save";
-  mkdir(user_dir.c_str(), 0755);
-  mkdir(savedir.c_str(), 0755);
+  dictionary_manager.add_directory("locale");
+  dictionary_manager.set_charset("UTF-8");
+}
 
-  // try current directory as datadir
-  if(datadir.empty()) {
-    if(FileSystem::faccessible("./data/credits.txt")) {
-      datadir = "./data/";
-    }
+static void init_physfs(const char* argv0)
+{
+  if(!PHYSFS_init(argv0)) {
+    std::stringstream msg;
+    msg << "Couldn't initialize physfs: " << PHYSFS_getLastError();
+    throw std::runtime_error(msg.str());
   }
 
-  // Detect datadir with some linux magic
-  if(datadir.empty()) {
-    std::string exedir;
-#ifdef WIN32
-    exedir = ".";
-#else
-    char exe_file[PATH_MAX];
-    if(readlink("/proc/self/exe", exe_file, PATH_MAX) >= 0) {
-      exedir = std::string(dirname(exe_file));
-    } else {
-#ifdef DEBUG
-      std::cerr << "Couldn't read /proc/self/exe \n";
-#endif
-      exedir = ".";
-    }
-#endif
-    std::string testdir = exedir + "/data/";
-    if(access(testdir.c_str(), F_OK) == 0) {
-      datadir = testdir;
+  // Initialize physfs (this is a slightly modified version of
+  // PHYSFS_setSaneConfig
+  const char* application = PACKAGE_NAME;
+  const char* userdir = PHYSFS_getUserDir();
+  const char* dirsep = PHYSFS_getDirSeparator();
+  char* writedir = new char[strlen(userdir) + strlen(application) + 2];
+
+  // Set configuration directory
+  sprintf(writedir, "%s.%s", userdir, application);
+  if(!PHYSFS_setWriteDir(writedir)) {
+    // try to create the directory
+    char* mkdir = new char[strlen(application) + 2];
+    sprintf(mkdir, ".%s", application);
+    if(!PHYSFS_setWriteDir(userdir) || !PHYSFS_mkdir(mkdir)) {
+      std::ostringstream msg;
+      msg << "Failed creating configuration directory '" 
+          << writedir << "': " << PHYSFS_getLastError();
+      delete[] writedir;
+      delete[] mkdir;
+      throw std::runtime_error(msg.str());
     }
+    delete[] mkdir;
     
-    testdir = exedir + "/../share/supertux/";
-    if(datadir.empty() && access(testdir.c_str(), F_OK) == 0) {
-      datadir = testdir;
+    if(!PHYSFS_setWriteDir(writedir)) {
+      std::ostringstream msg;
+      msg << "Failed to use configuration directory '" 
+          <<  writedir << "': " << PHYSFS_getLastError();
+      delete[] writedir;
+      throw std::runtime_error(msg.str());
     }
-  }  
+  }
+  PHYSFS_addToSearchPath(writedir, 0);
+  delete[] writedir;
+
+  // Search for archives and add them to the search path
+  const char* archiveExt = "zip";
+  char** rc = PHYSFS_enumerateFiles("/");
+  size_t extlen = strlen(archiveExt);
+
+  for(char** i = rc; *i != 0; ++i) {
+    size_t l = strlen(*i);
+    if((l > extlen) && ((*i)[l - extlen - 1] == '.')) {
+      const char* ext = (*i) + (l - extlen);
+      if(strcasecmp(ext, archiveExt) == 0) {
+        const char* d = PHYSFS_getRealDir(*i);
+        char* str = new char[strlen(d) + strlen(dirsep) + l + 1];
+        sprintf(str, "%s%s%s", d, dirsep, *i);
+        PHYSFS_addToSearchPath(str, 1);
+        delete[] str;
+      }
+    }
+  }
   
-#ifdef DATA_PREFIX
-  // use default location
-  if(datadir.empty()) {
-    datadir = DATA_PREFIX;
+  PHYSFS_freeList(rc);
+
+  // when started from source dir...
+  std::string dir = PHYSFS_getBaseDir();
+  dir += "/data";
+  std::string testfname = dir;
+  testfname += "/credits.txt";
+  FILE* f = fopen(testfname.c_str(), "r");
+  if(f) {
+    fclose(f);
+    if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+      std::cout << "Warning: Couldn't add '" << dir 
+                << "' to physfs searchpath: " << PHYSFS_getLastError() << "\n";
+    }
+  }
+
+#if defined(APPDATADIR) || defined(ENABLE_BINRELOC)
+  std::string datadir;
+#ifdef ENABLE_BINRELOC
+  char* brdatadir = br_strcat(DATADIR, "/" PACKAGE_NAME);
+  datadir = brdatadir;
+  free(brdatadir);
+#else
+  datadir = APPDATADIR;
+#endif
+  if(!PHYSFS_addToSearchPath(datadir.c_str(), 1)) {
+    std::cout << "Couldn't add '" << datadir
+              << "' to physfs searchpath: " << PHYSFS_getLastError() << "\n";
   }
 #endif
 
-  if(datadir.empty())
-    throw std::runtime_error("Couldn't find datadir");
-}
+  // allow symbolic links
+  PHYSFS_permitSymbolicLinks(1);
 
-static void init_tinygettext()
-{
-  dictionary_manager.add_directory(datadir + "/locale");
-  dictionary_manager.set_charset("UTF-8");
+  //show search Path
+  for(char** i = PHYSFS_getSearchPath(); *i != NULL; i++)
+    printf("[%s] is in the search path.\n", *i);
 }
 
 static void print_usage(const char* argv0)
@@ -284,8 +316,8 @@ void init_video()
   SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
 
   // set icon
-  SDL_Surface* icon = IMG_Load(
-    get_resource_filename("images/engine/icons/supertux.xpm").c_str());
+  SDL_Surface* icon = IMG_Load_RW(
+      get_physfs_SDLRWops("images/engine/icons/supertux.xpm"), true);
   if(icon != 0) {
     SDL_WM_SetIcon(icon, 0);
     SDL_FreeSurface(icon);
@@ -389,9 +421,9 @@ int main(int argc, char** argv)
   try {
 #endif
     srand(time(0));
+    init_physfs(argv[0]);
     init_sdl();
     main_controller = new JoystickKeyboardController();    
-    find_directories();
     init_config();
     init_tinygettext();
     parse_commandline(argc, argv);
@@ -401,7 +433,12 @@ int main(int argc, char** argv)
     setup_menu();
     load_shared();
     if(config->start_level != "") {
-      GameSession session(config->start_level, ST_GL_LOAD_LEVEL_FILE);
+      // we have a normal path specified at commandline not physfs paths.
+      // So we simply mount that path here...
+      std::string dir = FileSystem::dirname(config->start_level);
+      PHYSFS_addToSearchPath(dir.c_str(), true);
+      GameSession session(
+          FileSystem::basename(config->start_level), ST_GL_LOAD_LEVEL_FILE);
       if(config->start_demo != "")
         session.play_demo(config->start_demo);
       if(config->record_demo != "")
@@ -434,6 +471,7 @@ int main(int argc, char** argv)
   delete config;
   delete main_controller;
   SDL_Quit();
+  PHYSFS_deinit();
   
   return 0;
 }
index a353763..d2ef436 100644 (file)
@@ -92,7 +92,7 @@ Background::set_image(const std::string& name, float speed)
   this->speed = speed;
 
   delete image;
-  image = new Surface(datadir + "/images/background/" + name, false);
+  image = new Surface("images/background/" + name, false);
 }
 
 void
index 4a78b96..d930c1d 100644 (file)
@@ -73,9 +73,9 @@ void ParticleSystem::draw(DrawingContext& context)
 
 SnowParticleSystem::SnowParticleSystem()
 {
-    snowimages[0] = new Surface(datadir+"/images/objects/particles/snow0.png", true);
-    snowimages[1] = new Surface(datadir+"/images/objects/particles/snow1.png", true);
-    snowimages[2] = new Surface(datadir+"/images/objects/particles/snow2.png", true);
+    snowimages[0] = new Surface("images/objects/particles/snow0.png", true);
+    snowimages[1] = new Surface("images/objects/particles/snow1.png", true);
+    snowimages[2] = new Surface("images/objects/particles/snow2.png", true);
 
     virtual_width = SCREEN_WIDTH * 2;
 
@@ -133,8 +133,8 @@ void SnowParticleSystem::update(float elapsed_time)
 //       Ghosts don't change their movement pattern - not random
 GhostParticleSystem::GhostParticleSystem()
 {
-    ghosts[0] = new Surface(datadir+"/images/objects/particles/ghost0.png", true);
-    ghosts[1] = new Surface(datadir+"/images/objects/particles/ghost1.png", true);
+    ghosts[0] = new Surface("images/objects/particles/ghost0.png", true);
+    ghosts[1] = new Surface("images/objects/particles/ghost1.png", true);
 
     virtual_width = SCREEN_WIDTH * 2;
 
@@ -190,7 +190,7 @@ void GhostParticleSystem::update(float elapsed_time)
 
 CloudParticleSystem::CloudParticleSystem()
 {
-    cloudimage = new Surface(datadir + "/images/objects/particles/cloud.png", true);
+    cloudimage = new Surface("images/objects/particles/cloud.png", true);
 
     virtual_width = 2000.0;
 
index 13064bc..d411f89 100644 (file)
@@ -129,8 +129,8 @@ ParticleSystem_Interactive::collision(Particle* object, Vector movement)
 
 RainParticleSystem::RainParticleSystem()
 {
-    rainimages[0] = new Surface(datadir+"/images/objects/particles/rain0.png", true);
-    rainimages[1] = new Surface(datadir+"/images/objects/particles/rain1.png", true);
+    rainimages[0] = new Surface("images/objects/particles/rain0.png", true);
+    rainimages[1] = new Surface("images/objects/particles/rain1.png", true);
 
     virtual_width = SCREEN_WIDTH * 2;
 
@@ -209,8 +209,8 @@ void RainParticleSystem::update(float elapsed_time)
 
 CometParticleSystem::CometParticleSystem()
 {
-    cometimages[0] = new Surface(datadir+"/images/creatures/mr_bomb/exploding-left-0.png", true);
-    cometimages[1] = new Surface(datadir+"/images/creatures/mr_bomb/exploding-left-0.png", true);
+    cometimages[0] = new Surface("images/creatures/mr_bomb/exploding-left-0.png", true);
+    cometimages[1] = new Surface("images/creatures/mr_bomb/exploding-left-0.png", true);
 
     virtual_width = SCREEN_WIDTH * 2;
 
index a533ab7..7ab1cbf 100644 (file)
@@ -27,9 +27,6 @@
 #include "object/gameobjs.h"
 #include "object/player.h"
 
-std::string datadir;
-std::string user_dir;
-
 MusicRef herring_song;
 MusicRef level_end_song;
 MusicRef credits_song;
@@ -52,29 +49,26 @@ Font* white_big_text;
 void load_shared()
 {
   /* Load GUI/menu images: */
-  checkbox = new Surface(datadir + "/images/engine/menu/checkbox-unchecked.png", true);
-  checkbox_checked = new Surface(datadir + "/images/engine/menu/checkbox-checked.png", true);
-  back = new Surface(datadir + "/images/engine/menu/arrow-back.png", true);
-  arrow_left = new Surface(datadir + "/images/engine/menu/arrow-left.png", true);
-  arrow_right = new Surface(datadir + "/images/engine/menu/arrow-right.png", true);
+  checkbox = new Surface("images/engine/menu/checkbox-unchecked.png", true);
+  checkbox_checked = new Surface("images/engine/menu/checkbox-checked.png", true);
+  back = new Surface("images/engine/menu/arrow-back.png", true);
+  arrow_left = new Surface("images/engine/menu/arrow-left.png", true);
+  arrow_right = new Surface("images/engine/menu/arrow-right.png", true);
 
   /* Load the mouse-cursor */
-  mouse_cursor = new MouseCursor(datadir + "/images/engine/menu/mousecursor.png");
+  mouse_cursor = new MouseCursor("images/engine/menu/mousecursor.png");
   MouseCursor::set_current(mouse_cursor);
 
   /* Load global images: */
-  gold_text = new Font(datadir + "/images/engine/fonts/gold.png", Font::TEXT, 16,18);
-  blue_text = new Font(datadir + "/images/engine/fonts/blue.png", Font::TEXT, 16,18,3);
-  white_text  = new Font(datadir + "/images/engine/fonts/white.png",
-                         Font::TEXT, 16,18);
-  gray_text  = new Font(datadir + "/images/engine/fonts/gray.png",
-                        Font::TEXT, 16,18);
-  white_small_text = new Font(datadir + "/images/engine/fonts/white-small.png",
+  gold_text = new Font("images/engine/fonts/gold.png", Font::TEXT, 16,18);
+  blue_text = new Font("images/engine/fonts/blue.png", Font::TEXT, 16,18,3);
+  white_text  = new Font("images/engine/fonts/white.png", Font::TEXT, 16,18);
+  gray_text  = new Font("images/engine/fonts/gray.png", Font::TEXT, 16,18);
+  white_small_text = new Font("images/engine/fonts/white-small.png",
                               Font::TEXT, 8,9, 1);
-  white_big_text   = new Font(datadir + "/images/engine/fonts/white-big.png",
+  white_big_text   = new Font("images/engine/fonts/white-big.png",
                               Font::TEXT, 20,22, 3);
-  yellow_nums = new Font(datadir + "/images/engine/fonts/numbers.png",
-                         Font::NUM, 32,32);
+  yellow_nums = new Font("images/engine/fonts/numbers.png", Font::NUM, 32,32);
 
   Menu::default_font = white_text;
   Menu::active_font = blue_text;
@@ -84,18 +78,17 @@ void load_shared()
   
   Button::info_font = white_small_text;
 
-  sprite_manager = new SpriteManager(
-      get_resource_filename("/images/sprites.strf"));
-  tile_manager = new TileManager("/images/tiles.strf");
+  sprite_manager = new SpriteManager("images/sprites.strf");
+  tile_manager = new TileManager("images/tiles.strf");
 
   /* Tuxes: */
   char img_name[1024];
   for (int i = 0; i < GROWING_FRAMES; i++)
     {
-      sprintf(img_name, "%s/images/creatures/tux_grow/left-%i.png", datadir.c_str(), i+1);
+      sprintf(img_name, "images/creatures/tux_grow/left-%i.png", i+1);
       growingtux_left[i] = new Surface(img_name, true);
 
-      sprintf(img_name, "%s/images/creatures/tux_grow/right-%i.png", datadir.c_str(), i+1);
+      sprintf(img_name, "images/creatures/tux_grow/right-%i.png", i+1);
       growingtux_right[i] = new Surface(img_name, true);
     }
 
@@ -127,8 +120,7 @@ void load_shared()
   load_object_gfx();
 
   /* Tux life: */
-  tux_life = new Surface(datadir + "/images/creatures/tux_small/tux-life.png",
-                         true);
+  tux_life = new Surface("images/creatures/tux_small/tux-life.png", true);
 
   /* Sound effects: */
   sound_manager->preload_sound("jump");
@@ -154,8 +146,8 @@ void load_shared()
   sound_manager->preload_sound("fireworks");
 
   /* Herring song */
-  herring_song = sound_manager->load_music(datadir + "/music/salcon.mod");
-  level_end_song = sound_manager->load_music(datadir + "/music/leveldone.mod");
+  herring_song = sound_manager->load_music("music/salcon.mod");
+  level_end_song = sound_manager->load_music("music/leveldone.mod");
 }
 
 /* Free shared data: */
@@ -200,16 +192,3 @@ void unload_shared()
   delete mouse_cursor;
 }
 
-std::string get_resource_filename(const std::string& resource)
-{
-  std::string filepath = user_dir + "/" + resource;
-  if(FileSystem::faccessible(filepath))
-    return filepath;
-  
-  filepath = datadir + resource;
-  if(FileSystem::faccessible(filepath))
-    return filepath;
-
-  std::cerr << "Couldn't find resource: '" << resource  << "'." << std::endl;
-  return "";
-}
index c3bcbaa..200a9a2 100644 (file)
@@ -57,16 +57,6 @@ extern Font* white_small_text;
 extern Font* white_big_text;
 extern Font* yellow_nums;
 
-extern std::string datadir;
-extern std::string user_dir;
-
-/** maps a virtual resource path to a real path (ie. levels/bla is mapped to
- * $DATADIR/levels/bla or $HOME/.supertux/levels/bla)
- * All paths inside the game should be handled in as virtual paths and then
- * expanded with this function just before the call to fopen or std::ifstream.
- */
-std::string get_resource_filename(const std::string& resource);
-
 void load_shared();
 void unload_shared();
 
index a3c211a..b46e6e2 100644 (file)
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <string>
 #include <squirrel.h>
+#include <sqstdio.h>
 #include "textscroller.h"
 #include "functions.h"
 #include "script_interpreter.h"
@@ -28,5 +29,23 @@ void display_text_file(const std::string& filename)
   ::display_text_file(file);
 }
 
+void import(HSQUIRRELVM v, const std::string& filename)
+{
+  std::string file 
+    = ScriptInterpreter::current()->get_working_directory() + filename;
+  if(sqstd_loadfile(v, file.c_str(), true) < 0) {
+    std::cerr << "Warning couldn't load script '" << filename << "' ("
+      << file << ").\n";
+    return;
+  }
+
+  sq_push(v, -2);
+  if(sq_call(v, 1, false) < 0) {
+    std::cerr << "Couldn't execute script '" << filename << "' ("
+      << file << ").\n";
+    return;
+  }
+}
+
 }
 
index 2f4d6c8..a2c518d 100644 (file)
@@ -12,6 +12,10 @@ void set_wakeup_time(float seconds);
  * files)
  */
 std::string translate(const std::string& text);
+/** load a script file and executes it 
+ * This is typically used to import functions from external files.
+ * */
+void import(HSQUIRRELVM v, const std::string& filename);
 
 }
 
index 2d273f2..25c89d1 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdarg.h>
 #include <stdexcept>
 #include <sstream>
+#include <fstream>
 #include <sqstdio.h>
 #include <sqstdaux.h>
 #include <sqstdblob.h>
@@ -17,6 +18,8 @@
 #include "sector.h"
 #include "file_system.h"
 #include "game_session.h"
+#include "resources.h"
+#include "physfs/physfs_stream.h"
 #include "object/text_object.h"
 #include "object/scripted_object.h"
 #include "object/display_effect.h"
@@ -115,17 +118,12 @@ static SQInteger squirrel_read_char(SQUserPointer file)
   return c;
 }
 
-
 void
-ScriptInterpreter::load_script(std::istream& in, const std::string& sourcename)
+ScriptInterpreter::run_script(std::istream& in, const std::string& sourcename)
 {
   if(sq_compile(v, squirrel_read_char, &in, sourcename.c_str(), true) < 0)
     throw SquirrelError(v, "Couldn't parse script");
-}
-
-void
-ScriptInterpreter::start_script()
-{
   _current = this;
   sq_push(v, -2);
   if(sq_call(v, 1, false) < 0)
@@ -202,12 +200,22 @@ ScriptInterpreter::add_script_object(Sector* sector, const std::string& name,
     const std::string& script)
 {
   try {
-    std::auto_ptr<ScriptInterpreter> interpreter
-      (new ScriptInterpreter(GameSession::current()->get_working_directory()));
+    std::string workdir = GameSession::current()->get_working_directory();
+    std::auto_ptr<ScriptInterpreter> interpreter(
+               new ScriptInterpreter(workdir));
     interpreter->register_sector(sector);
+
+    // load default.nut file if it exists
+    try {
+      std::string filename = workdir + "/default.nut";
+      IFileStream in(filename);
+      interpreter->run_script(in, filename);
+    } catch(std::exception& e) {
+      // nothing
+    }
+       
     std::istringstream in(script);
-    interpreter->load_script(in, name);
-    interpreter->start_script();
+    interpreter->run_script(in, name);
     sector->add_object(interpreter.release());
   } catch(std::exception& e) {
     std::cerr << "Couldn't start '" << name << "' script: " << e.what() << "\n";
index 9cb99c5..d8db300 100644 (file)
@@ -21,8 +21,7 @@ public:
   void draw(DrawingContext& );
   void update(float );
 
-  void load_script(std::istream& in, const std::string& sourcename = "");
-  void start_script();
+  void run_script(std::istream& in, const std::string& sourcename = "");
   
   void expose_object(void* object, const std::string& name,
                      const std::string& type);
index 8c3ff52..7dfa7ac 100644 (file)
@@ -439,10 +439,22 @@ static int translate_wrapper(HSQUIRRELVM v)
   return 1;
 }
 
+static int import_wrapper(HSQUIRRELVM v)
+{
+  HSQUIRRELVM arg0 = v;
+  const char* arg1;
+  sq_getstring(v, 4, &arg1);
+  
+  Scripting::import(arg0, arg1);
+  
+  return 0;
+}
+
 WrappedFunction supertux_global_functions[] = {
   { "display_text_file", &display_text_file_wrapper },
   { "set_wakeup_time", &set_wakeup_time_wrapper },
   { "translate", &translate_wrapper },
+  { "import", &import_wrapper },
   { 0, 0 }
 };
 
index 058f045..cb7a2d9 100644 (file)
@@ -784,8 +784,7 @@ Sector::add_floating_text(const Vector& pos, const std::string& text)
 void
 Sector::load_music()
 {
-  level_song = sound_manager->load_music(
-    get_resource_filename("/music/" + song_title));
+  level_song = sound_manager->load_music("/music/" + song_title);
 }
 
 void
index a369d66..69c2d2e 100644 (file)
@@ -108,8 +108,7 @@ SpriteData::parse_action(const lisp::Lisp* lisp)
     }
 
     for(std::vector<std::string>::size_type i = 0; i < images.size(); i++) {
-      action->surfaces.push_back(
-          new Surface(datadir + "/images/" + images[i], true));
+      action->surfaces.push_back(new Surface("images/" + images[i], true));
     }
   }
   actions[action->name] = action;
index 859ca30..20d900c 100644 (file)
@@ -53,7 +53,7 @@ static void split_text(const std::string& text, std::vector<std::string>& lines)
   }
 }
 
-void display_text_file(const std::string& file)
+void display_text_file(const std::string& filename)
 {
   const Font* heading_font = white_big_text;
   const Font* normal_font = white_text;
@@ -66,7 +66,6 @@ void display_text_file(const std::string& file)
   std::string background_file;
   std::vector<std::string> lines;
 
-  std::string filename = datadir + "/" + file;
   lisp::Parser parser;
   try {
     std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
@@ -91,8 +90,8 @@ void display_text_file(const std::string& file)
   split_text(text, lines);
 
   // load background image
-  Surface* background = new Surface(
-      get_resource_filename("images/background/" + background_file), false);
+  Surface* background 
+    = new Surface("images/background/" + background_file, false);
 
   bool done = false;
   float scroll = 0;
index 7998892..0fba4df 100644 (file)
@@ -135,8 +135,7 @@ Tile::load_images(const std::string& tilesetpath)
       imagespecs.end(); ++i) {
     const ImageSpec& spec = *i;
     Surface* surface;
-    std::string file 
-      = get_resource_filename(tilesetpath + spec.file);
+    std::string file = tilesetpath + spec.file;
     if(spec.rect.get_width() <= 0) {
       surface = new Surface(file, true);
     } else {
@@ -149,8 +148,7 @@ Tile::load_images(const std::string& tilesetpath)
     images.push_back(surface);
   }
   if(editor_imagefile != "") {
-    editor_image = new Surface(
-        get_resource_filename(tilesetpath + editor_imagefile), true);
+    editor_image = new Surface(tilesetpath + editor_imagefile, true);
   }
 }
 
index 2bf90bf..7422e7c 100644 (file)
@@ -56,8 +56,7 @@ void TileManager::load_tileset(std::string filename)
   }
  
   lisp::Parser parser;
-  std::auto_ptr<lisp::Lisp> root (parser.parse(
-        get_resource_filename(filename)));
+  std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
 
   const lisp::Lisp* tiles_lisp = root->get_lisp("supertux-tiles");
   if(!tiles_lisp)
index f151e64..d908242 100644 (file)
 
 #include <sys/types.h>
 #include <iconv.h>
-#include <dirent.h>
 #include <fstream>
 #include <iostream>
 #include <ctype.h>
 #include <errno.h>
+
 #include "tinygettext.h"
+#include "physfs/physfs_stream.h"
 
 //#define TRANSLATION_DEBUG
 
@@ -34,7 +35,7 @@ namespace TinyGetText {
 /** Convert \a which is in \a from_charset to \a to_charset and return it */
 std::string convert(const std::string& text,
                     const std::string& from_charset,
-                    const std::string& to_charset)           
+                    const std::string& to_charset)
 {
   if (from_charset == to_charset)
     return text;
@@ -42,7 +43,7 @@ std::string convert(const std::string& text,
   iconv_t cd = iconv_open(to_charset.c_str(), from_charset.c_str());
   
   size_t in_len = text.length();
-  size_t out_len = text.length()*3; // FIXME: cross fingers that this is enough        
+  size_t out_len = text.length()*3; // FIXME: cross fingers that this is enough
 
   char*  out_orig = new char[out_len];
   char*  in_orig  = new char[in_len+1];
@@ -244,31 +245,27 @@ DictionaryManager::get_dictionary(const std::string& spec)
 
       for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
         {
-          DIR* dir = opendir(p->c_str());
-          if (!dir)
+          char** files = PHYSFS_enumerateFiles(p->c_str());
+          if(!files) 
             {
-              std::cerr << "Error: opendir() failed on " << *p << std::endl;
+              std::cerr << "Error: enumerateFiles() failed on " << *p << std::endl;
             }
           else
             {
-              struct dirent* ent;
-              while((ent = readdir(dir)))
-                {
-                  if (std::string(ent->d_name) == lang + ".po")
-                    {
-                      std::string pofile = *p + "/" + ent->d_name;
-                      std::ifstream in(pofile.c_str());
-                      if (!in)
-                        {
-                          std::cerr << "Error: Failure file opening: " << pofile << std::endl;
-                        }
-                      else
-                        {
-                          read_po_file(dict, in);
-                        }
-                    }
+              for(const char* const* filename = files;
+                      *filename != 0; filename++) {
+                if(std::string(*filename) == lang + ".po") {
+                  std::string pofile = *p + "/" + *filename;
+                  try {
+                      IFileStream in(pofile);
+                      read_po_file(dict, in);
+                  } catch(std::exception& e) {
+                      std::cerr << "Error: Failure file opening: " << pofile << std::endl;
+                      std::cerr << e.what() << "\n";
+                  }
                 }
-              closedir(dir);
+              }
+              PHYSFS_freeList(files);
             }
         }
 
@@ -283,23 +280,20 @@ DictionaryManager::get_languages()
 
   for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
     {
-      DIR* dir = opendir(p->c_str());
-      if (!dir)
+      char** files = PHYSFS_enumerateFiles(p->c_str());
+      if (!files)
         {
           std::cerr << "Error: opendir() failed on " << *p << std::endl;
         }
       else
         {
-          struct dirent* ent;
-          while((ent = readdir(dir)))
-            {
-              if (has_suffix(ent->d_name, ".po"))
-                {
-                  std::string filename = ent->d_name;
+          for(const char* const* file = files; *file != 0; file++) {
+              if(has_suffix(*file, ".po")) {
+                  std::string filename = *file;
                   languages.insert(filename.substr(0, filename.length()-3));
-                }
-            }
-          closedir(dir);
+              }
+          }
+          PHYSFS_freeList(files);
         }
     }  
   return languages;
@@ -312,6 +306,12 @@ DictionaryManager::set_language(const std::string& lang)
   current_dict = & (get_dictionary(language));
 }
 
+const std::string&
+DictionaryManager::get_language() const
+{
+  return language;
+}
+
 void
 DictionaryManager::set_charset(const std::string& charset)
 {
@@ -417,13 +417,13 @@ Dictionary::translate(const std::string& msgid, const std::string& msgid2, int n
     }
 }
 
-std::string
-Dictionary::translate(const std::string& msgid) 
+const char*
+Dictionary::translate(const char* msgid)
 {
   Entries::iterator i = entries.find(msgid);
   if (i != entries.end() && !i->second.empty())
     {
-      return i->second;
+      return i->second.c_str();
     }
   else
     {
@@ -434,18 +434,21 @@ Dictionary::translate(const std::string& msgid)
     }
 }
 
-const char*
-Dictionary::translate(const char* msgid)
+std::string
+Dictionary::translate(const std::string& msgid) 
 {
   Entries::iterator i = entries.find(msgid);
-  if(i == entries.end() || i->second.empty()) {
+  if (i != entries.end() && !i->second.empty())
+    {
+      return i->second;
+    }
+  else
+    {
 #ifdef TRANSLATION_DBEUG
-    std::cout << "Error: Couldn't translate: " << msgid << std::endl;
-#endif                                                                     
-    return msgid;
-  }
-
-  return i->second.c_str();
+      std::cout << "Error: Couldn't translate: " << msgid << std::endl;
+#endif
+      return msgid;
+    }
 }
   
 void
@@ -493,10 +496,10 @@ public:
     line_num = 0;
     char c = in.get();
     if(c == (char) 0xef) { // skip UTF-8 intro that some texteditors produce
-      in.get();
-      in.get();
+        in.get();
+        in.get();
     } else {
-      in.unget();
+        in.unget();
     }
     tokenize_po(in);
   }
@@ -532,8 +535,8 @@ public:
 
     to_charset = dict.get_charset();
     if (to_charset.empty())
-      { // No charset requested from the dict, so we use the one from the .po 
-        to_charset = from_charset;
+      { // No charset requested from the dict, use utf-8
+        to_charset = "utf-8";
         dict.set_charset(from_charset);
       }
   }
index a8a5846..d087901 100644 (file)
@@ -78,7 +78,7 @@ public:
 
   /** Translate the string \a msgid. */
   std::string translate(const std::string& msgid);
-
+  /** Translate the string \a msgid. */
   const char* translate(const char* msgid);
     
   /** Add a translation from \a msgid to \a msgstr to the dictionary,
@@ -125,6 +125,9 @@ public:
   /** Set a language based on a four? letter country code */
   void set_language(const std::string& langspec);
 
+  /** returns the (normalized) country code of the currently used language */
+  const std::string& get_language() const;
+
   /** Set a charset that will be set on the returned dictionaries */
   void set_charset(const std::string& charset);
 
index 9afb667..5842cd4 100644 (file)
@@ -31,6 +31,7 @@
 #include <cmath>
 #include <SDL.h>
 #include <SDL_image.h>
+#include <physfs.h>
 
 #ifndef WIN32
 #include <sys/types.h>
@@ -115,7 +116,14 @@ void free_contrib_menu()
 void generate_contrib_menu()
 {
   /** Generating contrib levels list by making use of Level Subset  */
-  std::set<std::string> level_subsets = FileSystem::dsubdirs("/levels", "info");
+  std::vector<std::string> level_subsets; 
+  char** files = PHYSFS_enumerateFiles("levels/");
+  for(const char* const* filename = files; *filename != 0; ++filename) {
+    std::string filepath = std::string("levels/") + *filename;
+    if(PHYSFS_isDirectory(filepath.c_str()))
+      level_subsets.push_back(filepath);
+  }
+  PHYSFS_freeList(files);
 
   free_contrib_menu();
 
@@ -123,24 +131,26 @@ void generate_contrib_menu()
   contrib_menu->add_hl();
   
   int i = 0;
-  for (std::set<std::string>::iterator it = level_subsets.begin();
-      it != level_subsets.end(); ++it)
-    {
-      LevelSubset* subset = new LevelSubset();
+  for (std::vector<std::string>::iterator it = level_subsets.begin();
+      it != level_subsets.end(); ++it) {
+    try {
+      std::auto_ptr<LevelSubset> subset (new LevelSubset());
       subset->load(*it);
       if(subset->hide_from_contribs) {
-        delete subset;
         continue;
       }
-      contrib_menu->add_submenu(subset->title, contrib_subset_menu, i);
-      contrib_subsets.push_back(subset);
-      ++i;
+      contrib_menu->add_submenu(subset->title, contrib_subset_menu, i++);
+      contrib_subsets.push_back(subset.release());
+    } catch(std::exception& e) {
+#ifdef DEBUG
+      std::cerr << "Couldn't parse levelset info for '"
+        << *it << "': " << e.what() << "\n";
+#endif
     }
+  }
 
   contrib_menu->add_hl();
   contrib_menu->add_back(_("Back"));
-  
-  level_subsets.clear();
 }
 
 std::string get_level_name(const std::string& filename)
@@ -184,7 +194,7 @@ void check_levels_contrib_menu()
     context.do_drawing();
 
     // TODO: slots should be available for contrib maps
-    worldmap.loadgame(user_dir + "/save/" + subset.name + "-slot1.stsg");
+    worldmap.loadgame("save/" + subset.name + "-slot1.stsg");
     worldmap.display();  // run the map
 
     Menu::set_current(main_menu);
@@ -275,13 +285,12 @@ void title()
   MusicRef credits_music;
   controller = new CodeController();
 
-  titlesession = new GameSession(get_resource_filename("levels/misc/menu.stl"),
-      ST_GL_DEMO_GAME);
+  titlesession = new GameSession("levels/misc/menu.stl", ST_GL_DEMO_GAME);
 
   /* Load images: */
-  bkg_title = new Surface(datadir + "/images/background/arctis.jpg", false);
-  logo = new Surface(datadir + "/images/engine/menu/logo.png", true);
-  //img_choose_subset = new Surface(datadir + "/images/status/choose-level-subset.png", true);
+  bkg_title = new Surface("images/background/arctis.jpg", false);
+  logo = new Surface("images/engine/menu/logo.png", true);
+  //img_choose_subset = new Surface("images/status/choose-level-subset.png", true);
 
   titlesession->get_current_sector()->activate("main");
   titlesession->set_current();
@@ -375,7 +384,7 @@ void title()
                 case MNID_CREDITS:
                   fadeout(500);
                   credits_music = sound_manager->load_music(
-                    get_resource_filename("/music/credits.ogg"));
+                      "/music/credits.ogg");
                   sound_manager->play_music(credits_music);
                   display_text_file("credits.txt");
                   fadeout(500);
@@ -399,12 +408,11 @@ void title()
                 stream << slot;
                 std::string str = _("Are you sure you want to delete slot") + stream.str() + "?";
                 
-                if(confirm_dialog(bkg_title, str.c_str()))
-                  {
-                  str = user_dir + "/save/slot" + stream.str() + ".stsg";
+                if(confirm_dialog(bkg_title, str.c_str())) {
+                  str = "save/slot" + stream.str() + ".stsg";
                   printf("Removing: %s\n",str.c_str());
-                  remove(str.c_str());
-                  }
+                  PHYSFS_delete(str.c_str());
+                }
 
                 update_load_save_game_menu(load_game_menu);
                 Menu::set_current(main_menu);
index 823e23a..a650b4f 100644 (file)
@@ -29,6 +29,7 @@
 #include <SDL_image.h>
 
 #include "gameconfig.h"
+#include "physfs/physfs_sdl.h"
 #include "video/surface.h"
 #include "video/screen.h"
 
@@ -295,7 +296,7 @@ sdl_surface_part_from_file(const std::string& file, int x, int y, int w, int h,
   SDL_Surface * temp;
   SDL_Surface * conv;
 
-  temp = IMG_Load(file.c_str());
+  temp = IMG_Load_RW(get_physfs_SDLRWops(file), true);
   if (temp == 0) {
     std::stringstream msg;
     msg << "Couldn't load '" << file << "': " << SDL_GetError();
@@ -344,7 +345,7 @@ sdl_surface_from_file(const std::string& file, bool use_alpha)
   SDL_Surface* sdl_surface;
   SDL_Surface* temp;
 
-  temp = IMG_Load(file.c_str());
+  temp = IMG_Load_RW(get_physfs_SDLRWops(file), true);
   if (temp == 0) {
     std::stringstream msg;
     msg << "Couldn't load file '" << file << "': " << SDL_GetError();
index 9f0c0c3..c4e3d29 100644 (file)
@@ -352,14 +352,10 @@ WorldMap::WorldMap()
   tux = new Tux(this);
   add_object(tux);
     
-  leveldot_green
-    = new Surface(datadir + "/images/tiles/worldmap/leveldot_green.png", true);
-  leveldot_red
-    = new Surface(datadir + "/images/tiles/worldmap/leveldot_red.png", true);
-  messagedot
-    = new Surface(datadir + "/images/tiles/worldmap/messagedot.png", true);
-  teleporterdot
-    = new Surface(datadir + "/images/tiles/worldmap/teleporterdot.png", true);
+  leveldot_green= new Surface("images/tiles/worldmap/leveldot_green.png", true);
+  leveldot_red = new Surface("images/tiles/worldmap/leveldot_red.png", true);
+  messagedot = new Surface("images/tiles/worldmap/messagedot.png", true);
+  teleporterdot = new Surface("images/tiles/worldmap/teleporterdot.png", true);
 
   name = "<no title>";
   music = "salcon.mod";
@@ -415,12 +411,11 @@ WorldMap::load_map()
 
   try {
     lisp::Parser parser;
-    std::string filename = get_resource_filename(map_filename);
-    std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
+    std::auto_ptr<lisp::Lisp> root (parser.parse(map_filename));
 
     const lisp::Lisp* lisp = root->get_lisp("supertux-worldmap");
     if(!lisp)
-      throw new std::runtime_error("file isn't a supertux-worldmap file.");
+      throw std::runtime_error("file isn't a supertux-worldmap file.");
 
     clear_objects();
     lisp::ListIterator iter(lisp);
@@ -548,8 +543,7 @@ WorldMap::get_level_title(Level& level)
 
   try {
     lisp::Parser parser;
-    std::auto_ptr<lisp::Lisp> root (
-        parser.parse(get_resource_filename(levels_path + level.name)));
+    std::auto_ptr<lisp::Lisp> root (parser.parse(levels_path + level.name));
 
     const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
     if(!level_lisp)
@@ -745,7 +739,7 @@ WorldMap::update(float delta)
           // do a shriking fade to the level
           shrink_fade(Vector((level->pos.x*32 + 16 + offset.x),
                              (level->pos.y*32 + 16 + offset.y)), 500);
-          GameSession session(get_resource_filename(levels_path + level->name),
+          GameSession session(levels_path + level->name,
                               ST_GL_LOAD_LEVEL_FILE, &level->statistics);
 
           switch (session.run())
@@ -847,8 +841,7 @@ WorldMap::update(float delta)
             std::auto_ptr<ScriptInterpreter> interpreter 
               (new ScriptInterpreter(levels_path));
             std::istringstream in(level->extro_script);
-            interpreter->load_script(in, "level-extro-script");
-            interpreter->start_script();
+            interpreter->run_script(in, "level-extro-script");
             add_object(interpreter.release());
           } catch(std::exception& e) {
             std::cerr << "Couldn't run level-extro-script:" << e.what() << "\n";
@@ -992,7 +985,7 @@ WorldMap::display()
 
   quit = false;
 
-  song = sound_manager->load_music(datadir +  "/music/" + music);
+  song = sound_manager->load_music("music/" + music);
   sound_manager->play_music(song);
 
   if(!intro_displayed && intro_script != "") {
@@ -1000,8 +993,7 @@ WorldMap::display()
       std::auto_ptr<ScriptInterpreter> interpreter 
         (new ScriptInterpreter(levels_path));
       std::istringstream in(intro_script);
-      interpreter->load_script(in, "worldmap-intro-script");
-      interpreter->start_script();
+      interpreter->run_script(in, "worldmap-intro-script");
       add_object(interpreter.release());
     } catch(std::exception& e) {
       std::cerr << "Couldn't execute worldmap-intro-script: "
@@ -1058,8 +1050,7 @@ WorldMap::savegame(const std::string& filename)
   if(filename == "")
     return;
 
-  std::ofstream file(filename.c_str(), std::ios::out);
-  lisp::Writer writer(file);
+  lisp::Writer writer(filename);
 
   int nb_solved_levels = 0, total_levels = 0;
   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {