Generated SuperTux libtool library containing more general source, that could prove...
authorTobias Gläßer <tobi.web@gmx.de>
Tue, 20 Jul 2004 17:51:39 +0000 (17:51 +0000)
committerTobias Gläßer <tobi.web@gmx.de>
Tue, 20 Jul 2004 17:51:39 +0000 (17:51 +0000)
Caution: It's not yet SuperTux independed, more work on this will follow, that's just the first step.
The file structure isn't fixed, better ideas will surely find there way in it!

SVN-Revision: 1592

51 files changed:
lib/Makefile.am [new file with mode: 0644]
lib/app/defines.h [new file with mode: 0644]
lib/app/gettext.h [new file with mode: 0644]
lib/app/globals.cpp [new file with mode: 0644]
lib/app/globals.h [new file with mode: 0644]
lib/app/setup.cpp [new file with mode: 0644]
lib/app/setup.h [new file with mode: 0644]
lib/audio/musicref.cpp [new file with mode: 0644]
lib/audio/musicref.h [new file with mode: 0644]
lib/audio/sound.cpp [new file with mode: 0644]
lib/audio/sound.h [new file with mode: 0644]
lib/audio/sound_manager.cpp [new file with mode: 0644]
lib/audio/sound_manager.h [new file with mode: 0644]
lib/gui/button.cpp [new file with mode: 0644]
lib/gui/button.h [new file with mode: 0644]
lib/gui/menu.cpp [new file with mode: 0644]
lib/gui/menu.h [new file with mode: 0644]
lib/gui/mousecursor.cpp [new file with mode: 0644]
lib/gui/mousecursor.h [new file with mode: 0644]
lib/math/physic.cpp [new file with mode: 0644]
lib/math/physic.h [new file with mode: 0644]
lib/math/vector.cpp [new file with mode: 0644]
lib/math/vector.h [new file with mode: 0644]
lib/special/base.h [new file with mode: 0644]
lib/special/game_object.cpp [new file with mode: 0644]
lib/special/game_object.h [new file with mode: 0644]
lib/special/moving_object.cpp [new file with mode: 0644]
lib/special/moving_object.h [new file with mode: 0644]
lib/special/sprite.cpp [new file with mode: 0644]
lib/special/sprite.h [new file with mode: 0644]
lib/special/sprite_manager.cpp [new file with mode: 0644]
lib/special/sprite_manager.h [new file with mode: 0644]
lib/special/stringlist.cpp [new file with mode: 0644]
lib/special/stringlist.h [new file with mode: 0644]
lib/special/timer.cpp [new file with mode: 0644]
lib/special/timer.h [new file with mode: 0644]
lib/utils/configfile.cpp [new file with mode: 0644]
lib/utils/configfile.h [new file with mode: 0644]
lib/utils/exceptions.h [new file with mode: 0644]
lib/utils/lispreader.cpp [new file with mode: 0644]
lib/utils/lispreader.h [new file with mode: 0644]
lib/utils/lispwriter.cpp [new file with mode: 0644]
lib/utils/lispwriter.h [new file with mode: 0644]
lib/video/drawing_context.cpp [new file with mode: 0644]
lib/video/drawing_context.h [new file with mode: 0644]
lib/video/font.cpp [new file with mode: 0644]
lib/video/font.h [new file with mode: 0644]
lib/video/screen.cpp [new file with mode: 0644]
lib/video/screen.h [new file with mode: 0644]
lib/video/surface.cpp [new file with mode: 0644]
lib/video/surface.h [new file with mode: 0644]

diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644 (file)
index 0000000..a72e686
--- /dev/null
@@ -0,0 +1,32 @@
+INCLUDES = 
+METASOURCES = AUTO
+lib_LTLIBRARIES = libsupertux.la
+libsupertux_la_SOURCES =app/defines.h \
+                       app/gettext.h \
+                       app/globals.h app/globals.cpp \
+                       app/setup.h app/setup.cpp \
+                       audio/musicref.h audio/musicref.cpp \
+                       audio/sound.h audio/sound.cpp \
+                       audio/sound_manager.h audio/sound_manager.cpp \
+                       gui/button.h gui/button.cpp \
+                       gui/menu.h gui/menu.cpp \
+                       gui/mousecursor.cpp gui/mousecursor.h \
+                       math/physic.h math/physic.cpp \
+                       math/vector.h math/vector.cpp \
+                       special/base.h \
+                       special/game_object.h special/game_object.cpp \
+                       special/moving_object.h special/moving_object.cpp \
+                       special/sprite.h special/sprite.cpp \
+                       special/sprite_manager.h special/sprite_manager.cpp \
+                       special/stringlist.h special/stringlist.cpp \
+                       special/timer.h special/timer.cpp \
+                       utils/configfile.h utils/configfile.cpp \
+                       utils/exceptions.h \
+                       utils/lispreader.h utils/lispreader.cpp \
+                       utils/lispwriter.h utils/lispwriter.cpp \
+                       video/drawing_context.h video/drawing_context.cpp \
+                       video/font.h video/font.cpp \
+                       video/screen.h video/screen.cpp \
+                       video/surface.h video/surface.cpp
+libsupertux_la_LDFLAGS = -module
+
diff --git a/lib/app/defines.h b/lib/app/defines.h
new file mode 100644 (file)
index 0000000..8d1e184
--- /dev/null
@@ -0,0 +1,101 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_DEFINES_H
+#define SUPERTUX_DEFINES_H 1
+
+#include <config.h>
+
+/* Version: */
+
+#ifndef VERSION
+       #define VERSION "0.1.1"
+#endif
+
+enum Direction { LEFT = 0, RIGHT = 1 };
+
+/* Direction (keyboard/joystick) states: */
+
+#define UP 0
+#define DOWN 1
+
+/* Dying types: */
+
+/* ---- NO 0 */
+enum DyingType {
+  DYING_NOT = 0,
+  DYING_SQUISHED = 1,
+  DYING_FALLING = 2
+};
+
+/* Screen-related stuff */
+// +1 is needed because when tiles are wrapping around the screen there
+//  are two partial tiles on the screen
+#define VISIBLE_TILES_X (25 +1)
+#define VISIBLE_TILES_Y (19 +1)
+
+/* Speed constraints: */
+
+#define MAX_WALK_XM 2.3
+#define MAX_RUN_XM 3.2
+#define MAX_YM 20.0
+#define MAX_JUMP_TIME 375
+#define MAX_LIVES 99
+
+#define WALK_SPEED 1.0
+#define RUN_SPEED 1.5
+#define JUMP_SPEED 1.2
+
+/* gameplay related defines */
+
+#define START_LIVES 4
+
+#define MAX_FIRE_BULLETS 2
+#define MAX_ICE_BULLETS  1
+#define FROZEN_TIME 3000
+
+#define YM_FOR_JUMP 6.0
+#define WALK_ACCELERATION_X 0.03
+#define RUN_ACCELERATION_X 0.04
+#define KILL_BOUNCE_YM 8.0
+
+#define SKID_XM 2.0
+#define SKID_TIME 200
+
+/* Size constraints: */
+
+#define X_OFFSCREEN_DISTANCE (screen->w/2)
+#define Y_OFFSCREEN_DISTANCE (screen->h/2)
+
+/* Debugging */
+
+#ifdef DEBUG
+        #define DEBUG_MSG( msg ) { \
+               printf( msg ); printf("\n"); \
+        }
+        #else
+       #define DEBUG_MSG( msg ) {}
+#endif
+
+#define UNUSED_ARG(a) do {/* null */} while (&a == 0)
+
+#endif /*SUPERTUX_DEFINES_H*/
+
diff --git a/lib/app/gettext.h b/lib/app/gettext.h
new file mode 100644 (file)
index 0000000..c9b81e3
--- /dev/null
@@ -0,0 +1,81 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+   Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+#ifdef HAVE_GETTEXT
+#  define _(String) gettext(String)
+#  define N_(String) gettext_noop(String)
+#else
+#  define _(String) String
+#  define N_(String) String
+#endif
+
+/* NLS can be disabled through the configure --disable-nls option.  */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions.  */
+#include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+   chokes if dcgettext is defined as a macro.  So include it now, to make
+   later inclusions of <locale.h> a NOP.  We don't include <libintl.h>
+   as well because people using "gettext.h" will not include <libintl.h>,
+   and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+   is OK.  */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+#ifndef gettext
+
+/* Disabled NLS.
+   The casts to 'const char *' serve the purpose of producing warnings
+   for invalid uses of the value returned from these functions.
+   On pre-ANSI systems without 'const', the config.h file is supposed to
+   contain "#define const".  */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+    ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+   extraction of messages, but does not call gettext().  The run-time
+   translation is done at a different place in the code.
+   The argument, String, should be a literal string.  Concatenated strings
+   and other string expressions won't work.
+   The macro's expansion is not parenthesized, so that it is suitable as
+   initializer for static 'char[]' or 'const char[]' variables.  */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
diff --git a/lib/app/globals.cpp b/lib/app/globals.cpp
new file mode 100644 (file)
index 0000000..035f7b5
--- /dev/null
@@ -0,0 +1,124 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 SuperTux Development Team, see AUTHORS for details
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include "app/globals.h"
+
+/** The datadir prefix prepended when loading game data file */
+std::string datadir;
+
+JoystickKeymap::JoystickKeymap()
+{
+  a_button     = 0;
+  b_button     = 1;
+  start_button = 2;
+  
+  x_axis = 0;
+  y_axis = 1;
+    
+  dead_zone = 4096;
+}
+
+JoystickKeymap joystick_keymap;
+
+SDL_Surface * screen;
+Font* gold_text;
+Font* blue_text;
+Font* gray_text;
+Font* yellow_nums;
+Font* white_text;
+Font* white_small_text;
+Font* white_big_text;
+
+MouseCursor * mouse_cursor;
+
+bool use_gl;
+bool use_joystick;
+bool use_fullscreen;
+bool debug_mode;
+bool show_fps;
+float game_speed = 1.0f;
+
+int joystick_num = 0;
+char* level_startup_file = 0;
+bool launch_leveleditor_mode = false;
+bool launch_worldmap_mode = false;
+
+/* SuperTux directory ($HOME/.supertux) and save directory($HOME/.supertux/save) */
+char *st_dir, *st_save_dir;
+
+SDL_Joystick * js;
+
+/* Returns 1 for every button event, 2 for a quit event and 0 for no event. */
+int wait_for_event(SDL_Event& event,unsigned int min_delay, unsigned int max_delay, bool empty_events)
+{
+  int i;
+  Timer maxdelay;
+  Timer mindelay;
+  
+  maxdelay.init(false);
+  mindelay.init(false);
+
+  if(max_delay < min_delay)
+    max_delay = min_delay;
+
+  maxdelay.start(max_delay);
+  mindelay.start(min_delay);
+
+  if(empty_events)
+    while (SDL_PollEvent(&event))
+    {}
+
+  /* Handle events: */
+
+  for(i = 0; maxdelay.check() || !i; ++i)
+    {
+      while (SDL_PollEvent(&event))
+        {
+          if(!mindelay.check())
+            {
+              if (event.type == SDL_QUIT)
+                {
+                  /* Quit event - quit: */
+                  return 2;
+                }
+              else if (event.type == SDL_KEYDOWN)
+                {
+                  /* Keypress - skip intro: */
+
+                  return 1;
+                }
+              else if (event.type == SDL_JOYBUTTONDOWN)
+                {
+                  /* Fire button - skip intro: */
+
+                  return 1;
+                }
+              else if (event.type == SDL_MOUSEBUTTONDOWN)
+                {
+                  /* Mouse button - skip intro: */
+                  return 1;
+                }
+            }
+        }
+      SDL_Delay(10);
+    }
+
+  return 0;
+}
diff --git a/lib/app/globals.h b/lib/app/globals.h
new file mode 100644 (file)
index 0000000..ca65289
--- /dev/null
@@ -0,0 +1,83 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Bill Kendrick <bill@newbreedsoftware.com>
+//                     Tobias Glaesser <tobi.web@gmx.de>
+//                     Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_GLOBALS_H
+#define SUPERTUX_GLOBALS_H
+
+#include <string>
+
+#include "SDL.h"
+
+#include "video/font.h"
+#include "gui/menu.h"
+#include "gui/mousecursor.h"
+
+extern std::string datadir;
+
+struct JoystickKeymap
+{
+  int a_button;
+  int b_button;
+  int start_button;
+
+  int x_axis;
+  int y_axis;
+  
+  int dead_zone;
+
+  JoystickKeymap();
+};
+
+extern JoystickKeymap joystick_keymap;
+
+extern SDL_Surface* screen;
+extern Font* gold_text;
+extern Font* white_text;
+extern Font* blue_text;
+extern Font* gray_text;
+extern Font* white_small_text;
+extern Font* white_big_text;
+extern Font* yellow_nums;
+
+extern MouseCursor * mouse_cursor;
+
+extern bool use_gl;
+extern bool use_joystick;
+extern bool use_fullscreen;
+extern bool debug_mode;
+extern bool show_fps;
+
+/** The number of the joystick that will be use in the game */
+extern int joystick_num;
+extern char* level_startup_file;
+extern bool launch_leveleditor_mode;
+extern bool launch_worldmap_mode;
+
+/* SuperTux directory ($HOME/.supertux) and save directory($HOME/.supertux/save) */
+extern char* st_dir;
+extern char* st_save_dir;
+
+extern float game_speed;
+extern SDL_Joystick * js;
+
+int wait_for_event(SDL_Event& event,unsigned int min_delay = 0, unsigned int max_delay = 0, bool empty_events = false);
+
+#endif /* SUPERTUX_GLOBALS_H */
diff --git a/lib/app/setup.cpp b/lib/app/setup.cpp
new file mode 100644 (file)
index 0000000..9218404
--- /dev/null
@@ -0,0 +1,936 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <unistd.h>
+
+#include "SDL.h"
+#include "SDL_image.h"
+#ifndef NOOPENGL
+#include "SDL_opengl.h"
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#ifndef WIN32
+#include <libgen.h>
+#endif
+
+#include <cctype>
+
+#include "app/globals.h"
+#include "app/defines.h"
+#include "app/setup.h"
+#include "video/screen.h"
+#include "video/surface.h"
+#include "gui/menu.h"
+#include "utils/configfile.h"
+#include "audio/sound_manager.h"
+#include "app/gettext.h"
+
+
+#ifdef WIN32
+#define mkdir(dir, mode)    mkdir(dir)
+// on win32 we typically don't want LFS paths
+#undef DATA_PREFIX
+#define DATA_PREFIX "./data/"
+#endif
+
+/* Screen proprities: */
+/* Don't use this to test for the actual screen sizes. Use screen->w/h instead! */
+#define SCREEN_W 800
+#define SCREEN_H 600
+
+/* Local function prototypes: */
+
+void seticon(void);
+void usage(char * prog, int ret);
+
+/* Does the given file exist and is it accessible? */
+int faccessible(const char *filename)
+{
+  struct stat filestat;
+  if (stat(filename, &filestat) == -1)
+    {
+      return false;
+    }
+  else
+    {
+      if(S_ISREG(filestat.st_mode))
+        return true;
+      else
+        return false;
+    }
+}
+
+/* Can we write to this location? */
+int fwriteable(const char *filename)
+{
+  FILE* fi;
+  fi = fopen(filename, "wa");
+  if (fi == NULL)
+    {
+      return false;
+    }
+  return true;
+}
+
+/* Makes sure a directory is created in either the SuperTux home directory or the SuperTux base directory.*/
+int fcreatedir(const char* relative_dir)
+{
+  char path[1024];
+  snprintf(path, 1024, "%s/%s/", st_dir, relative_dir);
+  if(mkdir(path,0755) != 0)
+    {
+      snprintf(path, 1024, "%s/%s/", datadir.c_str(), relative_dir);
+      if(mkdir(path,0755) != 0)
+        {
+          return false;
+        }
+      else
+        {
+          return true;
+        }
+    }
+  else
+    {
+      return true;
+    }
+}
+
+FILE * opendata(const char * rel_filename, const char * mode)
+{
+  char * filename = NULL;
+  FILE * fi;
+
+  filename = (char *) malloc(sizeof(char) * (strlen(st_dir) +
+                                             strlen(rel_filename) + 1));
+
+  strcpy(filename, st_dir);
+  /* Open the high score file: */
+
+  strcat(filename, rel_filename);
+
+  /* Try opening the file: */
+  fi = fopen(filename, mode);
+
+  if (fi == NULL)
+    {
+      fprintf(stderr, "Warning: Unable to open the file \"%s\" ", filename);
+
+      if (strcmp(mode, "r") == 0)
+        fprintf(stderr, "for read!!!\n");
+      else if (strcmp(mode, "w") == 0)
+        fprintf(stderr, "for write!!!\n");
+    }
+  free( filename );
+
+  return(fi);
+}
+
+/* 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. */
+string_list_type dsubdirs(const char *rel_path,const  char* expected_file)
+{
+  DIR *dirStructP;
+  struct dirent *direntp;
+  string_list_type sdirs;
+  char filename[1024];
+  char path[1024];
+
+  string_list_init(&sdirs);
+  sprintf(path,"%s/%s",st_dir,rel_path);
+  if((dirStructP = opendir(path)) != NULL)
+    {
+      while((direntp = readdir(dirStructP)) != NULL)
+        {
+          char absolute_filename[1024];
+          struct stat buf;
+
+          sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
+
+          if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
+            {
+              if(expected_file != NULL)
+                {
+                  sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
+                  if(!faccessible(filename))
+                    continue;
+                }
+
+              string_list_add_item(&sdirs,direntp->d_name);
+            }
+        }
+      closedir(dirStructP);
+    }
+
+  sprintf(path,"%s/%s",datadir.c_str(),rel_path);
+  if((dirStructP = opendir(path)) != NULL)
+    {
+      while((direntp = readdir(dirStructP)) != NULL)
+        {
+          char absolute_filename[1024];
+          struct stat buf;
+
+          sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
+
+          if (stat(absolute_filename, &buf) == 0 && S_ISDIR(buf.st_mode))
+            {
+              if(expected_file != NULL)
+                {
+                  sprintf(filename,"%s/%s/%s",path,direntp->d_name,expected_file);
+                  if(!faccessible(filename))
+                    {
+                      continue;
+                    }
+                  else
+                    {
+                      sprintf(filename,"%s/%s/%s/%s",st_dir,rel_path,direntp->d_name,expected_file);
+                      if(faccessible(filename))
+                        continue;
+                    }
+                }
+
+              string_list_add_item(&sdirs,direntp->d_name);
+            }
+        }
+      closedir(dirStructP);
+    }
+
+  return sdirs;
+}
+
+string_list_type dfiles(const char *rel_path, const  char* glob, const  char* exception_str)
+{
+  DIR *dirStructP;
+  struct dirent *direntp;
+  string_list_type sdirs;
+  char path[1024];
+
+  string_list_init(&sdirs);
+  sprintf(path,"%s/%s",st_dir,rel_path);
+  if((dirStructP = opendir(path)) != NULL)
+    {
+      while((direntp = readdir(dirStructP)) != NULL)
+        {
+          char absolute_filename[1024];
+          struct stat buf;
+
+          sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
+
+          if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
+            {
+              if(exception_str != NULL)
+                {
+                  if(strstr(direntp->d_name,exception_str) != NULL)
+                    continue;
+                }
+              if(glob != NULL)
+                if(strstr(direntp->d_name,glob) == NULL)
+                  continue;
+
+              string_list_add_item(&sdirs,direntp->d_name);
+            }
+        }
+      closedir(dirStructP);
+    }
+
+  sprintf(path,"%s/%s",datadir.c_str(),rel_path);
+  if((dirStructP = opendir(path)) != NULL)
+    {
+      while((direntp = readdir(dirStructP)) != NULL)
+        {
+          char absolute_filename[1024];
+          struct stat buf;
+
+          sprintf(absolute_filename, "%s/%s", path, direntp->d_name);
+
+          if (stat(absolute_filename, &buf) == 0 && S_ISREG(buf.st_mode))
+            {
+              if(exception_str != NULL)
+                {
+                  if(strstr(direntp->d_name,exception_str) != NULL)
+                    continue;
+                }
+              if(glob != NULL)
+                if(strstr(direntp->d_name,glob) == NULL)
+                  continue;
+
+              string_list_add_item(&sdirs,direntp->d_name);
+            }
+        }
+      closedir(dirStructP);
+    }
+
+  return sdirs;
+}
+
+void free_strings(char **strings, int num)
+{
+  int i;
+  for(i=0; i < num; ++i)
+    free(strings[i]);
+}
+
+/* --- SETUP --- */
+/* Set SuperTux configuration and save directories */
+void st_directory_setup(void)
+{
+  char *home;
+  char str[1024];
+  /* Get home directory (from $HOME variable)... if we can't determine it,
+     use the current directory ("."): */
+  if (getenv("HOME") != NULL)
+    home = getenv("HOME");
+  else
+    home = ".";
+
+  st_dir = (char *) malloc(sizeof(char) * (strlen(home) +
+                                           strlen("/.supertux") + 1));
+  strcpy(st_dir, home);
+  strcat(st_dir, "/.supertux");
+
+  /* Remove .supertux config-file from old SuperTux versions */
+  if(faccessible(st_dir))
+    {
+      remove
+        (st_dir);
+    }
+
+  st_save_dir = (char *) malloc(sizeof(char) * (strlen(st_dir) + strlen("/save") + 1));
+
+  strcpy(st_save_dir,st_dir);
+  strcat(st_save_dir,"/save");
+
+  /* Create them. In the case they exist they won't destroy anything. */
+  mkdir(st_dir, 0755);
+  mkdir(st_save_dir, 0755);
+
+  sprintf(str, "%s/levels", st_dir);
+  mkdir(str, 0755);
+
+  // User has not that a datadir, so we try some magic
+  if (datadir.empty())
+    {
+#ifndef WIN32
+      // Detect datadir
+      char exe_file[PATH_MAX];
+      if (readlink("/proc/self/exe", exe_file, PATH_MAX) < 0)
+        {
+          puts("Couldn't read /proc/self/exe, using default path: " DATA_PREFIX);
+          datadir = DATA_PREFIX;
+        }
+      else
+        {
+          std::string exedir = std::string(dirname(exe_file)) + "/";
+          
+          datadir = exedir + "../data"; // SuperTux run from source dir
+          if (access(datadir.c_str(), F_OK) != 0)
+            {
+              datadir = exedir + "../share/supertux"; // SuperTux run from PATH
+              if (access(datadir.c_str(), F_OK) != 0) 
+                { // If all fails, fall back to compiled path
+                  datadir = DATA_PREFIX; 
+                }
+            }
+        }
+#else
+  datadir = DATA_PREFIX;
+#endif
+    }
+  printf("Datadir: %s\n", datadir.c_str());
+}
+
+void st_general_setup(void)
+{
+  /* Seed random number generator: */
+
+  srand(SDL_GetTicks());
+
+  /* Set icon image: */
+
+  seticon();
+
+  /* Unicode needed for input handling: */
+
+  SDL_EnableUNICODE(1);
+
+  /* Load global images: */
+  gold_text = new Font(datadir + "/images/fonts/gold.png", Font::TEXT, 16,18);
+  blue_text = new Font(datadir + "/images/fonts/blue.png", Font::TEXT, 16,18,3);
+  white_text  = new Font(datadir + "/images/fonts/white.png",
+      Font::TEXT, 16,18);
+  gray_text  = new Font(datadir + "/images/fonts/gray.png",
+      Font::TEXT, 16,18);
+  white_small_text = new Font(datadir + "/images/fonts/white-small.png",
+          Font::TEXT, 8,9, 1);
+  white_big_text   = new Font(datadir + "/images/fonts/white-big.png",
+      Font::TEXT, 20,22, 3);
+  yellow_nums = new Font(datadir + "/images/fonts/numbers.png",
+      Font::NUM, 32,32);
+
+  /* Load GUI/menu images: */
+  checkbox = new Surface(datadir + "/images/status/checkbox.png", true);
+  checkbox_checked = new Surface(datadir + "/images/status/checkbox-checked.png", true);
+  back = new Surface(datadir + "/images/status/back.png", true);
+  arrow_left = new Surface(datadir + "/images/icons/left.png", true);
+  arrow_right = new Surface(datadir + "/images/icons/right.png", true);
+
+  /* Load the mouse-cursor */
+  mouse_cursor = new MouseCursor( datadir + "/images/status/mousecursor.png",1);
+  MouseCursor::set_current(mouse_cursor);
+  
+}
+
+void st_general_free(void)
+{
+
+  /* Free global images: */
+  delete gold_text;
+  delete white_text;
+  delete blue_text;
+  delete gray_text;
+  delete white_small_text;
+  delete white_big_text;
+  delete yellow_nums;
+
+  /* Free GUI/menu images: */
+  delete checkbox;
+  delete checkbox_checked;
+  delete back;
+  delete arrow_left;
+  delete arrow_right;
+
+  /* Free mouse-cursor */
+  delete mouse_cursor;
+  
+  /* Free menus */
+  delete main_menu;
+  delete game_menu;
+  delete options_menu;
+  delete options_keys_menu;
+  delete options_joystick_menu;
+  delete highscore_menu;
+  delete contrib_menu;
+  delete contrib_subset_menu;
+  delete save_game_menu;
+  delete load_game_menu;
+}
+
+void st_video_setup(void)
+{
+  /* Init SDL Video: */
+  if (SDL_Init(SDL_INIT_VIDEO) < 0)
+    {
+      fprintf(stderr,
+              "\nError: I could not initialize video!\n"
+              "The Simple DirectMedia error that occured was:\n"
+              "%s\n\n", SDL_GetError());
+      exit(1);
+    }
+
+  /* Open display: */
+  if(use_gl)
+    st_video_setup_gl();
+  else
+    st_video_setup_sdl();
+
+  Surface::reload_all();
+
+  /* Set window manager stuff: */
+  SDL_WM_SetCaption("SuperTux " VERSION, "SuperTux");
+}
+
+void st_video_setup_sdl(void)
+{
+  if (use_fullscreen)
+    {
+      screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN ) ; /* | SDL_HWSURFACE); */
+      if (screen == NULL)
+        {
+          fprintf(stderr,
+                  "\nWarning: I could not set up fullscreen video for "
+                  "800x600 mode.\n"
+                  "The Simple DirectMedia error that occured was:\n"
+                  "%s\n\n", SDL_GetError());
+          use_fullscreen = false;
+        }
+    }
+  else
+    {
+      screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_HWSURFACE | SDL_DOUBLEBUF );
+
+      if (screen == NULL)
+        {
+          fprintf(stderr,
+                  "\nError: I could not set up video for 800x600 mode.\n"
+                  "The Simple DirectMedia error that occured was:\n"
+                  "%s\n\n", SDL_GetError());
+          exit(1);
+        }
+    }
+}
+
+void st_video_setup_gl(void)
+{
+#ifndef NOOPENGL
+
+  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
+  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
+  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+  if (use_fullscreen)
+    {
+      screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_FULLSCREEN | SDL_OPENGL) ; /* | SDL_HWSURFACE); */
+      if (screen == NULL)
+        {
+          fprintf(stderr,
+                  "\nWarning: I could not set up fullscreen video for "
+                  "640x480 mode.\n"
+                  "The Simple DirectMedia error that occured was:\n"
+                  "%s\n\n", SDL_GetError());
+          use_fullscreen = false;
+        }
+    }
+  else
+    {
+      screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 0, SDL_OPENGL);
+
+      if (screen == NULL)
+        {
+          fprintf(stderr,
+                  "\nError: I could not set up video for 640x480 mode.\n"
+                  "The Simple DirectMedia error that occured was:\n"
+                  "%s\n\n", SDL_GetError());
+          exit(1);
+        }
+    }
+
+  /*
+   * Set up OpenGL for 2D rendering.
+   */
+  glDisable(GL_DEPTH_TEST);
+  glDisable(GL_CULL_FACE);
+
+  glViewport(0, 0, screen->w, screen->h);
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glOrtho(0, screen->w, screen->h, 0, -1.0, 1.0);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  glTranslatef(0.0f, 0.0f, 0.0f);
+
+#endif
+
+}
+
+void st_joystick_setup(void)
+{
+
+  /* Init Joystick: */
+
+  use_joystick = true;
+
+  if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
+    {
+      fprintf(stderr, "Warning: I could not initialize joystick!\n"
+              "The Simple DirectMedia error that occured was:\n"
+              "%s\n\n", SDL_GetError());
+
+      use_joystick = false;
+    }
+  else
+    {
+      /* Open joystick: */
+      if (SDL_NumJoysticks() <= 0)
+        {
+          fprintf(stderr, "Info: No joysticks were found.\n");
+
+          use_joystick = false;
+        }
+      else
+        {
+          js = SDL_JoystickOpen(joystick_num);
+
+          if (js == NULL)
+            {
+              fprintf(stderr, "Warning: Could not open joystick %d.\n"
+                      "The Simple DirectMedia error that occured was:\n"
+                      "%s\n\n", joystick_num, SDL_GetError());
+
+              use_joystick = false;
+            }
+          else
+            {
+              if (SDL_JoystickNumAxes(js) < 2)
+                {
+                  fprintf(stderr,
+                          "Warning: Joystick does not have enough axes!\n");
+
+                  use_joystick = false;
+                }
+              else
+                {
+                  if (SDL_JoystickNumButtons(js) < 2)
+                    {
+                      fprintf(stderr,
+                              "Warning: "
+                              "Joystick does not have enough buttons!\n");
+
+                      use_joystick = false;
+                    }
+                }
+            }
+        }
+    }
+}
+
+void st_audio_setup(void)
+{
+
+  /* Init SDL Audio silently even if --disable-sound : */
+
+  if (audio_device)
+    {
+      if (SDL_Init(SDL_INIT_AUDIO) < 0)
+        {
+          /* only print out message if sound or music
+             was not disabled at command-line
+           */
+          if (use_sound || use_music)
+            {
+              fprintf(stderr,
+                      "\nWarning: I could not initialize audio!\n"
+                      "The Simple DirectMedia error that occured was:\n"
+                      "%s\n\n", SDL_GetError());
+            }
+          /* keep the programming logic the same :-)
+             because in this case, use_sound & use_music' values are ignored
+             when there's no available audio device
+          */
+          use_sound = false;
+          use_music = false;
+          audio_device = false;
+        }
+    }
+
+
+  /* Open sound silently regarless the value of "use_sound": */
+
+  if (audio_device)
+    {
+      if (open_audio(44100, AUDIO_S16, 2, 2048) < 0)
+        {
+          /* only print out message if sound or music
+             was not disabled at command-line
+           */
+          if (use_sound || use_music)
+            {
+              fprintf(stderr,
+                      "\nWarning: I could not set up audio for 44100 Hz "
+                      "16-bit stereo.\n"
+                      "The Simple DirectMedia error that occured was:\n"
+                      "%s\n\n", SDL_GetError());
+            }
+          use_sound = false;
+          use_music = false;
+          audio_device = false;
+        }
+    }
+
+}
+
+
+/* --- SHUTDOWN --- */
+
+void st_shutdown(void)
+{
+  close_audio();
+  SDL_Quit();
+  config->save();
+}
+
+/* --- ABORT! --- */
+
+void st_abort(const std::string& reason, const std::string& details)
+{
+  fprintf(stderr, "\nError: %s\n%s\n\n", reason.c_str(), details.c_str());
+  st_shutdown();
+  abort();
+}
+
+/* Set Icon (private) */
+
+void seticon(void)
+{
+//  int masklen;
+//  Uint8 * mask;
+  SDL_Surface * icon;
+
+
+  /* Load icon into a surface: */
+
+  icon = IMG_Load((datadir + "/images/supertux.xpm").c_str());
+  if (icon == NULL)
+    {
+      fprintf(stderr,
+              "\nError: I could not load the icon image: %s%s\n"
+              "The Simple DirectMedia error that occured was:\n"
+              "%s\n\n", datadir.c_str(), "/images/supertux.xpm", SDL_GetError());
+      exit(1);
+    }
+
+
+  /* Create mask: */
+/*
+  masklen = (((icon -> w) + 7) / 8) * (icon -> h);
+  mask = (Uint8*) malloc(masklen * sizeof(Uint8));
+  memset(mask, 0xFF, masklen);
+*/
+
+  /* Set icon: */
+
+  SDL_WM_SetIcon(icon, NULL);//mask);
+
+
+  /* Free icon surface & mask: */
+
+//  free(mask);
+  SDL_FreeSurface(icon);
+}
+
+
+/* Parse command-line arguments: */
+
+void parseargs(int argc, char * argv[])
+{
+  int i;
+
+  config->load();
+
+  /* Parse arguments: */
+
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp(argv[i], "--fullscreen") == 0 ||
+          strcmp(argv[i], "-f") == 0)
+        {
+          use_fullscreen = true;
+        }
+      else if (strcmp(argv[i], "--window") == 0 ||
+               strcmp(argv[i], "-w") == 0)
+        {
+          use_fullscreen = false;
+        }
+      else if (strcmp(argv[i], "--joystick") == 0 || strcmp(argv[i], "-j") == 0)
+        {
+          assert(i+1 < argc);
+          joystick_num = atoi(argv[++i]);
+        }
+      else if (strcmp(argv[i], "--joymap") == 0)
+        {
+          assert(i+1 < argc);
+          if (sscanf(argv[++i],
+                     "%d:%d:%d:%d:%d", 
+                     &joystick_keymap.x_axis, 
+                     &joystick_keymap.y_axis, 
+                     &joystick_keymap.a_button, 
+                     &joystick_keymap.b_button, 
+                     &joystick_keymap.start_button) != 5)
+            {
+              puts("Warning: Invalid or incomplete joymap, should be: 'XAXIS:YAXIS:A:B:START'");
+            }
+          else
+            {
+              std::cout << "Using new joymap:\n"
+                        << "  X-Axis:       " << joystick_keymap.x_axis << "\n"
+                        << "  Y-Axis:       " << joystick_keymap.y_axis << "\n"
+                        << "  A-Button:     " << joystick_keymap.a_button << "\n"
+                        << "  B-Button:     " << joystick_keymap.b_button << "\n"
+                        << "  Start-Button: " << joystick_keymap.start_button << std::endl;
+            }
+        }
+      else if (strcmp(argv[i], "--leveleditor") == 0)
+        {
+          launch_leveleditor_mode = true;
+        }
+      else if (strcmp(argv[i], "--worldmap") == 0)
+        {
+          launch_worldmap_mode = true;
+        }
+      else if (strcmp(argv[i], "--datadir") == 0 
+               || strcmp(argv[i], "-d") == 0 )
+        {
+          assert(i+1 < argc);
+          datadir = argv[++i];
+        }
+      else if (strcmp(argv[i], "--show-fps") == 0)
+        {
+          /* Use full screen: */
+
+          show_fps = true;
+        }
+      else if (strcmp(argv[i], "--opengl") == 0 ||
+               strcmp(argv[i], "-gl") == 0)
+        {
+#ifndef NOOPENGL
+          /* Use OpengGL: */
+
+          use_gl = true;
+#endif
+        }
+      else if (strcmp(argv[i], "--sdl") == 0)
+          {
+            use_gl = false;
+          }
+      else if (strcmp(argv[i], "--usage") == 0)
+        {
+          /* Show usage: */
+
+          usage(argv[0], 0);
+        }
+      else if (strcmp(argv[i], "--version") == 0)
+        {
+          /* Show version: */
+          printf("SuperTux " VERSION "\n");
+          exit(0);
+        }
+      else if (strcmp(argv[i], "--disable-sound") == 0)
+        {
+          /* Disable the compiled in sound feature */
+          printf("Sounds disabled \n");
+          use_sound = false;
+          audio_device = false;
+        }
+      else if (strcmp(argv[i], "--disable-music") == 0)
+        {
+          /* Disable the compiled in sound feature */
+          printf("Music disabled \n");
+          use_music = false;
+        }
+      else if (strcmp(argv[i], "--debug") == 0)
+        {
+          /* Enable the debug-mode */
+          debug_mode = true;
+
+        }
+      else if (strcmp(argv[i], "--help") == 0)
+        {     /* Show help: */
+          puts(_("  SuperTux  " VERSION "\n"
+               "  Please see the file \"README.txt\" for more details.\n"));
+          printf(_("Usage: %s [OPTIONS] FILENAME\n\n"), argv[0]);
+          puts(_("Display Options:\n"
+               "  -f, --fullscreen    Run in fullscreen mode.\n"
+               "  -w, --window        Run in window mode.\n"
+               "  --opengl            If OpenGL support was compiled in, this will tell\n"
+               "                      SuperTux to make use of it.\n"
+               "  --sdl               Use the SDL software graphical renderer\n"
+               "\n"
+               "Sound Options:\n"
+               "  --disable-sound     If sound support was compiled in,  this will\n"
+               "                      disable sound for this session of the game.\n"
+               "  --disable-music     Like above, but this will disable music.\n"
+               "\n"
+               "Misc Options:\n"
+               "  -j, --joystick NUM  Use joystick NUM (default: 0)\n" 
+               "  --joymap XAXIS:YAXIS:A:B:START\n"
+               "                      Define how joystick buttons and axis should be mapped\n"
+               "  --leveleditor       Opens the leveleditor in a file.\n"
+               "  --worldmap          Opens the specified worldmap file.\n"
+               "  -d, --datadir DIR   Load Game data from DIR (default: automatic)\n"
+               "  --debug             Enables the debug mode, which is useful for developers.\n"
+               "  --help              Display a help message summarizing command-line\n"
+               "                      options, license and game controls.\n"
+               "  --usage             Display a brief message summarizing command-line options.\n"
+               "  --version           Display the version of SuperTux you're running.\n\n"
+               ));
+          exit(0);
+        }
+      else if (argv[i][0] != '-')
+        {
+          level_startup_file = argv[i];
+        }
+      else
+        {
+          /* Unknown - complain! */
+
+          usage(argv[0], 1);
+        }
+    }
+}
+
+
+/* Display usage: */
+
+void usage(char * prog, int ret)
+{
+  FILE * fi;
+
+
+  /* Determine which stream to write to: */
+
+  if (ret == 0)
+    fi = stdout;
+  else
+    fi = stderr;
+
+
+  /* Display the usage message: */
+
+  fprintf(fi, _("Usage: %s [--fullscreen] [--opengl] [--disable-sound] [--disable-music] [--debug] | [--usage | --help | --version] [--leveleditor] [--worldmap] FILENAME\n"),
+          prog);
+
+
+  /* Quit! */
+
+  exit(ret);
+}
+
+std::vector<std::string> read_directory(const std::string& pathname)
+{
+  std::vector<std::string> dirnames;
+  
+  DIR* dir = opendir(pathname.c_str());
+  if (dir)
+    {
+      struct dirent *direntp;
+      
+      while((direntp = readdir(dir)))
+        {
+          dirnames.push_back(direntp->d_name);
+        }
+      
+      closedir(dir);
+    }
+
+  return dirnames;
+}
+
+/* EOF */
diff --git a/lib/app/setup.h b/lib/app/setup.h
new file mode 100644 (file)
index 0000000..fa79017
--- /dev/null
@@ -0,0 +1,52 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SETUP_H
+#define SUPERTUX_SETUP_H
+
+#include <vector>
+#include <string>
+#include "gui/menu.h"
+#include "audio/sound.h"
+#include "special/base.h"
+
+int faccessible(const char *filename);
+int fcreatedir(const char* relative_dir);
+int fwriteable(const char *filename);
+std::vector<std::string> read_directory(const std::string& pathname);
+
+FILE * opendata(const char * filename, const char * mode);
+string_list_type dsubdirs(const char *rel_path, const char* expected_file);
+string_list_type dfiles(const char *rel_path, const char* glob, const char* exception_str);
+void free_strings(char **strings, int num);
+void st_directory_setup(void);
+void st_general_setup(void);
+void st_general_free();
+void st_video_setup_sdl(void);
+void st_video_setup_gl(void);
+void st_video_setup(void);
+void st_audio_setup(void);
+void st_joystick_setup(void);
+void st_shutdown(void);
+void st_abort(const std::string& reason, const std::string& details);
+
+void parseargs(int argc, char * argv[]);
+
+#endif /*SUPERTUX_SETUP_H*/
+
diff --git a/lib/audio/musicref.cpp b/lib/audio/musicref.cpp
new file mode 100644 (file)
index 0000000..2b5efcd
--- /dev/null
@@ -0,0 +1,66 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "audio/musicref.h"
+
+MusicRef::MusicRef()
+  : music(0)
+{
+}
+
+MusicRef::MusicRef(SoundManager::MusicResource* newmusic)
+  : music(newmusic)
+{
+  if(music)
+    music->refcount++;
+}
+
+MusicRef::~MusicRef()
+{
+  if(music) {
+    music->refcount--;
+    if(music->refcount == 0)
+      music->manager->free_music(music);
+  }
+}
+
+MusicRef::MusicRef(const MusicRef& other)
+  : music(other.music)
+{
+  if(music)
+    music->refcount++;
+}
+
+MusicRef&
+MusicRef::operator =(const MusicRef& other)
+{
+  SoundManager::MusicResource* oldres = music;
+  music = other.music;
+  if(music)
+    music->refcount++;
+  if(oldres) {
+    oldres->refcount--;
+    if(oldres->refcount == 0)
+      music->manager->free_music(music);
+  }
+
+  return *this;
+}
+
diff --git a/lib/audio/musicref.h b/lib/audio/musicref.h
new file mode 100644 (file)
index 0000000..b9899ed
--- /dev/null
@@ -0,0 +1,45 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_MUSICREF_H
+#define SUPERTUX_MUSICREF_H
+
+#include "audio/sound_manager.h"
+
+/** This class holds a reference to a music file and maintains a correct
+ * refcount for that file.
+ */
+class MusicRef
+{
+public:
+  MusicRef();
+  MusicRef(const MusicRef& other);
+  ~MusicRef();
+
+  MusicRef& operator= (const MusicRef& other);
+
+private:
+  friend class SoundManager;
+  MusicRef(SoundManager::MusicResource* music);
+  
+  SoundManager::MusicResource* music;
+};
+
+#endif /*SUPERTUX_MUSICREF_H*/
+
diff --git a/lib/audio/sound.cpp b/lib/audio/sound.cpp
new file mode 100644 (file)
index 0000000..3ba2752
--- /dev/null
@@ -0,0 +1,77 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//  Copyright (C) 2004 Duong-Khang NGUYEN <neoneurone@users.sf.net>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <string>
+#include "audio/sound.h"
+
+/*global variable*/
+bool use_sound = true;    /* handle sound on/off menu and command-line option */
+bool use_music = true;    /* handle music on/off menu and command-line option */
+bool audio_device = true; /* != 0: available and initialized */
+
+#include <SDL_mixer.h>
+
+std::vector<Mix_Chunk*> sounds;
+
+/* --- OPEN THE AUDIO DEVICE --- */
+
+int open_audio (int frequency, Uint16 format, int channels, int chunksize)
+{
+  if (Mix_OpenAudio( frequency, format, channels, chunksize ) < 0)
+    return -1;
+
+  // allocate 16 channels for mixing
+  if (Mix_AllocateChannels(8)  != 8)
+    return -2;
+  
+  return 0;
+}
+
+
+/* --- CLOSE THE AUDIO DEVICE --- */
+
+void close_audio( void )
+{
+  if (audio_device) {
+    Mix_CloseAudio();
+  }
+}
+
+
+/* --- LOAD A SOUND --- */
+
+Mix_Chunk* load_sound(const std::string& file)
+{
+  if(!audio_device)
+    return 0;
+  
+  Mix_Chunk* snd = Mix_LoadWAV(file.c_str());
+
+  /*if (snd == 0)
+    st_abort("Can't load", file);*/
+
+  return(snd);
+}
+
+void free_chunk(Mix_Chunk *chunk)
+{
+  Mix_FreeChunk( chunk );
+}
+
diff --git a/lib/audio/sound.h b/lib/audio/sound.h
new file mode 100644 (file)
index 0000000..975021f
--- /dev/null
@@ -0,0 +1,53 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//  Copyright (C) 2004 Duong-Khang NGUYEN <neoneurone@users.sf.net>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SOUND_H
+#define SUPERTUX_SOUND_H
+
+//#include "defines.h"     /* get YES/NO defines */
+#include <vector>
+
+/*global variable*/
+extern bool use_sound;           /* handle sound on/off menu and command-line option */
+extern bool use_music;           /* handle music on/off menu and command-line */
+extern bool audio_device;        /* != 0: available and initialized */
+
+/* enum of different internal music types */
+enum Music_Type {
+  NO_MUSIC,
+  LEVEL_MUSIC,
+  HURRYUP_MUSIC,
+  HERRING_MUSIC
+};
+
+#include <string>
+#include <SDL_mixer.h>
+
+/* variables for stocking the sound and music */
+extern std::vector<Mix_Chunk*> sounds;
+
+/* functions handling the sound and music */
+int open_audio(int frequency, Uint16 format, int channels, int chunksize);
+void close_audio( void );
+
+Mix_Chunk * load_sound(const std::string& file);
+void free_chunk(Mix_Chunk*chunk);
+
+#endif /*SUPERTUX_SOUND_H*/
diff --git a/lib/audio/sound_manager.cpp b/lib/audio/sound_manager.cpp
new file mode 100644 (file)
index 0000000..1c790f1
--- /dev/null
@@ -0,0 +1,188 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <cmath>
+#include <cassert>
+
+#include "audio/sound_manager.h"
+#include "audio/musicref.h"
+#include "audio/sound.h"
+#include "app/globals.h"
+#include "app/setup.h"
+#include "special/moving_object.h"
+
+SoundManager::SoundManager()
+  : current_music(0), music_enabled(true)
+{
+}
+
+SoundManager::~SoundManager()
+{
+  if(audio_device)
+    Mix_HaltMusic();
+}
+
+void
+SoundManager::play_sound(Mix_Chunk* sound)
+{
+  if(!audio_device || !use_sound)
+    return;
+
+  Mix_PlayChannel(-1, sound, 0);  
+}
+
+void
+SoundManager::play_sound(Mix_Chunk* sound, const MovingObject* object, const Vector& pos)
+{
+  // TODO keep track of the object later and move the sound along with the
+  // object.
+  play_sound(sound, object->get_pos(), pos);
+}
+
+void
+SoundManager::play_sound(Mix_Chunk* sound, const Vector& pos, const Vector& pos2)
+{
+  if(!audio_device || !use_sound)
+    return;
+
+  // TODO make sure this formula is good
+  float distance 
+    = pos2.x- pos.x;
+  int loud = int(255.0/float(screen->w*2) * fabsf(distance));
+  if(loud > 255)
+    return;
+
+  int chan = Mix_PlayChannel(-1, sound, 0);
+  if(chan < 0)
+    return;                                  
+  Mix_SetDistance(chan, loud);
+
+  // very bad way to do this...
+  if(distance > 100)
+    Mix_SetPanning(chan, 230, 24);
+  else if(distance < -100)
+    Mix_SetPanning(chan, 24, 230);
+}
+
+MusicRef
+SoundManager::load_music(const std::string& file)
+{
+  if(!audio_device)
+    return MusicRef(0);
+
+  if(!exists_music(file))
+    st_abort("Couldn't load musicfile ", file.c_str());
+
+  std::map<std::string, MusicResource>::iterator i = musics.find(file);
+  assert(i != musics.end());
+  return MusicRef(& (i->second));
+}
+
+bool
+SoundManager::exists_music(const std::string& file)
+{
+  if(!audio_device)
+    return true;
+  
+  // song already loaded?
+  std::map<std::string, MusicResource>::iterator i = musics.find(file);
+  if(i != musics.end()) {
+    return true;                                      
+  }
+  
+  Mix_Music* song = Mix_LoadMUS(file.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()));
+  MusicResource& resource = result.first->second;
+  resource.manager = this;
+  resource.music = song;
+
+  return true;
+}
+
+void
+SoundManager::free_music(MusicResource* )
+{
+  // TODO free music, currently we can't do this since SDL_mixer seems to have
+  // some bugs if you load/free alot of mod files.  
+}
+
+void
+SoundManager::play_music(const MusicRef& musicref, int loops)
+{
+  if(!audio_device)
+    return;
+
+  if(musicref.music == 0 || current_music == musicref.music)
+    return;
+
+  if(current_music)
+    current_music->refcount--;
+  
+  current_music = musicref.music;
+  current_music->refcount++;
+  
+  if(music_enabled)
+    Mix_PlayMusic(current_music->music, loops);
+}
+
+void
+SoundManager::halt_music()
+{
+  if(!audio_device)
+    return;
+  
+  Mix_HaltMusic();
+  
+  if(current_music) {
+    current_music->refcount--;
+    if(current_music->refcount == 0)
+      free_music(current_music);
+    current_music = 0;
+  }
+}
+
+void
+SoundManager::enable_music(bool enable)
+{
+  if(!audio_device)
+    return;
+
+  if(enable == music_enabled)
+    return;
+  
+  music_enabled = enable;
+  if(music_enabled == false) {
+    Mix_HaltMusic();
+  } else {
+    Mix_PlayMusic(current_music->music, -1);
+  }
+}
+
+SoundManager::MusicResource::~MusicResource()
+{
+  // don't free music buggy SDL_Mixer crashs for some mod files
+  // Mix_FreeMusic(music);
+}
+
diff --git a/lib/audio/sound_manager.h b/lib/audio/sound_manager.h
new file mode 100644 (file)
index 0000000..4a74244
--- /dev/null
@@ -0,0 +1,74 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SOUND_MANAGER_H
+#define SUPERTUX_SOUND_MANAGER_H
+
+#include <string>
+#include <map>
+
+#include "SDL_mixer.h"
+
+#include "math/vector.h"
+
+class MusicRef;
+class MovingObject;
+
+/** This class handles all sounds that are played
+ */
+class SoundManager
+{
+public:
+  SoundManager();
+  ~SoundManager();
+
+  void play_sound(Mix_Chunk* sound);
+  void play_sound(Mix_Chunk* sound, const Vector& pos, const Vector& pos2);
+  void play_sound(Mix_Chunk* sound, const MovingObject* object, const Vector& pos);
+
+  MusicRef load_music(const std::string& file);
+  bool exists_music(const std::string& filename);
+  
+  void play_music(const MusicRef& music, int loops = -1);
+  void halt_music();
+
+  void enable_music(bool enable);
+
+private:
+  // music part
+  friend class MusicRef;
+  class MusicResource
+  {
+  public:
+    ~MusicResource();
+
+    SoundManager* manager;
+    Mix_Music* music;
+    int refcount;
+  };
+
+  void free_music(MusicResource* music);
+
+  std::map<std::string, MusicResource> musics;
+  MusicResource* current_music;
+  bool music_enabled;
+};
+
+#endif /*SUPERTUX_SOUND_MANAGER_H*/
+
diff --git a/lib/gui/button.cpp b/lib/gui/button.cpp
new file mode 100644 (file)
index 0000000..66b0963
--- /dev/null
@@ -0,0 +1,324 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <cstring>
+#include <cstdlib>
+
+#include "app/setup.h"
+#include "video/screen.h"
+#include "video/drawing_context.h"
+#include "app/globals.h"
+#include "gui/button.h"
+
+Timer Button::popup_timer;
+
+Button::Button(Surface* button_image, const std::string& ninfo,
+    SDLKey nshortcut, int x, int y, int mw, int mh)
+{
+  popup_timer.init(false);
+
+  if(button_image)
+    icon.push_back(button_image);
+
+  info = ninfo;
+
+  shortcut = nshortcut;
+
+  rect.x = x;
+  rect.y = y;
+  rect.w = icon[0]->w;
+  rect.h = icon[0]->h;
+  tag = -1;
+  state = BUTTON_NONE;
+  show_info = false;
+}
+
+Button::Button(const std::string& imagefilename, const std::string& ninfo,
+    SDLKey nshortcut, int x, int y, int mw, int mh)
+{
+  popup_timer.init(false);
+
+  add_icon(imagefilename, mw, mh);
+  
+  info = ninfo;
+
+  shortcut = nshortcut;
+
+  rect.x = x;
+  rect.y = y;
+  rect.w = icon[0]->w;
+  rect.h = icon[0]->h;
+  tag = -1;
+  state = BUTTON_NONE;
+  show_info = false;
+}
+
+void Button::add_icon(const std::string& icon_file, int mw, int mh)
+{
+  char filename[1024];
+
+  if(!icon_file.empty())
+  {
+    snprintf(filename, 1024, "%s/%s", datadir.c_str(), icon_file.c_str());
+    if(!faccessible(filename))
+      snprintf(filename, 1024, "%s/images/icons/default-icon.png", datadir.c_str());
+  }
+  else
+  {
+    snprintf(filename, 1024, "%s/images/icons/default-icon.png", datadir.c_str());
+  }
+
+  if(mw != -1 || mh != -1)
+  {
+    icon.push_back(new Surface(filename,true));
+    icon.back()->resize(mw,mh);
+  }
+  else
+    icon.push_back(new Surface(filename,true));
+
+}
+
+void Button::draw(DrawingContext& context)
+{
+  if(state == BUTTON_HOVER)
+    if(!popup_timer.check())
+      show_info = true;
+
+  fillrect(rect.x,rect.y,rect.w,rect.h,75,75,75,200);
+  fillrect(rect.x+1,rect.y+1,rect.w-2,rect.h-2,175,175,175,200);
+
+  for(std::vector<Surface*>::iterator it = icon.begin(); it != icon.end(); ++it)
+    context.draw_surface(*it, Vector(rect.x,rect.y), LAYER_GUI);
+
+/*  if(drawable)
+  {
+    Camera viewport;
+    viewport.set_translation(Vector(rect.x, rect.y));
+    drawable->draw(viewport, 0);
+  }*/
+
+  if(show_info)
+  {
+    char str[80];
+    int i = -32;
+
+    if(0 > rect.x - white_small_text->get_text_width(info))
+      i = rect.w + (int)white_small_text->get_text_width(info);
+
+    if(!info.empty())
+      context.draw_text(white_small_text, info, Vector(i + rect.x - white_small_text->get_text_width(info), rect.y), LAYER_GUI);
+    sprintf(str,"(%s)", SDL_GetKeyName(shortcut));
+    context.draw_text(white_small_text, str, Vector(i + rect.x -  white_small_text->get_text_width(str), rect.y + white_small_text->get_height()+2), LAYER_GUI);
+  }
+  if(state == BUTTON_PRESSED || state == BUTTON_DEACTIVE)
+    fillrect(rect.x,rect.y,rect.w,rect.h,75,75,75,200);
+  else if(state == BUTTON_HOVER)
+    fillrect(rect.x,rect.y,rect.w,rect.h,150,150,150,128);
+}
+
+Button::~Button()
+{
+  for(std::vector<Surface*>::iterator it = icon.begin(); it != icon.end(); ++it)
+    delete (*it);
+  icon.clear();
+  // FIXME TODO XXX: commenting this out fixes the leveleditor quit crash
+  //   probably should be deleted somehow, though
+  //delete drawable;
+}
+
+void Button::event(SDL_Event &event)
+{
+  if(state == BUTTON_DEACTIVE)
+    return;
+
+  SDLKey key = event.key.keysym.sym;
+
+  if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP)
+  {
+    if(event.button.x < rect.x || event.button.x >= rect.x + rect.w ||
+        event.button.y < rect.y || event.button.y >= rect.y + rect.h)
+      return;
+
+    if(event.button.button == SDL_BUTTON_RIGHT)
+    {
+      show_info = true;
+      return;
+    }
+    else if(event.type == SDL_MOUSEBUTTONUP && event.button.button == 4) /* Mouse wheel up. */
+    {
+      state = BUTTON_WHEELUP;
+      return;
+    }
+    else if(event.type == SDL_MOUSEBUTTONUP && event.button.button == 5) /* Mouse wheel down. */
+    {
+      state = BUTTON_WHEELDOWN;
+      return;
+    }
+
+    if(event.button.button == SDL_BUTTON_LEFT)
+      if(event.type == SDL_MOUSEBUTTONDOWN)
+        state = BUTTON_PRESSED;
+      else
+        state = BUTTON_CLICKED;
+  }
+  else if(event.type == SDL_MOUSEMOTION)
+  {
+    if(event.motion.x < rect.x || event.motion.x >= rect.x + rect.w ||
+        event.motion.y < rect.y || event.motion.y >= rect.y + rect.h)
+    {
+      state = BUTTON_NONE;
+    }
+    else
+    {
+      state = BUTTON_HOVER;
+      popup_timer.start(1500);
+    }
+
+    if(show_info)
+    {
+      show_info = false;
+    }
+  }
+  else if(event.type == SDL_KEYDOWN)
+  {
+    if(key == shortcut)
+      state = BUTTON_PRESSED;
+  }
+  else if(event.type == SDL_KEYUP)
+  {
+    if(state == BUTTON_PRESSED && key == shortcut)
+      state = BUTTON_CLICKED;
+  }
+}
+
+int Button::get_state()
+{
+  int rstate;
+  switch(state)
+  {
+  case BUTTON_CLICKED:
+  case BUTTON_WHEELUP:
+  case BUTTON_WHEELDOWN:
+    rstate = state;
+    state = BUTTON_NONE;
+    return rstate;
+  default:
+    return state;
+  }
+}
+
+ButtonPanel::ButtonPanel(int x, int y, int w, int h)
+{
+  bw = 32;
+  bh = 32;
+  rect.x = x;
+  rect.y = y;
+  rect.w = w;
+  rect.h = h;
+  hidden = false;
+  hlast = false;
+}
+
+Button* ButtonPanel::event(SDL_Event& event)
+{
+  if(!hidden)
+  {
+  Button* ret = NULL;
+    for(std::vector<Button*>::iterator it = item.begin(); it != item.end(); ++it)
+    {
+      (*it)->event(event);
+      if((*it)->state != BUTTON_NONE)
+      {
+        if(hlast && (*it)->state == BUTTON_CLICKED)
+          last_clicked = it;
+       ret = (*it);
+      }
+    }
+    return ret;
+  }
+  else
+  {
+    return NULL;
+  }
+}
+
+ButtonPanel::~ButtonPanel()
+{
+  for(std::vector<Button*>::iterator it = item.begin(); it != item.end(); ++it)
+  {
+    delete (*it);
+  }
+  item.clear();
+}
+
+void ButtonPanel::draw(DrawingContext& context)
+{
+
+  if(hidden == false)
+  {
+    fillrect(rect.x,rect.y,rect.w,rect.h,100,100,100,200);
+    for(std::vector<Button*>::iterator it = item.begin(); it != item.end(); ++it)
+    {
+      (*it)->draw(context);
+      if(hlast && it == last_clicked)
+      {
+        fillrect((*it)->get_pos().x,(*it)->get_pos().y,(*it)->get_pos().w,(*it)->get_pos().h,100,100,100,128);
+      }
+    }
+  }
+}
+
+void ButtonPanel::additem(Button* pbutton, int tag)
+{
+  int max_cols, row, col;
+
+  item.push_back(pbutton);
+
+  /* A button_panel takes control of the buttons it contains and arranges them */
+
+  max_cols = rect.w / bw;
+
+  row = (item.size()-1) / max_cols;
+  col = (item.size()-1) % max_cols;
+
+  item[item.size()-1]->rect.x = rect.x + col * bw;
+  item[item.size()-1]->rect.y = rect.y + row * bh;
+  item[item.size()-1]->tag = tag;
+
+}
+
+void ButtonPanel::set_button_size(int w, int h)
+{
+  bw = w;
+  bh = h;
+}
+
+Button* ButtonPanel::manipulate_button(int i)
+{
+  if(int(item.size())-1 < i)
+    return item[item.size()-1];
+  else
+    return item[i];
+}
+
+void ButtonPanel::highlight_last(bool b)
+{
+  hlast = b;
+}
diff --git a/lib/gui/button.h b/lib/gui/button.h
new file mode 100644 (file)
index 0000000..a24cc9d
--- /dev/null
@@ -0,0 +1,98 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_BUTTON_H
+#define SUPERTUX_BUTTON_H
+
+#include <vector>
+
+#include "video/surface.h"
+#include "special/timer.h"
+
+enum ButtonState {
+  BUTTON_NONE = -1,
+  BUTTON_CLICKED,
+  BUTTON_PRESSED,
+  BUTTON_HOVER,
+  BUTTON_WHEELUP,
+  BUTTON_WHEELDOWN,
+  BUTTON_DEACTIVE
+};
+
+class ButtonPanel;
+
+class Button
+{
+  friend class ButtonPanel;
+
+public:
+  Button(Surface* icon_file, const std::string& info, SDLKey shortcut,
+      int x, int y, int mw = -1, int h = -1);
+  Button(const std::string& icon_name, const std::string& info, SDLKey shortcut,
+      int x, int y, int mw = -1, int h = -1);
+  
+  ~Button();
+  void event(SDL_Event& event);
+  void draw(DrawingContext& context);
+  int get_state();
+  void set_active(bool active) { active ? state = BUTTON_NONE : state = BUTTON_DEACTIVE; };
+  void add_icon(const std::string& imagefile, int mw, int mh);
+  SDL_Rect get_pos() { return rect; }
+  int get_tag(){return tag; }
+//  void set_drawable(Drawable* newdrawable)
+//  { drawable = newdrawable; }
+
+private:
+  static Timer popup_timer;
+//  Drawable* drawable;
+  std::vector<Surface*> icon;
+  std::string info;
+  SDLKey shortcut;
+  SDL_Rect rect;
+  bool show_info;
+  ButtonState state;
+  int tag;
+};
+
+class ButtonPanel
+{
+public:
+  ButtonPanel(int x, int y, int w, int h);
+  ~ButtonPanel();
+  void draw(DrawingContext& context);
+  Button* event(SDL_Event &event);
+  void additem(Button* pbutton, int tag);
+  Button* button_panel_event(SDL_Event& event);
+  void set_button_size(int w, int h);
+  Button* manipulate_button(int i);
+  void highlight_last(bool b);
+  void set_last_clicked(unsigned int last)
+  { if(hlast) { if(item.size() >= last) { last_clicked = item.begin() + last; } } };
+
+private:
+  int bw, bh;
+  bool hlast;
+  bool hidden;
+  SDL_Rect rect;
+  std::vector<Button*> item;
+  std::vector<Button*>::iterator last_clicked;
+};
+
+#endif /*SUPERTUX_BUTTON_H*/
diff --git a/lib/gui/menu.cpp b/lib/gui/menu.cpp
new file mode 100644 (file)
index 0000000..4dc253d
--- /dev/null
@@ -0,0 +1,914 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <ctype.h>
+#endif
+
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+#include <cassert>
+
+#include "app/globals.h"
+#include "gui/menu.h"
+#include "video/screen.h"
+#include "video/drawing_context.h"
+#include "app/setup.h"
+#include "audio/sound.h"
+#include "special/timer.h"
+#include "app/gettext.h"
+
+#define FLICK_CURSOR_TIME 500
+
+Surface* checkbox;
+Surface* checkbox_checked;
+Surface* back;
+Surface* arrow_left;
+Surface* arrow_right;
+
+Menu* main_menu      = 0;
+Menu* game_menu      = 0;
+Menu* options_menu   = 0;
+Menu* options_keys_menu     = 0;
+Menu* options_joystick_menu = 0;
+Menu* highscore_menu = 0;
+Menu* load_game_menu = 0;
+Menu* save_game_menu = 0;
+Menu* contrib_menu   = 0;
+Menu* contrib_subset_menu   = 0;
+
+std::vector<Menu*> Menu::last_menus;
+Menu* Menu::current_ = 0;
+
+/* just displays a Yes/No text that can be used to confirm stuff */
+bool confirm_dialog(Surface *background, std::string text)
+{
+  //Surface* cap_screen = Surface::CaptureScreen();
+  
+  Menu* dialog = new Menu;
+  dialog->additem(MN_DEACTIVE, text,0,0);
+  dialog->additem(MN_HL,"",0,0);
+  dialog->additem(MN_ACTION,_("Yes"),0,0,true);
+  dialog->additem(MN_ACTION,_("No"),0,0,false);
+  dialog->additem(MN_HL,"",0,0);
+
+  Menu::set_current(dialog);
+
+  DrawingContext context;
+
+  while(true)
+  {
+    SDL_Event event;
+
+    while (SDL_PollEvent(&event))
+    {
+      dialog->event(event);
+    }
+
+    if(background == NULL)
+      context.draw_gradient(Color(200,240,220), Color(200,200,220), LAYER_BACKGROUND0);
+    else
+      context.draw_surface(background, Vector(0,0), LAYER_BACKGROUND0);
+
+    dialog->draw(context);
+    dialog->action();
+
+    switch (dialog->check())
+    {
+    case true:
+      //delete cap_screen;
+      Menu::set_current(0);
+      delete dialog;
+      return true;
+      break;
+    case false:
+      //delete cap_screen;
+      Menu::set_current(0);
+      delete dialog;
+      return false;
+      break;
+    default:
+      break;
+    }
+
+    mouse_cursor->draw(context);
+    context.do_drawing();
+    SDL_Delay(25);
+  }
+
+  return false;
+}
+
+void
+Menu::push_current(Menu* pmenu)
+{
+  if (current_)
+    last_menus.push_back(current_);
+
+  current_ = pmenu;
+  current_->effect.start(500);
+}
+
+void
+Menu::pop_current()
+{
+  if (!last_menus.empty())
+  {
+    current_ = last_menus.back();
+    current_->effect.start(500);
+
+    last_menus.pop_back();
+  }
+  else
+  {
+    current_ = 0;
+  }
+}
+
+void
+Menu::set_current(Menu* menu)
+{
+  last_menus.clear();
+
+  if (menu)
+    menu->effect.start(500);
+
+  current_ = menu;
+}
+
+/* Return a pointer to a new menu item */
+MenuItem*
+MenuItem::create(MenuItemKind kind_, const char *text_, int init_toggle_, Menu* target_menu_, int id, int* int_p_)
+{
+  MenuItem *pnew_item = new MenuItem;
+
+  pnew_item->kind = kind_;
+  pnew_item->text = (char*) malloc(sizeof(char) * (strlen(text_) + 1));
+  strcpy(pnew_item->text, text_);
+
+  if(kind_ == MN_TOGGLE)
+    pnew_item->toggled = init_toggle_;
+  else
+    pnew_item->toggled = false;
+
+  pnew_item->target_menu = target_menu_;
+  pnew_item->input = (char*) malloc(sizeof(char));
+  pnew_item->input[0] = '\0';
+
+  if(kind_ == MN_STRINGSELECT)
+  {
+    pnew_item->list = (string_list_type*) malloc(sizeof(string_list_type));
+    string_list_init(pnew_item->list);
+  }
+  else
+    pnew_item->list = NULL;
+
+  pnew_item->id = id;
+  pnew_item->int_p = int_p_;
+
+  pnew_item->input_flickering = false;
+  pnew_item->input_flickering_timer.init(true);
+  pnew_item->input_flickering_timer.start(FLICK_CURSOR_TIME);
+
+  return pnew_item;
+}
+
+void
+MenuItem::change_text(const  char *text_)
+{
+  if (text_)
+  {
+    free(text);
+    text = (char*) malloc(sizeof(char )*(strlen(text_)+1));
+    strcpy(text, text_);
+  }
+}
+
+void
+MenuItem::change_input(const  char *text_)
+{
+  if(text)
+  {
+    free(input);
+    input = (char*) malloc(sizeof(char )*(strlen(text_)+1));
+    strcpy(input, text_);
+  }
+}
+
+std::string MenuItem::get_input_with_symbol(bool active_item)
+{
+  if(!active_item)
+    input_flickering = true;
+  else
+  {
+    if(input_flickering_timer.get_left() < 0)
+    {
+      if(input_flickering)
+        input_flickering = false;
+      else
+        input_flickering = true;
+      input_flickering_timer.start(FLICK_CURSOR_TIME);
+    }
+  }
+
+  char str[1024];
+  if(input_flickering)
+    sprintf(str,"%s ",input);
+  else
+    sprintf(str,"%s_",input);
+
+  std::string string = str;
+
+  return string;
+}
+
+/* Set ControlField for keyboard key */
+void Menu::get_controlfield_key_into_input(MenuItem *item)
+{
+  switch(*item->int_p)
+  {
+  case SDLK_UP:
+    item->change_input(_("Up cursor"));
+    break;
+  case SDLK_DOWN:
+    item->change_input(_("Down cursor"));
+    break;
+  case SDLK_LEFT:
+    item->change_input(_("Left cursor"));
+    break;
+  case SDLK_RIGHT:
+    item->change_input(_("Right cursor"));
+    break;
+  case SDLK_RETURN:
+    item->change_input(_("Return"));
+    break;
+  case SDLK_SPACE:
+    item->change_input(_("Space"));
+    break;
+  case SDLK_RSHIFT:
+    item->change_input(_("Right Shift"));
+    break;
+  case SDLK_LSHIFT:
+    item->change_input(_("Left Shift"));
+    break;
+  case SDLK_RCTRL:
+    item->change_input(_("Right Control"));
+    break;
+  case SDLK_LCTRL:
+    item->change_input(_("Left Control"));
+    break;
+  case SDLK_RALT:
+    item->change_input(_("Right Alt"));
+    break;
+  case SDLK_LALT:
+    item->change_input(_("Left Alt"));
+    break;
+  default:
+    {
+      char tmp[64];
+      snprintf(tmp, 64, "%d", *item->int_p);
+      item->change_input(tmp);
+    }
+    break;
+  }
+}
+
+/* Set ControlField for joystick button */
+void Menu::get_controlfield_js_into_input(MenuItem *item)
+{
+  std::ostringstream oss;
+  oss << "Button " << *item->int_p;
+  item->change_input(oss.str().c_str());
+}
+
+/* Free a menu and all its items */
+Menu::~Menu()
+{
+  if(item.size() != 0)
+  {
+    for(unsigned int i = 0; i < item.size(); ++i)
+    {
+      free(item[i].text);
+      free(item[i].input);
+      string_list_free(item[i].list);
+    }
+  }
+}
+
+
+Menu::Menu()
+{
+  hit_item = -1;
+  menuaction = MENU_ACTION_NONE;
+  delete_character = 0;
+  mn_input_char = '\0';
+
+  pos_x        = screen->w/2;
+  pos_y        = screen->h/2;
+  arrange_left = 0;
+  active_item  = 0;
+  effect.init(false);
+
+  joystick_timer.init(true);
+}
+
+void Menu::set_pos(int x, int y, float rw, float rh)
+{
+  pos_x = x + (int)((float)get_width() * rw);
+  pos_y = y + (int)((float)get_height() * rh);
+}
+
+void
+Menu::additem(MenuItemKind kind_, const std::string& text_, int toggle_, Menu* menu_, int id, int* int_p)
+{
+  additem(MenuItem::create(kind_, text_.c_str(), toggle_, menu_, id, int_p));
+}
+
+/* Add an item to a menu */
+void
+Menu::additem(MenuItem* pmenu_item)
+{
+  item.push_back(*pmenu_item);
+  delete pmenu_item;
+}
+
+void
+Menu::clear()
+{
+  item.clear();
+}
+
+/* Process actions done on the menu */
+void
+Menu::action()
+{
+  hit_item = -1;
+  if(item.size() != 0)
+  {
+    switch(menuaction)
+    {
+    case MENU_ACTION_UP:
+      if (active_item > 0)
+        --active_item;
+      else
+        active_item = int(item.size())-1;
+      break;
+
+    case MENU_ACTION_DOWN:
+      if(active_item < int(item.size())-1)
+        ++active_item;
+      else
+        active_item = 0;
+      break;
+
+    case MENU_ACTION_LEFT:
+      if(item[active_item].kind == MN_STRINGSELECT
+          && item[active_item].list->num_items != 0)
+      {
+        if(item[active_item].list->active_item > 0)
+          --item[active_item].list->active_item;
+        else
+          item[active_item].list->active_item = item[active_item].list->num_items-1;
+      }
+      break;
+
+    case MENU_ACTION_RIGHT:
+      if(item[active_item].kind == MN_STRINGSELECT
+          && item[active_item].list->num_items != 0)
+      {
+        if(item[active_item].list->active_item < item[active_item].list->num_items-1)
+          ++item[active_item].list->active_item;
+        else
+          item[active_item].list->active_item = 0;
+      }
+      break;
+
+    case MENU_ACTION_HIT:
+      {
+        hit_item = active_item;
+        switch (item[active_item].kind)
+        {
+        case MN_GOTO:
+          if (item[active_item].target_menu != NULL)
+            Menu::push_current(item[active_item].target_menu);
+          else
+            puts("NULLL");
+          break;
+
+        case MN_TOGGLE:
+          item[active_item].toggled = !item[active_item].toggled;
+          break;
+
+        case MN_ACTION:
+          Menu::set_current(0);
+          item[active_item].toggled = true;
+          break;
+        case MN_TEXTFIELD:
+        case MN_NUMFIELD:
+          menuaction = MENU_ACTION_DOWN;
+          action();
+          break;
+
+        case MN_BACK:
+          Menu::pop_current();
+          break;
+        default:
+          break;
+        }
+      }
+      break;
+
+    case MENU_ACTION_REMOVE:
+      if(item[active_item].kind == MN_TEXTFIELD
+          || item[active_item].kind == MN_NUMFIELD)
+      {
+        if(item[active_item].input != NULL)
+        {
+          int i = strlen(item[active_item].input);
+
+          while(delete_character > 0)  /* remove charactes */
+          {
+            item[active_item].input[i-1] = '\0';
+            delete_character--;
+          }
+        }
+      }
+      break;
+
+    case MENU_ACTION_INPUT:
+      if(item[active_item].kind == MN_TEXTFIELD
+          || (item[active_item].kind == MN_NUMFIELD && mn_input_char >= '0' && mn_input_char <= '9'))
+      {
+        if(item[active_item].input != NULL)
+        {
+          int i = strlen(item[active_item].input);
+          item[active_item].input = (char*) realloc(item[active_item].input,sizeof(char)*(i + 2));
+          item[active_item].input[i] = mn_input_char;
+          item[active_item].input[i+1] = '\0';
+        }
+        else
+        {
+          item[active_item].input = (char*) malloc(2*sizeof(char));
+          item[active_item].input[0] = mn_input_char;
+          item[active_item].input[1] = '\0';
+        }
+      }
+
+    case MENU_ACTION_NONE:
+      break;
+    }
+  }
+
+  MenuItem& new_item = item[active_item];
+  if(new_item.kind == MN_DEACTIVE
+      || new_item.kind == MN_LABEL
+      || new_item.kind == MN_HL)
+  {
+    // Skip the horzontal line item
+    if (menuaction != MENU_ACTION_UP && menuaction != MENU_ACTION_DOWN)
+      menuaction = MENU_ACTION_DOWN;
+
+    if (item.size() > 1)
+      action();
+  }
+
+  menuaction = MENU_ACTION_NONE;
+
+  if (active_item >= int(item.size()))
+    active_item = int(item.size()) - 1;
+}
+
+int
+Menu::check()
+{
+  if (hit_item != -1)
+    return item[hit_item].id;
+  else
+    return -1;
+}
+
+void
+Menu::draw_item(DrawingContext& context,
+    int index, // Position of the current item in the menu
+    int menu_width, int menu_height)
+{
+  MenuItem& pitem = item[index];
+
+  int effect_offset = 0;
+  {
+    int effect_time = 0;
+
+    if(effect.check())
+      effect_time = effect.get_left() / 4;
+
+    effect_offset = (index % 2) ? effect_time : -effect_time;
+  }
+
+  Font* text_font = white_text;
+  int x_pos       = pos_x;
+  int y_pos       = pos_y + 24*index - menu_height/2 + 12 + effect_offset;
+  int shadow_size = 2;
+  int text_width  = int(text_font->get_text_width(pitem.text));
+  int input_width = int(text_font->get_text_width(pitem.input) + 10);
+  int list_width  =
+    int(text_font->get_text_width(string_list_active(pitem.list)));
+
+  if (arrange_left)
+    x_pos += 24 - menu_width/2 + (text_width + input_width + list_width)/2;
+
+  if(index == active_item)
+  {
+    shadow_size = 3;
+    text_font = blue_text;
+  }
+
+  switch (pitem.kind)
+  {
+  case MN_DEACTIVE:
+    {
+      context.draw_text_center(gray_text, pitem.text,
+          Vector(0, y_pos - int(blue_text->get_height()/2)),
+          LAYER_GUI);
+      break;
+    }
+
+  case MN_HL:
+    {
+      // TODO
+      int x = pos_x - menu_width/2;
+      int y = y_pos - 12 - effect_offset;
+      /* Draw a horizontal line with a little 3d effect */
+      context.draw_filled_rect(Vector(x, y + 6),
+          Vector(menu_width, 4), Color(150,200,255,225), LAYER_GUI);
+      context.draw_filled_rect(Vector(x, y + 6),
+          Vector(menu_width, 2), Color(255,255,255,255), LAYER_GUI);
+      break;
+    }
+  case MN_LABEL:
+    {
+      context.draw_text_center(white_big_text,
+          pitem.text, Vector(0, y_pos - int(white_big_text->get_height()/2)),
+          LAYER_GUI);
+      break;
+    }
+  case MN_TEXTFIELD:
+  case MN_NUMFIELD:
+  case MN_CONTROLFIELD_KB:
+  case MN_CONTROLFIELD_JS:
+    {
+      int width = text_width + input_width + 5;
+      int text_pos = screen->w/2 - width/2;
+      int input_pos = text_pos + text_width + 10;
+
+      context.draw_filled_rect(
+          Vector(input_pos - 5, y_pos - 10),
+          Vector(input_width + 10, 20),
+          Color(255,255,255,255), LAYER_GUI-5);
+      context.draw_filled_rect(
+          Vector(input_pos - 4, y_pos - 9),
+          Vector(input_width + 8, 18),
+          Color(0,0,0,128), LAYER_GUI-4);
+
+      if(pitem.kind == MN_CONTROLFIELD_KB)
+        get_controlfield_key_into_input(&pitem);
+      else if (pitem.kind == MN_CONTROLFIELD_JS)
+        get_controlfield_js_into_input(&pitem);
+
+      if(pitem.kind == MN_TEXTFIELD || pitem.kind == MN_NUMFIELD)
+      {
+        if(active_item == index)
+          context.draw_text(gold_text,
+              pitem.get_input_with_symbol(true),
+              Vector(input_pos, y_pos - int(gold_text->get_height()/2)),
+              LAYER_GUI);
+        else
+          context.draw_text(gold_text,
+              pitem.get_input_with_symbol(false),
+              Vector(input_pos, y_pos - int(gold_text->get_height()/2)),
+              LAYER_GUI);
+      }
+      else
+        context.draw_text(gold_text, pitem.input,
+            Vector(input_pos, y_pos - int(gold_text->get_height()/2)),
+            LAYER_GUI);
+
+      context.draw_text(text_font, pitem.text,
+          Vector(text_pos, y_pos - int(text_font->get_height()/2)),
+          LAYER_GUI);
+      break;
+    }
+  case MN_STRINGSELECT:
+    {
+      int list_pos_2 = list_width + 16;
+      int list_pos   = list_width/2;
+      int text_pos   = (text_width + 16)/2;
+
+      /* Draw arrows */
+      context.draw_surface(arrow_left,
+          Vector(x_pos - list_pos + text_pos - 17, y_pos - 8),
+          LAYER_GUI);
+      context.draw_surface(arrow_right,
+          Vector(x_pos - list_pos + text_pos - 1 + list_pos_2, y_pos - 8),
+          LAYER_GUI);
+
+      /* Draw input background */
+      context.draw_filled_rect(
+          Vector(x_pos - list_pos + text_pos - 1, y_pos - 10),
+          Vector(list_pos_2 + 2, 20),
+          Color(255,255,255,255), LAYER_GUI - 4);
+      context.draw_filled_rect(
+          Vector(x_pos - list_pos + text_pos, y_pos - 9),
+          Vector(list_pos_2, 18),
+          Color(0,0,0,128), LAYER_GUI - 5);
+
+      context.draw_text_center(text_font, string_list_active(pitem.list),
+          Vector(text_pos, y_pos - int(text_font->get_height()/2)),
+          LAYER_GUI);
+      context.draw_text_center(text_font, pitem.text,
+          Vector(list_pos_2/2, y_pos - int(text_font->get_height()/2)),
+          LAYER_GUI);
+      break;
+    }
+  case MN_BACK:
+    {
+      context.draw_text_center(text_font, pitem.text,
+          Vector(0, y_pos - int(text_font->get_height()/2)),
+          LAYER_GUI);
+      context.draw_surface(back,
+          Vector(x_pos + text_width/2  + 16, y_pos - 8),
+          LAYER_GUI);
+      break;
+    }
+
+  case MN_TOGGLE:
+    {
+      context.draw_text_center(text_font, pitem.text,
+          Vector(0, y_pos - (text_font->get_height()/2)),
+          LAYER_GUI);
+
+      if(pitem.toggled)
+        context.draw_surface(checkbox_checked,
+            Vector(x_pos + (text_width+16)/2, y_pos - 8),
+            LAYER_GUI + 1);
+      else
+        context.draw_surface(checkbox,
+            Vector(x_pos + (text_width+16)/2, y_pos - 8),
+            LAYER_GUI + 1);                                      
+      break;
+    }
+  case MN_ACTION:
+    context.draw_text_center(text_font, pitem.text,
+        Vector(0, y_pos - int(text_font->get_height()/2)),
+        LAYER_GUI);
+    break;
+
+  case MN_GOTO:
+    context.draw_text_center(text_font, pitem.text,
+        Vector(0, y_pos - int(text_font->get_height()/2)),
+        LAYER_GUI);
+    break;
+  }
+}
+
+int Menu::get_width() const
+{
+  /* The width of the menu has to be more than the width of the text
+     with the most characters */
+  int menu_width = 0;
+  for(unsigned int i = 0; i < item.size(); ++i)
+  {
+    int w = strlen(item[i].text) + (item[i].input ? strlen(item[i].input) + 1 : 0) + strlen(string_list_active(item[i].list));
+    if( w > menu_width )
+    {
+      menu_width = w;
+      if( item[i].kind == MN_TOGGLE)
+        menu_width += 2;
+    }
+  }
+
+  return (menu_width * 16 + 24);
+}
+
+int Menu::get_height() const
+{
+  return item.size() * 24;
+}
+
+/* Draw the current menu. */
+void
+Menu::draw(DrawingContext& context)
+{
+  int menu_height = get_height();
+  int menu_width  = get_width();
+
+  /* Draw a transparent background */
+  context.draw_filled_rect(
+      Vector(pos_x - menu_width/2, pos_y - 24*item.size()/2 - 10),
+      Vector(menu_width,menu_height + 20),
+      Color(150,180,200,125), LAYER_GUI-10);
+
+  for(unsigned int i = 0; i < item.size(); ++i)
+  {
+    draw_item(context, i, menu_width, menu_height);
+  }
+}
+
+MenuItem&
+Menu::get_item_by_id(int id)
+{
+  for(std::vector<MenuItem>::iterator i = item.begin(); i != item.end(); ++i)
+  {
+    if(i->id == id)
+      return *i;
+  }
+
+  assert(false);
+  static MenuItem dummyitem;
+  return dummyitem;
+}
+
+int Menu::get_active_item_id()
+{
+  return item[active_item].id;
+}
+
+bool
+Menu::isToggled(int id)
+{
+  return get_item_by_id(id).toggled;
+}
+
+/* Check for menu event */
+void
+Menu::event(SDL_Event& event)
+{
+  switch(event.type)
+    {
+    case SDL_KEYDOWN:
+      {
+        SDLKey key = key = event.key.keysym.sym;
+        SDLMod keymod;
+        char ch[2];
+        keymod = SDL_GetModState();
+
+        /* If the current unicode character is an ASCII character,
+           assign it to ch. */
+        if ( (event.key.keysym.unicode & 0xFF80) == 0 )
+          {
+            ch[0] = event.key.keysym.unicode & 0x7F;
+            ch[1] = '\0';
+          }
+        else
+          {
+            /* An International Character. */
+          }
+
+        if(item[active_item].kind == MN_CONTROLFIELD_KB)
+          {
+            if(key == SDLK_ESCAPE)
+              {
+                Menu::pop_current();
+                return;
+              }
+            *item[active_item].int_p = key;
+            menuaction = MENU_ACTION_DOWN;
+            return;
+          }
+
+
+        switch(key)
+          {
+          case SDLK_UP:                /* Menu Up */
+            menuaction = MENU_ACTION_UP;
+            break;
+          case SDLK_DOWN:              /* Menu Down */
+            menuaction = MENU_ACTION_DOWN;
+            break;
+          case SDLK_LEFT:              /* Menu Up */
+            menuaction = MENU_ACTION_LEFT;
+            break;
+          case SDLK_RIGHT:             /* Menu Down */
+            menuaction = MENU_ACTION_RIGHT;
+            break;
+          case SDLK_SPACE:
+            if(item[active_item].kind == MN_TEXTFIELD)
+              {
+                menuaction = MENU_ACTION_INPUT;
+                mn_input_char = ' ';
+                break;
+              }
+          case SDLK_RETURN: /* Menu Hit */
+            menuaction = MENU_ACTION_HIT;
+            break;
+          case SDLK_DELETE:
+          case SDLK_BACKSPACE:
+            menuaction = MENU_ACTION_REMOVE;
+            delete_character++;
+            break;
+          case SDLK_ESCAPE:
+            Menu::pop_current();
+            break;
+          default:
+            if( (key >= SDLK_0 && key <= SDLK_9) || (key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_SPACE && key <= SDLK_SLASH))
+              {
+                menuaction = MENU_ACTION_INPUT;
+                mn_input_char = *ch;
+              }
+            else
+              {
+                mn_input_char = '\0';
+              }
+            break;
+          }
+      }
+      break;
+      
+    case  SDL_JOYAXISMOTION:
+      if(event.jaxis.axis == joystick_keymap.y_axis)
+        {
+          if (event.jaxis.value > joystick_keymap.dead_zone && !joystick_timer.started())
+            {
+              menuaction = MENU_ACTION_DOWN;
+              joystick_timer.start(JOYSTICK_MENU_DELAY);
+            }
+          else if (event.jaxis.value < -joystick_keymap.dead_zone && !joystick_timer.started())
+            {
+              menuaction = MENU_ACTION_UP;
+              joystick_timer.start(JOYSTICK_MENU_DELAY);
+            }
+          else
+            joystick_timer.stop();
+        }
+      break;
+    case  SDL_JOYBUTTONDOWN:
+      if (item[active_item].kind == MN_CONTROLFIELD_JS)
+        {
+          // FIXME: This next line does nothing useable, right? 
+          // *item[active_item].int_p = key;
+          menuaction = MENU_ACTION_DOWN;
+        }
+      menuaction = MENU_ACTION_HIT;
+      break;
+
+    case SDL_MOUSEBUTTONDOWN:
+      {
+        int x = event.motion.x;
+        int y = event.motion.y;
+
+        if(x > pos_x - get_width()/2 &&
+           x < pos_x + get_width()/2 &&
+           y > pos_y - get_height()/2 &&
+           y < pos_y + get_height()/2)
+          {
+            menuaction = MENU_ACTION_HIT;
+          }
+      }
+      break;
+
+    case SDL_MOUSEMOTION:
+      {
+        int x = event.motion.x;
+        int y = event.motion.y;
+
+        if(x > pos_x - get_width()/2 &&
+           x < pos_x + get_width()/2 &&
+           y > pos_y - get_height()/2 &&
+           y < pos_y + get_height()/2)
+          {
+            active_item = (y - (pos_y - get_height()/2)) / 24;
+            mouse_cursor->set_state(MC_LINK);
+          }
+        else
+          {
+            mouse_cursor->set_state(MC_NORMAL);
+          }
+      }
+      break;
+
+    default:
+      break;
+    }
+}
+
+
+// EOF //
diff --git a/lib/gui/menu.h b/lib/gui/menu.h
new file mode 100644 (file)
index 0000000..e2113d0
--- /dev/null
@@ -0,0 +1,194 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_MENU_H
+#define SUPERTUX_MENU_H
+
+#include <vector>
+
+#include "SDL.h"
+
+#include "video/surface.h"
+#include "special/timer.h"
+#include "special/base.h"
+#include "special/stringlist.h"
+#include "gui/mousecursor.h"
+
+
+/* Joystick menu delay */
+#define JOYSTICK_MENU_DELAY 500
+
+/* IDs for menus */
+
+bool confirm_dialog(Surface* background, std::string text);
+
+/* Kinds of menu items */
+enum MenuItemKind {
+  MN_ACTION,
+  MN_GOTO,
+  MN_TOGGLE,
+  MN_BACK,
+  MN_DEACTIVE,
+  MN_TEXTFIELD,
+  MN_NUMFIELD,
+  MN_CONTROLFIELD_KB,
+  MN_CONTROLFIELD_JS,
+  MN_STRINGSELECT,
+  MN_LABEL,
+  MN_HL, /* horizontal line */
+};
+
+class Menu;
+
+class MenuItem
+{
+public:
+  MenuItemKind kind;
+  int toggled;
+  char *text;
+  char *input;
+  int *int_p;   // used for setting keys (can be used for more stuff...)
+  int id;   // item id
+  string_list_type* list;
+  Menu* target_menu;
+
+  void change_text (const char *text);
+  void change_input(const char *text);
+
+  static MenuItem* create(MenuItemKind kind, const char *text, int init_toggle, Menu* target_menu, int id, int* int_p);
+
+  std::string get_input_with_symbol(bool active_item);   // returns the text with an input symbol
+private:
+  bool input_flickering;
+  Timer input_flickering_timer;
+};
+
+class Menu
+{
+private:  
+  static std::vector<Menu*> last_menus;
+  static Menu* current_;
+
+  static void push_current(Menu* pmenu);
+  static void pop_current();
+
+public:
+  /** Set the current menu, if pmenu is NULL, hide the current menu */
+  static void set_current(Menu* pmenu);
+
+  /** Return the current active menu or NULL if none is active */
+  static Menu* current() { return current_; }
+
+private:
+  /* Action done on the menu */
+  enum MenuAction {
+    MENU_ACTION_NONE = -1,
+    MENU_ACTION_UP,
+    MENU_ACTION_DOWN,
+    MENU_ACTION_LEFT,
+    MENU_ACTION_RIGHT,
+    MENU_ACTION_HIT,
+    MENU_ACTION_INPUT,
+    MENU_ACTION_REMOVE
+  };
+
+  /** Number of the item that got 'hit' (ie. pressed) in the last
+      event()/action() call, -1 if none */
+  int hit_item;
+
+  // position of the menu (ie. center of the menu, not top/left)
+  int pos_x;
+  int pos_y;
+
+  /** input event for the menu (up, down, left, right, etc.) */
+  MenuAction menuaction;
+
+  /* input implementation variables */
+  int delete_character;
+  char mn_input_char;
+  Timer joystick_timer;
+  
+public:
+  Timer effect;
+  int arrange_left;
+  int active_item;
+
+  std::vector<MenuItem> item;
+
+  Menu();
+  ~Menu();
+
+  void additem(MenuItem* pmenu_item);
+  void additem(MenuItemKind kind, const std::string& text, int init_toggle, Menu* target_menu, int id = -1, int *int_p = NULL);
+  
+  void  action ();
+  
+  /** Remove all entries from the menu */
+  void clear();
+
+  /** Return the index of the menu item that was 'hit' (ie. the user
+      clicked on it) in the last event() call */
+  int  check  ();
+
+  MenuItem& get_item(int index) { return item[index]; }
+  MenuItem& get_item_by_id(int id);
+
+  int get_active_item_id();
+
+  bool isToggled(int id);
+
+  void Menu::get_controlfield_key_into_input(MenuItem *item);
+  void Menu::get_controlfield_js_into_input(MenuItem *item);
+
+  void draw(DrawingContext& context);
+  void draw_item(DrawingContext& context,
+      int index, int menu_width, int menu_height);
+  void set_pos(int x, int y, float rw = 0, float rh = 0);
+
+  /** translate a SDL_Event into a menu_action */
+  void event(SDL_Event& event);
+
+  int get_width() const;
+  int get_height() const;
+
+  bool is_toggled(int id) const;
+};
+
+extern Surface* checkbox;
+extern Surface* checkbox_checked;
+extern Surface* back;
+extern Surface* arrow_left;
+extern Surface* arrow_right;
+
+extern Menu* contrib_menu;
+extern Menu* contrib_subset_menu;
+extern Menu* main_menu;
+extern Menu* game_menu;
+extern Menu* options_menu;
+extern Menu* options_keys_menu;
+extern Menu* options_joystick_menu;
+extern Menu* highscore_menu;
+extern Menu* load_game_menu;
+extern Menu* save_game_menu;
+
+#endif /*SUPERTUX_MENU_H*/
+
+/* Local Variables: */
+/* mode: c++ */
+/* End: */
diff --git a/lib/gui/mousecursor.cpp b/lib/gui/mousecursor.cpp
new file mode 100644 (file)
index 0000000..3956c0f
--- /dev/null
@@ -0,0 +1,96 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "video/drawing_context.h"
+#include "gui/mousecursor.h"
+
+MouseCursor* MouseCursor::current_ = 0;
+
+MouseCursor::MouseCursor(std::string cursor_file, int frames) : mid_x(0), mid_y(0)
+{
+  cursor = new Surface(cursor_file, true);
+  
+  cur_state = MC_NORMAL;
+  cur_frame = 0;
+  tot_frames = frames;
+
+  timer.init(false);
+  timer.start(MC_FRAME_PERIOD);
+  
+  SDL_ShowCursor(SDL_DISABLE);
+}
+
+MouseCursor::~MouseCursor()
+{
+  delete cursor;
+
+  SDL_ShowCursor(SDL_ENABLE);
+}
+
+int MouseCursor::state()
+{
+  return cur_state;
+}
+
+void MouseCursor::set_state(int nstate)
+{
+  cur_state = nstate;
+}
+
+void MouseCursor::set_mid(int x, int y)
+{
+  mid_x = x;
+  mid_y = y;
+}
+
+void MouseCursor::draw(DrawingContext& context)
+{
+  if(cur_state == MC_HIDE)
+    return;
+
+  int x,y,w,h;
+  Uint8 ispressed = SDL_GetMouseState(&x,&y);
+  w = cursor->w / tot_frames;
+  h = cursor->h / MC_STATES_NB;
+  if(ispressed &SDL_BUTTON(1) || ispressed &SDL_BUTTON(2))
+    {
+      if(cur_state != MC_CLICK)
+        {
+          state_before_click = cur_state;
+          cur_state = MC_CLICK;
+        }
+    }
+  else
+    {
+      if(cur_state == MC_CLICK)
+        cur_state = state_before_click;
+    }
+
+  if(timer.get_left() < 0 && tot_frames > 1)
+    {
+      cur_frame++;
+      if(cur_frame++ >= tot_frames)
+        cur_frame = 0;
+
+      timer.start(MC_FRAME_PERIOD);
+    }
+
+  context.draw_surface_part(cursor, Vector(w*cur_frame, h*cur_state), Vector(w,
+        h), Vector(x-mid_x, y-mid_y), LAYER_GUI+100);
+}
diff --git a/lib/gui/mousecursor.h b/lib/gui/mousecursor.h
new file mode 100644 (file)
index 0000000..8ff45e6
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_MOUSECURSOR_H
+#define SUPERTUX_MOUSECURSOR_H
+
+#include <string>
+
+#include "special/timer.h"
+#include "video/surface.h"
+
+#define MC_FRAME_PERIOD 800  // in ms
+
+#define MC_STATES_NB 3
+enum {
+  MC_NORMAL,
+  MC_CLICK,
+  MC_LINK,
+  MC_HIDE
+};
+
+class MouseCursor
+{
+public:
+  MouseCursor(std::string cursor_file, int frames);
+  ~MouseCursor();
+  int state();
+  void set_state(int nstate);
+  void set_mid(int x, int y);
+  void draw(DrawingContext& context);
+    
+  static MouseCursor* current() { return current_; };
+  static void set_current(MouseCursor* pcursor) {  current_ = pcursor; };
+    
+private:
+  int mid_x, mid_y;
+  static MouseCursor* current_;    
+  int state_before_click;
+  int cur_state;
+  int cur_frame, tot_frames;
+  Surface* cursor;
+  Timer timer;
+};
+
+#endif /*SUPERTUX_MOUSECURSOR_H*/
diff --git a/lib/math/physic.cpp b/lib/math/physic.cpp
new file mode 100644 (file)
index 0000000..09568f8
--- /dev/null
@@ -0,0 +1,140 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <cstdio>
+
+#include "math/physic.h"
+#include "special/timer.h"
+
+Physic::Physic()
+    : ax(0), ay(0), vx(0), vy(0), gravity_enabled(true)
+{
+}
+
+Physic::~Physic()
+{
+}
+
+void
+Physic::reset()
+{
+    ax = ay = vx = vy = 0;
+    gravity_enabled = true;
+}
+
+void
+Physic::set_velocity_x(float nvx)
+{
+  vx = nvx;
+}
+
+void
+Physic::set_velocity_y(float nvy)
+{
+  vy = -nvy;
+}
+
+void
+Physic::set_velocity(float nvx, float nvy)
+{
+  vx = nvx;
+  vy = -nvy;
+}
+
+void Physic::inverse_velocity_x()
+{
+vx = -vx;
+}
+
+void Physic::inverse_velocity_y()
+{
+vy = -vy;
+}
+
+float
+Physic::get_velocity_x()
+{
+    return vx;
+}
+
+float
+Physic::get_velocity_y()
+{
+    return -vy;
+}
+
+void
+Physic::set_acceleration_x(float nax)
+{
+  ax = nax;
+}
+
+void
+Physic::set_acceleration_y(float nay)
+{
+  ay = -nay;
+}
+
+void
+Physic::set_acceleration(float nax, float nay)
+{
+    ax = nax;
+    ay = -nay;
+}
+
+float
+Physic::get_acceleration_x()
+{
+    return ax;
+}
+
+float
+Physic::get_acceleration_y()
+{
+    return -ay;
+}
+
+void
+Physic::enable_gravity(bool enable_gravity)
+{
+  gravity_enabled = enable_gravity;
+}
+
+void
+Physic::apply(float elapsed_time, float &x, float &y, float& gravity)
+{
+  float grav;
+  if(gravity_enabled)
+    grav = gravity / 100.0;
+  else
+    grav = 0;
+
+  x += vx * elapsed_time + ax * elapsed_time * elapsed_time;
+  y += vy * elapsed_time + (ay + grav) * elapsed_time * elapsed_time;
+  vx += ax * elapsed_time;
+  vy += (ay + grav) * elapsed_time;
+}
+
+void
+Physic::apply(Vector& vector, float elapsed_time, float& gravity)
+{
+  apply(elapsed_time, vector.x, vector.y, gravity);
+}
+
diff --git a/lib/math/physic.h b/lib/math/physic.h
new file mode 100644 (file)
index 0000000..28dfbb0
--- /dev/null
@@ -0,0 +1,80 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_PHYSIC_H
+#define SUPERTUX_PHYSIC_H
+
+#include "math/vector.h"
+
+/** This is a very simplistic physics engine handling accelerated and constant
+  * movement along with gravity.
+  */
+class Physic
+{
+public:
+    Physic();
+    ~Physic();
+
+    /** resets all velocities and accelerations to 0 */
+    void reset();
+
+    /** sets velocity to a fixed value */
+    void set_velocity(float vx, float vy);
+
+    void set_velocity_x(float vx);
+    void set_velocity_y(float vy);
+
+    /** velocities invertion */
+    void inverse_velocity_x();
+    void inverse_velocity_y();
+
+    float get_velocity_x();
+    float get_velocity_y();
+    
+    /** sets acceleration applied to the object. (Note that gravity is
+     * eventually added to the vertical acceleration)
+     */
+    void set_acceleration(float ax, float ay);
+
+    void set_acceleration_x(float ax);
+    void set_acceleration_y(float ay);
+
+    float get_acceleration_x();
+    float get_acceleration_y();
+
+    /** enables or disables handling of gravity */
+    void enable_gravity(bool gravity_enabled);
+
+    /** applies the physical simulation to given x and y coordinates */
+    void apply(float frame_ratio, float &x, float &y, float& gravity = 10); 
+
+    /** applies the physical simulation to given x and y coordinates */
+    void apply(Vector& vector, float frame_ratio, float& gravity = 10);
+
+private:
+    /// horizontal and vertical acceleration
+    float ax, ay;
+    /// horizontal and vertical velocity
+    float vx, vy;
+    /// should we respect gravity in out calculations?
+    bool gravity_enabled;
+};
+
+#endif /*SUPERTUX_PHYSIC_H*/
diff --git a/lib/math/vector.cpp b/lib/math/vector.cpp
new file mode 100644 (file)
index 0000000..2901e35
--- /dev/null
@@ -0,0 +1,32 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <cmath>
+
+#include "math/vector.h"
+
+Vector Vector::unit() const
+{
+  return *this / norm();
+}
+
+float Vector::norm() const
+{
+  return sqrt(x*x + y*y);
+}
diff --git a/lib/math/vector.h b/lib/math/vector.h
new file mode 100644 (file)
index 0000000..b933454
--- /dev/null
@@ -0,0 +1,96 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_VECTOR_H
+#define SUPERTUX_VECTOR_H
+
+class Vector
+{
+public:
+  Vector(float nx, float ny)
+    : x(nx), y(ny)
+  { }
+  Vector(const Vector& other)
+    : x(other.x), y(other.y)
+  { }
+  Vector()
+    : x(0), y(0)
+  { }
+
+  bool operator ==(const Vector& other) const
+  {
+    return x == other.x && y == other.y;
+  }
+
+  const Vector& operator=(const Vector& other)
+  {
+    x = other.x;
+    y = other.y;
+    return *this;
+  }
+
+  Vector operator+(const Vector& other) const
+  {
+    return Vector(x + other.x, y + other.y);
+  }
+
+  Vector operator-(const Vector& other) const
+  {
+    return Vector(x - other.x, y - other.y);
+  }
+
+  Vector operator*(float s) const
+  {
+    return Vector(x * s, y * s);
+  }
+
+  Vector operator/(float s) const
+  {
+    return Vector(x / s, y / s);
+  }
+
+  Vector operator-() const
+  {
+    return Vector(-x, -y);
+  }
+
+  const Vector& operator +=(const Vector& other)
+  {
+    x += other.x;
+    y += other.y;
+    return *this;
+  }
+
+  // scalar product of 2 vectors
+  float operator*(const Vector& other) const
+  {
+    return x*other.x + y*other.y;
+  }
+
+  float norm() const;
+  Vector unit() const;
+
+  // ... add the other operators as needed, I'm too lazy now ...
+
+  float x, y; // leave this public, get/set methods just give me headaches
+              // for such simple stuff :)
+};
+
+#endif /*SUPERTUX_VECTOR_H*/
+
diff --git a/lib/special/base.h b/lib/special/base.h
new file mode 100644 (file)
index 0000000..67fbeb5
--- /dev/null
@@ -0,0 +1,40 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_TYPE_H
+#define SUPERTUX_TYPE_H
+
+#include <string>
+
+#include "SDL.h"
+
+/* 'Base' type for game objects */
+
+struct base_type
+{
+  float x;
+  float y;
+  float width;
+  float height;
+};
+
+
+#endif /*SUPERTUX_TYPE_H*/
+
diff --git a/lib/special/game_object.cpp b/lib/special/game_object.cpp
new file mode 100644 (file)
index 0000000..e1bf9b9
--- /dev/null
@@ -0,0 +1,30 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "special/game_object.h"
+
+GameObject::GameObject()
+  : wants_to_die(false)
+{
+}
+
+GameObject::~GameObject()
+{
+}
+
diff --git a/lib/special/game_object.h b/lib/special/game_object.h
new file mode 100644 (file)
index 0000000..8c6c3bf
--- /dev/null
@@ -0,0 +1,71 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_GAMEOBJECT_H
+#define SUPERTUX_GAMEOBJECT_H
+
+#include <string>
+
+class DrawingContext;
+
+/**
+ * Base class for all game objects. This contains functions for:
+ *  -querying the actual type of the object
+ *  -a flag that indicates if the object wants to be removed. Objects with this
+ *   flag will be removed at the end of each frame. This is alot safer than
+ *   having some uncontrollable "delete this" in the code.
+ *  -an action function that is called once per frame and allows the object to
+ *   update it's state.
+ * 
+ * Most GameObjects will also implement the DrawableObject interface so that
+ * they can actually be drawn on screen.
+ */
+class GameObject // TODO rename this once the game has been converted
+{
+public:
+  GameObject();
+  virtual ~GameObject();
+
+  /** This function is called once per frame and allows the object to update
+   * it's state. The elapsed_time is the time since the last frame and should be
+   * the base for all timed things.
+   */
+  virtual void action(float elapsed_time) = 0;
+
+  /** The GameObject should draw itself onto the provided DrawingContext if this
+   * function is called.
+   */
+  virtual void draw(DrawingContext& context) = 0;
+
+  /** returns true if the object is not scheduled to be removed yet */
+  bool is_valid() const
+  { return !wants_to_die; }
+  /** schedules this object to be removed at the end of the frame */
+  void remove_me()
+  { wants_to_die = true; }
+  
+private:
+  /** this flag indicates if the object should be removed at the end of the
+   * frame
+   */
+  bool wants_to_die;
+};
+
+#endif /*SUPERTUX_GAMEOBJECT_H*/
+
diff --git a/lib/special/moving_object.cpp b/lib/special/moving_object.cpp
new file mode 100644 (file)
index 0000000..92c9229
--- /dev/null
@@ -0,0 +1,31 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "special/moving_object.h"
+
+MovingObject::MovingObject()
+{
+  base.x = base.y = base.width = base.height = 0;
+  old_base = base;
+}
+
+MovingObject::~MovingObject()
+{
+}
+
diff --git a/lib/special/moving_object.h b/lib/special/moving_object.h
new file mode 100644 (file)
index 0000000..26c7db1
--- /dev/null
@@ -0,0 +1,61 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_MOVING_OBJECT_H
+#define SUPERTUX_MOVING_OBJECT_H
+
+#include "special/base.h"
+#include "special/game_object.h"
+#include "math/vector.h"
+//#include "rectangle.h"
+
+/**
+ * Base class for all dynamic/moving game objects. This class contains things
+ * for handling the bounding boxes and collision feedback.
+ */
+class MovingObject : public GameObject
+{
+public:
+  MovingObject();
+  virtual ~MovingObject();
+
+  /** this function is called when the object collided with any other object
+   */
+  virtual void collision(const MovingObject& other_object, 
+          int collision_type) = 0;
+
+  Vector get_pos() const
+  { return Vector(base.x, base.y); }
+
+  base_type base;
+  base_type old_base;
+
+protected:
+#if 0 // this will be used in my collision detection rewrite later
+  /// the current position of the object
+  Vector pos;
+  /// the position we want to move until next frame
+  Vector new_pos;
+  /// the bounding box relative to the current position
+  Rectangle bounding_box;
+#endif
+};
+
+#endif /*SUPERTUX_MOVING_OBJECT_H*/
+
diff --git a/lib/special/sprite.cpp b/lib/special/sprite.cpp
new file mode 100644 (file)
index 0000000..6ce6774
--- /dev/null
@@ -0,0 +1,129 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <iostream>
+#include <cmath>
+
+#include "app/globals.h"
+#include "app/setup.h"
+#include "special/sprite.h"
+#include "video/drawing_context.h"
+
+Sprite::Sprite(lisp_object_t* cur)
+{
+  init_defaults();
+
+  LispReader reader(cur);
+
+  if(!reader.read_string("name", name))
+    st_abort("Sprite wihtout name", "");
+  reader.read_int("x-hotspot", x_hotspot);
+  reader.read_int("y-hotspot", y_hotspot);
+  reader.read_float("fps",     fps);
+
+  std::vector<std::string> images;
+  if(!reader.read_string_vector("images", images))
+    st_abort("Sprite contains no images: ", name.c_str());
+
+  for(std::vector<std::string>::size_type i = 0; i < images.size(); ++i)
+    {
+      surfaces.push_back(
+          new Surface(datadir + "/images/" + images[i], true));
+    }        
+
+  frame_delay = 1000.0f/fps;
+}
+
+Sprite::~Sprite()
+{
+  for(std::vector<Surface*>::iterator i = surfaces.begin(); i != surfaces.end();
+      ++i)
+    delete *i;
+}
+
+void
+Sprite::init_defaults()
+{
+  x_hotspot = 0;
+  y_hotspot = 0;
+  fps = 10;
+  time = 0;
+  frame_delay = 1000.0f/fps;
+}
+
+void
+Sprite::update(float /*delta*/)
+{
+  //time += 10*delta;
+  //std::cout << "Delta: " << delta << std::endl;
+}
+
+void
+Sprite::draw(DrawingContext& context, const Vector& pos, int layer,
+    Uint32 drawing_effect)
+{
+  time = SDL_GetTicks();
+  unsigned int frame = get_current_frame();
+
+  if (frame < surfaces.size())
+  {
+    Surface* surface = surfaces[frame];
+    
+    context.draw_surface(surface, pos - Vector(x_hotspot, y_hotspot), layer, drawing_effect);
+  }
+}
+
+#if 0
+void
+Sprite::draw_part(float sx, float sy, float x, float y, float w, float h)
+{
+  time = SDL_GetTicks();
+  unsigned int frame = get_current_frame();
+
+  if (frame < surfaces.size())
+    surfaces[frame]->draw_part(sx, sy, x - x_hotspot, y - y_hotspot, w, h);
+}
+#endif
+
+void
+Sprite::reset()
+{
+  time = 0;
+}
+
+int
+Sprite::get_current_frame() const
+{
+  unsigned int frame = static_cast<int>(fmodf(time, surfaces.size()*frame_delay)/frame_delay);
+  return frame % surfaces.size();
+}
+
+int
+Sprite::get_width() const
+{
+  return surfaces[get_current_frame()]->w;
+}
+
+int
+Sprite::get_height() const
+{
+  return surfaces[get_current_frame()]->h;
+}
+
+/* EOF */
diff --git a/lib/special/sprite.h b/lib/special/sprite.h
new file mode 100644 (file)
index 0000000..3e03ee2
--- /dev/null
@@ -0,0 +1,80 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SPRITE_H
+#define SUPERTUX_SPRITE_H
+
+#include <string>
+#include <vector>
+
+#include "utils/lispreader.h"
+#include "video/surface.h"
+#include "math/vector.h"
+
+class Sprite
+{
+ private:
+  std::string name;
+
+  int x_hotspot;
+  int y_hotspot;
+
+  /** Frames per second */
+  float fps;
+
+  /** Number of seconds that a frame is displayed until it is switched
+      to the next frame */
+  float frame_delay;
+
+  float time;
+
+  std::vector<Surface*> surfaces;
+
+  void init_defaults();
+ public:
+  /** cur has to be a pointer to data in the form of ((x-hotspot 5)
+      (y-hotspot 10) ...) */
+  Sprite(lisp_object_t* cur);
+  ~Sprite();
+  
+  void reset();
+
+  /** Update the sprite and process to the next frame */
+  void update(float delta);
+  void draw(DrawingContext& context, const Vector& pos, int layer,
+      Uint32 drawing_effect = NONE_EFFECT);
+  int get_current_frame() const;
+
+  float get_fps() { return fps; } ;
+  int get_frames() { return surfaces.size(); } ;
+
+  std::string get_name() const { return name; } 
+  int get_width() const;
+  int get_height() const;
+
+  Surface* get_frame(unsigned int frame)
+    {  if(frame < surfaces.size()) return surfaces[frame];
+       else return surfaces[0];  }
+};
+
+#endif /*SUPERTUX_SPRITE_H*/
+
+/* Local Variables: */
+/* mode:c++ */
+/* End: */
diff --git a/lib/special/sprite_manager.cpp b/lib/special/sprite_manager.cpp
new file mode 100644 (file)
index 0000000..1c4b72e
--- /dev/null
@@ -0,0 +1,100 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <iostream>
+
+#include "utils/lispreader.h"
+#include "special/sprite_manager.h"
+
+SpriteManager::SpriteManager(const std::string& filename)
+{
+  load_resfile(filename);
+}
+
+SpriteManager::~SpriteManager()
+{
+  for(std::map<std::string, Sprite*>::iterator i = sprites.begin();
+      i != sprites.end(); ++i) {
+    delete i->second;
+  }
+}
+
+void
+SpriteManager::load_resfile(const std::string& filename)
+{
+  lisp_object_t* root_obj = lisp_read_from_file(filename);
+  if (!root_obj)
+    {
+      std::cout << "SpriteManager: Couldn't load: " << filename << std::endl;
+      return;
+    }
+
+  lisp_object_t* cur = root_obj;
+
+  if (strcmp(lisp_symbol(lisp_car(cur)), "supertux-resources") != 0)
+    return;
+  cur = lisp_cdr(cur);
+
+  while(cur)
+    {
+      lisp_object_t* el = lisp_car(cur);
+
+      if (strcmp(lisp_symbol(lisp_car(el)), "sprite") == 0)
+        {
+          Sprite* sprite = new Sprite(lisp_cdr(el));
+
+          Sprites::iterator i = sprites.find(sprite->get_name());
+          if (i == sprites.end())
+            {
+              sprites[sprite->get_name()] = sprite;
+            }
+          else
+            {
+              delete i->second;
+              i->second = sprite;
+              std::cout << "Warning: dulpicate entry: '" << sprite->get_name() << "'" << std::endl;
+            }
+        }
+      else
+        {
+          std::cout << "SpriteManager: Unknown tag" << std::endl;
+        }
+
+      cur = lisp_cdr(cur);
+    }
+
+  lisp_free(root_obj);
+}
+
+Sprite*
+SpriteManager::load(const std::string& name)
+{
+  Sprites::iterator i = sprites.find(name);
+  if (i != sprites.end())
+    {
+      return i->second;
+    }
+  else
+    {
+      std::cout << "SpriteManager: Sprite '" << name << "' not found" << std::endl;
+      return 0;
+    }
+}
+
+/* EOF */
diff --git a/lib/special/sprite_manager.h b/lib/special/sprite_manager.h
new file mode 100644 (file)
index 0000000..9b240a0
--- /dev/null
@@ -0,0 +1,47 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SPRITE_MANAGER_H
+#define SUPERTUX_SPRITE_MANAGER_H
+
+#include <map>
+
+#include "special/sprite.h"
+
+class SpriteManager
+{
+ private:
+  typedef std::map<std::string, Sprite*> Sprites;
+  Sprites sprites;
+ public:
+  SpriteManager(const std::string& filename);
+  ~SpriteManager();
+  
+  void    load_resfile(const std::string& filename);
+  /** loads a sprite.
+   * WARNING: You must not delete the returned object.
+   */
+  Sprite* load(const std::string& name);
+};
+
+#endif /*SUPERTUX_SPRITE_MANAGER_H*/
+
+/* Local Variables: */
+/* mode:c++ */
+/* End: */
diff --git a/lib/special/stringlist.cpp b/lib/special/stringlist.cpp
new file mode 100644 (file)
index 0000000..1a3440c
--- /dev/null
@@ -0,0 +1,119 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include "string.h"
+#include "stdlib.h"
+#include "special/stringlist.h"
+
+void string_list_init(string_list_type* pstring_list)
+{
+  pstring_list->num_items = 0;
+  pstring_list->active_item = -1;
+  pstring_list->item = NULL;
+}
+
+char* string_list_active(string_list_type* pstring_list)
+{
+  if(pstring_list == NULL)
+    return "";
+
+  if(pstring_list->active_item != -1)
+    return pstring_list->item[pstring_list->active_item];
+  else
+    return "";
+}
+
+void string_list_add_item(string_list_type* pstring_list,const  char* str)
+{
+  char *pnew_string;
+  pnew_string = (char*) malloc(sizeof(char)*(strlen(str)+1));
+  strcpy(pnew_string,str);
+  ++pstring_list->num_items;
+  pstring_list->item = (char**) realloc(pstring_list->item,sizeof(char**)*pstring_list->num_items);
+  pstring_list->item[pstring_list->num_items-1] = pnew_string;
+  if(pstring_list->active_item == -1)
+    pstring_list->active_item = 0;
+}
+
+void string_list_copy(string_list_type* pstring_list, string_list_type pstring_list_orig)
+{
+  int i;
+  string_list_free(pstring_list);
+  for(i = 0; i < pstring_list_orig.num_items; ++i)
+    string_list_add_item(pstring_list,pstring_list_orig.item[i]);
+}
+
+int string_list_find(string_list_type* pstring_list,const  char* str)
+{
+  int i;
+  for(i = 0; i < pstring_list->num_items; ++i)
+    {
+      if(strcmp(pstring_list->item[i],str) == 0)
+        {
+          return i;
+        }
+    }
+  return -1;
+}
+
+void string_list_sort(string_list_type* pstring_list)
+{
+  int i,j,y;
+
+  for(j = 0; j < pstring_list->num_items; ++j)
+    for(i = 0; i < pstring_list->num_items-1; ++i)
+      {
+
+        y = strcmp(pstring_list->item[i],pstring_list->item[i+1]);
+        if(y == 0)
+          {
+            continue;
+          }
+        else if(y < 0)
+          {
+            continue;
+          }
+        else if(y > 0)
+          {
+            char* char_pointer;
+            char_pointer = pstring_list->item[i];
+            pstring_list->item[i] = pstring_list->item[i+1];
+            pstring_list->item[i+1] = char_pointer;
+            continue;
+          }
+
+      }
+
+}
+
+void string_list_free(string_list_type* pstring_list)
+{
+  if(pstring_list != NULL)
+    {
+      int i;
+      for(i=0; i < pstring_list->num_items; ++i)
+        free(pstring_list->item[i]);
+      free(pstring_list->item);
+      pstring_list->item = NULL;
+      pstring_list->num_items = 0;
+      pstring_list->active_item = -1;
+    }
+}
+
diff --git a/lib/special/stringlist.h b/lib/special/stringlist.h
new file mode 100644 (file)
index 0000000..6428813
--- /dev/null
@@ -0,0 +1,42 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_STRINGLIST_H
+#define SUPERTUX_STRINGLIST_H
+
+struct string_list_type
+{
+  int num_items;
+  int active_item;
+  char **item;
+};
+
+void  string_list_init(string_list_type* pstring_list);
+char* string_list_active(string_list_type* pstring_list);
+void  string_list_copy(string_list_type* pstring_list, string_list_type pstring_list_orig);
+int   string_list_find(string_list_type* pstring_list, const char* str);
+void  string_list_sort(string_list_type* pstring_list);
+void  string_list_add_item(string_list_type* pstring_list, const char* str);
+void  string_list_free(string_list_type* pstring_list);
+
+
+
+#endif /*SUPERTUX_STRINGLIST_H*/
+
diff --git a/lib/special/timer.cpp b/lib/special/timer.cpp
new file mode 100644 (file)
index 0000000..b1a093b
--- /dev/null
@@ -0,0 +1,166 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include "SDL.h"
+#include "special/timer.h"
+
+unsigned int st_pause_ticks, st_pause_count;
+
+unsigned int st_get_ticks(void)
+{
+  if(st_pause_count != 0)
+    return /*SDL_GetTicks()*/ - st_pause_ticks /*- SDL_GetTicks()*/ + st_pause_count;
+  else
+    return SDL_GetTicks() - st_pause_ticks;
+}
+
+void st_pause_ticks_init(void)
+{
+  st_pause_ticks = 0;
+  st_pause_count = 0;
+}
+
+void st_pause_ticks_start(void)
+{
+  if(st_pause_count == 0)
+    st_pause_count = SDL_GetTicks();
+}
+
+void st_pause_ticks_stop(void)
+{
+if(st_pause_count == 0)
+return;
+
+  st_pause_ticks += SDL_GetTicks() - st_pause_count;
+  st_pause_count = 0;
+}
+
+bool st_pause_ticks_started(void)
+{
+if(st_pause_count == 0)
+return false;
+else
+return true;
+}
+
+Timer::Timer()
+{
+  init(true);
+}
+
+void
+Timer::init(bool st_ticks)
+{
+  period    = 0;
+  time      = 0;
+  get_ticks = st_ticks ? st_get_ticks : SDL_GetTicks;
+}
+
+void
+Timer::start(unsigned int period_)
+{
+  time   = get_ticks();
+  period = period_;
+}
+
+void
+Timer::stop()
+{
+  if(get_ticks == st_get_ticks)
+    init(true);
+  else
+    init(false);
+}
+
+int
+Timer::check()
+{
+  if((time != 0) && (time + period > get_ticks()))
+    return true;
+  else
+    {
+      time = 0;
+      return false;
+    }
+}
+
+int
+Timer::started()
+{
+  if(time != 0)
+    return true;
+  else
+    return false;
+}
+
+int
+Timer::get_left()
+{
+  return (period - (get_ticks() - time));
+}
+
+int
+Timer::get_gone()
+{
+  return (get_ticks() - time);
+}
+
+void
+Timer::fwrite(FILE* fi)
+{
+  unsigned int diff_ticks;
+  int tick_mode;
+  if(time != 0)
+    diff_ticks = get_ticks() - time;
+  else
+    diff_ticks = 0;
+
+  ::fwrite(&period,sizeof(unsigned int),1,fi);
+  ::fwrite(&diff_ticks,sizeof(unsigned int),1,fi);
+  if(get_ticks == st_get_ticks)
+      tick_mode = true;
+  else
+      tick_mode = false;
+  ::fwrite(&tick_mode,sizeof(unsigned int),1,fi);
+}
+
+void
+Timer::fread(FILE* fi)
+{
+  unsigned int diff_ticks;
+  int tick_mode;
+
+  ::fread(&period,sizeof(unsigned int),1,fi);
+  ::fread(&diff_ticks,sizeof(unsigned int),1,fi);
+  ::fread(&tick_mode,sizeof(unsigned int),1,fi);
+
+  if (tick_mode)
+    get_ticks = st_get_ticks;
+  else
+    get_ticks = SDL_GetTicks;
+
+  if (diff_ticks != 0)
+    time = get_ticks() - diff_ticks;
+  else
+    time = 0;
+
+}
+
+/* EOF */
diff --git a/lib/special/timer.h b/lib/special/timer.h
new file mode 100644 (file)
index 0000000..734c896
--- /dev/null
@@ -0,0 +1,69 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_TIMER_H
+#define SUPERTUX_TIMER_H
+
+extern unsigned int st_pause_ticks, st_pause_count;
+
+unsigned int st_get_ticks(void);
+void st_pause_ticks_init(void);
+void st_pause_ticks_start(void);
+void st_pause_ticks_stop(void);
+bool st_pause_ticks_started(void);
+
+class Timer
+{
+ public:
+  unsigned int period;
+  unsigned int time;
+  unsigned int (*get_ticks) (void);  
+
+ public:
+  Timer();
+  
+  void init(bool st_ticks);
+  void start(unsigned int period);
+  void stop();
+
+  /*======================================================================
+    return: NO  = the timer is not started
+    or it is over
+    YES = otherwise
+    ======================================================================*/
+  int check();
+  int started();
+
+  /*======================================================================
+    return: the time left (in millisecond)
+    note  : the returned value can be negative
+    ======================================================================*/
+  int get_left();
+
+  int  get_gone();
+  void fwrite(FILE* fi);
+  void fread(FILE* fi);
+};
+
+#endif /*SUPERTUX_TIMER_H*/
+
+/* Local Variables: */
+/* mode:c++ */
+/* End: */
diff --git a/lib/utils/configfile.cpp b/lib/utils/configfile.cpp
new file mode 100644 (file)
index 0000000..cbef85a
--- /dev/null
@@ -0,0 +1,143 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Michael George <mike@georgetech.com>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <cstdlib>
+#include <string>
+
+#include "utils/configfile.h"
+#include "app/setup.h"
+#include "app/globals.h"
+
+#ifdef WIN32
+const char * config_filename = "/st_config.dat";
+#else
+const char * config_filename = "/config";
+#endif
+
+Config* config = 0;
+
+static void defaults ()
+{
+  /* Set defaults: */
+  debug_mode = false;
+  audio_device = true;
+
+  use_fullscreen = false;
+  show_fps = false;
+  use_gl = false;
+
+  use_sound = true;
+  use_music = true;
+}
+
+void Config::load()
+{
+  FILE * file = NULL;
+
+  defaults();
+
+  /* override defaults from config file */
+
+  file = opendata(config_filename, "r");
+
+  if (file == NULL)
+    return;
+
+  /* read config file */
+
+  lisp_stream_t   stream;
+  lisp_object_t * root_obj = NULL;
+
+  lisp_stream_init_file (&stream, file);
+  root_obj = lisp_read (&stream);
+
+  if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
+    return;
+
+  if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-config") != 0)
+    return;
+
+  LispReader reader(lisp_cdr(root_obj));
+
+  reader.read_bool("fullscreen", use_fullscreen);
+  reader.read_bool("sound",      use_sound);
+  reader.read_bool("music",      use_music);
+  reader.read_bool("show_fps",   show_fps);
+
+  std::string video;
+  reader.read_string ("video", video);
+  if (video == "opengl")
+    use_gl = true;
+  else
+    use_gl = false;
+
+  reader.read_int ("joystick", joystick_num);
+
+  if (joystick_num >= 0)
+    {
+    reader.read_int ("joystick-x", joystick_keymap.x_axis);
+    reader.read_int ("joystick-y", joystick_keymap.y_axis);
+    reader.read_int ("joystick-a", joystick_keymap.a_button);
+    reader.read_int ("joystick-b", joystick_keymap.b_button);
+    reader.read_int ("joystick-start", joystick_keymap.start_button);
+    reader.read_int ("joystick-deadzone", joystick_keymap.dead_zone);
+    }
+
+  customload(reader);
+
+  lisp_free(root_obj);
+}
+
+void Config::save ()
+{
+  /* write settings to config file */
+  FILE * config = opendata(config_filename, "w");
+
+  if(config)
+    {
+      fprintf(config, "(supertux-config\n");
+      fprintf(config, "\t;; the following options can be set to #t or #f:\n");
+      fprintf(config, "\t(fullscreen %s)\n", use_fullscreen ? "#t" : "#f");
+      fprintf(config, "\t(sound      %s)\n", use_sound      ? "#t" : "#f");
+      fprintf(config, "\t(music      %s)\n", use_music      ? "#t" : "#f");
+      fprintf(config, "\t(show_fps   %s)\n", show_fps       ? "#t" : "#f");
+
+      fprintf(config, "\n\t;; either \"opengl\" or \"sdl\"\n");
+      fprintf(config, "\t(video      \"%s\")\n", use_gl ? "opengl" : "sdl");
+
+      if(use_joystick)
+        {
+        fprintf(config, "\n\t;; joystick number:\n");
+        fprintf(config, "\t(joystick   %d)\n", joystick_num);
+
+        fprintf(config, "\t(joystick-x   %d)\n", joystick_keymap.x_axis);
+        fprintf(config, "\t(joystick-y   %d)\n", joystick_keymap.y_axis);
+        fprintf(config, "\t(joystick-a   %d)\n", joystick_keymap.a_button);
+        fprintf(config, "\t(joystick-b   %d)\n", joystick_keymap.b_button);
+        fprintf(config, "\t(joystick-start  %d)\n", joystick_keymap.start_button);
+        fprintf(config, "\t(joystick-deadzone  %d)\n", joystick_keymap.dead_zone);
+        }
+       
+       customsave(config);
+
+      fprintf(config, ")\n");
+    }
+}
+
+/* EOF */
diff --git a/lib/utils/configfile.h b/lib/utils/configfile.h
new file mode 100644 (file)
index 0000000..9ef9d31
--- /dev/null
@@ -0,0 +1,40 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Michael George <mike@georgetech.com>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_CONFIGFILE_H
+#define SUPERTUX_CONFIGFILE_H
+
+#include "utils/lispreader.h"
+
+class Config {
+  public:
+  void load ();
+  void save ();
+  virtual void customload(LispReader& reader) {};
+  virtual void customsave(FILE * config) {};
+};
+
+extern Config* config;
+
+
+#endif
+
+/* Local Variables: */
+/* mode:c++ */
+/* End: */
diff --git a/lib/utils/exceptions.h b/lib/utils/exceptions.h
new file mode 100644 (file)
index 0000000..974bf0a
--- /dev/null
@@ -0,0 +1,44 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_EXCEPTIONS_H
+#define SUPERTUX_EXCEPTIONS_H
+
+// Exceptions
+#include <exception>
+#include <string>
+
+class SuperTuxException : public std::exception
+{
+  public:
+    SuperTuxException(const char* _message, const char* _file = "", const unsigned int _line = 0)
+      : message(_message), file(_file), line(_line) { };
+    virtual ~SuperTuxException() throw() { };
+
+    const char* what() const throw() { return message; };
+    const char* what_file() const throw() { return file; };
+    const unsigned int what_line() const throw() { return line; };
+    
+  private:
+    const char* message;
+    const char* file;
+    const unsigned int line;
+};
+
+#endif /*SUPERTUX_EXCEPTIONS_H*/
diff --git a/lib/utils/lispreader.cpp b/lib/utils/lispreader.cpp
new file mode 100644 (file)
index 0000000..c49d34f
--- /dev/null
@@ -0,0 +1,1293 @@
+/* $Id$ */
+/*
+ * lispreader.c
+ *
+ * Copyright (C) 1998-2000 Mark Probst
+ * Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <iostream>
+#include <vector>
+#include <string>
+#include <cctype>
+#include <cstdlib>
+#include <cstring>
+
+#include "app/globals.h"
+#include "app/setup.h"
+#include "utils/lispreader.h"
+
+#define TOKEN_ERROR                   -1
+#define TOKEN_EOF                     0
+#define TOKEN_OPEN_PAREN              1
+#define TOKEN_CLOSE_PAREN             2
+#define TOKEN_SYMBOL                  3
+#define TOKEN_STRING                  4
+#define TOKEN_INTEGER                 5
+#define TOKEN_REAL                    6
+#define TOKEN_PATTERN_OPEN_PAREN      7
+#define TOKEN_DOT                     8
+#define TOKEN_TRUE                    9
+#define TOKEN_FALSE                   10
+
+
+#define MAX_TOKEN_LENGTH           4096
+
+static char token_string[MAX_TOKEN_LENGTH + 1] = "";
+static int token_length = 0;
+
+static lisp_object_t end_marker = { LISP_TYPE_EOF, {{0, 0}} };
+static lisp_object_t error_object = { LISP_TYPE_PARSE_ERROR , {{0,0}}  };
+static lisp_object_t close_paren_marker = { LISP_TYPE_PARSE_ERROR , {{0,0}}  };
+static lisp_object_t dot_marker = { LISP_TYPE_PARSE_ERROR , {{0,0}} };
+
+static void
+_token_clear (void)
+{
+  token_string[0] = '\0';
+  token_length = 0;
+}
+
+static void
+_token_append (char c)
+{
+  if (token_length >= MAX_TOKEN_LENGTH)
+    throw LispReaderException("_token_append()", __FILE__, __LINE__);
+
+  token_string[token_length++] = c;
+  token_string[token_length] = '\0';
+}
+
+static int
+_next_char (lisp_stream_t *stream)
+{
+  switch (stream->type)
+    {
+    case LISP_STREAM_FILE :
+      return getc(stream->v.file);
+
+    case LISP_STREAM_STRING :
+      {
+        char c = stream->v.string.buf[stream->v.string.pos];
+
+        if (c == 0)
+          return EOF;
+
+        ++stream->v.string.pos;
+
+        return c;
+      }
+
+    case LISP_STREAM_ANY:
+      return stream->v.any.next_char(stream->v.any.data);
+    }
+
+  throw LispReaderException("_next_char()", __FILE__, __LINE__);
+  return EOF;
+}
+
+static void
+_unget_char (char c, lisp_stream_t *stream)
+{
+  switch (stream->type)
+    {
+    case LISP_STREAM_FILE :
+      ungetc(c, stream->v.file);
+      break;
+
+    case LISP_STREAM_STRING :
+      --stream->v.string.pos;
+      break;
+
+    case LISP_STREAM_ANY:
+      stream->v.any.unget_char(c, stream->v.any.data);
+      break;
+
+    default :
+      throw LispReaderException("_unget_char()", __FILE__, __LINE__);
+    }
+}
+
+static int
+_scan (lisp_stream_t *stream)
+{
+  static char *delims = "\"();";
+
+  int c;
+
+  _token_clear();
+
+  do
+    {
+      c = _next_char(stream);
+      if (c == EOF)
+        return TOKEN_EOF;
+      else if (c == ';')        /* comment start */
+        while (1)
+          {
+            c = _next_char(stream);
+            if (c == EOF)
+              return TOKEN_EOF;
+            else if (c == '\n')
+              break;
+          }
+    }
+  while (isspace(c));
+
+  switch (c)
+    {
+    case '(' :
+      return TOKEN_OPEN_PAREN;
+
+    case ')' :
+      return TOKEN_CLOSE_PAREN;
+
+    case '"' :
+      while (1)
+        {
+          c = _next_char(stream);
+          if (c == EOF)
+            return TOKEN_ERROR;
+          if (c == '"')
+            break;
+          if (c == '\\')
+            {
+              c = _next_char(stream);
+
+              switch (c)
+                {
+                case EOF :
+                  return TOKEN_ERROR;
+
+                case 'n' :
+                  c = '\n';
+                  break;
+
+                case 't' :
+                  c = '\t';
+                  break;
+                }
+            }
+
+          _token_append(c);
+        }
+      return TOKEN_STRING;
+
+    case '#' :
+      c = _next_char(stream);
+      if (c == EOF)
+        return TOKEN_ERROR;
+
+      switch (c)
+        {
+        case 't' :
+          return TOKEN_TRUE;
+
+        case 'f' :
+          return TOKEN_FALSE;
+
+        case '?' :
+          c = _next_char(stream);
+          if (c == EOF)
+            return TOKEN_ERROR;
+
+          if (c == '(')
+            return TOKEN_PATTERN_OPEN_PAREN;
+          else
+            return TOKEN_ERROR;
+        }
+      return TOKEN_ERROR;
+
+    default :
+      if (isdigit(c) || c == '-')
+        {
+          int have_nondigits = 0;
+          int have_digits = 0;
+          int have_floating_point = 0;
+
+          do
+            {
+              if (isdigit(c))
+                have_digits = 1;
+              else if (c == '.')
+                have_floating_point++;
+              _token_append(c);
+
+              c = _next_char(stream);
+
+              if (c != EOF && !isdigit(c) && !isspace(c) && c != '.' && !strchr(delims, c))
+                have_nondigits = 1;
+            }
+          while (c != EOF && !isspace(c) && !strchr(delims, c));
+
+          if (c != EOF)
+            _unget_char(c, stream);
+
+          if (have_nondigits || !have_digits || have_floating_point > 1)
+            return TOKEN_SYMBOL;
+          else if (have_floating_point == 1)
+            return TOKEN_REAL;
+          else
+            return TOKEN_INTEGER;
+        }
+      else
+        {
+          if (c == '.')
+            {
+              c = _next_char(stream);
+              if (c != EOF && !isspace(c) && !strchr(delims, c))
+                _token_append('.');
+              else
+                {
+                  _unget_char(c, stream);
+                  return TOKEN_DOT;
+                }
+            }
+          do
+            {
+              _token_append(c);
+              c = _next_char(stream);
+            }
+          while (c != EOF && !isspace(c) && !strchr(delims, c));
+          if (c != EOF)
+            _unget_char(c, stream);
+
+          return TOKEN_SYMBOL;
+        }
+    }
+
+  throw LispReaderException("_scan()", __FILE__, __LINE__);
+  return TOKEN_ERROR;
+}
+
+static lisp_object_t*
+lisp_object_alloc (int type)
+{
+  lisp_object_t *obj = (lisp_object_t*)malloc(sizeof(lisp_object_t));
+
+  obj->type = type;
+
+  return obj;
+}
+
+lisp_stream_t*
+lisp_stream_init_file (lisp_stream_t *stream, FILE *file)
+{
+  stream->type = LISP_STREAM_FILE;
+  stream->v.file = file;
+
+  return stream;
+}
+
+lisp_stream_t*
+lisp_stream_init_string (lisp_stream_t *stream, char *buf)
+{
+  stream->type = LISP_STREAM_STRING;
+  stream->v.string.buf = buf;
+  stream->v.string.pos = 0;
+
+  return stream;
+}
+
+lisp_stream_t*
+lisp_stream_init_any (lisp_stream_t *stream, void *data,
+                      int (*next_char) (void *data),
+                      void (*unget_char) (char c, void *data))
+{
+  if (next_char == 0 || unget_char == 0)
+    throw LispReaderException("lisp_stream_init_any()", __FILE__, __LINE__);
+
+  stream->type = LISP_STREAM_ANY;
+  stream->v.any.data = data;
+  stream->v.any.next_char= next_char;
+  stream->v.any.unget_char = unget_char;
+
+  return stream;
+}
+
+lisp_object_t*
+lisp_make_integer (int value)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_INTEGER);
+
+  obj->v.integer = value;
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_make_real (float value)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_REAL);
+
+  obj->v.real = value;
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_make_symbol (const char *value)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_SYMBOL);
+
+  obj->v.string = strdup(value);
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_make_string (const char *value)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_STRING);
+
+  obj->v.string = strdup(value);
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_make_cons (lisp_object_t *car, lisp_object_t *cdr)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_CONS);
+
+  obj->v.cons.car = car;
+  obj->v.cons.cdr = cdr;
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_make_boolean (int value)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_BOOLEAN);
+
+  obj->v.integer = value ? 1 : 0;
+
+  return obj;
+}
+
+static lisp_object_t*
+lisp_make_pattern_cons (lisp_object_t *car, lisp_object_t *cdr)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_PATTERN_CONS);
+
+  obj->v.cons.car = car;
+  obj->v.cons.cdr = cdr;
+
+  return obj;
+}
+
+static lisp_object_t*
+lisp_make_pattern_var (int type, int index, lisp_object_t *sub)
+{
+  lisp_object_t *obj = lisp_object_alloc(LISP_TYPE_PATTERN_VAR);
+
+  obj->v.pattern.type = type;
+  obj->v.pattern.index = index;
+  obj->v.pattern.sub = sub;
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_read (lisp_stream_t *in)
+{
+  int token = _scan(in);
+  lisp_object_t *obj = lisp_nil();
+
+  if (token == TOKEN_EOF)
+    return &end_marker;
+
+  switch (token)
+    {
+    case TOKEN_ERROR :
+      return &error_object;
+
+    case TOKEN_EOF :
+      return &end_marker;
+
+    case TOKEN_OPEN_PAREN :
+    case TOKEN_PATTERN_OPEN_PAREN :
+      {
+        lisp_object_t *last = lisp_nil(), *car;
+
+        do
+          {
+            car = lisp_read(in);
+            if (car == &error_object || car == &end_marker)
+              {
+                lisp_free(obj);
+                return &error_object;
+              }
+            else if (car == &dot_marker)
+              {
+                if (lisp_nil_p(last))
+                  {
+                    lisp_free(obj);
+                    return &error_object;
+                  }
+
+                car = lisp_read(in);
+                if (car == &error_object || car == &end_marker)
+                  {
+                    lisp_free(obj);
+                    return car;
+                  }
+                else
+                  {
+                    last->v.cons.cdr = car;
+
+                    if (_scan(in) != TOKEN_CLOSE_PAREN)
+                      {
+                        lisp_free(obj);
+                        return &error_object;
+                      }
+
+                    car = &close_paren_marker;
+                  }
+              }
+            else if (car != &close_paren_marker)
+              {
+                if (lisp_nil_p(last))
+                  obj = last = (token == TOKEN_OPEN_PAREN ? lisp_make_cons(car, lisp_nil()) : lisp_make_pattern_cons(car, lisp_nil()));
+                else
+                  last = last->v.cons.cdr = lisp_make_cons(car, lisp_nil());
+              }
+          }
+        while (car != &close_paren_marker);
+      }
+      return obj;
+
+    case TOKEN_CLOSE_PAREN :
+      return &close_paren_marker;
+
+    case TOKEN_SYMBOL :
+      return lisp_make_symbol(token_string);
+
+    case TOKEN_STRING :
+      return lisp_make_string(token_string);
+
+    case TOKEN_INTEGER :
+      return lisp_make_integer(atoi(token_string));
+
+    case TOKEN_REAL :
+      return lisp_make_real((float)atof(token_string));
+
+    case TOKEN_DOT :
+      return &dot_marker;
+
+    case TOKEN_TRUE :
+      return lisp_make_boolean(1);
+
+    case TOKEN_FALSE :
+      return lisp_make_boolean(0);
+    }
+
+  throw LispReaderException("lisp_read()", __FILE__, __LINE__);
+  return &error_object;
+}
+
+void
+lisp_free (lisp_object_t *obj)
+{
+  if (obj == 0)
+    return;
+
+  /** We have to use this iterative code, because the recursive function
+   * produces a stack overflow and crashs on OSX 10.2
+   */
+  std::vector<lisp_object_t*> objs;
+  objs.push_back(obj);
+
+  while(!objs.empty()) {
+    lisp_object_t* obj = objs.back();
+    objs.pop_back();
+        
+    switch (obj->type) {
+      case LISP_TYPE_INTERNAL :
+      case LISP_TYPE_PARSE_ERROR :
+      case LISP_TYPE_EOF :
+        return;
+
+      case LISP_TYPE_SYMBOL :
+      case LISP_TYPE_STRING :
+        free(obj->v.string);
+        break;
+
+      case LISP_TYPE_CONS :
+      case LISP_TYPE_PATTERN_CONS :
+        if(obj->v.cons.car)
+          objs.push_back(obj->v.cons.car);
+        if(obj->v.cons.cdr)
+          objs.push_back(obj->v.cons.cdr);
+        break;
+
+      case LISP_TYPE_PATTERN_VAR :
+        if(obj->v.pattern.sub)
+          objs.push_back(obj->v.pattern.sub);
+        break;
+    }
+
+    free(obj);
+  }
+}
+
+lisp_object_t*
+lisp_read_from_string (const char *buf)
+{
+  lisp_stream_t stream;
+
+  lisp_stream_init_string(&stream, (char*)buf);
+  return lisp_read(&stream);
+}
+
+static int
+_compile_pattern (lisp_object_t **obj, int *index)
+{
+  if (*obj == 0)
+    return 1;
+
+  switch (lisp_type(*obj))
+    {
+    case LISP_TYPE_PATTERN_CONS :
+      {
+        struct
+          {
+            char *name;
+            int type;
+          }
+        types[] =
+          {
+            { "any", LISP_PATTERN_ANY },
+            { "symbol", LISP_PATTERN_SYMBOL },
+            { "string", LISP_PATTERN_STRING },
+            { "integer", LISP_PATTERN_INTEGER },
+            { "real", LISP_PATTERN_REAL },
+            { "boolean", LISP_PATTERN_BOOLEAN },
+            { "list", LISP_PATTERN_LIST },
+            { "or", LISP_PATTERN_OR },
+            { 0, 0 }
+          };
+        char *type_name;
+        int type;
+        int i;
+        lisp_object_t *pattern;
+        type = -1;
+       
+        if (lisp_type(lisp_car(*obj)) != LISP_TYPE_SYMBOL)
+          return 0;
+
+        type_name = lisp_symbol(lisp_car(*obj));
+        for (i = 0; types[i].name != 0; ++i)
+          {
+            if (strcmp(types[i].name, type_name) == 0)
+              {
+                type = types[i].type;
+                break;
+              }
+          }
+
+        if (types[i].name == 0)
+          return 0;
+
+        if (type != LISP_PATTERN_OR && lisp_cdr(*obj) != 0)
+          return 0;
+
+        pattern = lisp_make_pattern_var(type, (*index)++, lisp_nil());
+
+        if (type == LISP_PATTERN_OR)
+          {
+            lisp_object_t *cdr = lisp_cdr(*obj);
+
+            if (!_compile_pattern(&cdr, index))
+              {
+                lisp_free(pattern);
+                return 0;
+              }
+
+            pattern->v.pattern.sub = cdr;
+
+            (*obj)->v.cons.cdr = lisp_nil();
+          }
+
+        lisp_free(*obj);
+
+        *obj = pattern;
+      }
+      break;
+
+    case LISP_TYPE_CONS :
+      if (!_compile_pattern(&(*obj)->v.cons.car, index))
+        return 0;
+      if (!_compile_pattern(&(*obj)->v.cons.cdr, index))
+        return 0;
+      break;
+    }
+
+  return 1;
+}
+
+int
+lisp_compile_pattern (lisp_object_t **obj, int *num_subs)
+{
+  int index = 0;
+  int result;
+
+  result = _compile_pattern(obj, &index);
+
+  if (result && num_subs != 0)
+    *num_subs = index;
+
+  return result;
+}
+
+static int _match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars);
+
+static int
+_match_pattern_var (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars)
+{
+  if (lisp_type(pattern) != LISP_TYPE_PATTERN_VAR)
+    throw LispReaderException("_match_pattern_var", __FILE__, __LINE__);
+
+  switch (pattern->v.pattern.type)
+    {
+    case LISP_PATTERN_ANY :
+      break;
+
+    case LISP_PATTERN_SYMBOL :
+      if (obj == 0 || lisp_type(obj) != LISP_TYPE_SYMBOL)
+        return 0;
+      break;
+
+    case LISP_PATTERN_STRING :
+      if (obj == 0 || lisp_type(obj) != LISP_TYPE_STRING)
+        return 0;
+      break;
+
+    case LISP_PATTERN_INTEGER :
+      if (obj == 0 || lisp_type(obj) != LISP_TYPE_INTEGER)
+        return 0;
+      break;
+
+    case LISP_PATTERN_REAL :
+      if (obj == 0 || lisp_type(obj) != LISP_TYPE_REAL)
+        return 0;
+      break;
+
+    case LISP_PATTERN_BOOLEAN :
+      if (obj == 0 || lisp_type(obj) != LISP_TYPE_BOOLEAN)
+        return 0;
+      break;
+
+    case LISP_PATTERN_LIST :
+      if (obj == 0 || lisp_type(obj) != LISP_TYPE_CONS)
+        return 0;
+      break;
+
+    case LISP_PATTERN_OR :
+      {
+        lisp_object_t *sub;
+        int matched = 0;
+
+        for (sub = pattern->v.pattern.sub; sub != 0; sub = lisp_cdr(sub))
+          {
+            if (lisp_type(sub) != LISP_TYPE_CONS)
+              throw LispReaderException("_match_pattern_var()", __FILE__, __LINE__);
+
+            if (_match_pattern(lisp_car(sub), obj, vars))
+              matched = 1;
+          }
+
+        if (!matched)
+          return 0;
+      }
+      break;
+
+    default :
+      throw LispReaderException("_match_pattern_var()", __FILE__, __LINE__);
+    }
+
+  if (vars != 0)
+    vars[pattern->v.pattern.index] = obj;
+
+  return 1;
+}
+
+static int
+_match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars)
+{
+  if (pattern == 0)
+    return obj == 0;
+
+  if (obj == 0)
+    return 0;
+
+  if (lisp_type(pattern) == LISP_TYPE_PATTERN_VAR)
+    return _match_pattern_var(pattern, obj, vars);
+
+  if (lisp_type(pattern) != lisp_type(obj))
+    return 0;
+
+  switch (lisp_type(pattern))
+    {
+    case LISP_TYPE_SYMBOL :
+      return strcmp(lisp_symbol(pattern), lisp_symbol(obj)) == 0;
+
+    case LISP_TYPE_STRING :
+      return strcmp(lisp_string(pattern), lisp_string(obj)) == 0;
+
+    case LISP_TYPE_INTEGER :
+      return lisp_integer(pattern) == lisp_integer(obj);
+
+    case LISP_TYPE_REAL :
+      return lisp_real(pattern) == lisp_real(obj);
+
+    case LISP_TYPE_CONS :
+      {
+        int result1, result2;
+
+        result1 = _match_pattern(lisp_car(pattern), lisp_car(obj), vars);
+        result2 = _match_pattern(lisp_cdr(pattern), lisp_cdr(obj), vars);
+
+        return result1 && result2;
+      }
+      break;
+
+    default :
+      throw LispReaderException("_match_pattern()", __FILE__, __LINE__);
+    }
+
+  return 0;
+}
+
+int
+lisp_match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars, int num_subs)
+{
+  int i;
+
+  if (vars != 0)
+    for (i = 0; i < num_subs; ++i)
+      vars[i] = &error_object;
+
+  return _match_pattern(pattern, obj, vars);
+}
+
+int
+lisp_match_string (const char *pattern_string, lisp_object_t *obj, lisp_object_t **vars)
+{
+  lisp_object_t *pattern;
+  int result;
+  int num_subs;
+
+  pattern = lisp_read_from_string(pattern_string);
+
+  if (pattern != 0 && (lisp_type(pattern) == LISP_TYPE_EOF
+                       || lisp_type(pattern) == LISP_TYPE_PARSE_ERROR))
+    return 0;
+
+  if (!lisp_compile_pattern(&pattern, &num_subs))
+    {
+      lisp_free(pattern);
+      return 0;
+    }
+
+  result = lisp_match_pattern(pattern, obj, vars, num_subs);
+
+  lisp_free(pattern);
+
+  return result;
+}
+
+int
+lisp_type (lisp_object_t *obj)
+{
+  if (obj == 0)
+    return LISP_TYPE_NIL;
+  return obj->type;
+}
+
+int
+lisp_integer (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_INTEGER)
+    throw LispReaderException("lisp_integer()", __FILE__, __LINE__);
+
+  return obj->v.integer;
+}
+
+char*
+lisp_symbol (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_SYMBOL)
+    throw LispReaderException("lisp_symbol()", __FILE__, __LINE__);
+
+  return obj->v.string;
+}
+
+char*
+lisp_string (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_STRING)
+    throw LispReaderException("lisp_string()", __FILE__, __LINE__);
+
+  return obj->v.string;
+}
+
+int
+lisp_boolean (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_BOOLEAN)
+    throw LispReaderException("lisp_boolean()", __FILE__, __LINE__);
+
+  return obj->v.integer;
+}
+
+float
+lisp_real (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_REAL && obj->type != LISP_TYPE_INTEGER)
+    throw LispReaderException("lisp_real()", __FILE__, __LINE__);
+
+  if (obj->type == LISP_TYPE_INTEGER)
+    return obj->v.integer;
+  return obj->v.real;
+}
+
+lisp_object_t*
+lisp_car (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS)
+    throw LispReaderException("lisp_car()", __FILE__, __LINE__);
+
+  return obj->v.cons.car;
+}
+
+lisp_object_t*
+lisp_cdr (lisp_object_t *obj)
+{
+  if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS)
+    throw LispReaderException("lisp_cdr()", __FILE__, __LINE__);
+
+  return obj->v.cons.cdr;
+}
+
+lisp_object_t*
+lisp_cxr (lisp_object_t *obj, const char *x)
+{
+  int i;
+
+  for (i = strlen(x) - 1; i >= 0; --i)
+    if (x[i] == 'a')
+      obj = lisp_car(obj);
+    else if (x[i] == 'd')
+      obj = lisp_cdr(obj);
+    else
+      throw LispReaderException("lisp_cxr()", __FILE__, __LINE__);
+
+  return obj;
+}
+
+int
+lisp_list_length (lisp_object_t *obj)
+{
+  int length = 0;
+
+  while (obj != 0)
+    {
+      if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS)
+        throw LispReaderException("lisp_list_length()", __FILE__, __LINE__);
+
+      ++length;
+      obj = obj->v.cons.cdr;
+    }
+
+  return length;
+}
+
+lisp_object_t*
+lisp_list_nth_cdr (lisp_object_t *obj, int index)
+{
+  while (index > 0)
+    {
+      if (obj == 0)
+        throw LispReaderException("lisp_list_nth_cdr()", __FILE__, __LINE__);
+      if (obj->type != LISP_TYPE_CONS && obj->type != LISP_TYPE_PATTERN_CONS)
+        throw LispReaderException("lisp_list_nth_cdr()", __FILE__, __LINE__);
+
+      --index;
+      obj = obj->v.cons.cdr;
+    }
+
+  return obj;
+}
+
+lisp_object_t*
+lisp_list_nth (lisp_object_t *obj, int index)
+{
+  obj = lisp_list_nth_cdr(obj, index);
+
+  if (obj == 0)
+    throw LispReaderException("lisp_list_nth()", __FILE__, __LINE__);
+
+  return obj->v.cons.car;
+}
+
+void
+lisp_dump (lisp_object_t *obj, FILE *out)
+{
+  if (obj == 0)
+    {
+      fprintf(out, "()");
+      return;
+    }
+
+  switch (lisp_type(obj))
+    {
+    case LISP_TYPE_EOF :
+      fputs("#<eof>", out);
+      break;
+
+    case LISP_TYPE_PARSE_ERROR :
+      fputs("#<error>", out);
+      break;
+
+    case LISP_TYPE_INTEGER :
+      fprintf(out, "%d", lisp_integer(obj));
+      break;
+
+    case LISP_TYPE_REAL :
+      fprintf(out, "%f", lisp_real(obj));
+      break;
+
+    case LISP_TYPE_SYMBOL :
+      fputs(lisp_symbol(obj), out);
+      break;
+
+    case LISP_TYPE_STRING :
+      {
+        char *p;
+
+        fputc('"', out);
+        for (p = lisp_string(obj); *p != 0; ++p)
+          {
+            if (*p == '"' || *p == '\\')
+              fputc('\\', out);
+            fputc(*p, out);
+          }
+        fputc('"', out);
+      }
+      break;
+
+    case LISP_TYPE_CONS :
+    case LISP_TYPE_PATTERN_CONS :
+      fputs(lisp_type(obj) == LISP_TYPE_CONS ? "(" : "#?(", out);
+      while (obj != 0)
+        {
+          lisp_dump(lisp_car(obj), out);
+          obj = lisp_cdr(obj);
+          if (obj != 0)
+            {
+              if (lisp_type(obj) != LISP_TYPE_CONS
+                  && lisp_type(obj) != LISP_TYPE_PATTERN_CONS)
+                {
+                  fputs(" . ", out);
+                  lisp_dump(obj, out);
+                  break;
+                }
+              else
+                fputc(' ', out);
+            }
+        }
+      fputc(')', out);
+      break;
+
+    case LISP_TYPE_BOOLEAN :
+      if (lisp_boolean(obj))
+        fputs("#t", out);
+      else
+        fputs("#f", out);
+      break;
+
+    default :
+      throw LispReaderException("lisp_dump()", __FILE__, __LINE__);
+    }
+}
+
+using namespace std;
+
+LispReader::LispReader (lisp_object_t* l)
+    : owner(0), lst (l)
+{
+}
+
+LispReader::~LispReader()
+{
+  if(owner)
+    lisp_free(owner);
+}
+
+LispReader*
+LispReader::load(const std::string& filename, const std::string& toplevellist)
+{
+  lisp_object_t* obj = lisp_read_from_file(filename);
+
+  if(obj->type == LISP_TYPE_EOF || obj->type == LISP_TYPE_PARSE_ERROR) {
+    lisp_free(obj);
+    throw LispReaderException("LispReader::load", __FILE__, __LINE__);
+  }
+
+  if(toplevellist != lisp_symbol(lisp_car(obj))) {
+    lisp_car(obj);
+    throw LispReaderException("LispReader::load wrong toplevel symbol",
+        __FILE__, __LINE__);
+  }
+  
+  LispReader* reader = new LispReader(lisp_cdr(obj));  
+  reader->owner = obj;
+
+  return reader;
+}
+
+lisp_object_t*
+LispReader::search_for(const char* name)
+{
+  //std::cout << "LispReader::search_for(" << name << ")" << std::endl;
+  lisp_object_t* cursor = lst;
+
+  while(!lisp_nil_p(cursor))
+    {
+      lisp_object_t* cur = lisp_car(cursor);
+
+      if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
+        {
+          lisp_dump(cur, stdout);
+          //throw ConstruoError (std::string("LispReader: Read error in search_for ") + name);
+         printf("LispReader: Read error in search\n");
+        }
+      else
+        {
+          if (strcmp(lisp_symbol(lisp_car(cur)), name) == 0)
+            {
+              return lisp_cdr(cur);
+            }
+        }
+
+      cursor = lisp_cdr (cursor);
+    }
+  return 0;
+}
+
+bool
+LispReader::read_int (const char* name, int& i)
+{
+  lisp_object_t* obj = search_for (name);
+  if(!obj)
+    return false;
+      
+  if (!lisp_integer_p(lisp_car(obj)))
+    return false;
+  
+  i = lisp_integer(lisp_car(obj));
+  return true;
+}
+
+bool
+LispReader::read_lisp(const char* name, lisp_object_t*& b)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+  
+  b = obj;
+  return true;
+}
+
+lisp_object_t*
+LispReader::read_lisp(const char* name)
+{
+  return search_for(name);
+}
+
+bool
+LispReader::read_float (const char* name, float& f)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+  
+  if (!lisp_real_p(lisp_car(obj)) && !lisp_integer_p(lisp_car(obj)))
+    st_abort("LispReader expected type real at token: ", name);
+  
+  f = lisp_real(lisp_car(obj));
+  return true;
+}
+
+bool
+LispReader::read_string_vector (const char* name, std::vector<std::string>& vec)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+
+  vec.clear();
+  while(!lisp_nil_p(obj))
+  {
+    if (!lisp_string_p(lisp_car(obj)))
+      st_abort("LispReader expected type string at token: ", name);
+    vec.push_back(lisp_string(lisp_car(obj)));
+    obj = lisp_cdr(obj);
+  }
+  return true;
+}
+
+bool
+LispReader::read_int_vector (const char* name, std::vector<int>& vec)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+
+  vec.clear();
+  while(!lisp_nil_p(obj))
+  {
+    if (!lisp_integer_p(lisp_car(obj)))
+      st_abort("LispReader expected type integer at token: ", name);
+    vec.push_back(lisp_integer(lisp_car(obj)));
+    obj = lisp_cdr(obj);
+  }
+  return true;
+}
+
+bool
+LispReader::read_int_vector (const char* name, std::vector<unsigned int>& vec)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+
+  vec.clear();
+  while(!lisp_nil_p(obj))
+  {
+    if (!lisp_integer_p(lisp_car(obj)))
+      st_abort("LispReader expected type integer at token: ", name);
+    vec.push_back(lisp_integer(lisp_car(obj)));
+    obj = lisp_cdr(obj);
+  }
+  return true;
+}
+
+bool
+LispReader::read_char_vector (const char* name, std::vector<char>& vec)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+  vec.clear();
+  while(!lisp_nil_p(obj))
+  {
+    vec.push_back(*lisp_string(lisp_car(obj)));
+    obj = lisp_cdr(obj);
+  }
+  return true;
+}
+
+bool
+LispReader::read_string (const char* name, std::string& str, bool translatable)
+{
+  lisp_object_t* obj;
+  if(translatable)
+    {
+  /* Internationalization support: check for the suffix: str + "-" + $LANG variable.
+     If not found, use the regular string.
+     So, translating a string in a Lisp file would result in something like:
+     (text "Hello World!")
+     (text-fr "Bonjour Monde!")
+     being fr the value of LANG (echo $LANG) for the language we want to translate to */
+
+    char* lang = getenv("LANG");
+
+    char str_[1024];  // check, for instance, for (title-fr_FR "Bonjour")
+    sprintf(str_, "%s-%s", name, lang);
+
+    obj = search_for (str_);
+
+    if(!obj)  // check, for instance, for (title-fr "Bonjour")
+      {
+      if(lang != NULL && strlen(lang) >= 2)
+        {
+        char lang_[3];
+        strncpy(lang_, lang, 2);
+        lang_[2] = '\0';
+        sprintf(str_, "%s-%s", name, lang_);
+
+        obj = search_for (str_);
+        }
+      else
+        obj = 0;
+      }
+
+    if(!obj)  // check, for instance, for (title "Hello")
+      obj = search_for (name);
+    }
+  else
+    obj = search_for (name);
+
+  if (!obj)
+    return false;
+
+  if (!lisp_string_p(lisp_car(obj)))
+    st_abort("LispReader expected type string at token: ", name);
+  str = lisp_string(lisp_car(obj));
+  return true;
+}
+
+bool
+LispReader::read_bool (const char* name, bool& b)
+{
+  lisp_object_t* obj = search_for (name);
+  if (!obj)
+    return false;
+  
+  if (!lisp_boolean_p(lisp_car(obj)))
+    st_abort("LispReader expected type bool at token: ", name);
+  b = lisp_boolean(lisp_car(obj));
+  return true;
+}
+
+lisp_object_t*
+LispReader::get_lisp()
+{
+  return lst;
+}
+
+lisp_object_t* lisp_read_from_file(const std::string& filename)
+{
+  FILE* in = fopen(filename.c_str(), "r");
+
+  if(!in)
+    return 0;
+
+  lisp_stream_t stream;
+  lisp_stream_init_file(&stream, in);
+  lisp_object_t* obj = lisp_read(&stream);
+  fclose(in);
+
+  return obj;
+}
+
+// EOF //
diff --git a/lib/utils/lispreader.h b/lib/utils/lispreader.h
new file mode 100644 (file)
index 0000000..f0f480e
--- /dev/null
@@ -0,0 +1,204 @@
+/* $Id$ */
+/*
+ * lispreader.h
+ *
+ * Copyright (C) 1998-2000 Mark Probst
+ * Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SUPERTUX_LISPREADER_H
+#define SUPERTUX_LISPREADER_H
+
+#include <cstdio>
+#include <string>
+#include <vector>
+#include <exception>
+
+#include <zlib.h>
+
+#include "utils/exceptions.h"
+
+#define LISP_STREAM_FILE       1
+#define LISP_STREAM_STRING     2
+#define LISP_STREAM_ANY        3
+
+#define LISP_TYPE_INTERNAL      -3
+#define LISP_TYPE_PARSE_ERROR   -2
+#define LISP_TYPE_EOF           -1
+#define LISP_TYPE_NIL           0
+#define LISP_TYPE_SYMBOL        1
+#define LISP_TYPE_INTEGER       2
+#define LISP_TYPE_STRING        3
+#define LISP_TYPE_REAL          4
+#define LISP_TYPE_CONS          5
+#define LISP_TYPE_PATTERN_CONS  6
+#define LISP_TYPE_BOOLEAN       7
+#define LISP_TYPE_PATTERN_VAR   8
+
+#define LISP_PATTERN_ANY        1
+#define LISP_PATTERN_SYMBOL     2
+#define LISP_PATTERN_STRING     3
+#define LISP_PATTERN_INTEGER    4
+#define LISP_PATTERN_REAL       5
+#define LISP_PATTERN_BOOLEAN    6
+#define LISP_PATTERN_LIST       7
+#define LISP_PATTERN_OR         8
+
+// Exception
+class LispReaderException : public SuperTuxException
+{
+  public:
+    LispReaderException(const char* _message = "lispreader error", const char* _file = "", const unsigned int _line = 0)
+      : SuperTuxException(_message, _file, _line) { };
+};
+
+typedef struct
+  {
+    int type;
+
+    union
+      {
+        FILE *file;
+        struct
+          {
+            char *buf;
+            int pos;
+          }
+        string;
+        struct
+          {
+            void *data;
+            int (*next_char) (void *data);
+            void (*unget_char) (char c, void *data);
+          }
+        any;
+      } v;
+  }
+lisp_stream_t;
+
+typedef struct _lisp_object_t lisp_object_t;
+struct _lisp_object_t
+  {
+    int type;
+
+    union
+      {
+        struct
+          {
+            struct _lisp_object_t *car;
+            struct _lisp_object_t *cdr;
+          }
+        cons;
+
+        char *string;
+        int integer;
+        float real;
+
+        struct
+          {
+            int type;
+            int index;
+            struct _lisp_object_t *sub;
+          }
+        pattern;
+      } v;
+  };
+
+lisp_stream_t* lisp_stream_init_file (lisp_stream_t *stream, FILE *file);
+lisp_stream_t* lisp_stream_init_string (lisp_stream_t *stream, char *buf);
+lisp_stream_t* lisp_stream_init_any (lisp_stream_t *stream, void *data,
+                                     int (*next_char) (void *data),
+                                     void (*unget_char) (char c, void *data));
+
+lisp_object_t* lisp_read (lisp_stream_t *in);
+lisp_object_t* lisp_read_from_file(const std::string& filename);
+void lisp_free (lisp_object_t *obj);
+
+lisp_object_t* lisp_read_from_string (const char *buf);
+
+int lisp_compile_pattern (lisp_object_t **obj, int *num_subs);
+int lisp_match_pattern (lisp_object_t *pattern, lisp_object_t *obj, lisp_object_t **vars, int num_subs);
+int lisp_match_string (const char *pattern_string, lisp_object_t *obj, lisp_object_t **vars);
+
+int lisp_type (lisp_object_t *obj);
+int lisp_integer (lisp_object_t *obj);
+float lisp_real (lisp_object_t *obj);
+char* lisp_symbol (lisp_object_t *obj);
+char* lisp_string (lisp_object_t *obj);
+int lisp_boolean (lisp_object_t *obj);
+lisp_object_t* lisp_car (lisp_object_t *obj);
+lisp_object_t* lisp_cdr (lisp_object_t *obj);
+
+lisp_object_t* lisp_cxr (lisp_object_t *obj, const char *x);
+
+lisp_object_t* lisp_make_integer (int value);
+lisp_object_t* lisp_make_real (float value);
+lisp_object_t* lisp_make_symbol (const char *value);
+lisp_object_t* lisp_make_string (const char *value);
+lisp_object_t* lisp_make_cons (lisp_object_t *car, lisp_object_t *cdr);
+lisp_object_t* lisp_make_boolean (int value);
+
+int lisp_list_length (lisp_object_t *obj);
+lisp_object_t* lisp_list_nth_cdr (lisp_object_t *obj, int index);
+lisp_object_t* lisp_list_nth (lisp_object_t *obj, int index);
+
+void lisp_dump (lisp_object_t *obj, FILE *out);
+
+#define lisp_nil()           ((lisp_object_t*)0)
+
+#define lisp_nil_p(obj)      (obj == 0)
+#define lisp_integer_p(obj)  (lisp_type((obj)) == LISP_TYPE_INTEGER)
+#define lisp_real_p(obj)     (lisp_type((obj)) == LISP_TYPE_REAL)
+#define lisp_symbol_p(obj)   (lisp_type((obj)) == LISP_TYPE_SYMBOL)
+#define lisp_string_p(obj)   (lisp_type((obj)) == LISP_TYPE_STRING)
+#define lisp_cons_p(obj)     (lisp_type((obj)) == LISP_TYPE_CONS)
+#define lisp_boolean_p(obj)  (lisp_type((obj)) == LISP_TYPE_BOOLEAN)
+
+/** */
+class LispReader
+{
+private:
+  lisp_object_t* owner;
+  lisp_object_t* lst;
+
+  lisp_object_t* search_for(const char* name);
+  
+public:
+  /** cur == ((pos 1 2 3) (id 12 3 4)...) */
+  LispReader(lisp_object_t* l);
+  ~LispReader();
+
+  bool read_int_vector(const char* name, std::vector<int>& vec);
+  bool read_int_vector(const char* name, std::vector<unsigned int>& vec);
+  bool read_char_vector(const char* name, std::vector<char>& vec);
+  bool read_string_vector(const char* name, std::vector<std::string>& vec);
+  bool read_string(const char* name, std::string& str, bool translatable = false);
+  bool read_int(const char* name, int& i);
+  bool read_float(const char* name, float& f);
+  bool read_bool(const char* name, bool& b);
+  bool read_lisp(const char* name, lisp_object_t*& b);
+  lisp_object_t* read_lisp(const char* name);
+
+  static LispReader* load(const std::string& filename,
+      const std::string& toplevellist);
+
+  lisp_object_t* get_lisp();
+};
+
+#endif /*SUPERTUX_LISPREADER_H*/
+
diff --git a/lib/utils/lispwriter.cpp b/lib/utils/lispwriter.cpp
new file mode 100644 (file)
index 0000000..6a08cab
--- /dev/null
@@ -0,0 +1,128 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <iostream>
+
+#include "utils/lispwriter.h"
+
+LispWriter::LispWriter(std::ostream& newout)
+  : out(newout), indent_depth(0)
+{
+}
+
+LispWriter::~LispWriter()
+{
+  if(lists.size() > 0) {
+    std::cerr << "Warning: Not all sections closed in lispwriter!\n";
+  }
+}
+
+void
+LispWriter::write_comment(const std::string& comment)
+{
+  out << "; " << comment << "\n";
+}
+
+void
+LispWriter::start_list(const std::string& listname)
+{
+  indent();
+  out << '(' << listname << '\n';
+  indent_depth += 2;
+
+  lists.push_back(listname);
+}
+
+void
+LispWriter::end_list(const std::string& listname)
+{
+  if(lists.size() == 0) {
+    std::cerr << "Trying to close list '" << listname 
+              << "', which is not open.\n";
+    return;
+  }
+  if(lists.back() != listname) {
+    std::cerr << "Warning: trying to close list '" << listname 
+              << "' while list '" << lists.back() << "' is open.\n";
+    return;
+  }
+  lists.pop_back();
+  
+  indent_depth -= 2;
+  indent();
+  out << ")\n";
+}
+
+void
+LispWriter::write_int(const std::string& name, int value)
+{
+  indent();
+  out << '(' << name << ' ' << value << ")\n";
+}
+
+void
+LispWriter::write_float(const std::string& name, float value)
+{
+  indent();
+  out << '(' << name << ' ' << value << ")\n";
+}
+
+void
+LispWriter::write_string(const std::string& name, const std::string& value)
+{
+  indent();
+  out << '(' << name << " \"" << value << "\")\n";
+}
+
+void
+LispWriter::write_bool(const std::string& name, bool value)
+{
+  indent();
+  out << '(' << name << ' ' << (value ? "#t" : "#f") << ")\n";
+}
+
+void
+LispWriter::write_int_vector(const std::string& name,
+    const std::vector<int>& value)
+{
+  indent();
+  out << '(' << name;
+  for(std::vector<int>::const_iterator i = value.begin(); i != value.end(); ++i)
+    out << " " << *i;
+  out << ")\n";
+}
+
+void
+LispWriter::write_int_vector(const std::string& name,
+    const std::vector<unsigned int>& value)
+{
+  indent();
+  out << '(' << name;
+  for(std::vector<unsigned int>::const_iterator i = value.begin(); i != value.end(); ++i)
+    out << " " << *i;
+  out << ")\n";
+}
+
+void
+LispWriter::indent()
+{
+  for(int i = 0; i<indent_depth; ++i)
+    out << ' ';
+}
+
diff --git a/lib/utils/lispwriter.h b/lib/utils/lispwriter.h
new file mode 100644 (file)
index 0000000..e7f4c4c
--- /dev/null
@@ -0,0 +1,56 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_LISPWRITER_H
+#define SUPERTUX_LISPWRITER_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+class LispWriter
+{
+public:
+  LispWriter(std::ostream& out);
+  ~LispWriter();
+
+  void write_comment(const std::string& comment);
+  
+  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);
+  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);
+
+private:
+  void indent();
+    
+  std::ostream& out;
+  int indent_depth;
+  std::vector<std::string> lists;
+};
+
+#endif
+
diff --git a/lib/video/drawing_context.cpp b/lib/video/drawing_context.cpp
new file mode 100644 (file)
index 0000000..846e38c
--- /dev/null
@@ -0,0 +1,353 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+
+#include "video/drawing_context.h"
+#include "video/surface.h"
+#include "app/globals.h"
+#include "video/font.h"
+
+DrawingContext::DrawingContext()
+{
+transform.draw_effect = NONE_EFFECT;
+}
+
+DrawingContext::~DrawingContext()
+{
+}
+
+void
+DrawingContext::draw_surface(const Surface* surface, const Vector& position,
+    int layer, Uint32 drawing_effect)
+{
+  assert(surface != 0);
+  
+  DrawingRequest request;
+
+  request.type = SURFACE;
+  request.layer = layer;
+  request.request_data = const_cast<Surface*> (surface);
+  request.pos = transform.apply(position);
+  request.drawing_effect = drawing_effect;
+  request.drawing_effect = transform.draw_effect | drawing_effect;
+
+  drawingrequests.push_back(request);
+}
+
+void
+DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
+    const Vector& size, const Vector& dest, int layer, Uint32 drawing_effect)
+{
+  assert(surface != 0);
+
+  DrawingRequest request;
+
+  request.type = SURFACE_PART;
+  request.layer = layer;
+  request.pos = transform.apply(dest);
+  request.drawing_effect = drawing_effect;
+  
+  SurfacePartRequest* surfacepartrequest = new SurfacePartRequest();
+  surfacepartrequest->size = size;
+  surfacepartrequest->source = source;
+  surfacepartrequest->surface = surface;
+  request.request_data = surfacepartrequest;
+
+  drawingrequests.push_back(request);
+}
+
+void
+DrawingContext::draw_text(Font* font, const std::string& text,
+    const Vector& position, int layer, Uint32 drawing_effect)
+{
+  DrawingRequest request;
+
+  request.type = TEXT;
+  request.layer = layer;
+  request.pos = transform.apply(position);
+  request.drawing_effect = drawing_effect;
+
+  TextRequest* textrequest = new TextRequest;
+  textrequest->font = font;
+  textrequest->text = text;
+  request.request_data = textrequest;
+
+  drawingrequests.push_back(request);
+}
+
+void
+DrawingContext::draw_text_center(Font* font, const std::string& text,
+    const Vector& position, int layer, Uint32 drawing_effect)
+{
+  DrawingRequest request;
+
+  request.type = TEXT;
+  request.layer = layer;
+  request.pos = transform.apply(position) + Vector(screen->w/2 - 
+      font->get_text_width(text)/2, 0);
+  request.drawing_effect = drawing_effect;
+
+  TextRequest* textrequest = new TextRequest;
+  textrequest->font = font;
+  textrequest->text = text;
+  request.request_data = textrequest;
+
+  drawingrequests.push_back(request);
+}
+
+void
+DrawingContext::draw_gradient(Color top, Color bottom, int layer)
+{
+  DrawingRequest request;
+
+  request.type = GRADIENT;
+  request.layer = layer;
+  request.pos = Vector(0,0);
+
+  GradientRequest* gradientrequest = new GradientRequest;
+  gradientrequest->top = top;
+  gradientrequest->bottom = bottom;
+  request.request_data = gradientrequest;
+
+  drawingrequests.push_back(request);
+}
+
+void
+DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
+        Color color, int layer)
+{
+  DrawingRequest request;
+
+  request.type = FILLRECT;
+  request.layer = layer;
+  request.pos = transform.apply(topleft);
+
+  FillRectRequest* fillrectrequest = new FillRectRequest;
+  fillrectrequest->size = size;
+  fillrectrequest->color = color;
+  request.request_data = fillrectrequest;
+
+  drawingrequests.push_back(request);
+}
+
+void
+DrawingContext::draw_surface_part(DrawingRequest& request)
+{
+  SurfacePartRequest* surfacepartrequest
+    = (SurfacePartRequest*) request.request_data;
+
+  surfacepartrequest->surface->impl->draw_part(
+      surfacepartrequest->source.x, surfacepartrequest->source.y,
+      request.pos.x, request.pos.y,
+      surfacepartrequest->size.x, surfacepartrequest->size.y, 255,
+      request.drawing_effect);
+
+  delete surfacepartrequest;
+}
+
+void
+DrawingContext::draw_gradient(DrawingRequest& request)
+{
+  GradientRequest* gradientrequest = (GradientRequest*) request.request_data;
+  const Color& top = gradientrequest->top;
+  const Color& bottom = gradientrequest->bottom;
+  
+#ifndef NOOPENGL
+  if(use_gl)
+    {
+      glBegin(GL_QUADS);
+      glColor3ub(top.red, top.green, top.blue);
+      glVertex2f(0, 0);
+      glVertex2f(screen->w, 0);
+      glColor3ub(bottom.red, bottom.green, bottom.blue);
+      glVertex2f(screen->w, screen->h);
+      glVertex2f(0, screen->h);
+      glEnd();
+    }
+  else
+  {
+#endif
+    if(&top == &bottom)
+      {
+      fillrect(0, 0, screen->w, screen->h, top.red, top.green, top.blue);
+      }
+    else
+      {
+      float redstep = (float(bottom.red)-float(top.red)) / float(screen->h);
+      float greenstep = (float(bottom.green)-float(top.green)) / float(screen->h);
+      float bluestep = (float(bottom.blue) - float(top.blue)) / float(screen->h);
+
+      for(float y = 0; y < screen->h; y += 2)
+        fillrect(0, (int)y, screen->w, 2,
+            int(float(top.red) + redstep * y),
+            int(float(top.green) + greenstep * y),
+            int(float(top.blue) + bluestep * y), 255);
+      }
+#ifndef NOOPENGL
+
+    }
+#endif
+
+  delete gradientrequest;
+}
+
+void
+DrawingContext::draw_text(DrawingRequest& request)
+{
+  TextRequest* textrequest = (TextRequest*) request.request_data;
+  
+  textrequest->font->draw(textrequest->text, request.pos, request.drawing_effect);
+
+  delete textrequest;
+}
+
+void
+DrawingContext::draw_filled_rect(DrawingRequest& request)
+{
+  FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data;
+
+  float x = request.pos.x;
+  float y = request.pos.y;
+  float w = fillrectrequest->size.x;
+  float h = fillrectrequest->size.y;
+
+#ifndef NOOPENGL
+  if(use_gl)
+    {
+      glEnable(GL_BLEND);
+      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glColor4ub(fillrectrequest->color.red, fillrectrequest->color.green,
+          fillrectrequest->color.blue, fillrectrequest->color.alpha);
+
+      glBegin(GL_POLYGON);
+      glVertex2f(x, y);
+      glVertex2f(x+w, y);
+      glVertex2f(x+w, y+h);
+      glVertex2f(x, y+h);
+      glEnd();
+      glDisable(GL_BLEND);
+    }
+  else
+    {
+#endif
+      SDL_Rect src, rect;
+      SDL_Surface *temp = NULL;
+                                                                                
+      rect.x = (int)x;
+      rect.y = (int)y;
+      rect.w = (int)w;
+      rect.h = (int)h;
+                                                                                
+      if(fillrectrequest->color.alpha != 255)
+        {
+          temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
+                                      screen->format->Rmask,
+                                      screen->format->Gmask,
+                                      screen->format->Bmask,
+                                      screen->format->Amask);
+                                                                                
+                                                                                
+          src.x = 0;
+          src.y = 0;
+          src.w = rect.w;
+          src.h = rect.h;
+                                                                                
+          SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, 
+                fillrectrequest->color.red, fillrectrequest->color.green,
+                fillrectrequest->color.blue));
+                                                                                
+          SDL_SetAlpha(temp, SDL_SRCALPHA, fillrectrequest->color.alpha);
+                                                                                
+          SDL_BlitSurface(temp,0,screen,&rect);
+                                                                                
+          SDL_FreeSurface(temp);
+        }
+      else
+        SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 
+              fillrectrequest->color.red, fillrectrequest->color.green,
+              fillrectrequest->color.blue));
+                                                                                
+#ifndef NOOPENGL
+                                                                                
+    }
+#endif
+
+  delete fillrectrequest;
+}
+
+void
+DrawingContext::do_drawing()
+{
+  std::stable_sort(drawingrequests.begin(), drawingrequests.end());
+
+  for(DrawingRequests::iterator i = drawingrequests.begin();
+      i != drawingrequests.end(); ++i) {
+    switch(i->type) {
+      case SURFACE:
+      {
+        const Surface* surface = (const Surface*) i->request_data;
+        surface->impl->draw(i->pos.x, i->pos.y, 255, i->drawing_effect);
+        break;
+      }
+      case SURFACE_PART:
+        draw_surface_part(*i);
+        break;
+      case GRADIENT:
+        draw_gradient(*i);
+        break;
+      case TEXT:
+        draw_text(*i);
+        break;
+      case FILLRECT:
+        draw_filled_rect(*i);
+        break;
+    }
+  }
+
+  // update screen
+  if(use_gl)
+    SDL_GL_SwapBuffers();
+  else
+    SDL_Flip(screen);
+
+  drawingrequests.clear();
+}
+
+void
+DrawingContext::push_transform()
+{
+  transformstack.push_back(transform);
+}
+
+void
+DrawingContext::pop_transform()
+{
+  assert(!transformstack.empty());
+
+  transform = transformstack.back();
+  transformstack.pop_back();
+}
+
+void
+DrawingContext::set_drawing_effect(int effect)
+{
+  transform.draw_effect = effect;
+}
diff --git a/lib/video/drawing_context.h b/lib/video/drawing_context.h
new file mode 100644 (file)
index 0000000..b08bd6c
--- /dev/null
@@ -0,0 +1,163 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2004 Matthias Braun <matze@braunis.de
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#ifndef SUPERTUX_DRAWINGCONTEXT_H
+#define SUPERTUX_DRAWINGCONTEXT_H
+
+#include <vector>
+#include <string>
+
+#include "SDL.h"
+
+#include "math/vector.h"
+#include "video/screen.h"
+
+class Surface;
+class Font;
+
+// some constants for predefined layer values
+enum {
+  LAYER_BACKGROUND0 = -300,
+  LAYER_BACKGROUND1 = -200,
+  LAYER_BACKGROUNDTILES = -100,
+  LAYER_TILES = 0,
+  LAYER_OBJECTS = 100,
+  LAYER_FOREGROUNDTILES = 200,
+  LAYER_FOREGROUND0 = 300,
+  LAYER_FOREGROUND1 = 400,
+  LAYER_GUI         = 500
+};
+
+/**
+ * This class provides functions for drawing things on screen. It also
+ * maintains a stack of transforms that are applied to graphics.
+ */
+class DrawingContext
+{
+public:
+  DrawingContext();
+  ~DrawingContext();
+
+  /** Adds a drawing request for a surface into the request list */
+  void draw_surface(const Surface* surface, const Vector& position, int layer,
+      Uint32 drawing_effect = NONE_EFFECT);
+  /** Adds a drawing request for part of a surface */
+  void draw_surface_part(const Surface* surface, const Vector& source,
+      const Vector& size, const Vector& dest, int layer,
+      Uint32 drawing_effect = NONE_EFFECT);
+  /** draws a text */
+  void draw_text(Font* font, const std::string& text, const Vector& position,
+      int layer, Uint32 drawing_effect = NONE_EFFECT);
+  /** draws aligned text */
+  void draw_text_center(Font* font, const std::string& text,
+      const Vector& position, int layer, Uint32 drawing_effect = NONE_EFFECT);
+  /** draws a color gradient onto the whole screen */  
+  void draw_gradient(Color from, Color to, int layer);
+  /** fills a rectangle */
+  void draw_filled_rect(const Vector& topleft, const Vector& size,
+          Color color, int layer);
+  
+  /** Processes all pending drawing requests and flushes the list */
+  void do_drawing();
+
+  const Vector& get_translation() const
+  { return transform.translation; }
+  void set_translation(const Vector& newtranslation)
+  { transform.translation = newtranslation; }
+
+  void push_transform();
+  void pop_transform();
+
+  /** apply that effect in the next draws (effects are listed on surface.h) */
+  void set_drawing_effect(int effect);
+
+private:
+  class Transform
+  {
+  public:
+    Vector translation; // only translation for now...
+
+    Vector apply(const Vector& v) const
+    {
+      return v - translation;
+    }
+
+    int draw_effect;
+  };
+
+  /// the transform stack
+  std::vector<Transform> transformstack;
+  /// the currently active transform
+  Transform transform;
+
+  enum RequestType
+  {
+    SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT
+  };
+
+  struct SurfacePartRequest
+  {
+    const Surface* surface;
+    Vector source, size;
+  };
+
+  struct TextRequest
+  {
+    Font* font;
+    std::string text;
+  };
+
+  struct GradientRequest
+  {
+    Color top, bottom;
+    Vector size;
+  };
+
+  struct FillRectRequest
+  {
+    Color color;
+    Vector size;
+  };
+
+  struct DrawingRequest
+  {
+    int layer;
+    Uint32 drawing_effect;
+
+    RequestType type;
+    Vector pos;
+
+    void* request_data;
+
+    bool operator<(const DrawingRequest& other) const
+    {
+      return layer < other.layer;
+    }
+  };
+
+  void draw_surface_part(DrawingRequest& request);
+  void draw_text(DrawingRequest& request);
+  void draw_gradient(DrawingRequest& request);
+  void draw_filled_rect(DrawingRequest& request);
+  
+  typedef std::vector<DrawingRequest> DrawingRequests;
+  DrawingRequests drawingrequests;
+};
+
+#endif /*SUPERTUX_DRAWINGCONTEXT_H*/
+
diff --git a/lib/video/font.cpp b/lib/video/font.cpp
new file mode 100644 (file)
index 0000000..831f406
--- /dev/null
@@ -0,0 +1,267 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <cstdlib>
+#include <cstring>
+
+#include "app/globals.h"
+#include "video/screen.h"
+#include "video/font.h"
+#include "video/drawing_context.h"
+#include "utils/lispreader.h"
+
+Font::Font(const std::string& file, FontType ntype, int nw, int nh,
+        int nshadowsize)
+    : chars(0), shadow_chars(0), type(ntype), w(nw), h(nh),
+      shadowsize(nshadowsize)
+{
+  chars = new Surface(file, true);
+  switch(type) {
+    case TEXT:
+      first_char = 32;
+      break;
+    case NUM:
+      first_char = 48;
+      break;
+  }
+  last_char = first_char + (chars->h / h) * 16;
+  if(last_char > 127) // we have left out some control chars at 128-159
+    last_char += 32;
+   
+  // Load shadow font.
+  if(shadowsize > 0) {
+    SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface());
+    int pixels = conv->w * conv->h;
+    SDL_LockSurface(conv);
+    for(int i = 0; i < pixels; ++i) {
+      Uint32 *p = (Uint32 *)conv->pixels + i;
+      *p = *p & conv->format->Amask;
+    }
+    SDL_UnlockSurface(conv);
+    SDL_SetAlpha(conv, SDL_SRCALPHA, 128);
+    shadow_chars = new Surface(conv, true);
+    SDL_FreeSurface(conv);
+  }
+}
+
+Font::~Font()
+{
+  delete chars;
+  delete shadow_chars;
+}
+
+float
+Font::get_height() const
+{
+  return h;
+}
+
+float
+Font::get_text_width(const std::string& text) const
+{
+  return text.size() * w;
+}
+
+void
+Font::draw(const std::string& text, const Vector& pos, Uint32 drawing_effect)
+{
+  if(shadowsize > 0)
+    draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
+               drawing_effect);
+
+  draw_chars(chars, text, pos, drawing_effect);
+}
+
+void
+Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
+                 Uint32 drawing_effect)
+{
+  SurfaceImpl* impl = pchars->impl;
+
+  Vector p = pos;
+  for(size_t i = 0; i < text.size(); ++i)
+  {
+    int c = (unsigned char) text[i];
+    if(c > 127) // correct for the 32 controlchars at 128-159
+      c -= 32;
+    // a non-printable character?
+    if(c == '\n') {
+      p.x = pos.x;
+      p.y += h + 2;
+      continue;
+    }
+    if(c == ' ' || c < first_char || c > last_char) {
+      p.x += w;
+      continue;
+    }
+    
+    int index = c - first_char;
+    int source_x = (index % 16) * w;
+    int source_y = (index / 16) * h;
+
+    impl->draw_part(source_x, source_y, p.x, p.y, w, h, 255, drawing_effect);
+    p.x += w;
+  }
+}
+
+/* --- SCROLL TEXT FUNCTION --- */
+
+#define MAX_VEL     10
+#define SPEED_INC   0.01
+#define SCROLL      60
+#define ITEMS_SPACE 4
+
+void display_text_file(const std::string& file, float scroll_speed)
+{
+  std::string text;
+  std::vector<std::string> names;
+
+  LispReader* reader = LispReader::load(datadir + "/" + file, "supertux-text");
+
+  if(!reader)
+    {
+    std::cerr << "Error: Could not open text. Ignoring...\n";
+    return;
+    }
+
+  reader->read_string("text", text, true);
+  std::string background_file;
+  reader->read_string("background", background_file, true);
+  delete reader;
+
+  // Split text string lines into a vector
+  names.clear();
+  unsigned int i, l;
+  i = 0;
+  while(true)
+    {
+    l = text.find("\n", i);
+
+    if(l == std::string::npos)
+      {
+      char temp[1024];
+      temp[text.copy(temp, text.size() - i, i)] = '\0';
+      names.push_back(temp);
+      break;
+      }
+
+    char temp[1024];
+    temp[text.copy(temp, l-i, i)] = '\0';
+    names.push_back(temp);
+
+    i = l+1;
+    }
+
+  // load background image
+  Surface* background = new Surface(datadir + "/images/background/" + background_file, false);
+
+  int done = 0;
+  float scroll = 0;
+  float speed = scroll_speed / 50;
+
+  DrawingContext context;
+  SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+
+  Uint32 lastticks = SDL_GetTicks();
+  while(!done)
+    {
+      /* in case of input, exit */
+      SDL_Event event;
+      while(SDL_PollEvent(&event))
+        switch(event.type)
+          {
+          case SDL_KEYDOWN:
+            switch(event.key.keysym.sym)
+              {
+              case SDLK_UP:
+                speed -= SPEED_INC;
+                break;
+              case SDLK_DOWN:
+                speed += SPEED_INC;
+                break;
+              case SDLK_SPACE:
+              case SDLK_RETURN:
+                if(speed >= 0)
+                  scroll += SCROLL;
+                break;
+              case SDLK_ESCAPE:
+                done = 1;
+                break;
+              default:
+                break;
+              }
+            break;
+          case SDL_QUIT:
+            done = 1;
+            break;
+          default:
+            break;
+          }
+
+      if(speed > MAX_VEL)
+        speed = MAX_VEL;
+      else if(speed < -MAX_VEL)
+        speed = -MAX_VEL;
+
+      /* draw the credits */
+      context.draw_surface(background, Vector(0,0), 0);
+
+      float y = 0;
+      for(size_t i = 0; i < names.size(); i++) {
+        if(names[i].size() == 0) {
+          y += white_text->get_height() + ITEMS_SPACE;
+          continue;
+        }
+
+        Font* font = 0;
+        switch(names[i][0])
+        {
+          case ' ': font = white_small_text; break;
+          case '\t': font = white_text; break;
+          case '-': font = white_big_text; break;
+          case '*': font = blue_text; break;
+          default: font = blue_text; break;
+        }
+
+        context.draw_text_center(font,
+            names[i].substr(1, names[i].size()-1),
+            Vector(0, screen->h + y - scroll), LAYER_FOREGROUND1);
+        y += font->get_height() + ITEMS_SPACE;
+      }
+
+      context.do_drawing();
+
+      if(screen->h+y-scroll < 0 && 20+screen->h+y-scroll < 0)
+        done = 1;
+
+      Uint32 ticks = SDL_GetTicks();
+      scroll += speed * (ticks - lastticks);
+      lastticks = ticks;
+      if(scroll < 0)
+        scroll = 0;
+
+      SDL_Delay(10);
+    }
+
+  SDL_EnableKeyRepeat(0, 0);    // disables key repeating
+  delete background;
+}
+
diff --git a/lib/video/font.h b/lib/video/font.h
new file mode 100644 (file)
index 0000000..4b2617f
--- /dev/null
@@ -0,0 +1,76 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_FONT_H
+#define SUPERTUX_FONT_H
+
+#include <string>
+
+#include "video/surface.h"
+#include "math/vector.h"
+
+/** Reads a text file (using LispReader, so it as to be in its formatting)
+    and displays it in a StarTrek fashion */
+void display_text_file(const std::string& file, float scroll_speed);
+
+/* Text type */
+class Font
+{
+public:
+  /* Kinds of texts. */
+  enum FontType {
+    TEXT, // images for all characters
+    NUM   // only images for numbers
+  };
+  
+  Font(const std::string& file, FontType type, int w, int h, int shadowsize=2);
+  ~Font();
+
+  /** returns the height of the font */
+  float get_height() const;
+  /** returns the width of a given text. (Note that I won't add a normal
+   * get_width function here, as we might switch to variable width fonts in the
+   * future.
+   */
+  float get_text_width(const std::string& text) const;
+
+private:
+  friend class DrawingContext;
+  
+  void draw(const std::string& text, const Vector& pos,
+            Uint32 drawing_effect = NONE_EFFECT);
+  void draw_chars(Surface* pchars, const std::string& text, 
+      const Vector& position, Uint32 drawing_effect);
+
+  Surface* chars;
+  Surface* shadow_chars;
+  FontType type;
+  int w;
+  int h;
+  int shadowsize;
+
+  /// the number of the first character that is represented in the font
+  int first_char;
+  /// the number of the last character that is represented in the font
+  int last_char;
+};
+
+#endif /*SUPERTUX_FONT_H*/
+
diff --git a/lib/video/screen.cpp b/lib/video/screen.cpp
new file mode 100644 (file)
index 0000000..b123cec
--- /dev/null
@@ -0,0 +1,299 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include <iostream>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+
+#include <unistd.h>
+
+#include "SDL.h"
+#include "SDL_image.h"
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <ctype.h>
+#endif
+
+#include "app/globals.h"
+#include "video/screen.h"
+#include "video/drawing_context.h"
+#include "special/base.h"
+
+/* 'Stolen' from the SDL documentation.
+ * Set the pixel at (x, y) to the given value
+ * NOTE: The surface must be locked before calling this!
+ */
+void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+  int bpp = surface->format->BytesPerPixel;
+  /* Here p is the address to the pixel we want to set */
+  Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+
+  switch(bpp)
+    {
+    case 1:
+      *p = pixel;
+      break;
+
+    case 2:
+      *(Uint16 *)p = pixel;
+      break;
+
+    case 3:
+      if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
+        {
+          p[0] = (pixel >> 16) & 0xff;
+          p[1] = (pixel >> 8) & 0xff;
+          p[2] = pixel & 0xff;
+        }
+      else
+        {
+          p[0] = pixel & 0xff;
+          p[1] = (pixel >> 8) & 0xff;
+          p[2] = (pixel >> 16) & 0xff;
+        }
+      break;
+
+    case 4:
+      *(Uint32 *)p = pixel;
+      break;
+    }
+}
+
+/* Draw a single pixel on the screen. */
+void drawpixel(int x, int y, Uint32 pixel)
+{
+  /* Lock the screen for direct access to the pixels */
+  if ( SDL_MUSTLOCK(screen) )
+    {
+      if ( SDL_LockSurface(screen) < 0 )
+        {
+          fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
+          return;
+        }
+    }
+
+  if(!(x < 0 || y < 0 || x > screen->w || y > screen->h))
+    putpixel(screen, x, y, pixel);
+
+  if ( SDL_MUSTLOCK(screen) )
+    {
+      SDL_UnlockSurface(screen);
+    }
+  /* Update just the part of the display that we've changed */
+  SDL_UpdateRect(screen, x, y, 1, 1);
+}
+
+/* --- FILL A RECT --- */
+
+void fillrect(float x, float y, float w, float h, int r, int g, int b, int a)
+{
+if(w < 0)
+       {
+       x += w;
+       w = -w;
+       }
+if(h < 0)
+       {
+       y += h;
+       h = -h;
+       }
+
+#ifndef NOOPENGL
+  if(use_gl)
+    {
+      glEnable(GL_BLEND);
+      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glColor4ub(r, g, b,a);
+
+      glBegin(GL_POLYGON);
+      glVertex2f(x, y);
+      glVertex2f(x+w, y);
+      glVertex2f(x+w, y+h);
+      glVertex2f(x, y+h);
+      glEnd();
+      glDisable(GL_BLEND);
+    }
+  else
+    {
+#endif
+      SDL_Rect src, rect;
+      SDL_Surface *temp = NULL;
+
+      rect.x = (int)x;
+      rect.y = (int)y;
+      rect.w = (int)w;
+      rect.h = (int)h;
+
+      if(a != 255)
+        {
+          temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
+                                      screen->format->Rmask,
+                                      screen->format->Gmask,
+                                      screen->format->Bmask,
+                                      screen->format->Amask);
+
+
+          src.x = 0;
+          src.y = 0;
+          src.w = rect.w;
+          src.h = rect.h;
+
+          SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b));
+
+          SDL_SetAlpha(temp, SDL_SRCALPHA, a);
+
+          SDL_BlitSurface(temp,0,screen,&rect);
+
+          SDL_FreeSurface(temp);
+        }
+      else
+        SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
+
+#ifndef NOOPENGL
+
+    }
+#endif
+}
+
+/* Needed for line calculations */
+#define SGN(x) ((x)>0 ? 1 : ((x)==0 ? 0:(-1)))
+#define ABS(x) ((x)>0 ? (x) : (-x))
+
+void
+draw_line(float x1, float y1, float x2, float y2, int r, int g, int b, int a)
+{
+#ifndef NOOPENGL
+  if(use_gl)
+    {
+      glEnable(GL_BLEND);
+      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glColor4ub(r, g, b,a);
+
+      glBegin(GL_LINES);
+      glVertex2f(x1, y1);
+      glVertex2f(x2, y2);
+      glEnd();
+      glDisable(GL_BLEND);
+    }
+  else
+    {
+#endif
+      /* Basic unantialiased Bresenham line algorithm */
+      int lg_delta, sh_delta, cycle, lg_step, sh_step;
+      Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a);
+
+      lg_delta = (int)(x2 - x1);
+      sh_delta = (int)(y2 - y1);
+      lg_step = SGN(lg_delta);
+      lg_delta = ABS(lg_delta);
+      sh_step = SGN(sh_delta);
+      sh_delta = ABS(sh_delta);
+      if (sh_delta < lg_delta)
+        {
+          cycle = lg_delta >> 1;
+          while (x1 != x2)
+            {
+              drawpixel((int)x1, (int)y1, color);
+              cycle += sh_delta;
+              if (cycle > lg_delta)
+                {
+                  cycle -= lg_delta;
+                  y1 += sh_step;
+                }
+              x1 += lg_step;
+            }
+          drawpixel((int)x1, (int)y1, color);
+        }
+      cycle = sh_delta >> 1;
+      while (y1 != y2)
+        {
+          drawpixel((int)x1, (int)y1, color);
+          cycle += lg_delta;
+          if (cycle > sh_delta)
+            {
+              cycle -= sh_delta;
+              x1 += lg_step;
+            }
+          y1 += sh_step;
+        }
+      drawpixel((int)x1, (int)y1, color);
+#ifndef NOOPENGL
+
+    }
+#endif
+}
+
+#define LOOP_DELAY 20.0
+
+void fadeout(int fade_time)
+{
+  float alpha_inc  = 256 / (fade_time / LOOP_DELAY);
+  float alpha = 256;
+
+  while(alpha > 0)
+    {
+    alpha -= alpha_inc;
+    fillrect(0, 0, screen->w, screen->h, 0,0,0, (int)alpha_inc);  // left side
+                                                   
+    DrawingContext context; // ugly...
+    context.do_drawing();
+
+    SDL_Delay(int(LOOP_DELAY));
+    }
+
+  fillrect(0, 0, screen->w, screen->h, 0, 0, 0, 255);
+  
+  DrawingContext context;
+  context.draw_text_center(white_text, "Loading...",
+      Vector(0, screen->h/2), LAYER_FOREGROUND1);
+  context.do_drawing();
+}
+
+void shrink_fade(const Vector& point, int fade_time)
+{
+  float left_inc  = point.x / ((float)fade_time / LOOP_DELAY);
+  float right_inc = (screen->w - point.x) / ((float)fade_time / LOOP_DELAY);
+  float up_inc    = point.y / ((float)fade_time / LOOP_DELAY);
+  float down_inc  = (screen->h - point.y) / ((float)fade_time / LOOP_DELAY);
+                                                                                
+  float left_cor = 0, right_cor = 0, up_cor = 0, down_cor = 0;
+                                                                                
+  while(left_cor < point.x && right_cor < screen->w - point.x &&
+      up_cor < point.y && down_cor < screen->h - point.y)
+  {
+    left_cor  += left_inc;
+    right_cor += right_inc;
+    up_cor    += up_inc;
+    down_cor  += down_inc;
+                                                                                
+    fillrect(0, 0, left_cor, screen->h, 0,0,0);  // left side
+    fillrect(screen->w - right_cor, 0, right_cor, screen->h, 0,0,0);  // right side
+    fillrect(0, 0, screen->w, up_cor, 0,0,0);  // up side
+    fillrect(0, screen->h - down_cor, screen->w, down_cor+1, 0,0,0);  // down side                                                                                
+    DrawingContext context; // ugly...
+    context.do_drawing();
+    
+    SDL_Delay(int(LOOP_DELAY));
+  }
+}
+
diff --git a/lib/video/screen.h b/lib/video/screen.h
new file mode 100644 (file)
index 0000000..263bdae
--- /dev/null
@@ -0,0 +1,64 @@
+//  $Id$
+//
+//  SuperTux -  A Jump'n Run
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef SUPERTUX_SCREEN_H
+#define SUPERTUX_SCREEN_H
+
+#include <SDL.h>
+#ifndef NOOPENGL
+#include <SDL_opengl.h>
+#endif
+#include <iostream>
+class Color
+{
+public:
+  Color() 
+    : red(0), green(0), blue(0), alpha(255)
+  {}
+  
+  Color(Uint8 red_, Uint8 green_, Uint8 blue_, Uint8 alpha_ = 255)
+    : red(red_), green(green_), blue(blue_), alpha(alpha_)
+  {}
+
+  Color(const Color& o)
+    : red(o.red), green(o.green), blue(o.blue), alpha(o.alpha)
+  { }
+
+  bool operator==(const Color& o)
+    {  if(red == o.red && green == o.green &&
+          blue == o.blue && alpha == o.alpha)
+         return true;
+       return false;  }
+
+  Uint8 red, green, blue, alpha;
+};
+
+#include "video/surface.h"
+
+class Vector;
+
+void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel);
+void drawpixel(int x, int y, Uint32 pixel);
+void fillrect(float x, float y, float w, float h, int r, int g, int b, int a = 255);
+void draw_line(float x1, float y1, float x2, int r, int g, int b, int a = 255);
+
+void fadeout(int fade_time);
+void shrink_fade(const Vector& point, int fade_time);
+
+#endif /*SUPERTUX_SCREEN_H*/
diff --git a/lib/video/surface.cpp b/lib/video/surface.cpp
new file mode 100644 (file)
index 0000000..e423aa3
--- /dev/null
@@ -0,0 +1,877 @@
+//  $Id$
+//
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <cassert>
+#include <iostream>
+#include <algorithm>
+
+#include "SDL.h"
+#include "SDL_image.h"
+
+#include "video/surface.h"
+#include "app/globals.h"
+#include "app/setup.h"
+
+Surface::Surfaces Surface::surfaces;
+
+SurfaceData::SurfaceData(SDL_Surface* temp, bool use_alpha_)
+    : type(SURFACE), surface(0), use_alpha(use_alpha_)
+{
+  // Copy the given surface and make sure that it is not stored in
+  // video memory
+  surface = SDL_CreateRGBSurface(temp->flags & (~SDL_HWSURFACE),
+                                 temp->w, temp->h,
+                                 temp->format->BitsPerPixel,
+                                 temp->format->Rmask,
+                                 temp->format->Gmask,
+                                 temp->format->Bmask,
+                                 temp->format->Amask);
+  if(!surface)
+    st_abort("No memory left.", "");
+  SDL_SetAlpha(temp,0,0);
+  SDL_BlitSurface(temp, NULL, surface, NULL);
+}
+
+SurfaceData::SurfaceData(const std::string& file_, bool use_alpha_)
+    : type(LOAD), surface(0), file(file_), use_alpha(use_alpha_)
+{}
+
+SurfaceData::SurfaceData(const std::string& file_, int x_, int y_, int w_, int h_, bool use_alpha_)
+    : type(LOAD_PART), surface(0), file(file_), use_alpha(use_alpha_),
+    x(x_), y(y_), w(w_), h(h_)
+{}
+
+SurfaceData::SurfaceData(Color top_gradient_, Color bottom_gradient_, int w_, int h_)
+    : type(GRADIENT), surface(0), use_alpha(false), w(w_), h(h_)
+{
+top_gradient = top_gradient_;
+bottom_gradient = bottom_gradient_;
+}
+
+
+SurfaceData::~SurfaceData()
+{
+  SDL_FreeSurface(surface);
+}
+
+SurfaceImpl*
+SurfaceData::create()
+{
+#ifndef NOOPENGL
+  if (use_gl)
+    return create_SurfaceOpenGL();
+  else
+    return create_SurfaceSDL();
+#else
+  return create_SurfaceSDL();
+#endif
+}
+
+SurfaceSDL*
+SurfaceData::create_SurfaceSDL()
+{
+  switch(type)
+  {
+  case LOAD:
+    return new SurfaceSDL(file, use_alpha);
+  case LOAD_PART:
+    return new SurfaceSDL(file, x, y, w, h, use_alpha);
+  case SURFACE:
+    return new SurfaceSDL(surface, use_alpha);
+  case GRADIENT:
+    return new SurfaceSDL(top_gradient, bottom_gradient, w, h);
+  }
+  assert(0);
+}
+
+SurfaceOpenGL*
+SurfaceData::create_SurfaceOpenGL()
+{
+#ifndef NOOPENGL
+  switch(type)
+  {
+  case LOAD:
+    return new SurfaceOpenGL(file, use_alpha);
+  case LOAD_PART:
+    return new SurfaceOpenGL(file, x, y, w, h, use_alpha);
+  case SURFACE:
+    return new SurfaceOpenGL(surface, use_alpha);
+  case GRADIENT:
+    return new SurfaceOpenGL(top_gradient, bottom_gradient, w, h);
+  }
+#endif
+  assert(0);
+}
+
+#ifndef NOOPENGL
+/* Quick utility function for texture creation */
+static int power_of_two(int input)
+{
+  int value = 1;
+
+  while ( value < input )
+  {
+    value <<= 1;
+  }
+  return value;
+}
+#endif
+
+Surface::Surface(SDL_Surface* surf, bool use_alpha)
+    : data(surf, use_alpha), w(0), h(0)
+{
+  impl = data.create();
+  if (impl)
+  {
+    w = impl->w;
+    h = impl->h;
+  }
+  surfaces.push_back(this);
+}
+
+Surface::Surface(const std::string& file, bool use_alpha)
+    : data(file, use_alpha), w(0), h(0)
+{
+  impl = data.create();
+  if (impl)
+  {
+    w = impl->w;
+    h = impl->h;
+  }
+  surfaces.push_back(this);
+}
+
+Surface::Surface(const std::string& file, int x, int y, int w, int h, bool use_alpha)
+    : data(file, x, y, w, h, use_alpha), w(0), h(0)
+{
+  impl = data.create();
+  if (impl)
+  {
+    w = impl->w;
+    h = impl->h;
+  }
+  surfaces.push_back(this);
+}
+
+Surface::Surface(Color top_background, Color bottom_background, int w_, int h_)
+    : data(top_background, bottom_background, 0, 0), w(0), h(0)
+{
+  // FIXME: Gradient surfaces currently don't accept width/height
+  //        If nonzero values are passed to data.create(), supertux
+  //        crashes.
+  impl = data.create();
+  if (impl)
+  {
+    w = impl->w;
+    h = impl->h;
+  }
+  surfaces.push_back(this);
+}
+
+void
+Surface::reload()
+{
+  delete impl;
+  impl = data.create();
+  if (impl)
+  {
+    w = impl->w;
+    h = impl->h;
+  }
+}
+
+Surface::~Surface()
+{
+#ifdef DEBUG
+  bool found = false;
+  for(std::list<Surface*>::iterator i = surfaces.begin(); i != surfaces.end();
+      ++i)
+  {
+    if(*i == this)
+    {
+      found = true; break;
+    }
+  }
+  if(!found)
+    printf("Error: Surface freed twice!!!\n");
+#endif
+  surfaces.remove(this);
+  delete impl;
+}
+
+void
+Surface::reload_all()
+{
+  for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
+  {
+    (*i)->reload();
+  }
+}
+
+void
+Surface::debug_check()
+{
+  for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
+  {
+    printf("Surface not freed: T:%d F:%s.\n", (*i)->data.type,
+           (*i)->data.file.c_str());
+  }
+}
+
+void
+Surface::resize(int w_, int h_)
+{
+  if (impl)
+  {
+    w = w_;
+    h = h_;
+    if (impl->resize(w_,h_) == -2)
+      reload();
+  }
+}
+
+SDL_Surface*
+sdl_surface_part_from_file(const std::string& file, int x, int y, int w, int h,  bool use_alpha)
+{
+  SDL_Rect src;
+  SDL_Surface * sdl_surface;
+  SDL_Surface * temp;
+  SDL_Surface * conv;
+
+  temp = IMG_Load(file.c_str());
+
+  if (temp == NULL)
+    st_abort("Can't load", file);
+
+  /* Set source rectangle for conv: */
+
+  src.x = x;
+  src.y = y;
+  src.w = w;
+  src.h = h;
+
+  conv = SDL_CreateRGBSurface(temp->flags, w, h, temp->format->BitsPerPixel,
+                              temp->format->Rmask,
+                              temp->format->Gmask,
+                              temp->format->Bmask,
+                              temp->format->Amask);
+
+  /* #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+     0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+     #else
+
+     0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+     #endif*/
+
+  SDL_SetAlpha(temp,0,0);
+
+  SDL_BlitSurface(temp, &src, conv, NULL);
+  if(use_alpha == false && !use_gl)
+    sdl_surface = SDL_DisplayFormat(conv);
+  else
+    sdl_surface = SDL_DisplayFormatAlpha(conv);
+
+  if (sdl_surface == NULL)
+    st_abort("Can't covert to display format", file);
+
+  if (use_alpha == false && !use_gl)
+    SDL_SetAlpha(sdl_surface, 0, 0);
+
+  SDL_FreeSurface(temp);
+  SDL_FreeSurface(conv);
+
+  return sdl_surface;
+}
+
+SDL_Surface*
+sdl_surface_from_file(const std::string& file, bool use_alpha)
+{
+  SDL_Surface* sdl_surface;
+  SDL_Surface* temp;
+
+  temp = IMG_Load(file.c_str());
+
+  if (temp == NULL)
+    st_abort("Can't load", file);
+
+  if(use_alpha == false && !use_gl)
+    sdl_surface = SDL_DisplayFormat(temp);
+  else
+    sdl_surface = SDL_DisplayFormatAlpha(temp);
+
+  if (sdl_surface == NULL)
+    st_abort("Can't covert to display format", file);
+
+  if (use_alpha == false && !use_gl)
+    SDL_SetAlpha(sdl_surface, 0, 0);
+
+  SDL_FreeSurface(temp);
+
+  return sdl_surface;
+}
+
+SDL_Surface*
+sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, bool use_alpha)
+{
+  SDL_Surface* sdl_surface;
+  Uint32 saved_flags;
+  Uint8  saved_alpha;
+
+  /* Save the alpha blending attributes */
+  saved_flags = sdl_surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
+  saved_alpha = sdl_surf->format->alpha;
+  if ( (saved_flags & SDL_SRCALPHA)
+       == SDL_SRCALPHA )
+  {
+    SDL_SetAlpha(sdl_surf, 0, 0);
+  }
+
+  if(use_alpha == false && !use_gl)
+    sdl_surface = SDL_DisplayFormat(sdl_surf);
+  else
+    sdl_surface = SDL_DisplayFormatAlpha(sdl_surf);
+
+  /* Restore the alpha blending attributes */
+  if ( (saved_flags & SDL_SRCALPHA)
+       == SDL_SRCALPHA )
+  {
+    SDL_SetAlpha(sdl_surface, saved_flags, saved_alpha);
+  }
+
+  if (sdl_surface == NULL)
+    st_abort("Can't covert to display format", "SURFACE");
+
+  if (use_alpha == false && !use_gl)
+    SDL_SetAlpha(sdl_surface, 0, 0);
+
+  return sdl_surface;
+}
+
+SDL_Surface*
+sdl_surface_from_gradient(Color top, Color bottom, int w, int h)
+{
+  SDL_Surface* sdl_surface;
+
+  sdl_surface = SDL_CreateRGBSurface(screen->flags, w, h,
+                    screen->format->BitsPerPixel, screen->format->Rmask,
+                    screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
+
+  if(sdl_surface == NULL)
+      st_abort("Cannot create surface for the gradient", "SURFACE");
+
+  if(top == bottom)
+    {
+    SDL_FillRect(sdl_surface, NULL, SDL_MapRGB(sdl_surface->format,
+        top.red, top.green, top.blue));
+    }
+  else
+    {
+    float redstep = (float(bottom.red)-float(top.red)) / float(h);
+    float greenstep = (float(bottom.green)-float(top.green)) / float(h);
+    float bluestep = (float(bottom.blue) - float(top.blue)) / float(h);
+
+    SDL_Rect rect;
+    rect.x = 0;
+    rect.w = w;
+    rect.h = 1;
+    for(float y = 0; y < h; y++)
+      {
+      rect.y = (int)y;
+      SDL_FillRect(sdl_surface, &rect, SDL_MapRGB(sdl_surface->format,
+          int(float(top.red) + redstep * y),
+          int(float(top.green) + greenstep * y),
+          int(float(top.blue) + bluestep * y)));
+      }
+    }
+
+  return sdl_surface;
+}
+
+//---------------------------------------------------------------------------
+
+SurfaceImpl::SurfaceImpl()
+{}
+
+SurfaceImpl::~SurfaceImpl()
+{
+  SDL_FreeSurface(sdl_surface);
+}
+
+SDL_Surface* SurfaceImpl::get_sdl_surface() const
+{
+  return sdl_surface;
+}
+
+int SurfaceImpl::resize(int w_, int h_)
+{
+  w = w_;
+  h = h_;
+  SDL_Rect dest;
+  dest.x = 0;
+  dest.y = 0;
+  dest.w = w;
+  dest.h = h;
+  int ret = SDL_SoftStretch(sdl_surface, NULL,
+                            sdl_surface, &dest);
+  return ret;
+}
+
+#ifndef NOOPENGL
+SurfaceOpenGL::SurfaceOpenGL(SDL_Surface* surf, bool use_alpha)
+{
+  sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
+  create_gl(sdl_surface,&gl_texture);
+
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceOpenGL::SurfaceOpenGL(const std::string& file, bool use_alpha)
+{
+  sdl_surface = sdl_surface_from_file(file, use_alpha);
+  create_gl(sdl_surface,&gl_texture);
+
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceOpenGL::SurfaceOpenGL(const std::string& file_, int x_, int y_, int w_, int h_, bool use_alpha_)
+{
+  sdl_surface = sdl_surface_part_from_file(file_,x_,y_,w_,h_,use_alpha_);
+  
+  create_gl(sdl_surface, &gl_texture);
+
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceOpenGL::SurfaceOpenGL(Color top_gradient, Color bottom_gradient, int w, int h)
+{
+  sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient, w, h);
+  create_gl(sdl_surface, &gl_texture);
+
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceOpenGL::~SurfaceOpenGL()
+{
+  glDeleteTextures(1, &gl_texture);
+}
+
+void
+SurfaceOpenGL::create_gl(SDL_Surface * surf, GLuint * tex)
+{
+  Uint32 saved_flags;
+  Uint8  saved_alpha;
+  int w, h;
+  SDL_Surface *conv;
+
+  w = power_of_two(surf->w);
+  h = power_of_two(surf->h),
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+      conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
+                                  0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+#else
+      conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
+                                  0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+#endif
+
+  /* Save the alpha blending attributes */
+  saved_flags = surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
+  saved_alpha = surf->format->alpha;
+  if ( (saved_flags & SDL_SRCALPHA)
+       == SDL_SRCALPHA )
+  {
+    SDL_SetAlpha(surf, 0, 0);
+  }
+
+  SDL_BlitSurface(surf, 0, conv, 0);
+
+  /* Restore the alpha blending attributes */
+  if ( (saved_flags & SDL_SRCALPHA)
+       == SDL_SRCALPHA )
+  {
+    SDL_SetAlpha(surf, saved_flags, saved_alpha);
+  }
+
+  // We check all the pixels of the surface to figure out which
+  // internal format OpenGL should use for storing it, ie. if no alpha
+  // is present store in RGB instead of RGBA, this saves a few bytes
+  // of memory, but much more importantly it makes the game look
+  // *much* better in 16bit color mode
+  int internal_format = GL_RGB10_A2;
+  bool has_alpha = false;
+
+  unsigned char* buf = static_cast<unsigned char*>(conv->pixels);
+  for (int y = 0; y < surf->h; ++y)
+    for (int x = 0; x < surf->w; ++x)
+      {
+        if (buf[(conv->pitch*y + x*4) + 3] != 255)
+          {
+            has_alpha = true;
+            break;
+          }
+      }
+
+  if (!has_alpha)
+    {
+      internal_format = GL_RGB;
+    }
+
+  glGenTextures(1, &*tex);
+  glBindTexture(GL_TEXTURE_2D , *tex);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, conv->pitch / conv->format->BytesPerPixel);
+  glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, conv->pixels);
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+  SDL_FreeSurface(conv);
+}
+
+int
+SurfaceOpenGL::draw(float x, float y, Uint8 alpha, Uint32 effect)
+{
+  float pw = power_of_two(w);
+  float ph = power_of_two(h);
+
+  if(effect & SEMI_TRANSPARENT)
+    alpha = 128;
+
+  glEnable(GL_TEXTURE_2D);
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  glColor4ub(alpha, alpha, alpha, alpha);
+
+  glBindTexture(GL_TEXTURE_2D, gl_texture);
+
+  glBegin(GL_QUADS);
+
+  if(effect & VERTICAL_FLIP)
+    {
+    glTexCoord2f(0, 0);
+    glVertex2f(x, (float)h+y);
+
+    glTexCoord2f((float)w / pw, 0);
+    glVertex2f((float)w+x, (float)h+y);
+
+    glTexCoord2f((float)w / pw, (float)h / ph);
+    glVertex2f((float)w+x, y);
+    
+    glTexCoord2f(0, (float)h / ph);
+    glVertex2f(x, y);
+    }
+  else
+    {
+    glTexCoord2f(0, 0);
+    glVertex2f(x, y);
+
+    glTexCoord2f((float)w / pw, 0);
+    glVertex2f((float)w+x, y);
+
+    glTexCoord2f((float)w / pw, (float)h / ph);
+    glVertex2f((float)w+x, (float)h+y);
+
+    glTexCoord2f(0, (float)h / ph);
+    glVertex2f(x, (float)h+y);
+    }
+  glEnd();
+
+  glDisable(GL_TEXTURE_2D);
+  glDisable(GL_BLEND);
+
+  return 0;
+}
+
+int
+SurfaceOpenGL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect)
+{
+  float pw = power_of_two(int(this->w));
+  float ph = power_of_two(int(this->h));
+
+  if(effect & SEMI_TRANSPARENT)
+    alpha = 128;
+
+  glBindTexture(GL_TEXTURE_2D, gl_texture);
+
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  glColor4ub(alpha, alpha, alpha, alpha);
+
+  glEnable(GL_TEXTURE_2D);
+
+
+  glBegin(GL_QUADS);
+
+  if(effect & VERTICAL_FLIP)
+    {
+    glTexCoord2f(sx / pw, sy / ph);
+    glVertex2f(x, y);
+
+    glTexCoord2f((float)(sx + w) / pw, sy / ph);
+    glVertex2f(w+x, y);
+
+    glTexCoord2f((sx+w) / pw, (sy+h) / ph);
+    glVertex2f(w +x, h+y);
+
+    glTexCoord2f(sx / pw, (float)(sy+h) / ph);
+    glVertex2f(x, h+y);
+    }
+  else
+    {
+    glTexCoord2f(sx / pw, (float)(sy+h) / ph);
+    glVertex2f(x, h+y);
+
+    glTexCoord2f((sx+w) / pw, (sy+h) / ph);
+    glVertex2f(w +x, h+y);
+
+    glTexCoord2f((float)(sx + w) / pw, sy / ph);
+    glVertex2f(w+x, y);
+
+    glTexCoord2f(sx / pw, sy / ph);
+    glVertex2f(x, y);
+    }
+
+  glEnd();
+
+  glDisable(GL_TEXTURE_2D);
+  glDisable(GL_BLEND);
+
+  return 0;
+}
+
+#if 0
+int
+SurfaceOpenGL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha)
+{
+  float pw = power_of_two(int(this->w));
+  float ph = power_of_two(int(this->h));
+
+  glBindTexture(GL_TEXTURE_2D, gl_texture);
+
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  glColor4ub(alpha, alpha, alpha, alpha);
+
+  glEnable(GL_TEXTURE_2D);
+
+
+  glBegin(GL_QUADS);
+  glTexCoord2f(0, 0);
+  glVertex2f(x, y);
+  glTexCoord2f((float)w / pw, 0);
+  glVertex2f(sw+x, y);
+  glTexCoord2f((float)w / pw, (float)h / ph);  glVertex2f((float)sw+x, (float)sh+y);
+  glVertex2f(sw +x, sh+y);
+  glTexCoord2f(0, (float)h / ph);
+  glVertex2f(x, sh+y);
+  glEnd();
+
+  glDisable(GL_TEXTURE_2D);
+  glDisable(GL_BLEND);
+
+  return 0;
+}
+#endif
+
+#endif
+
+SurfaceSDL::SurfaceSDL(SDL_Surface* surf, bool use_alpha)
+{
+  sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceSDL::SurfaceSDL(const std::string& file, bool use_alpha)
+{
+  sdl_surface = sdl_surface_from_file(file, use_alpha);
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceSDL::SurfaceSDL(const std::string& file, int x, int y, int w, int h,  bool use_alpha)
+{
+  sdl_surface = sdl_surface_part_from_file(file, x, y, w, h, use_alpha);
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+SurfaceSDL::SurfaceSDL(Color top_gradient, Color bottom_gradient, int w, int h)
+{
+  sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient, w, h);
+  w = sdl_surface->w;
+  h = sdl_surface->h;
+}
+
+int
+SurfaceSDL::draw(float x, float y, Uint8 alpha, Uint32 effect)
+{
+  SDL_Rect dest;
+
+  dest.x = (int)x;
+  dest.y = (int)y;
+  dest.w = w;
+  dest.h = h;
+
+  if(effect & SEMI_TRANSPARENT)
+    alpha = 128;
+
+  if(effect & VERTICAL_FLIP)    // FIXME: feel free to replace this hack
+    {
+    for(float sy = 0; sy < h; sy++)
+      if(draw_part(0, sy, x, y+(h-sy), w, 1, alpha, NONE_EFFECT) == -2)
+        return -2;
+    return 0;
+    }
+
+  if(alpha != 255)
+    {
+    /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha
+      to temp sur, blit the temp into the screen */
+    /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that
+      already have an alpha mask yet... */
+
+    SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
+                                    sdl_surface->w, sdl_surface->h, sdl_surface->format->BitsPerPixel,
+                                    sdl_surface->format->Rmask, sdl_surface->format->Gmask,
+                                    sdl_surface->format->Bmask,
+                                    0);
+    int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255);
+    SDL_FillRect(sdl_surface_copy, NULL, colorkey);
+    SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey);
+
+
+    SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
+    SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha);
+
+    int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest);
+
+    SDL_FreeSurface (sdl_surface_copy);
+    return ret;
+    }
+
+  int ret = SDL_BlitSurface(sdl_surface, NULL, screen, &dest);
+
+  return ret;
+}
+
+int
+SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect)
+{
+  SDL_Rect src, dest;
+
+  src.x = (int)sx;
+  src.y = (int)sy;
+  src.w = (int)w;
+  src.h = (int)h;
+
+  dest.x = (int)x;
+  dest.y = (int)y;
+  dest.w = (int)w;
+  dest.h = (int)h;
+
+  if(effect & SEMI_TRANSPARENT)
+    alpha = 128;
+
+  if(effect & VERTICAL_FLIP)    // FIXME: feel free to replace this hack
+    {
+    for(float sy_ = sy; sy_ < h; sy_++)
+      if(draw_part(sx, sy_, x, y+(h-sy_), w, 1, alpha, NONE_EFFECT) == -2)
+        return -2;
+    return 0;
+    }
+
+  if(alpha != 255)
+    {
+    /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha
+      to temp sur, blit the temp into the screen */
+    /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that
+      already have an alpha mask yet... */
+
+    SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
+                                    sdl_surface->w, sdl_surface->h, sdl_surface->format->BitsPerPixel,
+                                    sdl_surface->format->Rmask, sdl_surface->format->Gmask,
+                                    sdl_surface->format->Bmask,
+                                    0);
+    int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255);
+    SDL_FillRect(sdl_surface_copy, NULL, colorkey);
+    SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey);
+
+
+    SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
+    SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha);
+
+    int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest);
+
+    SDL_FreeSurface (sdl_surface_copy);
+    return ret;
+    }
+
+  int ret = SDL_BlitSurface(sdl_surface, &src, screen, &dest);
+
+  return ret;
+}
+
+#if 0
+int
+SurfaceSDL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha, bool update)
+{
+  SDL_Rect dest;
+
+  dest.x = (int)x;
+  dest.y = (int)y;
+  dest.w = (int)sw;
+  dest.h = (int)sh;
+
+  if(alpha != 255)
+    SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
+
+
+  SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
+                                  sw, sh, sdl_surface->format->BitsPerPixel,
+                                  sdl_surface->format->Rmask, sdl_surface->format->Gmask,
+                                  sdl_surface->format->Bmask,
+                                  0);
+
+  SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
+  SDL_SoftStretch(sdl_surface_copy, NULL, sdl_surface_copy, &dest);
+
+  int ret = SDL_BlitSurface(sdl_surface_copy,NULL,screen,&dest);
+  SDL_FreeSurface(sdl_surface_copy);
+
+  if (update == UPDATE)
+    update_rect(screen, dest.x, dest.y, dest.w, dest.h);
+
+  return ret;
+}
+#endif
+
+SurfaceSDL::~SurfaceSDL()
+{}
+
+/* EOF */
diff --git a/lib/video/surface.h b/lib/video/surface.h
new file mode 100644 (file)
index 0000000..aa38548
--- /dev/null
@@ -0,0 +1,180 @@
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#ifndef SUPERTUX_TEXTURE_H
+#define SUPERTUX_TEXTURE_H
+
+#include <string>
+#include <list>
+
+#ifndef NOOPENGL
+#include "SDL_opengl.h"
+#endif
+
+#include "SDL.h"
+
+#include "video/screen.h"
+#include "math/vector.h"
+
+SDL_Surface* sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, bool use_alpha);
+SDL_Surface* sdl_surface_from_nothing();
+
+class SurfaceImpl;
+class SurfaceSDL;
+class SurfaceOpenGL;
+class DrawingContext;
+
+/// bitset for drawing effects
+enum {
+      /** Don't apply anything */
+      NONE_EFFECT       = 0x0000,
+      /** Draw the Surface upside down */
+      VERTICAL_FLIP     = 0x0001,
+      /** Draw the Surface with alpha equal to 128 */
+      SEMI_TRANSPARENT  = 0x0002
+  };
+
+/** This class holds all the data necessary to construct a surface */
+class SurfaceData 
+{
+public:
+  enum ConstructorType { LOAD, LOAD_PART, SURFACE, GRADIENT };
+  ConstructorType type;
+  SDL_Surface* surface;
+  std::string file;
+  bool use_alpha;
+  int x;
+  int y;
+  int w;
+  int h;
+  Color top_gradient;
+  Color bottom_gradient;
+
+  SurfaceData(SDL_Surface* surf, bool use_alpha_);
+  SurfaceData(const std::string& file_, bool use_alpha_);
+  SurfaceData(const std::string& file_, int x_, int y_, int w_, int h_, bool use_alpha_);
+  SurfaceData(Color top_gradient_, Color bottom_gradient_, int w_, int h_);
+  ~SurfaceData();
+
+  SurfaceSDL* create_SurfaceSDL();
+  SurfaceOpenGL* create_SurfaceOpenGL();
+  SurfaceImpl* create();
+};
+
+/** Container class that holds a surface, necessary so that we can
+    switch Surface implementations (OpenGL, SDL) on the fly */
+class Surface
+{
+public:
+  SurfaceData data;
+  SurfaceImpl* impl;
+  int w; 
+  int h;
+  
+  typedef std::list<Surface*> Surfaces;
+  static Surfaces surfaces;
+public:
+  static void reload_all();
+  static void debug_check();
+
+  Surface(SDL_Surface* surf, bool use_alpha);  
+  Surface(const std::string& file, bool use_alpha);  
+  Surface(const std::string& file, int x, int y, int w, int h, bool use_alpha);
+  Surface(Color top_gradient, Color bottom_gradient, int w_, int h_);
+  ~Surface();
+  
+  /** Reload the surface, which is necesarry in case of a mode swich */
+  void reload();
+
+  void resize(int widht, int height);
+};
+
+/** Surface implementation, all implementation have to inherit from
+    this class */
+class SurfaceImpl
+{
+protected:
+  SDL_Surface* sdl_surface;
+
+public:
+  int w;
+  int h;
+
+public:
+  SurfaceImpl();
+  virtual ~SurfaceImpl();
+  
+  /** Return 0 on success, -2 if surface needs to be reloaded */
+  virtual int draw(float x, float y, Uint8 alpha, Uint32 effect = NONE_EFFECT) = 0;
+  virtual int draw_part(float sx, float sy, float x, float y, float w, float h,  Uint8 alpha, Uint32 effect = NONE_EFFECT) = 0;
+#if 0
+  virtual int draw_stretched(float x, float y, int w, int h, Uint8 alpha, bool update) = 0;
+#endif
+  int resize(int w_, int h_);
+
+  SDL_Surface* get_sdl_surface() const; // @evil@ try to avoid this function
+};
+
+class SurfaceSDL : public SurfaceImpl
+{
+public:
+  SurfaceSDL(SDL_Surface* surf, bool use_alpha);
+  SurfaceSDL(const std::string& file, bool use_alpha);  
+  SurfaceSDL(const std::string& file, int x, int y, int w, int h, bool use_alpha);
+  SurfaceSDL(Color top_gradient, Color bottom_gradient, int w, int h);
+  virtual ~SurfaceSDL();
+
+  int draw(float x, float y, Uint8 alpha, Uint32 effect = NONE_EFFECT);
+  int draw_part(float sx, float sy, float x, float y, float w, float h,  Uint8 alpha, Uint32 effect = NONE_EFFECT);
+#if 0
+  int draw_stretched(float x, float y, int w, int h, Uint8 alpha);
+#endif
+};
+
+#ifndef NOOPENGL
+class SurfaceOpenGL : public SurfaceImpl
+{
+public:
+  GLuint gl_texture;
+
+public:
+  SurfaceOpenGL(SDL_Surface* surf, bool use_alpha);
+  SurfaceOpenGL(const std::string& file, bool use_alpha);  
+  SurfaceOpenGL(const std::string& file, int x, int y, int w, int h, bool use_alpha);
+  SurfaceOpenGL(Color top_gradient, Color bottom_gradient, int w, int h);
+
+  virtual ~SurfaceOpenGL();
+
+  int draw(float x, float y, Uint8 alpha, Uint32 effect = NONE_EFFECT);
+  int draw_part(float sx, float sy, float x, float y, float w, float h,  Uint8 alpha, Uint32 effect = NONE_EFFECT);
+#if 0
+  int draw_stretched(float x, float y, int w, int h, Uint8 alpha);
+#endif
+
+private:
+  void create_gl(SDL_Surface * surf, GLuint * tex);
+};
+#endif 
+
+#endif /*SUPERTUX_TEXTURE_H*/
+
+/* Local Variables: */
+/* mode: c++ */
+/* End: */