Merge branch 'master' into releases 2.1.0
authorHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:38:02 +0000 (10:38 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:38:02 +0000 (10:38 +0200)
64 files changed:
CHANGES
Makefile
src/Makefile
src/cartoons.c
src/cartoons.h
src/config.c [new file with mode: 0644]
src/config.h [new file with mode: 0644]
src/conftime.h [new file with mode: 0644]
src/editor.c
src/editor.h
src/events.c
src/events.h
src/files.c
src/files.h
src/game.c
src/game.h
src/init.c
src/init.h
src/joystick.c [deleted file]
src/joystick.h [deleted file]
src/libgame/Makefile
src/libgame/gadgets.c
src/libgame/gadgets.h
src/libgame/image.c
src/libgame/image.h
src/libgame/joystick.c [new file with mode: 0644]
src/libgame/joystick.h [new file with mode: 0644]
src/libgame/libgame.h
src/libgame/misc.c
src/libgame/misc.h
src/libgame/msdos.c
src/libgame/msdos.h
src/libgame/pcx.c
src/libgame/pcx.h
src/libgame/platform.h
src/libgame/random.c
src/libgame/random.h
src/libgame/sdl.c
src/libgame/sdl.h
src/libgame/setup.c [new file with mode: 0644]
src/libgame/setup.h [new file with mode: 0644]
src/libgame/sound.c
src/libgame/sound.h
src/libgame/system.c
src/libgame/system.h
src/libgame/text.c
src/libgame/text.h
src/libgame/toons.c [new file with mode: 0644]
src/libgame/toons.h [new file with mode: 0644]
src/libgame/types.h
src/libgame/x11.c
src/libgame/x11.h
src/main.c
src/main.h
src/netserv.c
src/netserv.h
src/network.c
src/network.h
src/screens.c
src/screens.h
src/tape.c
src/tape.h
src/tools.c
src/tools.h

diff --git a/CHANGES b/CHANGES
index 4cac89f6eeedc1227d630b6e9dc8632e835cd538..6112c0b02cec3bd4e935b69af2632bef23dd93e3 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,30 @@
+Release Version 2.1.0 [05 AUG 2002]
+-----------------------------------
+       - native Mac OS X port (finally!)
+       - graphics, sounds and music now fully configurable
+       - added support for TrueColor PCX graphics files
+       - added support for 16 bit WAV sound files
+       - enhanced sound system (especially regarding stereo and loop sounds)
+       - new structured setup menu (with sub-menues for graphics and sounds)
+       - added "quick save" and "quick load" functions with shortcut key
+       - added single-step playing mode (automatic pause after each step)
+       - behaviour of "Escape" key in level editor now more intuitive
+       - changed default slipping behaviour of gems back to 2.0.0 style;
+         this is now an element property for gems in the level editor,
+         although existing converted levels use the new EM gems behaviour
+       - bug fixed that prevented walking through tubes when gravity on
+       - added Boulder Dash style "snap-pushing" (thanks to Achim Härtel)
+       - fixed memory leak in image loading code
+       - fixed some "solid" elements that were accidentally destructible
+       - fixed some tape stuff
+       - added new contributed levels from the following players:
+         + Conor Mancone
+         + Gavin Davidson
+         + Jerome Kunegis
+         + Rüdiger Schäfer
+         + Flyboy: level group "Cops and Robbers", with own graphics set
+       - added custom graphics set "Animal Kingdom" by Flyboy
+
 Release Version 2.0.1 [19 MAR 2002]
 -----------------------------------
        - bug in explosion code fixed that broke level 24 of "Baby Ghost Mine"
@@ -7,6 +34,11 @@ Release Version 2.0.1 [19 MAR 2002]
          + explosion chain reactions are now a bit slower than murphy
        - behaviour of robots adjusted to make them less aggressive
          (needed for quite some Emerald Mine Club levels)
+       - emeralds and diamonds now fall off normal, steel and growing walls,
+         as this is the correct behaviour in Emerald Mine; existing private
+         and contributed levels will still behave as before, unless saved
+         again (with or without modifications) from the level editor of the
+         current version of the game
        - icon for Windows executable added
        - bug when selecting default level series fixed
        - new IFF style file format for level and tape files
@@ -40,7 +72,7 @@ Release Version 2.0.0 [01 JAN 2001]
 
 Release Version 1.4.0 [27 OCT 1999]
 -----------------------------------
-       - new Boulderdash elements for better game emulation
+       - new Boulder Dash elements for better game emulation
        - new cool medium-sized crystal font
        - new elements and graphics for Diamond Caves II levels
        - new elements and graphics for Emerald Mine Club levels
@@ -127,7 +159,7 @@ Version 1.0 [9 APR 1997] [NOT RELEASED]
          there are diagonal directions with keyboard playing
          and the fire buttons are mapped to the shift keys)
        - a lot of new elements for better emulation of levels
-         from the games "Boulderdash", "Emerald Mine" and
+         from the games "Boulder Dash", "Emerald Mine" and
          "Sokoban". New elements to build "Dynablaster" style
          levels.
        - enhanced functionality of the level tape recorder
index 3794cd878840e9bb6928fe6e8690ea0dc2dcdf2e..0496dc3c38b4eee1d8f826081b0075f1df459ac0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,18 +1,22 @@
 #=============================================================================#
 # Makefile for Rocks'n'Diamonds                                               #
-# (c) 1995-2000 Holger Schemel, info@artsoft.org                              #
+# (c) 1995-2002 Holger Schemel, info@artsoft.org                              #
 #=============================================================================#
 
 #-----------------------------------------------------------------------------#
 # configuration section                                                       #
 #-----------------------------------------------------------------------------#
 
-# specify your favorite ANSI C compiler
+# specify command name of your favorite ANSI C compiler
+# (this must be set to "cc" for some systems)
 CC = gcc
 
+# specify command name of GNU make on your system
+# (this must be set to "gmake" for some systems)
+MAKE = make
+
 # specify path to X11 on your system
-# if undefined, use system defaults (works with Linux/gcc/libc5)
-X11_PATH = /usr/X11
+X11_PATH = /usr/X11R6
 
 # specify directory for read-only game data (like graphics, sounds, levels)
 # default is '.', so you can play without installing game data somewhere
@@ -42,8 +46,6 @@ CROSS_PATH_WIN32=/usr/local/cross-tools/i386-mingw32msvc
 
 .EXPORT_ALL_VARIABLES:
 
-MAKE = make
-
 SRC_DIR = src
 MAKE_CMD = $(MAKE) -C $(SRC_DIR)
 
@@ -62,6 +64,9 @@ solaris:
 solaris-sdl:
        @$(MAKE_CMD) PLATFORM=solaris TARGET=sdl
 
+mac:
+       @$(MAKE_CMD) PLATFORM=macosx
+
 msdos:
        @$(MAKE_CMD) PLATFORM=msdos
 
@@ -79,6 +84,9 @@ clean:
 # development only stuff                                                      #
 #-----------------------------------------------------------------------------#
 
+run:
+       @$(MAKE_CMD) TARGET=x11 && ./rocksndiamonds --verbose
+
 backup:
        ./Scripts/make_backup.sh src
 
@@ -97,6 +105,9 @@ dist-msdos:
 dist-win32:
        ./Scripts/make_dist.sh win .
 
+dist-macosx:
+       ./Scripts/make_dist.sh mac . $(MAKE)
+
 dist-clean:
        @$(MAKE_CMD) dist-clean
 
@@ -106,7 +117,7 @@ dist-build-all:
        @BUILD_DIST=TRUE $(MAKE) cross-win32    ; $(MAKE) dist-clean
        @BUILD_DIST=TRUE $(MAKE) cross-msdos    ; $(MAKE) dist-clean
 
-dist-all: dist-build-all dist-unix dist-msdos dist-win32
+dist-all: dist-build-all dist-unix dist-msdos dist-win32 dist-macosx
 
 depend dep:
        $(MAKE_CMD) depend
index f4f503fc9cf2555e6242422fe1ab605f6af7a198..e935b51d4715fd8bc1c6fababb8cbf01e98f710a 100644 (file)
@@ -1,6 +1,6 @@
 #=============================================================================#
 # Makefile for Rocks'n'Diamonds                                               #
-# (c) 1995-2000 Holger Schemel, info@artsoft.org                              #
+# (c) 1995-2002 Holger Schemel, info@artsoft.org                              #
 #=============================================================================#
 
 .EXPORT_ALL_VARIABLES:
@@ -48,6 +48,11 @@ EXTRA_LDFLAGS = -lnsl -lsocket -R$(XLIB_PATH)
 PLATFORM = unix
 endif
 
+ifeq ($(PLATFORM),macosx)
+PLATFORM = unix
+TARGET=sdl
+endif
+
 ifeq ($(PLATFORM),unix)
 PROFILING_FLAGS = -pg
 endif
@@ -97,7 +102,7 @@ CONFIG_GAME_DIR = $(CONFIG_RO_GAME_DIR) $(CONFIG_RW_GAME_DIR)
 
 
 CONFIG = $(CONFIG_GAME_DIR) $(CONFIG_SCORE_ENTRIES) $(JOYSTICK)
-DEBUG = -DDEBUG -g
+DEBUG = -DDEBUG -g
 # PROFILING = $(PROFILING_FLAGS)
 
 # OPTIONS = $(DEBUG) -Wall                     # only for debugging purposes
@@ -118,6 +123,7 @@ LDFLAGS = $(SYS_LDFLAGS) $(EXTRA_LDFLAGS) -lm
 
 SRCS = main.c          \
        init.c          \
+       config.c        \
        events.c        \
        tools.c         \
        screens.c       \
@@ -125,13 +131,13 @@ SRCS =    main.c          \
        editor.c        \
        files.c         \
        tape.c          \
-       joystick.c      \
        cartoons.c      \
        network.c       \
        netserv.c
 
 OBJS = main.o          \
        init.o          \
+       config.o        \
        events.o        \
        tools.o         \
        screens.o       \
@@ -139,11 +145,12 @@ OBJS =    main.o          \
        editor.o        \
        files.o         \
        tape.o          \
-       joystick.o      \
        cartoons.o      \
        network.o       \
        netserv.o
 
+TIMESTAMP_FILE = conftime.h
+
 LIBDIR = libgame
 LIBGAME = $(LIBDIR)/libgame.a
 
@@ -156,7 +163,7 @@ endif
 
 all: libgame_dir $(PROGNAME)
 
-$(PROGNAME): $(LIBGAME) $(OBJS) $(ICON)
+$(PROGNAME): $(LIBGAME) $(TIMESTAMP_FILE) $(OBJS) $(ICON)
        $(CC) $(PROFILING) $(OBJS) $(ICON) $(LIBGAME) $(LDFLAGS) -o $(PROGNAME)
 
 libgame_dir:
@@ -164,6 +171,11 @@ libgame_dir:
 $(LIBGAME):
        @$(MAKE) -C $(LIBDIR)
 
+$(TIMESTAMP_FILE): $(SRCS)
+       @date '+"[%Y-%m-%d %H:%M]"' \
+       | sed -e 's/^/#define COMPILE_DATE_STRING /' \
+       > $(TIMESTAMP_FILE)
+
 $(ICON):
        $(BMP2ICO) -transparent $(ICONBASE).ico $(ICON32X32)
        echo "$(ICONBASE) ICON $(ICONBASE).ico" | $(WINDRES) -o $(ICON)
index 19f6cac2f42c8cb0415c16c6fb3baa09707db8af..e97005dd9635a51e17fd5109c2c4ee41d62b474f 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "tools.h"
 
 
-static void HandleAnimation(int);
-static boolean AnimateToon(int, boolean);
-static void DrawAnim(Bitmap *, GC, int, int, int, int, int, int, int, int);
-
-struct AnimInfo
-{
-  int width, height;
-  int src_x, src_y;
-  int frames;
-  int frames_per_second;
-  int stepsize;
-  boolean pingpong;
-  int direction;
-  int position;
-};
-
-/* values for cartoon figures */
+/* values for toon definition */
 #define NUM_TOONS      18
 
 #define DWARF_XSIZE    40
@@ -74,490 +58,260 @@ struct AnimInfo
 #define GAMETOON_FPS           20
 #define GAMETOON_STEPSIZE      4
 
-#define ANIMDIR_LEFT   1
-#define ANIMDIR_RIGHT  2
-#define ANIMDIR_UP     4
-#define ANIMDIR_DOWN   8
-
-#define ANIMPOS_ANY    0
-#define ANIMPOS_LEFT   1
-#define ANIMPOS_RIGHT  2
-#define ANIMPOS_UP     4
-#define ANIMPOS_DOWN   8
-#define ANIMPOS_UPPER  16
-
-#define ANIM_START     0
-#define ANIM_CONTINUE  1
-#define ANIM_STOP      2
-
-void InitAnimation()
-{
-  HandleAnimation(ANIM_START);
-}
-
-void StopAnimation()
+struct ToonInfo toons[NUM_TOONS] =
 {
-  HandleAnimation(ANIM_STOP);
-}
-
-void DoAnimation()
-{
-  HandleAnimation(ANIM_CONTINUE);
-}
-
-void HandleAnimation(int mode)
-{
-  static unsigned long animstart_delay = -1;
-  static unsigned long animstart_delay_value = 0;
-  static boolean anim_restart = TRUE;
-  static boolean reset_delay = TRUE;
-  static int toon_nr = 0;
-  int draw_mode;
-
-  if (!setup.toons)
-    return;
-
-  switch(mode)
   {
-    case ANIM_START:
-      anim_restart = TRUE;
-      reset_delay = TRUE;
-
-      /* Fill empty backbuffer for animation functions */
-      if (setup.direct_draw && game_status == PLAYING)
-      {
-       int xx,yy;
-
-       SetDrawtoField(DRAW_BACKBUFFER);
-
-       for(xx=0; xx<SCR_FIELDX; xx++)
-         for(yy=0; yy<SCR_FIELDY; yy++)
-           DrawScreenField(xx,yy);
-       DrawAllPlayers();
-
-       SetDrawtoField(DRAW_DIRECT);
-      }
-
-      if (setup.soft_scrolling && game_status == PLAYING)
-      {
-       int fx = FX, fy = FY;
-
-        fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
-        fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
-
-       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
-      }
-
-      return;
-      break;
-    case ANIM_CONTINUE:
-      break;
-    case ANIM_STOP:
-      redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
-
-      /* Redraw background even when in direct drawing mode */
-      draw_mode = setup.direct_draw;
-      setup.direct_draw = FALSE;
-
-      BackToFront();
-
-      setup.direct_draw = draw_mode;
-
-      return;
-      break;
-    default:
-      break;
-  }
-
-  if (reset_delay)
+    PIX_TOONS,
+    DWARF_XSIZE, DWARF_YSIZE,
+    DWARF_X, DWARF_Y,
+    DWARF_FRAMES,
+    DWARF_FPS,
+    DWARF_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
   {
-    animstart_delay = Counter();
-    animstart_delay_value = SimpleRND(3000);
-    reset_delay = FALSE;
-  }
-
-  if (anim_restart)
+    PIX_TOONS,
+    DWARF_XSIZE, DWARF_YSIZE,
+    DWARF_X, DWARF2_Y,
+    DWARF_FRAMES,
+    DWARF_FPS,
+    DWARF_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_DOWN
+  },
   {
-    if (!DelayReached(&animstart_delay, animstart_delay_value))
-      return;
-
-    toon_nr = SimpleRND(NUM_TOONS);
-  }
-
-  anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
-}
-
-boolean AnimateToon(int toon_nr, boolean restart)
-{
-  static int pos_x = 0, pos_y = 0;
-  static int delta_x = 0, delta_y = 0;
-  static int frame = 0, frame_step = 1;
-  static boolean horiz_move, vert_move;
-  static unsigned long anim_delay = 0;
-  static unsigned long anim_delay_value = 0;
-  static int width,height;
-  static int pad_x,pad_y;
-  static int cut_x,cut_y;
-  static int src_x, src_y;
-  static int dest_x, dest_y;
-  static struct AnimInfo toon[NUM_TOONS] =
+    PIX_TOONS,
+    JUMPER_XSIZE, JUMPER_YSIZE,
+    JUMPER_X, JUMPER_Y,
+    JUMPER_FRAMES,
+    JUMPER_FPS,
+    JUMPER_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
   {
-    {
-      DWARF_XSIZE, DWARF_YSIZE,
-      DWARF_X, DWARF_Y,
-      DWARF_FRAMES,
-      DWARF_FPS,
-      DWARF_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      DWARF_XSIZE, DWARF_YSIZE,
-      DWARF_X, DWARF2_Y,
-      DWARF_FRAMES,
-      DWARF_FPS,
-      DWARF_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      JUMPER_XSIZE, JUMPER_YSIZE,
-      JUMPER_X, JUMPER_Y,
-      JUMPER_FRAMES,
-      JUMPER_FPS,
-      JUMPER_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      CLOWN_XSIZE, CLOWN_YSIZE,
-      CLOWN_X, CLOWN_Y,
-      CLOWN_FRAMES,
-      CLOWN_FPS,
-      CLOWN_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_UP,
-      ANIMPOS_ANY
-    },
-    {
-      BIRD_XSIZE, BIRD_YSIZE,
-      BIRD1_X, BIRD1_Y,
-      BIRD_FRAMES,
-      BIRD_FPS,
-      BIRD_STEPSIZE,
-      ANIM_OSCILLATE,
-      ANIMDIR_RIGHT,
-      ANIMPOS_UPPER
-    },
-    {
-      BIRD_XSIZE, BIRD_YSIZE,
-      BIRD2_X, BIRD2_Y,
-      BIRD_FRAMES,
-      BIRD_FPS,
-      BIRD_STEPSIZE,
-      ANIM_OSCILLATE,
-      ANIMDIR_LEFT,
-      ANIMPOS_UPPER
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_MOLE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_MOLE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_MOLE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_MOLE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_4,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_DOWN
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_8,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_LEFT,
-      ANIMPOS_ANY
-    },
-    {
-      GAMETOON_XSIZE, GAMETOON_YSIZE,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
-      ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
-      GAMETOON_FRAMES_8,
-      GAMETOON_FPS,
-      GAMETOON_STEPSIZE,
-      ANIM_NORMAL,
-      ANIMDIR_RIGHT,
-      ANIMPOS_ANY
-    },
-  };
-  struct AnimInfo *anim = &toon[toon_nr];
-  int anim_bitmap_nr = (toon_nr < 6 ? PIX_TOONS : PIX_HEROES);
-  Bitmap *anim_bitmap = pix[anim_bitmap_nr];
-  GC anim_clip_gc = pix[anim_bitmap_nr]->stored_clip_gc;
-
-  if (restart)
+    PIX_TOONS,
+    CLOWN_XSIZE, CLOWN_YSIZE,
+    CLOWN_X, CLOWN_Y,
+    CLOWN_FRAMES,
+    CLOWN_FPS,
+    CLOWN_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_UP,
+    ANIMPOS_ANY
+  },
   {
-    horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
-    vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
-    anim_delay_value = 1000/anim->frames_per_second;
-    frame = 0;
-
-    if (horiz_move)
-    {
-      if (anim->position==ANIMPOS_UP)
-       pos_y = 0;
-      else if (anim->position==ANIMPOS_DOWN)
-       pos_y = FULL_SYSIZE-anim->height;
-      else if (anim->position==ANIMPOS_UPPER)
-       pos_y = SimpleRND((FULL_SYSIZE-anim->height)/2);
-      else
-       pos_y = SimpleRND(FULL_SYSIZE-anim->height);
-
-      if (anim->direction==ANIMDIR_RIGHT)
-      {
-       delta_x = anim->stepsize;
-       pos_x = -anim->width+delta_x;
-      }
-      else
-      {
-       delta_x = -anim->stepsize;
-       pos_x = FULL_SXSIZE+delta_x;
-      }
-      delta_y = 0;
-    }
-    else
-    {
-      if (anim->position==ANIMPOS_LEFT)
-       pos_x = 0;
-      else if (anim->position==ANIMPOS_RIGHT)
-       pos_x = FULL_SXSIZE-anim->width;
-      else
-       pos_x = SimpleRND(FULL_SXSIZE-anim->width);
-
-      if (anim->direction==ANIMDIR_DOWN)
-      {
-       delta_y = anim->stepsize;
-       pos_y = -anim->height+delta_y;
-      }
-      else
-      {
-       delta_y = -anim->stepsize;
-       pos_y = FULL_SYSIZE+delta_y;
-      }
-      delta_x = 0;
-    }
-  }
-
-  if (pos_x <= -anim->width  - anim->stepsize ||
-      pos_x >=  FULL_SXSIZE  + anim->stepsize ||
-      pos_y <= -anim->height - anim->stepsize ||
-      pos_y >=  FULL_SYSIZE  + anim->stepsize)
-    return(TRUE);
-
-  if (!DelayReached(&anim_delay, anim_delay_value))
+    PIX_TOONS,
+    BIRD_XSIZE, BIRD_YSIZE,
+    BIRD1_X, BIRD1_Y,
+    BIRD_FRAMES,
+    BIRD_FPS,
+    BIRD_STEPSIZE,
+    ANIM_OSCILLATE,
+    ANIMDIR_RIGHT,
+    ANIMPOS_UPPER
+  },
+  {
+    PIX_TOONS,
+    BIRD_XSIZE, BIRD_YSIZE,
+    BIRD2_X, BIRD2_Y,
+    BIRD_FRAMES,
+    BIRD_FPS,
+    BIRD_STEPSIZE,
+    ANIM_OSCILLATE,
+    ANIMDIR_LEFT,
+    ANIMPOS_UPPER
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_SPIELER1_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_SPIELER1_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_PINGUIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_PINGUIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_MOLE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_MOLE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_MOLE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_MOLE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
   {
-    if ((game_status == HELPSCREEN ||
-        (game_status == MAINMENU && redraw_mask & REDRAW_MICROLEVEL))
-       && !restart)
-      DrawAnim(anim_bitmap, anim_clip_gc,
-              src_x + cut_x, src_y + cut_y, width, height,
-              REAL_SX + dest_x, REAL_SY + dest_y, pad_x, pad_y);
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_SCHWEIN_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_SCHWEIN_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_DRACHE_LEFT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_DRACHE_RIGHT - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_4,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_DOWN
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_8,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_LEFT,
+    ANIMPOS_ANY
+  },
+  {
+    PIX_HEROES,
+    GAMETOON_XSIZE, GAMETOON_YSIZE,
+    ((GFX_SONDE - GFX_START_ROCKSHEROES) % HEROES_PER_LINE)*TILEX,
+    ((GFX_SONDE - GFX_START_ROCKSHEROES) / HEROES_PER_LINE)*TILEY,
+    GAMETOON_FRAMES_8,
+    GAMETOON_FPS,
+    GAMETOON_STEPSIZE,
+    ANIM_NORMAL,
+    ANIMDIR_RIGHT,
+    ANIMPOS_ANY
+  },
+};
 
-    return(FALSE);
-  }
+static void PrepareBackbuffer()
+{
+  /* Fill empty backbuffer for animation functions */
+  if (setup.direct_draw && game_status == PLAYING)
+  {
+    int xx,yy;
 
-  if (pos_x<-anim->width)
-    pos_x = -anim->width;
-  else if (pos_x>FULL_SXSIZE)
-    pos_x = FULL_SXSIZE;
-  if (pos_y<-anim->height)
-    pos_y = -anim->height;
-  else if (pos_y>FULL_SYSIZE)
-    pos_y = FULL_SYSIZE;
+    SetDrawtoField(DRAW_BACKBUFFER);
 
-  pad_x = (horiz_move ? anim->stepsize : 0);
-  pad_y = (vert_move  ? anim->stepsize : 0);
-  src_x = anim->src_x + frame * anim->width;
-  src_y = anim->src_y;
-  dest_x = pos_x;
-  dest_y = pos_y;
-  cut_x = cut_y = 0;
-  width  = anim->width;
-  height = anim->height;
+    for(xx=0; xx<SCR_FIELDX; xx++)
+      for(yy=0; yy<SCR_FIELDY; yy++)
+       DrawScreenField(xx,yy);
+    DrawAllPlayers();
 
-  if (pos_x<0)
-  {
-    dest_x = 0;
-    width += pos_x;
-    cut_x = -pos_x;
+    SetDrawtoField(DRAW_DIRECT);
   }
-  else if (pos_x>FULL_SXSIZE-anim->width)
-    width -= (pos_x - (FULL_SXSIZE-anim->width));
 
-  if (pos_y<0)
+  if (setup.soft_scrolling && game_status == PLAYING)
   {
-    dest_y = 0;
-    height += pos_y;
-    cut_y = -pos_y;
-  }
-  else if (pos_y>FULL_SYSIZE-anim->height)
-    height -= (pos_y - (FULL_SYSIZE-anim->height));
+    int fx = FX, fy = FY;
 
-  DrawAnim(anim_bitmap,anim_clip_gc,
-          src_x+cut_x,src_y+cut_y, width,height,
-          REAL_SX+dest_x,REAL_SY+dest_y, pad_x,pad_y);
+    fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
+    fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
 
-  pos_x += delta_x;
-  pos_y += delta_y;
-  frame += frame_step;
-
-  if (frame<0 || frame>=anim->frames)
-  {
-    if (anim->pingpong)
-    {
-      frame_step *= -1;
-      frame = (frame<0 ? 1 : anim->frames-2);
-    }
-    else
-      frame = (frame<0 ? anim->frames-1 : 0);
+    BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
   }
-
-  return(FALSE);
 }
 
-void DrawAnim(Bitmap *toon_bitmap, GC toon_clip_gc,
-             int src_x, int src_y, int width, int height,
-             int dest_x, int dest_y, int pad_x, int pad_y)
+boolean ToonNeedsRedraw()
 {
-  int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
-
-#if 1
-  /* special method to avoid flickering interference with BackToFront() */
-  BlitBitmap(backbuffer, pix[PIX_DB_DOOR], dest_x-pad_x, dest_y-pad_y,
-            width+2*pad_x, height+2*pad_y, buf_x, buf_y);
-  SetClipOrigin(toon_bitmap, toon_clip_gc, dest_x-src_x, dest_y-src_y);
-  BlitBitmapMasked(toon_bitmap, backbuffer,
-                  src_x, src_y, width, height, dest_x, dest_y);
-  BlitBitmap(backbuffer, window, dest_x-pad_x, dest_y-pad_y,
-            width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
-  BackToFront();
-  BlitBitmap(pix[PIX_DB_DOOR], backbuffer, buf_x, buf_y,
-           width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
-#else
-  /* normal method, causing flickering interference with BackToFront() */
-  BlitBitmap(backbuffer, pix[PIX_DB_DOOR], dest_x-pad_x, dest_y-pad_y,
-            width+2*pad_x, height+2*pad_y, buf_x, buf_y);
-  SetClipOrigin(toon_bitmap,toon_clip_gc, buf_x-src_x+pad_x,buf_y-src_y+pad_y);
-  BlitBitmapMasked(toon_bitmap, pix[PIX_DB_DOOR],
-                  src_x, src_y, width, height, buf_x+pad_x, buf_y+pad_y);
-  BlitBitmap(pix[PIX_DB_DOOR], window, buf_x, buf_y,
-            width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
-#endif
+  return (game_status == HELPSCREEN ||
+         (game_status == MAINMENU &&
+          ((redraw_mask & REDRAW_MICROLEVEL) ||
+           (redraw_mask & REDRAW_MICROLABEL))));
+}
 
-  FlushDisplay();
+void InitToons()
+{
+  InitToonScreen(pix, pix[PIX_DB_DOOR],
+                BackToFront, PrepareBackbuffer, ToonNeedsRedraw,
+                toons, NUM_TOONS,
+                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
 }
index 4e004671ccb83e515efab2ba458db7420f16ee6f..44cfb6d21582bb4aac46242149271c348f450699 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -14,8 +14,6 @@
 #ifndef CARTOONS_H
 #define CARTOONS_H
 
-void InitAnimation(void);
-void StopAnimation(void);
-void DoAnimation(void);
+void InitToons(void);
 
 #endif
diff --git a/src/config.c b/src/config.c
new file mode 100644 (file)
index 0000000..2d21f40
--- /dev/null
@@ -0,0 +1,30 @@
+/***********************************************************
+* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
+*----------------------------------------------------------*
+* (c) 1995-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* config.c                                                 *
+***********************************************************/
+
+#include "libgame/libgame.h"
+
+#include "config.h"
+#include "conftime.h"
+
+/* use timestamp created at compile-time */
+#define PROGRAM_BUILD_STRING   PROGRAM_IDENT_STRING " " COMPILE_DATE_STRING
+#ifdef DEBUG
+#undef WINDOW_TITLE_STRING
+#define WINDOW_TITLE_STRING    PROGRAM_TITLE_STRING " " PROGRAM_BUILD_STRING
+#endif
+
+
+char *getWindowTitleString()
+{
+  return WINDOW_TITLE_STRING;
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644 (file)
index 0000000..d9e5d94
--- /dev/null
@@ -0,0 +1,21 @@
+/***********************************************************
+* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
+*----------------------------------------------------------*
+* (c) 1995-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* config.h                                                 *
+***********************************************************/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include "main.h"
+
+char *getWindowTitleString(void);
+
+#endif /* CONFIG_H */
diff --git a/src/conftime.h b/src/conftime.h
new file mode 100644 (file)
index 0000000..28ed84b
--- /dev/null
@@ -0,0 +1 @@
+#define COMPILE_DATE_STRING "[2002-08-05 02:26]"
index 3ce6762af51d45acdae9a7870a42938fab6b1391..eaf1aa29c1102780beb2acd3cfcab5e75b5a3dfc 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #define GADGET_ID_DOUBLE_SPEED         73
 #define GADGET_ID_GRAVITY              74
 #define GADGET_ID_STICK_ELEMENT                75
+#define GADGET_ID_EM_SLIPPERY_GEMS     76
 
 /* another drawing area for random placement */
-#define GADGET_ID_RANDOM_BACKGROUND    76
+#define GADGET_ID_RANDOM_BACKGROUND    77
 
 /* gadgets for buttons in element list */
-#define GADGET_ID_ELEMENTLIST_FIRST    77
-#define GADGET_ID_ELEMENTLIST_LAST     (77 + ED_NUM_ELEMENTLIST_BUTTONS - 1)
+#define GADGET_ID_ELEMENTLIST_FIRST    78
+#define GADGET_ID_ELEMENTLIST_LAST     (78 + ED_NUM_ELEMENTLIST_BUTTONS - 1)
 
 #define NUM_EDITOR_GADGETS             (GADGET_ID_ELEMENTLIST_LAST + 1)
 
 #define ED_CHECKBUTTON_ID_GRAVITY              1
 #define ED_CHECKBUTTON_ID_RANDOM_RESTRICTED    2
 #define ED_CHECKBUTTON_ID_STICK_ELEMENT                3
+#define ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS     4
 
-#define ED_NUM_CHECKBUTTONS                    4
+#define ED_NUM_CHECKBUTTONS                    5
 
 #define ED_CHECKBUTTON_ID_LEVEL_FIRST  ED_CHECKBUTTON_ID_DOUBLE_SPEED
 #define ED_CHECKBUTTON_ID_LEVEL_LAST   ED_CHECKBUTTON_ID_RANDOM_RESTRICTED
@@ -670,6 +672,12 @@ static struct
     GADGET_ID_STICK_ELEMENT,
     &stick_element_properties_window,
     "stick window to edit content",    "stick window to edit content"
+  },
+  {
+    ED_SETTINGS_XPOS,                  ED_COUNTER_YPOS(4),
+    GADGET_ID_EM_SLIPPERY_GEMS,
+    &level.em_slippery_gems,
+    "slip down from certain flat walls","use EM style slipping behaviour"
   }
 };
 
@@ -1265,8 +1273,8 @@ static char *getElementInfoText(int element)
 {
   char *info_text = "unknown";
 
-  if (element < num_element_info)
-    info_text = element_info[element];
+  if (element < NUM_LEVEL_ELEMENTS)
+    info_text = element_info[element].editor_description;
   else
     Error(ERR_WARN, "no element description for element %d", element);
 
@@ -2732,6 +2740,19 @@ static void DrawPropertiesWindow()
     else
       DrawElementContentAreas();
   }
+
+  if (IS_GEM(properties_element))
+  {
+    /* draw checkbutton gadget */
+    i = ED_CHECKBUTTON_ID_EM_SLIPPERY_GEMS;
+    x = checkbutton_info[i].x + xoffset_right2;
+    y = checkbutton_info[i].y + yoffset_right2;
+
+    DrawTextF(x, y, font_color, checkbutton_info[i].text);
+    ModifyGadget(level_editor_gadget[checkbutton_info[i].gadget_id],
+                GDI_CHECKED, *checkbutton_info[i].value, GDI_END);
+    MapCheckbuttonGadget(i);
+  }
 }
 
 static void DrawLineElement(int sx, int sy, int element, boolean change_level)
@@ -3620,6 +3641,7 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
 
     case ED_COUNTER_ID_SELECT_LEVEL:
       LoadLevel(level_nr);
+      TapeErase();
       ResetUndoBuffer();
       DrawEditModeWindow();
       break;
@@ -3899,6 +3921,9 @@ static void HandleControlButtons(struct GadgetInfo *gi)
        Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
       else
       {
+       if (LevelChanged())
+         level.game_version = GAME_VERSION_ACTUAL;
+
        for(x=0; x<lev_fieldx; x++)
          for(y=0; y<lev_fieldy; y++)
            FieldBackup[x][y] = Ur[x][y];
@@ -3925,27 +3950,7 @@ static void HandleControlButtons(struct GadgetInfo *gi)
       break;
 
     case GADGET_ID_EXIT:
-      if (!LevelChanged() ||
-         Request("Level has changed! Exit without saving ?",
-                 REQ_ASK | REQ_STAY_OPEN))
-      {
-       CloseDoor(DOOR_CLOSE_1);
-
-       /*
-       CloseDoor(DOOR_CLOSE_ALL);
-       */
-
-       game_status = MAINMENU;
-       DrawMainMenu();
-      }
-      else
-      {
-       CloseDoor(DOOR_CLOSE_1);
-       BlitBitmap(pix[PIX_DB_DOOR], pix[PIX_DB_DOOR],
-                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
-                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
-       OpenDoor(DOOR_OPEN_1);
-      }
+      RequestExitLevelEditor(TRUE);    /* if level has changed, ask user */
       break;
 
     default:
@@ -3997,10 +4002,12 @@ void HandleLevelEditorKeyInput(Key key)
       DrawLevelText(0, 0, 0, TEXT_BACKSPACE);
     else if (key == KSYM_Return)
       DrawLevelText(0, 0, 0, TEXT_NEWLINE);
+    else if (key == KSYM_Escape)
+      DrawLevelText(0, 0, 0, TEXT_END);
   }
   else if (button_status == MB_RELEASED)
   {
-    int i, id;
+    int i, id = GADGET_ID_NONE;
 
     switch (key)
     {
@@ -4025,8 +4032,19 @@ void HandleLevelEditorKeyInput(Key key)
        button = MB_RIGHTBUTTON;
        break;
 
+      case KSYM_Escape:
+        if (edit_mode == ED_MODE_DRAWING)
+       {
+         RequestExitLevelEditor(setup.ask_on_escape);
+       }
+        else
+       {
+         DrawDrawingWindow();
+         edit_mode = ED_MODE_DRAWING;
+       }
+        break;
+
       default:
-       id = GADGET_ID_NONE;
        break;
     }
 
@@ -4034,7 +4052,7 @@ void HandleLevelEditorKeyInput(Key key)
       ClickOnGadget(level_editor_gadget[id], button);
     else if (letter == '.')
       ClickOnGadget(level_editor_gadget[GADGET_ID_SINGLE_ITEMS], button);
-    else if (key == KSYM_space || key == KSYM_Return)
+    else if (key == KSYM_Return || key == setup.shortcut.toggle_pause)
       ClickOnGadget(level_editor_gadget[GADGET_ID_TEST], button);
     else
       for (i=0; i<ED_NUM_CTRL_BUTTONS; i++)
@@ -4062,6 +4080,9 @@ void HandleEditorGadgetInfoText(void *ptr)
 
   ClearEditorGadgetInfoText();
 
+  if (gi->event.type == GD_EVENT_INFO_LEAVING)
+    return;
+
   /* misuse this function to delete brush cursor, if needed */
   if (edit_mode == ED_MODE_DRAWING && draw_with_brush)
     DeleteBrushFromCursor();
@@ -4109,6 +4130,9 @@ static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
 
   ClearEditorGadgetInfoText();
 
+  if (gi->event.type == GD_EVENT_INFO_LEAVING)
+    return;
+
   /* make sure to stay inside drawing area boundaries */
   sx = (sx < min_sx ? min_sx : sx > max_sx ? max_sx : sx);
   sy = (sy < min_sy ? min_sy : sy > max_sy ? max_sy : sy);
@@ -4217,3 +4241,27 @@ static void HandleDrawingAreaInfo(struct GadgetInfo *gi)
              "Content area %d position: %d, %d",
              id - GADGET_ID_ELEM_CONTENT_0 + 1, sx, sy);
 }
+
+void RequestExitLevelEditor(boolean ask_if_level_has_changed)
+{
+  if (!ask_if_level_has_changed ||
+      !LevelChanged() ||
+      Request("Level has changed! Exit without saving ?",
+             REQ_ASK | REQ_STAY_OPEN))
+  {
+    CloseDoor(DOOR_CLOSE_1);
+    /*
+    CloseDoor(DOOR_CLOSE_ALL);
+    */
+    game_status = MAINMENU;
+    DrawMainMenu();
+  }
+  else
+  {
+    CloseDoor(DOOR_CLOSE_1);
+    BlitBitmap(pix[PIX_DB_DOOR], pix[PIX_DB_DOOR],
+              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
+              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+    OpenDoor(DOOR_OPEN_1);
+  }
+}
index 466b571fb8c2550ab6c388713ad5292e29665a6e..7966bda17b2a315539162db11d50cb6aba62c831 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -29,5 +29,6 @@ void UnmapLevelEditorGadgets();
 void DrawLevelEd(void);
 void HandleLevelEditorKeyInput(Key);
 void HandleEditorGadgetInfoText(void *ptr);
+void RequestExitLevelEditor(boolean);
 
 #endif
index 79ffdd4ad02613fb5bf65e609be6ed563b87405b..fc6140fc84a0c959d5eaaeb5966acdd1c3fa2d95 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -21,7 +21,6 @@
 #include "editor.h"
 #include "files.h"
 #include "tape.h"
-#include "joystick.h"
 #include "network.h"
 
 /* values for key_status */
@@ -177,6 +176,16 @@ void ClearEventQueue()
   }
 }
 
+void ClearPlayerAction()
+{
+  int i;
+
+  /* simulate key release events for still pressed keys */
+  key_joystick_mapping = 0;
+  for (i=0; i<MAX_PLAYERS; i++)
+    stored_player[i].action = 0;
+}
+
 void SleepWhileUnmapped()
 {
   boolean window_unmapped = TRUE;
@@ -223,38 +232,7 @@ void SleepWhileUnmapped()
 void HandleExposeEvent(ExposeEvent *event)
 {
 #ifndef TARGET_SDL
-  int x = event->x, y = event->y;
-  int width = event->width, height = event->height;
-
-  if (setup.direct_draw && game_status==PLAYING)
-  {
-    int xx,yy;
-    int x1 = (x-SX)/TILEX, y1 = (y-SY)/TILEY;
-    int x2 = (x-SX+width)/TILEX, y2 = (y-SY+height)/TILEY;
-
-    SetDrawtoField(DRAW_BACKBUFFER);
-
-    for(xx=0; xx<SCR_FIELDX; xx++)
-      for(yy=0; yy<SCR_FIELDY; yy++)
-       if (xx>=x1 && xx<=x2 && yy>=y1 && yy<=y2)
-         DrawScreenField(xx,yy);
-    DrawAllPlayers();
-
-    SetDrawtoField(DRAW_DIRECT);
-  }
-
-  if (setup.soft_scrolling && game_status == PLAYING)
-  {
-    int fx = FX, fy = FY;
-
-    fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
-    fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
-
-    BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
-  }
-
-  BlitBitmap(drawto, window, x,y, width,height, x,y);
-
+  RedrawPlayfield(FALSE, event->x, event->y, event->width, event->height);
   FlushDisplay();
 #endif
 }
@@ -301,16 +279,11 @@ void HandleFocusEvent(FocusChangeEvent *event)
 
   if (event->type == EVENT_FOCUSOUT)
   {
-    int i;
-
     KeyboardAutoRepeatOn();
-    old_joystick_status = joystick_status;
-    joystick_status = JOYSTICK_OFF;
+    old_joystick_status = joystick.status;
+    joystick.status = JOYSTICK_NOT_AVAILABLE;
 
-    /* simulate key release events for still pressed keys */
-    key_joystick_mapping = 0;
-    for (i=0; i<MAX_PLAYERS; i++)
-      stored_player[i].action = 0;
+    ClearPlayerAction();
   }
   else if (event->type == EVENT_FOCUSIN)
   {
@@ -336,7 +309,7 @@ void HandleFocusEvent(FocusChangeEvent *event)
       KeyboardAutoRepeatOff();
     }
     if (old_joystick_status != -1)
-      joystick_status = old_joystick_status;
+      joystick.status = old_joystick_status;
   }
 }
 
@@ -393,10 +366,6 @@ void HandleButton(int mx, int my, int button)
       HandleSetupScreen(mx,my, 0,0, button);
       break;
 
-    case SETUPINPUT:
-      HandleSetupInputScreen(mx,my, 0,0, button);
-      break;
-
     case PLAYING:
 #ifdef DEBUG
       if (button == MB_RELEASED)
@@ -454,6 +423,9 @@ void HandleKey(Key key, int key_status)
 
   if (game_status == PLAYING)
   {
+    /* only needed for single-step tape recording mode */
+    static boolean clear_button_2[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
+    static boolean bomb_placed[MAX_PLAYERS] = { FALSE,FALSE,FALSE,FALSE };
     int pnr;
 
     for (pnr=0; pnr<MAX_PLAYERS; pnr++)
@@ -470,10 +442,46 @@ void HandleKey(Key key, int key_status)
        if (key == *key_info[i].key_custom)
          key_action |= key_info[i].action;
 
+      if (tape.single_step && clear_button_2[pnr])
+      {
+       stored_player[pnr].action &= ~KEY_BUTTON_2;
+       clear_button_2[pnr] = FALSE;
+      }
+
       if (key_status == KEY_PRESSED)
        stored_player[pnr].action |= key_action;
       else
        stored_player[pnr].action &= ~key_action;
+
+      if (tape.single_step && tape.recording && tape.pausing)
+      {
+       if (key_status == KEY_PRESSED &&
+           (key_action & (KEY_MOTION | KEY_BUTTON_1)))
+       {
+         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+         if (key_action & KEY_MOTION)
+         {
+           if (stored_player[pnr].action & KEY_BUTTON_2)
+             bomb_placed[pnr] = TRUE;
+         }
+       }
+       else if (key_status == KEY_RELEASED &&
+                (key_action & KEY_BUTTON_2))
+       {
+         if (!bomb_placed[pnr])
+         {
+           TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+           stored_player[pnr].action |= KEY_BUTTON_2;
+           clear_button_2[pnr] = TRUE;
+         }
+
+         bomb_placed[pnr] = FALSE;
+       }
+      }
+      else if (tape.recording && tape.pausing && (key_action & KEY_ACTION))
+       TapeTogglePause(TAPE_TOGGLE_MANUAL);
     }
   }
   else
@@ -501,7 +509,7 @@ void HandleKey(Key key, int key_status)
   if (key_status == KEY_RELEASED)
     return;
 
-  if ((key == KSYM_Return || key == KSYM_space) &&
+  if ((key == KSYM_Return || key == setup.shortcut.toggle_pause) &&
       game_status == PLAYING && AllPlayersGone)
   {
     CloseDoor(DOOR_CLOSE_1);
@@ -511,14 +519,28 @@ void HandleKey(Key key, int key_status)
   }
 
   /* allow quick escape to the main menu with the Escape key */
-  if (key == KSYM_Escape && game_status != MAINMENU)
+  if (key == KSYM_Escape &&
+      game_status != MAINMENU &&
+      game_status != PLAYING &&
+      game_status != LEVELED &&
+      game_status != CHOOSELEVEL &&
+      game_status != SETUP)
   {
-    CloseDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY);
     game_status = MAINMENU;
     DrawMainMenu();
     return;
   }
 
+  /* special key shortcuts */
+  if (game_status == MAINMENU || game_status == PLAYING)
+  {
+    if (key == setup.shortcut.save_game)
+      TapeQuickSave();
+    else if (key == setup.shortcut.load_game)
+      TapeQuickLoad();
+    else if (key == setup.shortcut.toggle_pause)
+      TapeTogglePause(TAPE_TOGGLE_MANUAL);
+  }
 
 
 #ifndef DEBUG
@@ -541,29 +563,36 @@ void HandleKey(Key key, int key_status)
     case MAINMENU:
     case CHOOSELEVEL:
     case SETUP:
-    case SETUPINPUT:
       switch(key)
       {
        case KSYM_Return:
-       case KSYM_space:
          if (game_status == MAINMENU)
            HandleMainMenu(0,0, 0,0, MB_MENU_CHOICE);
           else if (game_status == CHOOSELEVEL)
             HandleChooseLevel(0,0, 0,0, MB_MENU_CHOICE);
          else if (game_status == SETUP)
            HandleSetupScreen(0,0, 0,0, MB_MENU_CHOICE);
-         else if (game_status == SETUPINPUT)
-           HandleSetupInputScreen(0,0, 0,0, MB_MENU_CHOICE);
+         break;
+
+       case KSYM_Escape:
+          if (game_status == CHOOSELEVEL)
+            HandleChooseLevel(0,0, 0,0, MB_MENU_LEAVE);
+         else if (game_status == SETUP)
+           HandleSetupScreen(0,0, 0,0, MB_MENU_LEAVE);
          break;
 
         case KSYM_Page_Up:
           if (game_status == CHOOSELEVEL)
             HandleChooseLevel(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
+         else if (game_status == SETUP)
+           HandleSetupScreen(0,0, 0,-SCR_FIELDY, MB_MENU_MARK);
          break;
 
         case KSYM_Page_Down:
           if (game_status == CHOOSELEVEL)
             HandleChooseLevel(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
+         else if (game_status == SETUP)
+           HandleSetupScreen(0,0, 0,SCR_FIELDY, MB_MENU_MARK);
          break;
 
 #ifdef DEBUG
@@ -585,7 +614,6 @@ void HandleKey(Key key, int key_status)
       switch(key)
       {
        case KSYM_Return:
-       case KSYM_space:
          game_status = MAINMENU;
          DrawMainMenu();
          BackToFront();
@@ -605,7 +633,7 @@ void HandleKey(Key key, int key_status)
       break;
 
     case LEVELED:
-      if (!anyTextGadgetActiveOrJustFinished)
+      if (!anyTextGadgetActiveOrJustFinished || key == KSYM_Escape)
        HandleLevelEditorKeyInput(key);
       break;
 
@@ -613,6 +641,9 @@ void HandleKey(Key key, int key_status)
     {
       switch(key)
       {
+        case KSYM_Escape:
+         RequestQuitGame(setup.ask_on_escape);
+         break;
 
 #ifdef DEBUG
        case KSYM_0:
@@ -788,11 +819,9 @@ static int HandleJoystickForAllPlayers()
     joy_action = Joystick(i);
     result |= joy_action;
 
-
     if (!setup.input[i].use_joystick)
       continue;
 
-
     stored_player[i].action = joy_action;
   }
 
@@ -818,7 +847,6 @@ void HandleJoystick()
     case MAINMENU:
     case CHOOSELEVEL:
     case SETUP:
-    case SETUPINPUT:
     {
       static unsigned long joystickmove_delay = 0;
 
@@ -826,15 +854,12 @@ void HandleJoystick()
          !DelayReached(&joystickmove_delay, GADGET_FRAME_DELAY))
        newbutton = dx = dy = 0;
 
-      if (game_status==MAINMENU)
+      if (game_status == MAINMENU)
        HandleMainMenu(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
-      else if (game_status==CHOOSELEVEL)
+      else if (game_status == CHOOSELEVEL)
         HandleChooseLevel(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
-      else if (game_status==SETUP)
+      else if (game_status == SETUP)
        HandleSetupScreen(0,0,dx,dy,newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
-      else if (game_status==SETUPINPUT)
-       HandleSetupInputScreen(0,0,dx,dy,
-                              newbutton ? MB_MENU_CHOICE : MB_MENU_MARK);
       break;
     }
 
index e2c4e994d95a2b78df413af5a7e9424cfbbab3b5..8c5d74df1dcd5690a84369a79579b75498e26ad7 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -20,6 +20,7 @@ int FilterMouseMotionEvents(const Event *);
 void EventLoop(void);
 void HandleOtherEvents(Event *);
 void ClearEventQueue(void);
+void ClearPlayerAction(void);
 void SleepWhileUnmapped(void);
 
 void HandleExposeEvent(ExposeEvent *);
index cbf242ca3a5de0c98f1e808a60924fad5f56cff3..58cc705c9c1c6f227ca7df73e3ad9065d5cda033 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -12,7 +12,6 @@
 ***********************************************************/
 
 #include <ctype.h>
-#include <dirent.h>
 #include <sys/stat.h>
 
 #include "libgame/libgame.h"
 #include "files.h"
 #include "tools.h"
 #include "tape.h"
-#include "joystick.h"
+
 
 #define CHUNK_ID_LEN           4       /* IFF style chunk id length  */
 #define CHUNK_SIZE_UNDEFINED   0       /* undefined chunk size == 0  */
 #define CHUNK_SIZE_NONE                -1      /* do not write chunk size    */
 #define FILE_VERS_CHUNK_SIZE   8       /* size of file version chunk */
 #define LEVEL_HEADER_SIZE      80      /* size of level file header  */
-#define LEVEL_HEADER_UNUSED    15      /* unused level header bytes  */
+#define LEVEL_HEADER_UNUSED    14      /* unused level header bytes  */
 #define LEVEL_CHUNK_CNT2_SIZE  160     /* size of level CNT2 chunk   */
 #define LEVEL_CHUNK_CNT2_UNUSED        11      /* unused CNT2 chunk bytes    */
 #define TAPE_HEADER_SIZE       20      /* size of tape file header   */
-#define TAPE_HEADER_UNUSED     7       /* unused tape header bytes   */
+#define TAPE_HEADER_UNUSED     3       /* unused tape header bytes   */
 
 /* file identifier strings */
 #define LEVEL_COOKIE_TMPL      "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
 #define TAPE_COOKIE_TMPL       "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
 #define SCORE_COOKIE           "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
-#define SETUP_COOKIE           "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
-#define LEVELSETUP_COOKIE      "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
-#define LEVELINFO_COOKIE       "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
-
-/* file names and filename extensions */
-#if !defined(PLATFORM_MSDOS)
-#define LEVELSETUP_DIRECTORY   "levelsetup"
-#define SETUP_FILENAME         "setup.conf"
-#define LEVELSETUP_FILENAME    "levelsetup.conf"
-#define LEVELINFO_FILENAME     "levelinfo.conf"
-#define LEVELFILE_EXTENSION    "level"
-#define TAPEFILE_EXTENSION     "tape"
-#define SCOREFILE_EXTENSION    "score"
-#else
-#define LEVELSETUP_DIRECTORY   "lvlsetup"
-#define SETUP_FILENAME         "setup.cnf"
-#define LEVELSETUP_FILENAME    "lvlsetup.cnf"
-#define LEVELINFO_FILENAME     "lvlinfo.cnf"
-#define LEVELFILE_EXTENSION    "lvl"
-#define TAPEFILE_EXTENSION     "tap"
-#define SCOREFILE_EXTENSION    "sco"
-#endif
-
-/* sort priorities of level series (also used as level series classes) */
-#define LEVELCLASS_TUTORIAL_START      10
-#define LEVELCLASS_TUTORIAL_END                99
-#define LEVELCLASS_CLASSICS_START      100
-#define LEVELCLASS_CLASSICS_END                199
-#define LEVELCLASS_CONTRIBUTION_START  200
-#define LEVELCLASS_CONTRIBUTION_END    299
-#define LEVELCLASS_USER_START          300
-#define LEVELCLASS_USER_END            399
-#define LEVELCLASS_BD_START            400
-#define LEVELCLASS_BD_END              499
-#define LEVELCLASS_EM_START            500
-#define LEVELCLASS_EM_END              599
-#define LEVELCLASS_SP_START            600
-#define LEVELCLASS_SP_END              699
-#define LEVELCLASS_DX_START            700
-#define LEVELCLASS_DX_END              799
-
-#define LEVELCLASS_TUTORIAL            LEVELCLASS_TUTORIAL_START
-#define LEVELCLASS_CLASSICS            LEVELCLASS_CLASSICS_START
-#define LEVELCLASS_CONTRIBUTION                LEVELCLASS_CONTRIBUTION_START
-#define LEVELCLASS_USER                        LEVELCLASS_USER_START
-#define LEVELCLASS_BD                  LEVELCLASS_BD_START
-#define LEVELCLASS_EM                  LEVELCLASS_EM_START
-#define LEVELCLASS_SP                  LEVELCLASS_SP_START
-#define LEVELCLASS_DX                  LEVELCLASS_DX_START
-
-#define LEVELCLASS_UNDEFINED           999
-
-#define NUM_LEVELCLASS_DESC    8
-char *levelclass_desc[NUM_LEVELCLASS_DESC] =
-{
-  "Tutorial Levels",
-  "Classic Originals",
-  "Contributions",
-  "Private Levels",
-  "Boulderdash",
-  "Emerald Mine",
-  "Supaplex",
-  "DX Boulderdash"
-};
-
-#define IS_LEVELCLASS_TUTORIAL(p) \
-       ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
-        (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
-#define IS_LEVELCLASS_CLASSICS(p) \
-       ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
-        (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
-#define IS_LEVELCLASS_CONTRIBUTION(p) \
-       ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
-        (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
-#define IS_LEVELCLASS_USER(p) \
-       ((p)->sort_priority >= LEVELCLASS_USER_START && \
-        (p)->sort_priority <= LEVELCLASS_USER_END)
-#define IS_LEVELCLASS_BD(p) \
-       ((p)->sort_priority >= LEVELCLASS_BD_START && \
-        (p)->sort_priority <= LEVELCLASS_BD_END)
-#define IS_LEVELCLASS_EM(p) \
-       ((p)->sort_priority >= LEVELCLASS_EM_START && \
-        (p)->sort_priority <= LEVELCLASS_EM_END)
-#define IS_LEVELCLASS_SP(p) \
-       ((p)->sort_priority >= LEVELCLASS_SP_START && \
-        (p)->sort_priority <= LEVELCLASS_SP_END)
-#define IS_LEVELCLASS_DX(p) \
-       ((p)->sort_priority >= LEVELCLASS_DX_START && \
-        (p)->sort_priority <= LEVELCLASS_DX_END)
-
-#define LEVELCLASS(n)  (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
-                        IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
-                        IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
-                        IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
-                        IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
-                        IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
-                        IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
-                        IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
-                        LEVELCLASS_UNDEFINED)
-
-#define LEVELCOLOR(n)  (IS_LEVELCLASS_TUTORIAL(n) ?            FC_BLUE : \
-                        IS_LEVELCLASS_CLASSICS(n) ?            FC_RED : \
-                        IS_LEVELCLASS_BD(n) ?                  FC_GREEN : \
-                        IS_LEVELCLASS_EM(n) ?                  FC_YELLOW : \
-                        IS_LEVELCLASS_SP(n) ?                  FC_GREEN : \
-                        IS_LEVELCLASS_DX(n) ?                  FC_YELLOW : \
-                        IS_LEVELCLASS_CONTRIBUTION(n) ?        FC_GREEN : \
-                        IS_LEVELCLASS_USER(n) ?                FC_RED : \
-                        FC_BLUE)
-
-#define LEVELSORTING(n)        (IS_LEVELCLASS_TUTORIAL(n) ?            0 : \
-                        IS_LEVELCLASS_CLASSICS(n) ?            1 : \
-                        IS_LEVELCLASS_BD(n) ?                  2 : \
-                        IS_LEVELCLASS_EM(n) ?                  3 : \
-                        IS_LEVELCLASS_SP(n) ?                  4 : \
-                        IS_LEVELCLASS_DX(n) ?                  5 : \
-                        IS_LEVELCLASS_CONTRIBUTION(n) ?        6 : \
-                        IS_LEVELCLASS_USER(n) ?                7 : \
-                        9)
-
-char *getLevelClassDescription(struct LevelDirInfo *ldi)
-{
-  int position = ldi->sort_priority / 100;
-
-  if (position >= 0 && position < NUM_LEVELCLASS_DESC)
-    return levelclass_desc[position];
-  else
-    return "Unknown Level Class";
-}
-
-static void SaveUserLevelInfo();               /* for 'InitUserLevelDir()' */
-static char *getSetupLine(char *, int);                /* for 'SaveUserLevelInfo()' */
-
-static char *getUserLevelDir(char *level_subdir)
-{
-  static char *userlevel_dir = NULL;
-  char *data_dir = getUserDataDir();
-  char *userlevel_subdir = LEVELS_DIRECTORY;
 
-  if (userlevel_dir)
-    free(userlevel_dir);
 
-  if (strlen(level_subdir) > 0)
-    userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
-  else
-    userlevel_dir = getPath2(data_dir, userlevel_subdir);
-
-  return userlevel_dir;
-}
-
-static char *getTapeDir(char *level_subdir)
-{
-  static char *tape_dir = NULL;
-  char *data_dir = getUserDataDir();
-  char *tape_subdir = TAPES_DIRECTORY;
-
-  if (tape_dir)
-    free(tape_dir);
-
-  if (strlen(level_subdir) > 0)
-    tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
-  else
-    tape_dir = getPath2(data_dir, tape_subdir);
-
-  return tape_dir;
-}
-
-static char *getScoreDir(char *level_subdir)
-{
-  static char *score_dir = NULL;
-  char *data_dir = options.rw_base_directory;
-  char *score_subdir = SCORES_DIRECTORY;
-
-  if (score_dir)
-    free(score_dir);
-
-  if (strlen(level_subdir) > 0)
-    score_dir = getPath3(data_dir, score_subdir, level_subdir);
-  else
-    score_dir = getPath2(data_dir, score_subdir);
-
-  return score_dir;
-}
-
-static char *getLevelSetupDir(char *level_subdir)
-{
-  static char *levelsetup_dir = NULL;
-  char *data_dir = getUserDataDir();
-  char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
-
-  if (levelsetup_dir)
-    free(levelsetup_dir);
-
-  if (strlen(level_subdir) > 0)
-    levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
-  else
-    levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
-
-  return levelsetup_dir;
-}
-
-static char *getLevelFilename(int nr)
-{
-  static char *filename = NULL;
-  char basename[MAX_FILENAME_LEN];
-
-  if (filename != NULL)
-    free(filename);
-
-  sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
-  filename = getPath3((leveldir_current->user_defined ?
-                      getUserLevelDir("") :
-                      options.level_directory),
-                     leveldir_current->fullpath,
-                     basename);
-
-  return filename;
-}
-
-static char *getTapeFilename(int nr)
-{
-  static char *filename = NULL;
-  char basename[MAX_FILENAME_LEN];
-
-  if (filename != NULL)
-    free(filename);
-
-  sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
-  filename = getPath2(getTapeDir(leveldir_current->filename), basename);
-
-  return filename;
-}
-
-static char *getScoreFilename(int nr)
-{
-  static char *filename = NULL;
-  char basename[MAX_FILENAME_LEN];
-
-  if (filename != NULL)
-    free(filename);
-
-  sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
-  filename = getPath2(getScoreDir(leveldir_current->filename), basename);
-
-  return filename;
-}
-
-static void InitTapeDirectory(char *level_subdir)
-{
-  createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
-  createDirectory(getTapeDir(""), "main tape", PERMS_PRIVATE);
-  createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
-}
-
-static void InitScoreDirectory(char *level_subdir)
-{
-  createDirectory(getScoreDir(""), "main score", PERMS_PUBLIC);
-  createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
-}
-
-static void InitUserLevelDirectory(char *level_subdir)
-{
-  if (access(getUserLevelDir(level_subdir), F_OK) != 0)
-  {
-    createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
-    createDirectory(getUserLevelDir(""), "main user level", PERMS_PRIVATE);
-    createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
-
-    SaveUserLevelInfo();
-  }
-}
-
-static void InitLevelSetupDirectory(char *level_subdir)
-{
-  createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
-  createDirectory(getLevelSetupDir(""), "main level setup", PERMS_PRIVATE);
-  createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
-}
-
-static void ReadChunk_VERS(FILE *file, int *file_version, int *game_version)
-{
-  int file_version_major, file_version_minor, file_version_patch;
-  int game_version_major, game_version_minor, game_version_patch;
-
-  file_version_major = fgetc(file);
-  file_version_minor = fgetc(file);
-  file_version_patch = fgetc(file);
-  fgetc(file);         /* not used */
-
-  game_version_major = fgetc(file);
-  game_version_minor = fgetc(file);
-  game_version_patch = fgetc(file);
-  fgetc(file);         /* not used */
-
-  *file_version = VERSION_IDENT(file_version_major,
-                               file_version_minor,
-                               file_version_patch);
-
-  *game_version = VERSION_IDENT(game_version_major,
-                               game_version_minor,
-                               game_version_patch);
-}
-
-static void WriteChunk_VERS(FILE *file, int file_version, int game_version)
-{
-  int file_version_major = VERSION_MAJOR(file_version);
-  int file_version_minor = VERSION_MINOR(file_version);
-  int file_version_patch = VERSION_PATCH(file_version);
-  int game_version_major = VERSION_MAJOR(game_version);
-  int game_version_minor = VERSION_MINOR(game_version);
-  int game_version_patch = VERSION_PATCH(game_version);
-
-  fputc(file_version_major, file);
-  fputc(file_version_minor, file);
-  fputc(file_version_patch, file);
-  fputc(0, file);      /* not used */
-
-  fputc(game_version_major, file);
-  fputc(game_version_minor, file);
-  fputc(game_version_patch, file);
-  fputc(0, file);      /* not used */
-}
+/* ========================================================================= */
+/* level file functions                                                      */
+/* ========================================================================= */
 
 static void setLevelInfoToDefaults()
 {
@@ -388,6 +70,7 @@ static void setLevelInfoToDefaults()
   level.amoeba_content = EL_DIAMANT;
   level.double_speed = FALSE;
   level.gravity = FALSE;
+  level.em_slippery_gems = FALSE;
 
   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     level.name[i] = '\0';
@@ -457,7 +140,8 @@ static int checkLevelElement(int element)
 
 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
 {
-  ReadChunk_VERS(file, &(level->file_version), &(level->game_version));
+  level->file_version = getFileVersion(file);
+  level->game_version = getFileVersion(file);
 
   return chunk_size;
 }
@@ -469,8 +153,8 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   lev_fieldx = level->fieldx = fgetc(file);
   lev_fieldy = level->fieldy = fgetc(file);
 
-  level->time          = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
-  level->gems_needed   = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+  level->time          = getFile16BitBE(file);
+  level->gems_needed   = getFile16BitBE(file);
 
   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     level->name[i] = fgetc(file);
@@ -491,8 +175,8 @@ static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
   level->amoeba_content                = checkLevelElement(fgetc(file));
   level->double_speed          = (fgetc(file) == 1 ? TRUE : FALSE);
   level->gravity               = (fgetc(file) == 1 ? TRUE : FALSE);
-
   level->encoding_16bit_field  = (fgetc(file) == 1 ? TRUE : FALSE);
+  level->em_slippery_gems      = (fgetc(file) == 1 ? TRUE : FALSE);
 
   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
 
@@ -546,8 +230,7 @@ static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
       for(x=0; x<3; x++)
        level->yam_content[i][x][y] =
          checkLevelElement(level->encoding_16bit_field ?
-                           getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
-                           fgetc(file));
+                           getFile16BitBE(file) : fgetc(file));
   return chunk_size;
 }
 
@@ -574,8 +257,7 @@ static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
     for(x=0; x<level->fieldx; x++)
       Feld[x][y] = Ur[x][y] =
        checkLevelElement(level->encoding_16bit_field ?
-                         getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
-                         fgetc(file));
+                         getFile16BitBE(file) : fgetc(file));
   return chunk_size;
 }
 
@@ -586,7 +268,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   int num_contents, content_xsize, content_ysize;
   int content_array[MAX_ELEMENT_CONTENTS][3][3];
 
-  element = checkLevelElement(getFile16BitInteger(file,BYTE_ORDER_BIG_ENDIAN));
+  element = checkLevelElement(getFile16BitBE(file));
   num_contents = fgetc(file);
   content_xsize = fgetc(file);
   content_ysize = fgetc(file);
@@ -595,8 +277,7 @@ static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       content_array[i][x][y] =
-         checkLevelElement(getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN));
+       content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
 
   /* correct invalid number of content fields -- should never happen */
   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
@@ -640,12 +321,12 @@ void LoadLevel(int level_nr)
     return;
   }
 
-  getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+  getFileChunkBE(file, chunk_name, NULL);
   if (strcmp(chunk_name, "RND1") == 0)
   {
-    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  /* not used */
+    getFile32BitBE(file);              /* not used */
 
-    getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+    getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "CAVE") != 0)
     {
       Error(ERR_WARN, "unknown format of level file '%s'", filename);
@@ -673,6 +354,9 @@ void LoadLevel(int level_nr)
       fclose(file);
       return;
     }
+
+    /* pre-2.0 level files have no game version, so use file version here */
+    level.game_version = level.file_version;
   }
 
   if (level.file_version < FILE_VERSION_1_2)
@@ -700,7 +384,7 @@ void LoadLevel(int level_nr)
       {  NULL,  0,                     NULL }
     };
 
-    while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
+    while (getFileChunkBE(file, chunk_name, &chunk_size))
     {
       int i = 0;
 
@@ -744,29 +428,60 @@ void LoadLevel(int level_nr)
   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
       IS_LEVELCLASS_USER(leveldir_current))
   {
-    /* for user contributed and private levels, use the version of
-       the game engine the levels were created for */
-    level.game_version = level.file_version;
-
-    /* player was faster than monsters in pre-1.0 levels */
+    /* For user contributed and private levels, use the version of
+       the game engine the levels were created for.
+       Since 2.0.1, the game engine version is now directly stored
+       in the level file (chunk "VERS"), so there is no need anymore
+       to set the game version from the file version (except for old,
+       pre-2.0 levels, where the game version is still taken from the
+       file format version used to store the level -- see above). */
+
+    /* do some special adjustments to support older level versions */
     if (level.file_version == FILE_VERSION_1_0)
     {
       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
       Error(ERR_WARN, "using high speed movement for player");
+
+      /* player was faster than monsters in (pre-)1.0 levels */
       level.double_speed = TRUE;
     }
+
+    /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
+    if (level.game_version == VERSION_IDENT(2,0,1))
+      level.em_slippery_gems = TRUE;
   }
   else
   {
-    /* always use the latest version of the game engine for all but
-       user contributed and private levels */
+    /* Always use the latest version of the game engine for all but
+       user contributed and private levels; this allows for actual
+       corrections in the game engine to take effect for existing,
+       converted levels (from "classic" or other existing games) to
+       make the game emulation more accurate, while (hopefully) not
+       breaking existing levels created from other players. */
+
     level.game_version = GAME_VERSION_ACTUAL;
+
+    /* Set special EM style gems behaviour: EM style gems slip down from
+       normal, steel and growing wall. As this is a more fundamental change,
+       it seems better to set the default behaviour to "off" (as it is more
+       natural) and make it configurable in the level editor (as a property
+       of gem style elements). Already existing converted levels (neither
+       private nor contributed levels) are changed to the new behaviour. */
+
+    if (level.file_version < FILE_VERSION_2_0)
+      level.em_slippery_gems = TRUE;
   }
 
   /* determine border element for this level */
   SetBorderElement();
 }
 
+static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
+{
+  putFileVersion(file, level->file_version);
+  putFileVersion(file, level->game_version);
+}
+
 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
 {
   int i, x, y;
@@ -774,8 +489,8 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
   fputc(level->fieldx, file);
   fputc(level->fieldy, file);
 
-  putFile16BitInteger(file, level->time,        BYTE_ORDER_BIG_ENDIAN);
-  putFile16BitInteger(file, level->gems_needed, BYTE_ORDER_BIG_ENDIAN);
+  putFile16BitBE(file, level->time);
+  putFile16BitBE(file, level->gems_needed);
 
   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
     fputc(level->name[i], file);
@@ -796,8 +511,8 @@ static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
        file);
   fputc((level->double_speed ? 1 : 0), file);
   fputc((level->gravity ? 1 : 0), file);
-
   fputc((level->encoding_16bit_field ? 1 : 0), file);
+  fputc((level->em_slippery_gems ? 1 : 0), file);
 
   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
 }
@@ -824,8 +539,7 @@ static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
        if (level->encoding_16bit_field)
-         putFile16BitInteger(file, level->yam_content[i][x][y],
-                             BYTE_ORDER_BIG_ENDIAN);
+         putFile16BitBE(file, level->yam_content[i][x][y]);
        else
          fputc(level->yam_content[i][x][y], file);
 }
@@ -838,7 +552,7 @@ static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
   for(y=0; y<level->fieldy; y++) 
     for(x=0; x<level->fieldx; x++) 
       if (level->encoding_16bit_field)
-       putFile16BitInteger(file, Ur[x][y], BYTE_ORDER_BIG_ENDIAN);
+       putFile16BitBE(file, Ur[x][y]);
       else
        fputc(Ur[x][y], file);
 }
@@ -881,7 +595,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
     return;
   }
 
-  putFile16BitInteger(file, element, BYTE_ORDER_BIG_ENDIAN);
+  putFile16BitBE(file, element);
   fputc(num_contents, file);
   fputc(content_xsize, file);
   fputc(content_ysize, file);
@@ -891,8 +605,7 @@ static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
     for(y=0; y<3; y++)
       for(x=0; x<3; x++)
-       putFile16BitInteger(file, content_array[i][x][y],
-                           BYTE_ORDER_BIG_ENDIAN);
+       putFile16BitBE(file, content_array[i][x][y]);
 }
 
 void SaveLevel(int level_nr)
@@ -908,6 +621,8 @@ void SaveLevel(int level_nr)
     return;
   }
 
+  level.file_version = FILE_VERSION_ACTUAL;
+  level.game_version = GAME_VERSION_ACTUAL;
 
   /* check level field for 16-bit elements */
   level.encoding_16bit_field = FALSE;
@@ -932,31 +647,31 @@ void SaveLevel(int level_nr)
   body_chunk_size =
     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
 
-  putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
-  putFileChunk(file, "CAVE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
+  putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
 
-  putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
-  WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
+  putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
+  SaveLevel_VERS(file, &level);
 
-  putFileChunk(file, "HEAD", LEVEL_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
   SaveLevel_HEAD(file, &level);
 
-  putFileChunk(file, "AUTH", MAX_LEVEL_AUTHOR_LEN, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
   SaveLevel_AUTH(file, &level);
 
-  putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "BODY", body_chunk_size);
   SaveLevel_BODY(file, &level);
 
   if (level.encoding_16bit_yamyam ||
       level.num_yam_contents != STD_ELEMENT_CONTENTS)
   {
-    putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
+    putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
     SaveLevel_CNT2(file, &level, EL_MAMPFER);
   }
 
   if (level.encoding_16bit_amoeba)
   {
-    putFileChunk(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE, BYTE_ORDER_BIG_ENDIAN);
+    putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
     SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
   }
 
@@ -965,13 +680,16 @@ void SaveLevel(int level_nr)
   SetFilePermissions(filename, PERMS_PRIVATE);
 }
 
+
+/* ========================================================================= */
+/* tape file functions                                                       */
+/* ========================================================================= */
+
 static void setTapeInfoToDefaults()
 {
   int i;
 
   /* always start with reliable default values (empty tape) */
-  tape.file_version = FILE_VERSION_ACTUAL;
-  tape.game_version = GAME_VERSION_ACTUAL;
   TapeErase();
 
   /* default values (also for pre-1.2 tapes) with only the first player */
@@ -993,7 +711,8 @@ static void setTapeInfoToDefaults()
 
 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
 {
-  ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
+  tape->file_version = getFileVersion(file);
+  tape->game_version = getFileVersion(file);
 
   return chunk_size;
 }
@@ -1002,16 +721,15 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
 {
   int i;
 
-  tape->random_seed = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
-  tape->date        = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
-  tape->length      = getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+  tape->random_seed = getFile32BitBE(file);
+  tape->date        = getFile32BitBE(file);
+  tape->length      = getFile32BitBE(file);
 
   /* read header fields that are new since version 1.2 */
   if (tape->file_version >= FILE_VERSION_1_2)
   {
     byte store_participating_players = fgetc(file);
-
-    ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
+    int engine_version;
 
     /* since version 1.2, tapes store which players participate in the tape */
     tape->num_participating_players = 0;
@@ -1025,6 +743,12 @@ static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
        tape->num_participating_players++;
       }
     }
+
+    ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
+
+    engine_version = getFileVersion(file);
+    if (engine_version > 0)
+      tape->engine_version = engine_version;
   }
 
   return chunk_size;
@@ -1086,6 +810,8 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
     }
     else if (tape->file_version < FILE_VERSION_2_0)
     {
+      /* convert pre-2.0 tapes to new tape format */
+
       if (tape->pos[i].delay > 1)
       {
        /* action part */
@@ -1126,12 +852,12 @@ void LoadTape(int level_nr)
   if (!(file = fopen(filename, MODE_READ)))
     return;
 
-  getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+  getFileChunkBE(file, chunk_name, NULL);
   if (strcmp(chunk_name, "RND1") == 0)
   {
-    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  /* not used */
+    getFile32BitBE(file);              /* not used */
 
-    getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
+    getFileChunkBE(file, chunk_name, NULL);
     if (strcmp(chunk_name, "TAPE") != 0)
     {
       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
@@ -1159,9 +885,10 @@ void LoadTape(int level_nr)
       fclose(file);
       return;
     }
-  }
 
-  tape.game_version = tape.file_version;
+    /* pre-2.0 tape files have no game version, so use file version here */
+    tape.game_version = tape.file_version;
+  }
 
   if (tape.file_version < FILE_VERSION_1_2)
   {
@@ -1185,7 +912,7 @@ void LoadTape(int level_nr)
       {  NULL,  0,                     NULL }
     };
 
-    while (getFileChunk(file, chunk_name, &chunk_size, BYTE_ORDER_BIG_ENDIAN))
+    while (getFileChunkBE(file, chunk_name, &chunk_size))
     {
       int i = 0;
 
@@ -1229,6 +956,12 @@ void LoadTape(int level_nr)
   tape.length_seconds = GetTapeLength();
 }
 
+static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
+{
+  putFileVersion(file, tape->file_version);
+  putFileVersion(file, tape->game_version);
+}
+
 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
 {
   int i;
@@ -1239,13 +972,16 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
     if (tape->player_participates[i])
       store_participating_players |= (1 << i);
 
-  putFile32BitInteger(file, tape->random_seed, BYTE_ORDER_BIG_ENDIAN);
-  putFile32BitInteger(file, tape->date, BYTE_ORDER_BIG_ENDIAN);
-  putFile32BitInteger(file, tape->length, BYTE_ORDER_BIG_ENDIAN);
+  putFile32BitBE(file, tape->random_seed);
+  putFile32BitBE(file, tape->date);
+  putFile32BitBE(file, tape->length);
 
   fputc(store_participating_players, file);
 
+  /* unused bytes not at the end here for 4-byte alignment of engine_version */
   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
+
+  putFileVersion(file, tape->engine_version);
 }
 
 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
@@ -1287,6 +1023,9 @@ void SaveTape(int level_nr)
     return;
   }
 
+  tape.file_version = FILE_VERSION_ACTUAL;
+  tape.game_version = GAME_VERSION_ACTUAL;
+
   /* count number of participating players  */
   for(i=0; i<MAX_PLAYERS; i++)
     if (tape.player_participates[i])
@@ -1294,16 +1033,16 @@ void SaveTape(int level_nr)
 
   body_chunk_size = (num_participating_players + 1) * tape.length;
 
-  putFileChunk(file, "RND1", CHUNK_SIZE_UNDEFINED, BYTE_ORDER_BIG_ENDIAN);
-  putFileChunk(file, "TAPE", CHUNK_SIZE_NONE,      BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
+  putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
 
-  putFileChunk(file, "VERS", FILE_VERS_CHUNK_SIZE, BYTE_ORDER_BIG_ENDIAN);
-  WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
+  putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
+  SaveTape_VERS(file, &tape);
 
-  putFileChunk(file, "HEAD", TAPE_HEADER_SIZE, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
   SaveTape_HEAD(file, &tape);
 
-  putFileChunk(file, "BODY", body_chunk_size, BYTE_ORDER_BIG_ENDIAN);
+  putFileChunkBE(file, "BODY", body_chunk_size);
   SaveTape_BODY(file, &tape);
 
   fclose(file);
@@ -1328,7 +1067,7 @@ void DumpTape(struct TapeInfo *tape)
 
   printf("\n");
   printf("-------------------------------------------------------------------------------\n");
-  printf("Tape of Level %d (file version %06d, game version %06d\n",
+  printf("Tape of Level %d (file version %06d, game version %06d)\n",
         tape->level_nr, tape->file_version, tape->game_version);
   printf("-------------------------------------------------------------------------------\n");
 
@@ -1360,6 +1099,11 @@ void DumpTape(struct TapeInfo *tape)
   printf("-------------------------------------------------------------------------------\n");
 }
 
+
+/* ========================================================================= */
+/* score file functions                                                      */
+/* ========================================================================= */
+
 void LoadScore(int level_nr)
 {
   int i;
@@ -1437,91 +1181,107 @@ void SaveScore(int level_nr)
   SetFilePermissions(filename, PERMS_PUBLIC);
 }
 
-/* ------------------------------------------------------------------------- */
-/* setup file stuff                                                          */
-/* ------------------------------------------------------------------------- */
 
-#define TOKEN_STR_LAST_LEVEL_SERIES    "last_level_series"
-#define TOKEN_STR_LAST_PLAYED_LEVEL    "last_played_level"
-#define TOKEN_STR_HANDICAP_LEVEL       "handicap_level"
-#define TOKEN_STR_PLAYER_PREFIX                "player_"
+/* ========================================================================= */
+/* setup file functions                                                      */
+/* ========================================================================= */
+
+#define TOKEN_STR_PLAYER_PREFIX                        "player_"
 
 /* global setup */
-#define SETUP_TOKEN_PLAYER_NAME                0
-#define SETUP_TOKEN_SOUND              1
-#define SETUP_TOKEN_SOUND_LOOPS                2
-#define SETUP_TOKEN_SOUND_MUSIC                3
-#define SETUP_TOKEN_SOUND_SIMPLE       4
-#define SETUP_TOKEN_SCROLL_DELAY       5
-#define SETUP_TOKEN_SOFT_SCROLLING     6
-#define SETUP_TOKEN_FADING             7
-#define SETUP_TOKEN_AUTORECORD         8
-#define SETUP_TOKEN_QUICK_DOORS                9
-#define SETUP_TOKEN_TEAM_MODE          10
-#define SETUP_TOKEN_HANDICAP           11
-#define SETUP_TOKEN_TIME_LIMIT         12
-#define SETUP_TOKEN_FULLSCREEN         13
+#define SETUP_TOKEN_PLAYER_NAME                        0
+#define SETUP_TOKEN_SOUND                      1
+#define SETUP_TOKEN_SOUND_LOOPS                        2
+#define SETUP_TOKEN_SOUND_MUSIC                        3
+#define SETUP_TOKEN_SOUND_SIMPLE               4
+#define SETUP_TOKEN_TOONS                      5
+#define SETUP_TOKEN_SCROLL_DELAY               6
+#define SETUP_TOKEN_SOFT_SCROLLING             7
+#define SETUP_TOKEN_FADING                     8
+#define SETUP_TOKEN_AUTORECORD                 9
+#define SETUP_TOKEN_QUICK_DOORS                        10
+#define SETUP_TOKEN_TEAM_MODE                  11
+#define SETUP_TOKEN_HANDICAP                   12
+#define SETUP_TOKEN_TIME_LIMIT                 13
+#define SETUP_TOKEN_FULLSCREEN                 14
+#define SETUP_TOKEN_ASK_ON_ESCAPE              15
+#define SETUP_TOKEN_GRAPHICS_SET               16
+#define SETUP_TOKEN_SOUNDS_SET                 17
+#define SETUP_TOKEN_MUSIC_SET                  18
+#define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS    19
+#define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS      20
+#define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC       21
+
+#define NUM_GLOBAL_SETUP_TOKENS                        22
+
+/* shortcut setup */
+#define SETUP_TOKEN_SAVE_GAME                  0
+#define SETUP_TOKEN_LOAD_GAME                  1
+#define SETUP_TOKEN_TOGGLE_PAUSE               2
+
+#define NUM_SHORTCUT_SETUP_TOKENS              3
 
 /* player setup */
-#define SETUP_TOKEN_USE_JOYSTICK       14
-#define SETUP_TOKEN_JOY_DEVICE_NAME    15
-#define SETUP_TOKEN_JOY_XLEFT          16
-#define SETUP_TOKEN_JOY_XMIDDLE                17
-#define SETUP_TOKEN_JOY_XRIGHT         18
-#define SETUP_TOKEN_JOY_YUPPER         19
-#define SETUP_TOKEN_JOY_YMIDDLE                20
-#define SETUP_TOKEN_JOY_YLOWER         21
-#define SETUP_TOKEN_JOY_SNAP           22
-#define SETUP_TOKEN_JOY_BOMB           23
-#define SETUP_TOKEN_KEY_LEFT           24
-#define SETUP_TOKEN_KEY_RIGHT          25
-#define SETUP_TOKEN_KEY_UP             26
-#define SETUP_TOKEN_KEY_DOWN           27
-#define SETUP_TOKEN_KEY_SNAP           28
-#define SETUP_TOKEN_KEY_BOMB           29
-
-/* level directory info */
-#define LEVELINFO_TOKEN_NAME           30
-#define LEVELINFO_TOKEN_NAME_SHORT     31
-#define LEVELINFO_TOKEN_NAME_SORTING   32
-#define LEVELINFO_TOKEN_AUTHOR         33
-#define LEVELINFO_TOKEN_IMPORTED_FROM  34
-#define LEVELINFO_TOKEN_LEVELS         35
-#define LEVELINFO_TOKEN_FIRST_LEVEL    36
-#define LEVELINFO_TOKEN_SORT_PRIORITY  37
-#define LEVELINFO_TOKEN_LEVEL_GROUP    38
-#define LEVELINFO_TOKEN_READONLY       39
-
-#define FIRST_GLOBAL_SETUP_TOKEN       SETUP_TOKEN_PLAYER_NAME
-#define LAST_GLOBAL_SETUP_TOKEN                SETUP_TOKEN_FULLSCREEN
-
-#define FIRST_PLAYER_SETUP_TOKEN       SETUP_TOKEN_USE_JOYSTICK
-#define LAST_PLAYER_SETUP_TOKEN                SETUP_TOKEN_KEY_BOMB
-
-#define FIRST_LEVELINFO_TOKEN          LEVELINFO_TOKEN_NAME
-#define LAST_LEVELINFO_TOKEN           LEVELINFO_TOKEN_READONLY
+#define SETUP_TOKEN_USE_JOYSTICK               0
+#define SETUP_TOKEN_JOY_DEVICE_NAME            1
+#define SETUP_TOKEN_JOY_XLEFT                  2
+#define SETUP_TOKEN_JOY_XMIDDLE                        3
+#define SETUP_TOKEN_JOY_XRIGHT                 4
+#define SETUP_TOKEN_JOY_YUPPER                 5
+#define SETUP_TOKEN_JOY_YMIDDLE                        6
+#define SETUP_TOKEN_JOY_YLOWER                 7
+#define SETUP_TOKEN_JOY_SNAP                   8
+#define SETUP_TOKEN_JOY_BOMB                   9
+#define SETUP_TOKEN_KEY_LEFT                   10
+#define SETUP_TOKEN_KEY_RIGHT                  11
+#define SETUP_TOKEN_KEY_UP                     12
+#define SETUP_TOKEN_KEY_DOWN                   13
+#define SETUP_TOKEN_KEY_SNAP                   14
+#define SETUP_TOKEN_KEY_BOMB                   15
+
+#define NUM_PLAYER_SETUP_TOKENS                        16
 
 static struct SetupInfo si;
+static struct SetupShortcutInfo ssi;
 static struct SetupInputInfo sii;
-static struct LevelDirInfo ldi;
-static struct TokenInfo token_info[] =
+
+static struct TokenInfo global_setup_tokens[] =
 {
   /* global setup */
-  { TYPE_STRING,  &si.player_name,     "player_name"                   },
-  { TYPE_SWITCH,  &si.sound,           "sound"                         },
-  { TYPE_SWITCH,  &si.sound_loops,     "repeating_sound_loops"         },
-  { TYPE_SWITCH,  &si.sound_music,     "background_music"              },
-  { TYPE_SWITCH,  &si.sound_simple,    "simple_sound_effects"          },
-  { TYPE_SWITCH,  &si.scroll_delay,    "scroll_delay"                  },
-  { TYPE_SWITCH,  &si.soft_scrolling,  "soft_scrolling"                },
-  { TYPE_SWITCH,  &si.fading,          "screen_fading"                 },
-  { TYPE_SWITCH,  &si.autorecord,      "automatic_tape_recording"      },
-  { TYPE_SWITCH,  &si.quick_doors,     "quick_doors"                   },
-  { TYPE_SWITCH,  &si.team_mode,       "team_mode"                     },
-  { TYPE_SWITCH,  &si.handicap,                "handicap"                      },
-  { TYPE_SWITCH,  &si.time_limit,      "time_limit"                    },
-  { TYPE_SWITCH,  &si.fullscreen,      "fullscreen"                    },
+  { TYPE_STRING, &si.player_name,      "player_name"                   },
+  { TYPE_SWITCH, &si.sound,            "sound"                         },
+  { TYPE_SWITCH, &si.sound_loops,      "repeating_sound_loops"         },
+  { TYPE_SWITCH, &si.sound_music,      "background_music"              },
+  { TYPE_SWITCH, &si.sound_simple,     "simple_sound_effects"          },
+  { TYPE_SWITCH, &si.toons,            "toons"                         },
+  { TYPE_SWITCH, &si.scroll_delay,     "scroll_delay"                  },
+  { TYPE_SWITCH, &si.soft_scrolling,   "soft_scrolling"                },
+  { TYPE_SWITCH, &si.fading,           "screen_fading"                 },
+  { TYPE_SWITCH, &si.autorecord,       "automatic_tape_recording"      },
+  { TYPE_SWITCH, &si.quick_doors,      "quick_doors"                   },
+  { TYPE_SWITCH, &si.team_mode,                "team_mode"                     },
+  { TYPE_SWITCH, &si.handicap,         "handicap"                      },
+  { TYPE_SWITCH, &si.time_limit,       "time_limit"                    },
+  { TYPE_SWITCH, &si.fullscreen,       "fullscreen"                    },
+  { TYPE_SWITCH, &si.ask_on_escape,    "ask_on_escape"                 },
+  { TYPE_STRING, &si.graphics_set,     "graphics_set"                  },
+  { TYPE_STRING, &si.sounds_set,       "sounds_set"                    },
+  { TYPE_STRING, &si.music_set,                "music_set"                     },
+  { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics"        },
+  { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"  },
+  { TYPE_SWITCH, &si.override_level_music,    "override_level_music"   },
+};
+
+static struct TokenInfo shortcut_setup_tokens[] =
+{
+  /* shortcut setup */
+  { TYPE_KEY_X11, &ssi.save_game,      "shortcut.save_game"            },
+  { TYPE_KEY_X11, &ssi.load_game,      "shortcut.load_game"            },
+  { TYPE_KEY_X11, &ssi.toggle_pause,   "shortcut.toggle_pause"         }
+};
 
+static struct TokenInfo player_setup_tokens[] =
+{
   /* player setup */
   { TYPE_BOOLEAN, &sii.use_joystick,   ".use_joystick"                 },
   { TYPE_STRING,  &sii.joy.device_name,        ".joy.device_name"              },
@@ -1533,91 +1293,14 @@ static struct TokenInfo token_info[] =
   { TYPE_INTEGER, &sii.joy.ylower,     ".joy.ylower"                   },
   { TYPE_INTEGER, &sii.joy.snap,       ".joy.snap_field"               },
   { TYPE_INTEGER, &sii.joy.bomb,       ".joy.place_bomb"               },
-  { TYPE_KEY,     &sii.key.left,       ".key.move_left"                },
-  { TYPE_KEY,     &sii.key.right,      ".key.move_right"               },
-  { TYPE_KEY,     &sii.key.up,         ".key.move_up"                  },
-  { TYPE_KEY,     &sii.key.down,       ".key.move_down"                },
-  { TYPE_KEY,     &sii.key.snap,       ".key.snap_field"               },
-  { TYPE_KEY,     &sii.key.bomb,       ".key.place_bomb"               },
-
-  /* level directory info */
-  { TYPE_STRING,  &ldi.name,           "name"                          },
-  { TYPE_STRING,  &ldi.name_short,     "name_short"                    },
-  { TYPE_STRING,  &ldi.name_sorting,   "name_sorting"                  },
-  { TYPE_STRING,  &ldi.author,         "author"                        },
-  { TYPE_STRING,  &ldi.imported_from,  "imported_from"                 },
-  { TYPE_INTEGER, &ldi.levels,         "levels"                        },
-  { TYPE_INTEGER, &ldi.first_level,    "first_level"                   },
-  { TYPE_INTEGER, &ldi.sort_priority,  "sort_priority"                 },
-  { TYPE_BOOLEAN, &ldi.level_group,    "level_group"                   },
-  { TYPE_BOOLEAN, &ldi.readonly,       "readonly"                      }
+  { TYPE_KEY_X11, &sii.key.left,       ".key.move_left"                },
+  { TYPE_KEY_X11, &sii.key.right,      ".key.move_right"               },
+  { TYPE_KEY_X11, &sii.key.up,         ".key.move_up"                  },
+  { TYPE_KEY_X11, &sii.key.down,       ".key.move_down"                },
+  { TYPE_KEY_X11, &sii.key.snap,       ".key.snap_field"               },
+  { TYPE_KEY_X11, &sii.key.bomb,       ".key.place_bomb"               }
 };
 
-static void setLevelDirInfoToDefaults(struct LevelDirInfo *ldi)
-{
-  ldi->filename = NULL;
-  ldi->fullpath = NULL;
-  ldi->basepath = NULL;
-  ldi->name = getStringCopy(ANONYMOUS_NAME);
-  ldi->name_short = NULL;
-  ldi->name_sorting = NULL;
-  ldi->author = getStringCopy(ANONYMOUS_NAME);
-  ldi->imported_from = NULL;
-  ldi->levels = 0;
-  ldi->first_level = 0;
-  ldi->last_level = 0;
-  ldi->sort_priority = LEVELCLASS_UNDEFINED;   /* default: least priority */
-  ldi->level_group = FALSE;
-  ldi->parent_link = FALSE;
-  ldi->user_defined = FALSE;
-  ldi->readonly = TRUE;
-  ldi->color = 0;
-  ldi->class_desc = NULL;
-  ldi->handicap_level = 0;
-  ldi->cl_first = -1;
-  ldi->cl_cursor = -1;
-
-  ldi->node_parent = NULL;
-  ldi->node_group = NULL;
-  ldi->next = NULL;
-}
-
-static void setLevelDirInfoToDefaultsFromParent(struct LevelDirInfo *ldi,
-                                               struct LevelDirInfo *parent)
-{
-  if (parent == NULL)
-  {
-    setLevelDirInfoToDefaults(ldi);
-    return;
-  }
-
-  /* first copy all values from the parent structure ... */
-  *ldi = *parent;
-
-  /* ... then set all fields to default that cannot be inherited from parent.
-     This is especially important for all those fields that can be set from
-     the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
-     calls 'free()' for all already set token values which requires that no
-     other structure's pointer may point to them!
-  */
-
-  ldi->filename = NULL;
-  ldi->fullpath = NULL;
-  ldi->basepath = NULL;
-  ldi->name = getStringCopy(ANONYMOUS_NAME);
-  ldi->name_short = NULL;
-  ldi->name_sorting = NULL;
-  ldi->author = getStringCopy(parent->author);
-  ldi->imported_from = getStringCopy(parent->imported_from);
-
-  ldi->level_group = FALSE;
-  ldi->parent_link = FALSE;
-
-  ldi->node_parent = parent;
-  ldi->node_group = NULL;
-  ldi->next = NULL;
-}
-
 static void setSetupInfoToDefaults(struct SetupInfo *si)
 {
   int i;
@@ -1640,11 +1323,23 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->handicap = TRUE;
   si->time_limit = TRUE;
   si->fullscreen = FALSE;
+  si->ask_on_escape = TRUE;
+
+  si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
+  si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
+  si->music_set = getStringCopy(MUSIC_SUBDIR);
+  si->override_level_graphics = FALSE;
+  si->override_level_sounds = FALSE;
+  si->override_level_music = FALSE;
+
+  si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
+  si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
+  si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
 
   for (i=0; i<MAX_PLAYERS; i++)
   {
     si->input[i].use_joystick = FALSE;
-    si->input[i].joy.device_name = getStringCopy(joystick_device_name[i]);
+    si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
@@ -1662,41 +1357,6 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   }
 }
 
-static void setSetupInfo(int token_nr, char *token_value)
-{
-  int token_type = token_info[token_nr].type;
-  void *setup_value = token_info[token_nr].value;
-
-  if (token_value == NULL)
-    return;
-
-  /* set setup field to corresponding token value */
-  switch (token_type)
-  {
-    case TYPE_BOOLEAN:
-    case TYPE_SWITCH:
-      *(boolean *)setup_value = get_string_boolean_value(token_value);
-      break;
-
-    case TYPE_KEY:
-      *(Key *)setup_value = getKeyFromX11KeyName(token_value);
-      break;
-
-    case TYPE_INTEGER:
-      *(int *)setup_value = get_string_integer_value(token_value);
-      break;
-
-    case TYPE_STRING:
-      if (*(char **)setup_value != NULL)
-       free(*(char **)setup_value);
-      *(char **)setup_value = getStringCopy(token_value);
-      break;
-
-    default:
-      break;
-  }
-}
-
 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
 {
   int i, pnr;
@@ -1704,13 +1364,21 @@ static void decodeSetupFileList(struct SetupFileList *setup_file_list)
   if (!setup_file_list)
     return;
 
-  /* handle global setup values */
+  /* global setup */
   si = setup;
-  for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
-    setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
+  for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
+    setSetupInfo(global_setup_tokens, i,
+                getTokenValue(setup_file_list, global_setup_tokens[i].text));
   setup = si;
 
-  /* handle player specific setup values */
+  /* shortcut setup */
+  ssi = setup.shortcut;
+  for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
+    setSetupInfo(shortcut_setup_tokens, i,
+                getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
+  setup.shortcut = ssi;
+
+  /* player setup */
   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
   {
     char prefix[30];
@@ -1718,264 +1386,31 @@ static void decodeSetupFileList(struct SetupFileList *setup_file_list)
     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
 
     sii = setup.input[pnr];
-    for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
+    for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
     {
       char full_token[100];
 
-      sprintf(full_token, "%s%s", prefix, token_info[i].text);
-      setSetupInfo(i, getTokenValue(setup_file_list, full_token));
+      sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
+      setSetupInfo(player_setup_tokens, i,
+                  getTokenValue(setup_file_list, full_token));
     }
     setup.input[pnr] = sii;
   }
 }
 
-static int compareLevelDirInfoEntries(const void *object1, const void *object2)
-{
-  const struct LevelDirInfo *entry1 = *((struct LevelDirInfo **)object1);
-  const struct LevelDirInfo *entry2 = *((struct LevelDirInfo **)object2);
-  int compare_result;
-
-  if (entry1->parent_link || entry2->parent_link)
-    compare_result = (entry1->parent_link ? -1 : +1);
-  else if (entry1->sort_priority == entry2->sort_priority)
-  {
-    char *name1 = getStringToLower(entry1->name_sorting);
-    char *name2 = getStringToLower(entry2->name_sorting);
-
-    compare_result = strcmp(name1, name2);
-
-    free(name1);
-    free(name2);
-  }
-  else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
-    compare_result = entry1->sort_priority - entry2->sort_priority;
-  else
-    compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
-
-  return compare_result;
-}
-
-static void createParentLevelDirNode(struct LevelDirInfo *node_parent)
-{
-  struct LevelDirInfo *leveldir_new = newLevelDirInfo();
-
-  setLevelDirInfoToDefaults(leveldir_new);
-
-  leveldir_new->node_parent = node_parent;
-  leveldir_new->parent_link = TRUE;
-
-  leveldir_new->name = ".. (parent directory)";
-  leveldir_new->name_short = getStringCopy(leveldir_new->name);
-  leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
-
-  leveldir_new->filename = "..";
-  leveldir_new->fullpath = getStringCopy(node_parent->fullpath);
-
-  leveldir_new->sort_priority = node_parent->sort_priority;
-  leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
-
-  pushLevelDirInfo(&node_parent->node_group, leveldir_new);
-}
-
-static void LoadLevelInfoFromLevelDir(struct LevelDirInfo **node_first,
-                                     struct LevelDirInfo *node_parent,
-                                     char *level_directory)
-{
-  DIR *dir;
-  struct dirent *dir_entry;
-  boolean valid_entry_found = FALSE;
-
-  if ((dir = opendir(level_directory)) == NULL)
-  {
-    Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
-    return;
-  }
-
-  while ((dir_entry = readdir(dir)) != NULL)   /* loop until last dir entry */
-  {
-    struct SetupFileList *setup_file_list = NULL;
-    struct stat file_status;
-    char *directory_name = dir_entry->d_name;
-    char *directory_path = getPath2(level_directory, directory_name);
-    char *filename = NULL;
-
-    /* skip entries for current and parent directory */
-    if (strcmp(directory_name, ".")  == 0 ||
-       strcmp(directory_name, "..") == 0)
-    {
-      free(directory_path);
-      continue;
-    }
-
-    /* find out if directory entry is itself a directory */
-    if (stat(directory_path, &file_status) != 0 ||     /* cannot stat file */
-       (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
-    {
-      free(directory_path);
-      continue;
-    }
-
-    filename = getPath2(directory_path, LEVELINFO_FILENAME);
-    setup_file_list = loadSetupFileList(filename);
-
-    if (setup_file_list)
-    {
-      struct LevelDirInfo *leveldir_new = newLevelDirInfo();
-      int i;
-
-      checkSetupFileListIdentifier(setup_file_list, LEVELINFO_COOKIE);
-      setLevelDirInfoToDefaultsFromParent(leveldir_new, node_parent);
-
-      /* set all structure fields according to the token/value pairs */
-      ldi = *leveldir_new;
-      for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
-       setSetupInfo(i, getTokenValue(setup_file_list, token_info[i].text));
-      *leveldir_new = ldi;
-
-      DrawInitText(leveldir_new->name, 150, FC_YELLOW);
-
-      if (leveldir_new->name_short == NULL)
-       leveldir_new->name_short = getStringCopy(leveldir_new->name);
-
-      if (leveldir_new->name_sorting == NULL)
-       leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
-
-      leveldir_new->filename = getStringCopy(directory_name);
-
-      if (node_parent == NULL)         /* top level group */
-      {
-       leveldir_new->basepath = level_directory;
-       leveldir_new->fullpath = leveldir_new->filename;
-      }
-      else                             /* sub level group */
-      {
-       leveldir_new->basepath = node_parent->basepath;
-       leveldir_new->fullpath = getPath2(node_parent->fullpath,
-                                         directory_name);
-      }
-
-      if (leveldir_new->levels < 1)
-       leveldir_new->levels = 1;
-
-      leveldir_new->last_level =
-       leveldir_new->first_level + leveldir_new->levels - 1;
-
-      leveldir_new->user_defined =
-       (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
-
-      leveldir_new->color = LEVELCOLOR(leveldir_new);
-      leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
-
-      leveldir_new->handicap_level =   /* set handicap to default value */
-       (leveldir_new->user_defined ?
-        leveldir_new->last_level :
-        leveldir_new->first_level);
-
-      pushLevelDirInfo(node_first, leveldir_new);
-
-      freeSetupFileList(setup_file_list);
-      valid_entry_found = TRUE;
-
-      if (leveldir_new->level_group)
-      {
-       /* create node to link back to current level directory */
-       createParentLevelDirNode(leveldir_new);
-
-       /* step into sub-directory and look for more level series */
-       LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
-                                 leveldir_new, directory_path);
-      }
-    }
-    else
-      Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
-
-    free(directory_path);
-    free(filename);
-  }
-
-  closedir(dir);
-
-  if (!valid_entry_found)
-    Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
-         level_directory);
-}
-
-void LoadLevelInfo()
-{
-  InitUserLevelDirectory(getLoginName());
-
-  DrawInitText("Loading level series:", 120, FC_GREEN);
-
-  LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
-  LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(""));
-
-  leveldir_current = getFirstValidLevelSeries(leveldir_first);
-
-  if (leveldir_first == NULL)
-    Error(ERR_EXIT, "cannot find any valid level series in any directory");
-
-  sortLevelDirInfo(&leveldir_first, compareLevelDirInfoEntries);
-
-#if 0
-  dumpLevelDirInfo(leveldir_first, 0);
-#endif
-}
-
-static void SaveUserLevelInfo()
-{
-  char *filename;
-  FILE *file;
-  int i;
-
-  filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
-
-  if (!(file = fopen(filename, MODE_WRITE)))
-  {
-    Error(ERR_WARN, "cannot write level info file '%s'", filename);
-    free(filename);
-    return;
-  }
-
-  /* always start with reliable default values */
-  setLevelDirInfoToDefaults(&ldi);
-
-  ldi.name = getLoginName();
-  ldi.author = getRealName();
-  ldi.levels = 100;
-  ldi.first_level = 1;
-  ldi.sort_priority = LEVELCLASS_USER_START;
-  ldi.readonly = FALSE;
-
-  fprintf(file, "%s\n\n",
-         getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, LEVELINFO_COOKIE));
-
-  for (i=FIRST_LEVELINFO_TOKEN; i<=LAST_LEVELINFO_TOKEN; i++)
-    if (i != LEVELINFO_TOKEN_NAME_SHORT &&
-       i != LEVELINFO_TOKEN_NAME_SORTING &&
-       i != LEVELINFO_TOKEN_IMPORTED_FROM)
-      fprintf(file, "%s\n", getSetupLine("", i));
-
-  fclose(file);
-  free(filename);
-
-  SetFilePermissions(filename, PERMS_PRIVATE);
-}
-
 void LoadSetup()
 {
-  char *filename;
+  char *filename = getSetupFilename();
   struct SetupFileList *setup_file_list = NULL;
 
   /* always start with reliable default values */
   setSetupInfoToDefaults(&setup);
 
-  filename = getPath2(getSetupDir(), SETUP_FILENAME);
-
   setup_file_list = loadSetupFileList(filename);
 
   if (setup_file_list)
   {
-    checkSetupFileListIdentifier(setup_file_list, SETUP_COOKIE);
+    checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
     decodeSetupFileList(setup_file_list);
 
     setup.direct_draw = !setup.double_buffering;
@@ -1996,106 +1431,44 @@ void LoadSetup()
   }
   else
     Error(ERR_WARN, "using default setup values");
-
-  free(filename);
-}
-
-static char *getSetupLine(char *prefix, int token_nr)
-{
-  int i;
-  static char entry[MAX_LINE_LEN];
-  int token_type = token_info[token_nr].type;
-  void *setup_value = token_info[token_nr].value;
-  char *token_text = token_info[token_nr].text;
-
-  /* start with the prefix, token and some spaces to format output line */
-  sprintf(entry, "%s%s:", prefix, token_text);
-  for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
-    strcat(entry, " ");
-
-  /* continue with the token's value (which can have different types) */
-  switch (token_type)
-  {
-    case TYPE_BOOLEAN:
-      strcat(entry, (*(boolean *)setup_value ? "true" : "false"));
-      break;
-
-    case TYPE_SWITCH:
-      strcat(entry, (*(boolean *)setup_value ? "on" : "off"));
-      break;
-
-    case TYPE_KEY:
-      {
-       Key key = *(Key *)setup_value;
-       char *keyname = getKeyNameFromKey(key);
-
-       strcat(entry, getX11KeyNameFromKey(key));
-       for (i=strlen(entry); i<50; i++)
-         strcat(entry, " ");
-
-       /* add comment, if useful */
-       if (strcmp(keyname, "(undefined)") != 0 &&
-           strcmp(keyname, "(unknown)") != 0)
-       {
-         strcat(entry, "# ");
-         strcat(entry, keyname);
-       }
-      }
-      break;
-
-    case TYPE_INTEGER:
-      {
-       char buffer[MAX_LINE_LEN];
-
-       sprintf(buffer, "%d", *(int *)setup_value);
-       strcat(entry, buffer);
-      }
-      break;
-
-    case TYPE_STRING:
-      strcat(entry, *(char **)setup_value);
-      break;
-
-    default:
-      break;
-  }
-
-  return entry;
 }
 
 void SaveSetup()
 {
-  int i, pnr;
-  char *filename;
+  char *filename = getSetupFilename();
   FILE *file;
+  int i, pnr;
 
   InitUserDataDirectory();
 
-  filename = getPath2(getSetupDir(), SETUP_FILENAME);
-
   if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot write setup file '%s'", filename);
-    free(filename);
     return;
   }
 
-  fprintf(file, "%s\n",
-         getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, SETUP_COOKIE));
+  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
+                                              getCookie("SETUP")));
   fprintf(file, "\n");
 
-  /* handle global setup values */
+  /* global setup */
   si = setup;
-  for (i=FIRST_GLOBAL_SETUP_TOKEN; i<=LAST_GLOBAL_SETUP_TOKEN; i++)
+  for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
   {
-    fprintf(file, "%s\n", getSetupLine("", i));
+    fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
 
     /* just to make things nicer :) */
-    if (i == SETUP_TOKEN_PLAYER_NAME)
+    if (i == SETUP_TOKEN_PLAYER_NAME || i == SETUP_TOKEN_GRAPHICS_SET - 1)
       fprintf(file, "\n");
   }
 
-  /* handle player specific setup values */
+  /* shortcut setup */
+  ssi = setup.shortcut;
+  fprintf(file, "\n");
+  for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
+    fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
+
+  /* player setup */
   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
   {
     char prefix[30];
@@ -2104,224 +1477,11 @@ void SaveSetup()
     fprintf(file, "\n");
 
     sii = setup.input[pnr];
-    for (i=FIRST_PLAYER_SETUP_TOKEN; i<=LAST_PLAYER_SETUP_TOKEN; i++)
-      fprintf(file, "%s\n", getSetupLine(prefix, i));
+    for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
+      fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
   }
 
   fclose(file);
-  free(filename);
-
-  SetFilePermissions(filename, PERMS_PRIVATE);
-}
-
-void LoadLevelSetup_LastSeries()
-{
-  char *filename;
-  struct SetupFileList *level_setup_list = NULL;
-
-  /* always start with reliable default values */
-  leveldir_current = getFirstValidLevelSeries(leveldir_first);
-
-  /* ----------------------------------------------------------------------- */
-  /* ~/.rocksndiamonds/levelsetup.conf                                       */
-  /* ----------------------------------------------------------------------- */
-
-  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
-
-  if ((level_setup_list = loadSetupFileList(filename)))
-  {
-    char *last_level_series =
-      getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
-
-    leveldir_current = getLevelDirInfoFromFilename(last_level_series);
-    if (leveldir_current == NULL)
-      leveldir_current = leveldir_first;
-
-    checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
-
-    freeSetupFileList(level_setup_list);
-  }
-  else
-    Error(ERR_WARN, "using default setup values");
-
-  free(filename);
-}
-
-void SaveLevelSetup_LastSeries()
-{
-  char *filename;
-  char *level_subdir = leveldir_current->filename;
-  FILE *file;
-
-  /* ----------------------------------------------------------------------- */
-  /* ~/.rocksndiamonds/levelsetup.conf                                       */
-  /* ----------------------------------------------------------------------- */
-
-  InitUserDataDirectory();
-
-  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
-
-  if (!(file = fopen(filename, MODE_WRITE)))
-  {
-    Error(ERR_WARN, "cannot write setup file '%s'", filename);
-    free(filename);
-    return;
-  }
-
-  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
-                                                LEVELSETUP_COOKIE));
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
-                                              level_subdir));
-
-  fclose(file);
-  free(filename);
-
-  SetFilePermissions(filename, PERMS_PRIVATE);
-}
-
-static void checkSeriesInfo()
-{
-  static char *level_directory = NULL;
-  DIR *dir;
-  struct dirent *dir_entry;
-
-  /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
-
-  level_directory = getPath2((leveldir_current->user_defined ?
-                             getUserLevelDir("") :
-                             options.level_directory),
-                            leveldir_current->fullpath);
-
-  if ((dir = opendir(level_directory)) == NULL)
-  {
-    Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
-    return;
-  }
-
-  while ((dir_entry = readdir(dir)) != NULL)   /* last directory entry */
-  {
-    if (strlen(dir_entry->d_name) > 4 &&
-       dir_entry->d_name[3] == '.' &&
-       strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
-    {
-      char levelnum_str[4];
-      int levelnum_value;
-
-      strncpy(levelnum_str, dir_entry->d_name, 3);
-      levelnum_str[3] = '\0';
-
-      levelnum_value = atoi(levelnum_str);
-
-      if (levelnum_value < leveldir_current->first_level)
-      {
-       Error(ERR_WARN, "additional level %d found", levelnum_value);
-       leveldir_current->first_level = levelnum_value;
-      }
-      else if (levelnum_value > leveldir_current->last_level)
-      {
-       Error(ERR_WARN, "additional level %d found", levelnum_value);
-       leveldir_current->last_level = levelnum_value;
-      }
-    }
-  }
-
-  closedir(dir);
-}
-
-void LoadLevelSetup_SeriesInfo()
-{
-  char *filename;
-  struct SetupFileList *level_setup_list = NULL;
-  char *level_subdir = leveldir_current->filename;
-
-  /* always start with reliable default values */
-  level_nr = leveldir_current->first_level;
-
-  checkSeriesInfo(leveldir_current);
-
-  /* ----------------------------------------------------------------------- */
-  /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
-  /* ----------------------------------------------------------------------- */
-
-  level_subdir = leveldir_current->filename;
-
-  filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
-
-  if ((level_setup_list = loadSetupFileList(filename)))
-  {
-    char *token_value;
-
-    token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
-
-    if (token_value)
-    {
-      level_nr = atoi(token_value);
-
-      if (level_nr < leveldir_current->first_level)
-       level_nr = leveldir_current->first_level;
-      if (level_nr > leveldir_current->last_level)
-       level_nr = leveldir_current->last_level;
-    }
-
-    token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
-
-    if (token_value)
-    {
-      int level_nr = atoi(token_value);
-
-      if (level_nr < leveldir_current->first_level)
-       level_nr = leveldir_current->first_level;
-      if (level_nr > leveldir_current->last_level + 1)
-       level_nr = leveldir_current->last_level;
-
-      if (leveldir_current->user_defined)
-       level_nr = leveldir_current->last_level;
-
-      leveldir_current->handicap_level = level_nr;
-    }
-
-    checkSetupFileListIdentifier(level_setup_list, LEVELSETUP_COOKIE);
-
-    freeSetupFileList(level_setup_list);
-  }
-  else
-    Error(ERR_WARN, "using default setup values");
-
-  free(filename);
-}
-
-void SaveLevelSetup_SeriesInfo()
-{
-  char *filename;
-  char *level_subdir = leveldir_current->filename;
-  char *level_nr_str = int2str(level_nr, 0);
-  char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
-  FILE *file;
-
-  /* ----------------------------------------------------------------------- */
-  /* ~/.rocksndiamonds/levelsetup/<level series>/levelsetup.conf             */
-  /* ----------------------------------------------------------------------- */
-
-  InitLevelSetupDirectory(level_subdir);
-
-  filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
-
-  if (!(file = fopen(filename, MODE_WRITE)))
-  {
-    Error(ERR_WARN, "cannot write setup file '%s'", filename);
-    free(filename);
-    return;
-  }
-
-  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
-                                                LEVELSETUP_COOKIE));
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
-                                              level_nr_str));
-  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
-                                              handicap_level_str));
-
-  fclose(file);
-  free(filename);
 
   SetFilePermissions(filename, PERMS_PRIVATE);
 }
index bd593b5a8d5db3a349550cede2d9db8a9ab96eee..1409ba9c3063a0fcd0ebcd88da50d3fef1bbba4b 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -26,12 +26,7 @@ void DumpTape(struct TapeInfo *);
 void LoadScore(int);
 void SaveScore(int);
 
-void LoadLevelInfo(void);
 void LoadSetup(void);
 void SaveSetup(void);
-void LoadLevelSetup_LastSeries(void);
-void SaveLevelSetup_LastSeries(void);
-void LoadLevelSetup_SeriesInfo(void);
-void SaveLevelSetup_SeriesInfo(void);
 
 #endif /* FILES_H */
index 5dbf9df02a21b95923757ebeff666a09bc1c15a7..bc31c194de0672b1a07164c40710502c6d7562ca 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -19,7 +19,6 @@
 #include "init.h"
 #include "files.h"
 #include "tape.h"
-#include "joystick.h"
 #include "network.h"
 
 /* this switch controls how rocks move horizontally */
 #define DX_TIME                        (DX + XX_TIME)
 #define DY_TIME                        (DY + YY_TIME)
 
-#define IS_LOOP_SOUND(s)       ((s)==SND_KLAPPER || (s)==SND_ROEHR ||  \
-                                (s)==SND_NJAM || (s)==SND_MIEP)
-#define IS_MUSIC_SOUND(s)      ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
-                                (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
-                                (s)==SND_TYGER || (s)==SND_VOYAGER || \
-                                (s)==SND_TWILIGHT)
+/* values for initial player move delay (initial delay counter value) */
+#define INITIAL_MOVE_DELAY_OFF -1
+#define INITIAL_MOVE_DELAY_ON  0
 
 /* values for player movement speed (which is in fact a delay value) */
 #define MOVE_DELAY_NORMAL_SPEED        8
@@ -108,11 +104,58 @@ static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void KillHeroUnlessProtected(int, int);
 
+void PlaySoundLevel(int, int, int);
+void PlaySoundLevelAction(int, int, int);
+void PlaySoundLevelElementAction(int, int, int, int);
+
 static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
+#define SND_ACTION_UNKNOWN             0
+#define SND_ACTION_WAITING             1
+#define SND_ACTION_MOVING              2
+#define SND_ACTION_DIGGING             3
+#define SND_ACTION_COLLECTING          4
+#define SND_ACTION_PASSING             5
+#define SND_ACTION_IMPACT              6
+#define SND_ACTION_PUSHING             7
+#define SND_ACTION_ACTIVATING          8
+#define SND_ACTION_BURNING             9
+
+#define NUM_SND_ACTIONS                        10
+
+static struct
+{
+  char *text;
+  int value;
+  boolean is_loop;
+} sound_action_properties[] =
+{
+  /* insert _all_ loop sound actions here */
+  { ".waiting",                SND_ACTION_WAITING,     TRUE },
+  { ".moving",         SND_ACTION_MOVING,      TRUE }, /* continuos moving */
+  { ".running",                SND_ACTION_UNKNOWN,     TRUE },
+  { ".burning",                SND_ACTION_BURNING,     TRUE },
+  { ".growing",                SND_ACTION_UNKNOWN,     TRUE },
+  { ".attacking",      SND_ACTION_UNKNOWN,     TRUE },
+  { ".activated",      SND_ACTION_UNKNOWN,     TRUE },
+
+  /* other (non-loop) sound actions are optional */
+  { ".stepping",       SND_ACTION_MOVING,      FALSE }, /* discrete moving */
+  { ".digging",                SND_ACTION_DIGGING,     FALSE },
+  { ".collecting",     SND_ACTION_COLLECTING,  FALSE },
+  { ".passing",                SND_ACTION_PASSING,     FALSE },
+  { ".impact",         SND_ACTION_IMPACT,      FALSE },
+  { ".pushing",                SND_ACTION_PUSHING,     FALSE },
+  { ".activating",     SND_ACTION_ACTIVATING,  FALSE },
+  { NULL,              0,                      0 },
+};
+static int element_action_sound[NUM_LEVEL_ELEMENTS][NUM_SND_ACTIONS];
+static boolean is_loop_sound[NUM_SOUND_EFFECTS];
+
+#define IS_LOOP_SOUND(x)       (is_loop_sound[x])
 
 
 #ifdef DEBUG
@@ -174,10 +217,10 @@ void GetPlayerConfig()
     setup.sound = FALSE;
 
   if (!audio.loops_available)
-  {
     setup.sound_loops = FALSE;
+
+  if (!audio.music_available)
     setup.sound_music = FALSE;
-  }
 
   if (!video.fullscreen_available)
     setup.fullscreen = FALSE;
@@ -433,12 +476,200 @@ static void InitField(int x, int y, boolean init_game)
   }
 }
 
+void DrawGameDoorValues()
+{
+  int i, j;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+    for (j=0; j<4; j++)
+      if (stored_player[i].key[j])
+       DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
+                          GFX_SCHLUESSEL1 + j);
+
+  DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
+          int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
+          int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_SCORE, DY + YY_SCORE,
+          int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_TIME, DY + YY_TIME,
+          int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+}
+
+
+/*
+  =============================================================================
+  InitGameSound()
+  -----------------------------------------------------------------------------
+  initialize sound effect lookup table for element actions
+  =============================================================================
+*/
+
+void InitGameSound()
+{
+  int sound_effect_properties[NUM_SOUND_EFFECTS];
+  int i, j;
+
+#if 0
+  debug_print_timestamp(0, NULL);
+#endif
+
+  for (i=0; i<NUM_SND_ACTIONS; i++)
+    for (j=0; j<NUM_LEVEL_ELEMENTS; j++)
+      element_action_sound[j][i] = -1;
+
+  for (i=0; i<NUM_SOUND_EFFECTS; i++)
+  {
+    int len_effect_text = strlen(sound_effects[i].text);
+
+    sound_effect_properties[i] = SND_ACTION_UNKNOWN;
+    is_loop_sound[i] = FALSE;
+
+    /* determine all loop sounds and identify certain sound classes */
+
+    j = 0;
+    while (sound_action_properties[j].text)
+    {
+      int len_action_text = strlen(sound_action_properties[j].text);
+
+      if (len_action_text < len_effect_text &&
+         strcmp(&sound_effects[i].text[len_effect_text - len_action_text],
+                sound_action_properties[j].text) == 0)
+      {
+       sound_effect_properties[i] = sound_action_properties[j].value;
+
+       if (sound_action_properties[j].is_loop)
+         is_loop_sound[i] = TRUE;
+      }
+
+      j++;
+    }
+
+    /* associate elements and some selected sound actions */
+
+    for (j=0; j<NUM_LEVEL_ELEMENTS; j++)
+    {
+      if (element_info[j].sound_class_name)
+      {
+       int len_class_text = strlen(element_info[j].sound_class_name);
+
+       if (len_class_text + 1 < len_effect_text &&
+           strncmp(sound_effects[i].text,
+                   element_info[j].sound_class_name, len_class_text) == 0 &&
+           sound_effects[i].text[len_class_text] == '.')
+       {
+         int sound_action_value = sound_effect_properties[i];
+
+         element_action_sound[j][sound_action_value] = i;
+       }
+      }
+    }
+  }
+
+#if 0
+  debug_print_timestamp(0, "InitGameEngine");
+#endif
+
+#if 0
+  /* TEST ONLY */
+  {
+    int element = EL_ERDREICH;
+    int sound_action = SND_ACTION_DIGGING;
+    int j = 0;
+
+    while (sound_action_properties[j].text)
+    {
+      if (sound_action_properties[j].value == sound_action)
+       printf("element %d, sound action '%s'  == %d\n",
+              element, sound_action_properties[j].text,
+              element_action_sound[element][sound_action]);
+      j++;
+    }
+  }
+#endif
+}
+
+
+/*
+  =============================================================================
+  InitGameEngine()
+  -----------------------------------------------------------------------------
+  initialize game engine due to level / tape version number
+  =============================================================================
+*/
+
+static void InitGameEngine()
+{
+  int i;
+
+  game.engine_version = (tape.playing ? tape.engine_version :
+                        level.game_version);
+
+#if 0
+    printf("level %d: level version == %06d\n", level_nr, level.game_version);
+    printf("          tape version == %06d [%s] [file: %06d]\n",
+          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
+          tape.file_version);
+    printf("       => game.engine_version == %06d\n", game.engine_version);
+#endif
+
+  /* dynamically adjust player properties according to game engine version */
+  game.initial_move_delay =
+    (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
+     INITIAL_MOVE_DELAY_OFF);
+
+  /* dynamically adjust player properties according to level information */
+  game.initial_move_delay_value =
+    (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+
+  /* dynamically adjust element properties according to game engine version */
+  {
+    static int ep_em_slippery_wall[] =
+    {
+      EL_BETON,
+      EL_MAUERWERK,
+      EL_MAUER_LEBT,
+      EL_MAUER_X,
+      EL_MAUER_Y,
+      EL_MAUER_XY
+    };
+    static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
+
+    for (i=0; i<ep_em_slippery_wall_num; i++)
+    {
+      if (level.em_slippery_gems)      /* special EM style gems behaviour */
+       Elementeigenschaften2[ep_em_slippery_wall[i]] |=
+         EP_BIT_EM_SLIPPERY_WALL;
+      else
+       Elementeigenschaften2[ep_em_slippery_wall[i]] &=
+         ~EP_BIT_EM_SLIPPERY_WALL;
+    }
+
+    /* "EL_MAUERND" was not slippery for EM gems in version 2.0.1 */
+    if (level.em_slippery_gems && game.engine_version > VERSION_IDENT(2,0,1))
+      Elementeigenschaften2[EL_MAUERND] |=  EP_BIT_EM_SLIPPERY_WALL;
+    else
+      Elementeigenschaften2[EL_MAUERND] &= ~EP_BIT_EM_SLIPPERY_WALL;
+  }
+}
+
+
+/*
+  =============================================================================
+  InitGame()
+  -----------------------------------------------------------------------------
+  initialize and start new game
+  =============================================================================
+*/
+
 void InitGame()
 {
-  int i, j, x, y;
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
+  int i, j, x, y;
+
+  InitGameEngine();
 
 #if DEBUG
 #if USE_NEW_AMOEBA_CODE
@@ -491,15 +722,14 @@ void InitGame()
 
     player->frame_reset_delay = 0;
 
-    player->push_delay = 0;
-    player->push_delay_value = 5;
-
-    player->move_delay = 0;
     player->last_move_dir = MV_NO_MOVING;
     player->is_moving = FALSE;
 
-    player->move_delay_value =
-      (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+    player->move_delay       = game.initial_move_delay;
+    player->move_delay_value = game.initial_move_delay_value;
+
+    player->push_delay = 0;
+    player->push_delay_value = 5;
 
     player->snapped = FALSE;
 
@@ -587,6 +817,10 @@ void InitGame()
     }
   }
 
+  game.emulation = (emulate_bd ? EMU_BOULDERDASH :
+                   emulate_sb ? EMU_SOKOBAN :
+                   emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
   /* correct non-moving belts to start moving left */
   for (i=0; i<4; i++)
     if (game.belt_dir[i] == MV_NO_MOVING)
@@ -686,33 +920,6 @@ void InitGame()
     }
   }
 
-  game.version = (tape.playing ? tape.game_version : level.game_version);
-  game.emulation = (emulate_bd ? EMU_BOULDERDASH :
-                   emulate_sb ? EMU_SOKOBAN :
-                   emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
-
-  /* dynamically adjust element properties according to game engine version */
-  {
-    static int ep_slippery[] =
-    {
-      EL_BETON,
-      EL_MAUERWERK,
-      EL_MAUER_LEBT,
-      EL_MAUER_X,
-      EL_MAUER_Y,
-      EL_MAUER_XY
-    };
-    static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
-
-    for (i=0; i<ep_slippery_num; i++)
-    {
-      if (game.version >= GAME_VERSION_2_0)
-       Elementeigenschaften2[ep_slippery[i]] |= EP_BIT_SLIPPERY_GEMS;
-      else
-       Elementeigenschaften2[ep_slippery[i]] &= ~EP_BIT_SLIPPERY_GEMS;
-    }
-  }
-
   if (BorderElement == EL_LEERRAUM)
   {
     SBX_Left = 0;
@@ -777,16 +984,10 @@ void InitGame()
               DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
   }
 
-  DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
-          int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
-  DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
-          int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
-  DrawText(DX + XX_SCORE, DY + YY_SCORE,
-          int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
-  DrawText(DX + XX_TIME, DY + YY_TIME,
-          int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+  DrawGameDoorValues();
 
   UnmapGameButtons();
+  UnmapTapeButtons();
   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
@@ -799,8 +1000,9 @@ void InitGame()
 
   OpenDoor(DOOR_OPEN_ALL);
 
-  if (setup.sound_music && num_bg_loops)
-    PlayMusic(level_nr % num_bg_loops);
+  PlaySoundStereo(SND_GAME_STARTING, SOUND_MAX_RIGHT);
+  if (setup.sound_music)
+    PlayMusic(level_nr);
 
   KeyboardAutoRepeatOff();
 
@@ -951,15 +1153,18 @@ void GameWon()
 
   local_player->LevelSolved = FALSE;
 
+  PlaySoundStereo(SND_GAME_WINNING, SOUND_MAX_RIGHT);
+
   if (TimeLeft)
   {
     if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+                  SND_CTRL_PLAY_LOOP);
 
-    while(TimeLeft > 0)
+    while (TimeLeft > 0)
     {
       if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
       if (TimeLeft > 0 && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
@@ -974,17 +1179,18 @@ void GameWon()
     }
 
     if (!tape.playing && setup.sound_loops)
-      StopSound(SND_SIRR);
+      StopSound(SND_GAME_LEVELTIME_BONUS);
   }
   else if (level.time == 0)            /* level without time limit */
   {
     if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+                  SND_CTRL_PLAY_LOOP);
 
-    while(TimePlayed < 999)
+    while (TimePlayed < 999)
     {
       if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
       if (TimePlayed < 999 && !(TimePlayed % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimePlayed < 900 && !(TimePlayed % 10))
@@ -999,7 +1205,7 @@ void GameWon()
     }
 
     if (!tape.playing && setup.sound_loops)
-      StopSound(SND_SIRR);
+      StopSound(SND_GAME_LEVELTIME_BONUS);
   }
 
 #if 0
@@ -1278,8 +1484,8 @@ void CheckDynamite(int x, int y)
     MovDelay[x][y]--;
     if (MovDelay[x][y])
     {
-      if (!(MovDelay[x][y] % 12))
-       PlaySoundLevel(x, y, SND_ZISCH);
+      if (!(MovDelay[x][y] % 6))
+       PlaySoundLevelAction(x, y, SND_ACTION_BURNING);
 
       if (IS_ACTIVE_BOMB(Feld[x][y]))
       {
@@ -1293,7 +1499,11 @@ void CheckDynamite(int x, int y)
     }
   }
 
-  StopSound(SND_ZISCH);
+  if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
+    StopSound(SND_DYNAMITE_BURNING);
+  else
+    StopSound(SND_DYNABOMB_BURNING);
+
   Bang(x, y);
 }
 
@@ -1570,9 +1780,9 @@ void Bang(int x, int y)
   int element = Feld[x][y];
 
   if (game.emulation == EMU_SUPAPLEX)
-    PlaySoundLevel(x, y, SND_SP_BOOOM);
+    PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
   else
-    PlaySoundLevel(x, y, SND_ROAAAR);
+    PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
 
 #if 0
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
@@ -1622,7 +1832,7 @@ void Blurb(int x, int y)
 
   if (element != EL_BLURB_LEFT && element != EL_BLURB_RIGHT)   /* start */
   {
-    PlaySoundLevel(x, y, SND_BLURB);
+    PlaySoundLevel(x, y, SND_ACID_SPLASHING);
     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
        (!IN_LEV_FIELD(x-1, y-1) ||
         !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
@@ -1739,13 +1949,13 @@ static void ToggleSwitchgateSwitch(int x, int y)
               element == EL_SWITCHGATE_OPENING)
       {
        Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
-       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+       PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
       }
       else if (element == EL_SWITCHGATE_CLOSED ||
               element == EL_SWITCHGATE_CLOSING)
       {
        Feld[xx][yy] = EL_SWITCHGATE_OPENING;
-       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+       PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
       }
     }
   }
@@ -1809,7 +2019,7 @@ static void ActivateTimegateSwitch(int x, int y)
          element == EL_TIMEGATE_CLOSING)
       {
        Feld[xx][yy] = EL_TIMEGATE_OPENING;
-       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+       PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
       }
 
       /*
@@ -1862,7 +2072,7 @@ void Impact(int x, int y)
   else if (element == EL_PEARL)
   {
     Feld[x][y] = EL_PEARL_BREAKING;
-    PlaySoundLevel(x, y, SND_KNACK);
+    PlaySoundLevel(x, y, SND_PEARL_BREAKING);
     return;
   }
 
@@ -1885,20 +2095,23 @@ void Impact(int x, int y)
     if (CAN_CHANGE(element) && 
        (smashed == EL_MAGIC_WALL_OFF || smashed == EL_MAGIC_WALL_BD_OFF))
     {
-      int xy;
+      int xx, yy;
       int activated_magic_wall =
        (smashed == EL_MAGIC_WALL_OFF ? EL_MAGIC_WALL_EMPTY :
         EL_MAGIC_WALL_BD_EMPTY);
 
       /* activate magic wall / mill */
-
-      for (y=0; y<lev_fieldy; y++)
-       for (x=0; x<lev_fieldx; x++)
-         if (Feld[x][y] == smashed)
-           Feld[x][y] = activated_magic_wall;
+      for (yy=0; yy<lev_fieldy; yy++)
+       for (xx=0; xx<lev_fieldx; xx++)
+         if (Feld[xx][yy] == smashed)
+           Feld[xx][yy] = activated_magic_wall;
 
       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
       game.magic_wall_active = TRUE;
+
+      PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL_OFF ?
+                           SND_MAGIC_WALL_ACTIVATING :
+                           SND_BD_MAGIC_WALL_ACTIVATING));
     }
 
     if (IS_PLAYER(x, y+1))
@@ -1949,20 +2162,20 @@ void Impact(int x, int y)
        else if (smashed == EL_KOKOSNUSS)
        {
          Feld[x][y+1] = EL_CRACKINGNUT;
-         PlaySoundLevel(x, y, SND_KNACK);
+         PlaySoundLevel(x, y, SND_NUT_CRACKING);
          RaiseScoreElement(EL_KOKOSNUSS);
          return;
        }
        else if (smashed == EL_PEARL)
        {
          Feld[x][y+1] = EL_PEARL_BREAKING;
-         PlaySoundLevel(x, y, SND_KNACK);
+         PlaySoundLevel(x, y, SND_PEARL_BREAKING);
          return;
        }
        else if (smashed == EL_DIAMANT)
        {
          Feld[x][y+1] = EL_LEERRAUM;
-         PlaySoundLevel(x, y, SND_QUIRK);
+         PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
          return;
        }
        else if (IS_BELT_SWITCH(smashed))
@@ -1988,59 +2201,17 @@ void Impact(int x, int y)
       (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ||
        Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY))
   {
-    PlaySoundLevel(x, y, SND_QUIRK);
+    if (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY)
+      PlaySoundLevel(x, y, SND_MAGIC_WALL_CHANGING);
+    else if (Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY)
+      PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_CHANGING);
+
     return;
   }
 
   /* play sound of object that hits the ground */
   if (lastline || object_hit)
-  {
-    int sound;
-
-    switch(element)
-    {
-      case EL_EDELSTEIN:
-      case EL_EDELSTEIN_BD:
-      case EL_EDELSTEIN_GELB:
-      case EL_EDELSTEIN_ROT:
-      case EL_EDELSTEIN_LILA:
-      case EL_DIAMANT:
-      case EL_SP_INFOTRON:
-        sound = SND_PLING;
-       break;
-      case EL_KOKOSNUSS:
-       sound = SND_KLUMPF;
-       break;
-      case EL_FELSBROCKEN:
-      case EL_BD_ROCK:
-       sound = SND_KLOPF;
-       break;
-      case EL_SP_ZONK:
-       sound = SND_SP_ZONKDOWN;
-       break;
-      case EL_SCHLUESSEL:
-      case EL_SCHLUESSEL1:
-      case EL_SCHLUESSEL2:
-      case EL_SCHLUESSEL3:
-      case EL_SCHLUESSEL4:
-      case EL_EM_KEY_1:
-      case EL_EM_KEY_2:
-      case EL_EM_KEY_3:
-      case EL_EM_KEY_4:
-       sound = SND_KINK;
-       break;
-      case EL_ZEIT_VOLL:
-      case EL_ZEIT_LEER:
-       sound = SND_DENG;
-       break;
-      default:
-       sound = -1;
-        break;
-    }
-
-    if (sound >= 0)
-      PlaySoundLevel(x, y, sound);
-  }
+    PlaySoundLevelElementAction(x, y, element, SND_ACTION_IMPACT);
 }
 
 void TurnRound(int x, int y)
@@ -2505,6 +2676,7 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, MV_DOWN);
        Feld[x][y] = EL_QUICKSAND_EMPTYING;
        Store[x][y] = EL_FELSBROCKEN;
+       PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
       }
       else if (Feld[x][y+1] == EL_MORAST_LEER)
       {
@@ -2522,6 +2694,7 @@ void StartMoving(int x, int y)
        Feld[x][y+1] = EL_MORAST_VOLL;
        Store[x][y+1] = Store[x][y];
        Store[x][y] = 0;
+       PlaySoundLevel(x, y, SND_QUICKSAND_SLIPPING_THROUGH);
       }
     }
     else if ((element == EL_FELSBROCKEN || element == EL_BD_ROCK) &&
@@ -2530,6 +2703,7 @@ void StartMoving(int x, int y)
       InitMovingField(x, y, MV_DOWN);
       Feld[x][y] = EL_QUICKSAND_FILLING;
       Store[x][y] = element;
+      PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
     }
     else if (element == EL_MAGIC_WALL_FULL)
     {
@@ -2626,7 +2800,7 @@ void StartMoving(int x, int y)
 #endif
 #else
     else if ((IS_SLIPPERY(Feld[x][y+1]) ||
-             (IS_SLIPPERY_GEMS(Feld[x][y+1]) && IS_GEM(element))) &&
+             (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
             !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
             element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
 #endif
@@ -2675,6 +2849,7 @@ void StartMoving(int x, int y)
       if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
       {
        TurnRound(x, y);
+
        if (MovDelay[x][y] && (element == EL_KAEFER ||
                               element == EL_FLIEGER ||
                               element == EL_SP_SNIKSNAK ||
@@ -2693,15 +2868,19 @@ void StartMoving(int x, int y)
       {
        int phase = MovDelay[x][y] % 8;
 
-       if (phase>3)
-         phase = 7-phase;
+       if (phase > 3)
+         phase = 7 - phase;
 
        if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
-         DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element)+phase);
+         DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element) + phase);
 
-       if ((element == EL_MAMPFER || element == EL_MAMPFER2)
-           && MovDelay[x][y]%4 == 3)
-         PlaySoundLevel(x, y, SND_NJAM);
+       if (MovDelay[x][y] % 4 == 3)
+       {
+         if (element == EL_MAMPFER)
+           PlaySoundLevel(x, y, SND_YAMYAM_WAITING);
+         else if (element == EL_MAMPFER2)
+           PlaySoundLevel(x, y, SND_DARK_YAMYAM_WAITING);
+       }
       }
       else if (element == EL_SP_ELECTRON)
        DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
@@ -2748,17 +2927,12 @@ void StartMoving(int x, int y)
        }
       }
 
-      if (MovDelay[x][y])
-       return;
-    }
+      if (MovDelay[x][y])      /* element still has to wait some time */
+      {
+       PlaySoundLevelAction(x, y, SND_ACTION_WAITING);
 
-    if (element == EL_KAEFER || element == EL_BUTTERFLY)
-    {
-      PlaySoundLevel(x, y, SND_KLAPPER);
-    }
-    else if (element == EL_FLIEGER || element == EL_FIREFLY)
-    {
-      PlaySoundLevel(x, y, SND_ROEHR);
+       return;
+      }
     }
 
     /* now make next step */
@@ -2795,7 +2969,7 @@ void StartMoving(int x, int y)
        Feld[x][y] = EL_LEERRAUM;
        DrawLevelField(x, y);
 
-       PlaySoundLevel(newx, newy, SND_BUING);
+       PlaySoundLevel(newx, newy, SND_PENGUIN_ENTERING_EXIT);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
          DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2gfx(element));
 
@@ -2833,6 +3007,8 @@ void StartMoving(int x, int y)
          Feld[newx][newy] = EL_LEERRAUM;
          DrawLevelField(newx, newy);
        }
+
+       PlaySoundLevel(x, y, SND_PIG_EATING_GEM);
       }
       else if (!IS_FREE(newx, newy))
       {
@@ -2873,6 +3049,8 @@ void StartMoving(int x, int y)
          else
            DrawLevelField(x, y);
 
+         PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
+
          MovDelay[x][y] = 50;
          Feld[newx][newy] = EL_BURNING;
          if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_LEERRAUM)
@@ -2893,6 +3071,8 @@ void StartMoving(int x, int y)
        Feld[newx][newy] = EL_LEERRAUM;
        DrawLevelField(newx, newy);
       }
+
+      PlaySoundLevel(x, y, SND_YAMYAM_EATING_DIAMOND);
     }
     else if (element == EL_MAMPFER2 && IN_LEV_FIELD(newx, newy) &&
             IS_MAMPF2(Feld[newx][newy]))
@@ -2912,6 +3092,8 @@ void StartMoving(int x, int y)
        Feld[newx][newy] = EL_LEERRAUM;
        DrawLevelField(newx, newy);
       }
+
+      PlaySoundLevel(x, y, SND_DARK_YAMYAM_EATING_ANY);
     }
     else if ((element == EL_PACMAN || element == EL_MOLE)
             && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
@@ -2927,6 +3109,7 @@ void StartMoving(int x, int y)
       if (element == EL_MOLE)
       {
        Feld[newx][newy] = EL_DEAMOEBING;
+       PlaySoundLevel(x, y, SND_MOLE_EATING_AMOEBA);
        MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
        return;                         /* wait for shrinking amoeba */
       }
@@ -2934,6 +3117,7 @@ void StartMoving(int x, int y)
       {
        Feld[newx][newy] = EL_LEERRAUM;
        DrawLevelField(newx, newy);
+       PlaySoundLevel(x, y, SND_PACMAN_EATING_AMOEBA);
       }
     }
     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
@@ -2962,13 +3146,14 @@ void StartMoving(int x, int y)
       if (DONT_TOUCH(element))
        TestIfBadThingTouchesHero(x, y);
 
+      PlaySoundLevelAction(x, y, SND_ACTION_WAITING);
+
       return;
     }
 
-    if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
-      PlaySoundLevel(x, y, SND_SCHLURF);
-
     InitMovingField(x, y, MovDir[x][y]);
+
+    PlaySoundLevelAction(x, y, SND_ACTION_MOVING);
   }
 
   if (MovDir[x][y])
@@ -3008,7 +3193,7 @@ void ContinueMoving(int x, int y)
 
   MovPos[x][y] += step;
 
-  if (ABS(MovPos[x][y])>=TILEX)                /* object reached its destination */
+  if (ABS(MovPos[x][y]) >= TILEX)      /* object reached its destination */
   {
     Feld[x][y] = EL_LEERRAUM;
     Feld[newx][newy] = element;
@@ -3223,6 +3408,9 @@ void AmoebeUmwandeln(int ax, int ay)
        }
       }
     }
+    PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
+                           SND_AMOEBA_TURNING_TO_GEM :
+                           SND_AMOEBA_TURNING_TO_ROCK));
     Bang(ax, ay);
   }
   else
@@ -3244,7 +3432,12 @@ void AmoebeUmwandeln(int ax, int ay)
        continue;
 
       if (Feld[x][y] == EL_AMOEBA2DIAM)
+      {
+       PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
+                             SND_AMOEBA_TURNING_TO_GEM :
+                             SND_AMOEBA_TURNING_TO_ROCK));
        Bang(x, y);
+      }
     }
   }
 }
@@ -3283,8 +3476,9 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
   }
 
   if (done)
-    PlaySoundLevel(ax, ay,
-                  (new_element == EL_BD_ROCK ? SND_KLOPF : SND_PLING));
+    PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
+                           SND_BD_AMOEBA_TURNING_TO_ROCK :
+                           SND_BD_AMOEBA_TURNING_TO_GEM));
 }
 
 void AmoebeWaechst(int x, int y)
@@ -3298,7 +3492,10 @@ void AmoebeWaechst(int x, int y)
 
     if (DelayReached(&sound_delay, sound_delay_value))
     {
-      PlaySoundLevel(x, y, SND_AMOEBE);
+      if (Store[x][y] == EL_AMOEBE_BD)
+       PlaySoundLevel(x, y, SND_BD_AMOEBA_CREATING);
+      else
+       PlaySoundLevel(x, y, SND_AMOEBA_CREATING);
       sound_delay_value = 30;
     }
   }
@@ -3318,7 +3515,7 @@ void AmoebeWaechst(int x, int y)
   }
 }
 
-void AmoebeSchrumpft(int x, int y)
+void AmoebaDisappearing(int x, int y)
 {
   static unsigned long sound_delay = 0;
   static unsigned long sound_delay_value = 0;
@@ -3328,10 +3525,7 @@ void AmoebeSchrumpft(int x, int y)
     MovDelay[x][y] = 7;
 
     if (DelayReached(&sound_delay, sound_delay_value))
-    {
-      PlaySoundLevel(x, y, SND_BLURB);
       sound_delay_value = 30;
-    }
   }
 
   if (MovDelay[x][y])          /* wait some time before shrinking */
@@ -3481,7 +3675,10 @@ void AmoebeAbleger(int ax, int ay)
     Store[newax][neway] = element;
   }
   else if (neway == ay)
+  {
     Feld[newax][neway] = EL_TROPFEN;   /* drop left or right from amoeba */
+    PlaySoundLevel(newax, neway, SND_AMOEBA_DROPPING);
+  }
   else
   {
     InitMovingField(ax, ay, MV_DOWN);  /* drop dripping out of amoeba */
@@ -3500,6 +3697,7 @@ void Life(int ax, int ay)
   static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
   int life_time = 40;
   int element = Feld[ax][ay];
+  boolean changed = FALSE;
 
   if (Stop[ax][ay])
     return;
@@ -3538,29 +3736,35 @@ void Life(int ax, int ay)
 
     if (xx == ax && yy == ay)          /* field in the middle */
     {
-      if (nachbarn<life[0] || nachbarn>life[1])
+      if (nachbarn < life[0] || nachbarn > life[1])
       {
        Feld[xx][yy] = EL_LEERRAUM;
        if (!Stop[xx][yy])
          DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
+       changed = TRUE;
       }
     }
     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH)
     {                                  /* free border field */
-      if (nachbarn>=life[2] && nachbarn<=life[3])
+      if (nachbarn >= life[2] && nachbarn <= life[3])
       {
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
          DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
+       changed = TRUE;
       }
     }
   }
+
+  if (changed)
+    PlaySoundLevel(ax, ay, element == EL_LIFE ? SND_GAMEOFLIFE_CREATING :
+                  SND_BIOMAZE_CREATING);
 }
 
-void Ablenk(int x, int y)
+void RobotWheel(int x, int y)
 {
   if (!MovDelay[x][y])         /* next animation frame */
     MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
@@ -3573,7 +3777,7 @@ void Ablenk(int x, int y)
       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
        DrawGraphic(SCREENX(x), SCREENY(y), GFX_ABLENK+MovDelay[x][y]%4);
       if (!(MovDelay[x][y]%4))
-       PlaySoundLevel(x, y, SND_MIEP);
+       PlaySoundLevel(x, y, SND_ROBOT_WHEEL_RUNNING);
       return;
     }
   }
@@ -3598,7 +3802,7 @@ void TimegateWheel(int x, int y)
        DrawGraphic(SCREENX(x), SCREENY(y),
                    GFX_TIMEGATE_SWITCH + MovDelay[x][y]%4);
       if (!(MovDelay[x][y]%4))
-       PlaySoundLevel(x, y, SND_MIEP);
+       PlaySoundLevel(x, y, SND_TIMEGATE_WHEEL_RUNNING);
       return;
     }
   }
@@ -3705,7 +3909,7 @@ void AusgangstuerPruefen(int x, int y)
                   (x > LEVELX(BX2) ? LEVELX(BX2) : x),
                   y < LEVELY(BY1) ? LEVELY(BY1) :
                   (y > LEVELY(BY2) ? LEVELY(BY2) : y),
-                  SND_OEFFNEN);
+                  SND_EXIT_OPENING);
   }
 }
 
@@ -3847,7 +4051,7 @@ static void CloseAllOpenTimegates()
       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
       {
        Feld[x][y] = EL_TIMEGATE_CLOSING;
-       PlaySoundLevel(x, y, SND_OEFFNEN);
+       PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
       }
     }
   }
@@ -3957,6 +4161,7 @@ void MauerAbleger(int ax, int ay)
   boolean links_frei = FALSE, rechts_frei = FALSE;
   boolean oben_massiv = FALSE, unten_massiv = FALSE;
   boolean links_massiv = FALSE, rechts_massiv = FALSE;
+  boolean new_wall = FALSE;
 
   if (!MovDelay[ax][ay])       /* start building new wall */
     MovDelay[ax][ay] = 6;
@@ -3986,6 +4191,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax][ay-1] = MV_UP;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
        DrawGraphic(SCREENX(ax), SCREENY(ay-1), GFX_MAUER_UP);
+      new_wall = TRUE;
     }
     if (unten_frei)
     {
@@ -3994,6 +4200,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax][ay+1] = MV_DOWN;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
        DrawGraphic(SCREENX(ax), SCREENY(ay+1), GFX_MAUER_DOWN);
+      new_wall = TRUE;
     }
   }
 
@@ -4007,7 +4214,9 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax-1][ay] = MV_LEFT;
       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
        DrawGraphic(SCREENX(ax-1), SCREENY(ay), GFX_MAUER_LEFT);
+      new_wall = TRUE;
     }
+
     if (rechts_frei)
     {
       Feld[ax+1][ay] = EL_MAUERND;
@@ -4015,6 +4224,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax+1][ay] = MV_RIGHT;
       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
        DrawGraphic(SCREENX(ax+1), SCREENY(ay), GFX_MAUER_RIGHT);
+      new_wall = TRUE;
     }
   }
 
@@ -4035,6 +4245,9 @@ void MauerAbleger(int ax, int ay)
       ((links_massiv && rechts_massiv) ||
        element == EL_MAUER_Y))
     Feld[ax][ay] = EL_MAUERWERK;
+
+  if (new_wall)
+    PlaySoundLevel(ax, ay, SND_WALL_GROWING);
 }
 
 void CheckForDragon(int x, int y)
@@ -4134,7 +4347,7 @@ static void CheckBuggyBase(int x, int y)
 
          if (IS_PLAYER(xx, yy))
          {
-           PlaySoundLevel(x, y, SND_SP_BUG);
+           PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVATING);
            break;
          }
        }
@@ -4164,6 +4377,7 @@ static void CheckTrap(int x, int y)
        return;
 
       Feld[x][y] = EL_TRAP_ACTIVE;
+      PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
     }
   }
   else if (element == EL_TRAP_ACTIVE)
@@ -4215,6 +4429,9 @@ static void DrawBeltAnimation(int x, int y, int element)
     int graphic = el2gfx(element) + (belt_dir == MV_LEFT ? 0 : 7);
 
     DrawGraphicAnimation(x, y, graphic, 8, delay, mode);
+
+    if (!(FrameCounter % 2))
+      PlaySoundLevel(x, y, SND_CONVEYOR_BELT_RUNNING);
   }
 }
 
@@ -4257,6 +4474,15 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
       moved = MoveFigure(player, dx, dy);
     }
 
+    if (tape.single_step && tape.recording && !tape.pausing)
+    {
+      if (button1 || (bombed && !moved))
+      {
+       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+       SnapField(player, 0, 0);                /* stop snapping */
+      }
+    }
+
 #if 0
     if (tape.recording && (moved || snapped || bombed))
     {
@@ -4364,6 +4590,9 @@ void GameActions()
   action_delay_value =
     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
 
+  if (tape.playing && tape.index_search && !tape.pausing)
+    action_delay_value = 0;
+
   /* ---------- main game synchronization point ---------- */
 
   WaitUntilDelayReached(&action_delay, action_delay_value);
@@ -4398,11 +4627,6 @@ void GameActions()
   if (tape.pausing)
     return;
 
-  if (tape.playing)
-    TapePlayDelay();
-  else if (tape.recording)
-    TapeRecordDelay();
-
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
   for (i=0; i<MAX_PLAYERS; i++)
@@ -4513,7 +4737,7 @@ void GameActions()
     else if (element == EL_AMOEBING)
       AmoebeWaechst(x, y);
     else if (element == EL_DEAMOEBING)
-      AmoebeSchrumpft(x, y);
+      AmoebaDisappearing(x, y);
 
 #if !USE_NEW_AMOEBA_CODE
     else if (IS_AMOEBALIVE(element))
@@ -4523,7 +4747,7 @@ void GameActions()
     else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
       Life(x, y);
     else if (element == EL_ABLENK_EIN)
-      Ablenk(x, y);
+      RobotWheel(x, y);
     else if (element == EL_TIMEGATE_SWITCH_ON)
       TimegateWheel(x, y);
     else if (element == EL_SALZSAEURE)
@@ -4570,9 +4794,21 @@ void GameActions()
     else if (element == EL_EXTRA_TIME)
       DrawGraphicAnimation(x, y, GFX_EXTRA_TIME, 6, 4, ANIM_NORMAL);
     else if (element == EL_SHIELD_PASSIVE)
+    {
       DrawGraphicAnimation(x, y, GFX_SHIELD_PASSIVE, 6, 4, ANIM_NORMAL);
+#if 0
+      if (!(FrameCounter % 4))
+       PlaySoundLevel(x, y, SND_SHIELD_PASSIVE_ACTIVATED);
+#endif
+    }
     else if (element == EL_SHIELD_ACTIVE)
+    {
       DrawGraphicAnimation(x, y, GFX_SHIELD_ACTIVE, 6, 4, ANIM_NORMAL);
+#if 0
+      if (!(FrameCounter % 4))
+       PlaySoundLevel(x, y, SND_SHIELD_ACTIVE_ACTIVATED);
+#endif
+    }
 
     if (game.magic_wall_active)
     {
@@ -4665,7 +4901,16 @@ void GameActions()
   if (game.magic_wall_active)
   {
     if (!(game.magic_wall_time_left % 4))
-      PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
+    {
+      int element = Feld[sieb_x][sieb_y];
+
+      if (element == EL_MAGIC_WALL_BD_FULL ||
+         element == EL_MAGIC_WALL_BD_EMPTY ||
+         element == EL_MAGIC_WALL_BD_EMPTYING)
+       PlaySoundLevel(sieb_x, sieb_y, SND_BD_MAGIC_WALL_RUNNING);
+      else
+       PlaySoundLevel(sieb_x, sieb_y, SND_MAGIC_WALL_RUNNING);
+    }
 
     if (game.magic_wall_time_left > 0)
     {
@@ -4726,6 +4971,19 @@ void GameActions()
       CloseAllOpenTimegates();
   }
 
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (SHIELD_ON(player))
+    {
+      if (player->shield_active_time_left)
+       PlaySoundLevel(player->jx, player->jy, SND_SHIELD_ACTIVE_ACTIVATED);
+      else if (player->shield_passive_time_left)
+       PlaySoundLevel(player->jx, player->jy, SND_SHIELD_PASSIVE_ACTIVATED);
+    }
+  }
+
   if (TimeFrames >= (1000 / GameFrameDelay))
   {
     TimeFrames = 0;
@@ -4733,12 +4991,14 @@ void GameActions()
 
     for (i=0; i<MAX_PLAYERS; i++)
     {
-      if (SHIELD_ON(&stored_player[i]))
+      struct PlayerInfo *player = &stored_player[i];
+
+      if (SHIELD_ON(player))
       {
-       stored_player[i].shield_passive_time_left--;
+       player->shield_passive_time_left--;
 
-       if (stored_player[i].shield_active_time_left > 0)
-         stored_player[i].shield_active_time_left--;
+       if (player->shield_active_time_left > 0)
+         player->shield_active_time_left--;
       }
     }
 
@@ -4750,7 +5010,7 @@ void GameActions()
       TimeLeft--;
 
       if (TimeLeft <= 10 && setup.time_limit)
-       PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MAX_RIGHT);
 
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
@@ -4758,7 +5018,7 @@ void GameActions()
        for (i=0; i<MAX_PLAYERS; i++)
          KillHero(&stored_player[i]);
     }
-    else if (level.time == 0)          /* level without time limit */
+    else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
   }
 
@@ -4873,7 +5133,9 @@ static void CheckGravityMovement(struct PlayerInfo *player)
        (Feld[new_jx][new_jy] == EL_SP_BASE ||
        Feld[new_jx][new_jy] == EL_ERDREICH));
 
-    if (field_under_player_is_free && !player_is_moving_to_valid_field)
+    if (field_under_player_is_free &&
+       !player_is_moving_to_valid_field &&
+       !IS_TUBE(Feld[jx][jy]))
       player->programmed_action = MV_DOWN;
   }
 }
@@ -5164,6 +5426,10 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
       if (!local_player->friends_still_needed)
        player->LevelSolved = player->GameOver = TRUE;
     }
+
+    if (tape.single_step && tape.recording && !tape.pausing &&
+       !player->programmed_action)
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
   }
 }
 
@@ -5451,8 +5717,8 @@ void BuryHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
-  PlaySoundLevel(jx, jy, SND_AUTSCH);
-  PlaySoundLevel(jx, jy, SND_LACHEN);
+  PlaySoundLevel(jx, jy, SND_PLAYER_DYING);
+  PlaySoundLevel(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
   RemoveHero(player);
@@ -5491,7 +5757,7 @@ int DigField(struct PlayerInfo *player,
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
-  if (!player->MovPos)
+  if (player->MovPos == 0)
     player->Pushing = FALSE;
 
   if (mode == DF_NO_PUSH)
@@ -5539,20 +5805,13 @@ int DigField(struct PlayerInfo *player,
   switch (element)
   {
     case EL_LEERRAUM:
-      PlaySoundLevel(x, y, SND_EMPTY);
-      break;
-
     case EL_ERDREICH:
     case EL_SAND_INVISIBLE:
     case EL_TRAP_INACTIVE:
-      Feld[x][y] = EL_LEERRAUM;
-      PlaySoundLevel(x, y, SND_SCHLURF);
-      break;
-
     case EL_SP_BASE:
     case EL_SP_BUG:
-      Feld[x][y] = EL_LEERRAUM;
-      PlaySoundLevel(x, y, SND_SP_BASE);
+      RemoveField(x, y);
+      PlaySoundLevelElementAction(x, y, element, SND_ACTION_DIGGING);
       break;
 
     case EL_EDELSTEIN:
@@ -5574,21 +5833,18 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3),
               FS_SMALL, FC_YELLOW);
-      if (element == EL_SP_INFOTRON)
-       PlaySoundLevel(x, y, SND_SP_INFOTRON);
-      else
-       PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevelElementAction(x, y, element, SND_ACTION_COLLECTING);
       break;
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
       break;
 
     case EL_ENVELOPE:
       Feld[x][y] = EL_LEERRAUM;
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
       break;
 
     case EL_EXTRA_TIME:
@@ -5598,20 +5854,20 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       }
-      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MAX_RIGHT);
       break;
 
     case EL_SHIELD_PASSIVE:
       RemoveField(x, y);
       player->shield_passive_time_left += 10;
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_SHIELD_PASSIVE_COLLECTING);
       break;
 
     case EL_SHIELD_ACTIVE:
       RemoveField(x, y);
       player->shield_passive_time_left += 10;
       player->shield_active_time_left += 10;
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_SHIELD_ACTIVE_COLLECTING);
       break;
 
     case EL_DYNAMITE_INACTIVE:
@@ -5622,10 +5878,7 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_DYNAMITE, DY_DYNAMITE,
               int2str(local_player->dynamite, 3),
               FS_SMALL, FC_YELLOW);
-      if (element == EL_SP_DISK_RED)
-       PlaySoundLevel(x, y, SND_SP_INFOTRON);
-      else
-       PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevelElementAction(x, y, element, SND_ACTION_COLLECTING);
       break;
 
     case EL_DYNABOMB_NR:
@@ -5633,21 +5886,21 @@ int DigField(struct PlayerInfo *player,
       player->dynabomb_count++;
       player->dynabombs_left++;
       RaiseScoreElement(EL_DYNAMITE_INACTIVE);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_DYNABOMB_NR_COLLECTING);
       break;
 
     case EL_DYNABOMB_SZ:
       RemoveField(x, y);
       player->dynabomb_size++;
       RaiseScoreElement(EL_DYNAMITE_INACTIVE);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_DYNABOMB_SZ_COLLECTING);
       break;
 
     case EL_DYNABOMB_XL:
       RemoveField(x, y);
       player->dynabomb_xl = TRUE;
       RaiseScoreElement(EL_DYNAMITE_INACTIVE);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_DYNABOMB_XL_COLLECTING);
       break;
 
     case EL_SCHLUESSEL1:
@@ -5664,7 +5917,7 @@ int DigField(struct PlayerInfo *player,
                         GFX_SCHLUESSEL1 + key_nr);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         GFX_SCHLUESSEL1 + key_nr);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_KEY_COLLECTING);
       break;
     }
 
@@ -5682,7 +5935,7 @@ int DigField(struct PlayerInfo *player,
                         GFX_SCHLUESSEL1 + key_nr);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         GFX_SCHLUESSEL1 + key_nr);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_KEY_COLLECTING);
       break;
     }
 
@@ -5691,6 +5944,7 @@ int DigField(struct PlayerInfo *player,
       ZX = x;
       ZY = y;
       DrawLevelField(x, y);
+      PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING);
       return MF_ACTION;
       break;
 
@@ -5698,6 +5952,8 @@ int DigField(struct PlayerInfo *player,
       {
        int xx, yy;
 
+       PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING);
+
        for (yy=0; yy<lev_fieldy; yy++)
        {
          for (xx=0; xx<lev_fieldx; xx++)
@@ -5729,6 +5985,7 @@ int DigField(struct PlayerInfo *player,
       {
        player->Switching = TRUE;
        ToggleBeltSwitch(x, y);
+       PlaySoundLevel(x, y, SND_CONVEYOR_BELT_SWITCH_ACTIVATING);
       }
       return MF_ACTION;
       break;
@@ -5739,6 +5996,7 @@ int DigField(struct PlayerInfo *player,
       {
        player->Switching = TRUE;
        ToggleSwitchgateSwitch(x, y);
+       PlaySoundLevel(x, y, SND_SWITCHGATE_SWITCH_ACTIVATING);
       }
       return MF_ACTION;
       break;
@@ -5749,12 +6007,16 @@ int DigField(struct PlayerInfo *player,
       {
        player->Switching = TRUE;
        ToggleLightSwitch(x, y);
+       PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH_OFF ?
+                      SND_LIGHT_SWITCH_ACTIVATING :
+                      SND_LIGHT_SWITCH_DEACTIVATING);
       }
       return MF_ACTION;
       break;
 
     case EL_TIMEGATE_SWITCH_OFF:
       ActivateTimegateSwitch(x, y);
+      PlaySoundLevel(x, y, SND_TIMEGATE_WHEEL_ACTIVATING);
 
       return MF_ACTION;
       break;
@@ -5772,6 +6034,7 @@ int DigField(struct PlayerInfo *player,
                            element == EL_BALLOON_SEND_UP    ? MV_UP :
                            element == EL_BALLOON_SEND_DOWN  ? MV_DOWN :
                            MV_NO_MOVING);
+      PlaySoundLevel(x, y, SND_BALLOON_SWITCH_ACTIVATING);
 
       return MF_ACTION;
       break;
@@ -5781,11 +6044,11 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
 
       player->LevelSolved = player->GameOver = TRUE;
-      PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_SP_EXIT_ENTERING, SOUND_MAX_RIGHT);
       break;
 
+      /* the following elements cannot be pushed by "snapping" */
     case EL_FELSBROCKEN:
-    case EL_BD_ROCK:
     case EL_BOMBE:
     case EL_DX_SUPABOMB:
     case EL_KOKOSNUSS:
@@ -5793,7 +6056,12 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_ZONK:
     case EL_SP_DISK_ORANGE:
     case EL_SPRING:
-      if (dy || mode == DF_SNAP)
+      if (mode == DF_SNAP)
+       return MF_NO_ACTION;
+      /* no "break" -- fall through to next case */
+      /* the following elements can be pushed by "snapping" */
+    case EL_BD_ROCK:
+      if (dy)
        return MF_NO_ACTION;
 
       player->Pushing = TRUE;
@@ -5820,8 +6088,16 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
 #endif
 
-      RemoveField(x, y);
-      Feld[x+dx][y+dy] = element;
+      if (mode == DF_SNAP)
+      {
+       InitMovingField(x, y, move_direction);
+       ContinueMoving(x, y);
+      }
+      else
+      {
+       RemoveField(x, y);
+       Feld[x+dx][y+dy] = element;
+      }
 
       if (element == EL_SPRING)
       {
@@ -5832,14 +6108,7 @@ int DigField(struct PlayerInfo *player,
       player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
 
       DrawLevelField(x+dx, y+dy);
-      if (element == EL_FELSBROCKEN || element == EL_BD_ROCK)
-       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
-      else if (element == EL_KOKOSNUSS)
-       PlaySoundLevel(x+dx, y+dy, SND_KNURK);
-      else if (IS_SP_ELEMENT(element))
-       PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
-      else
-       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);  /* better than "SND_KLOPF" */
+      PlaySoundLevelElementAction(x, y, element, SND_ACTION_PUSHING);
       break;
 
     case EL_PFORTE1:
@@ -5871,8 +6140,7 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
-
+      PlaySoundLevel(x, y, SND_GATE_PASSING);
       break;
 
     case EL_EM_GATE_1X:
@@ -5888,8 +6156,7 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
-
+      PlaySoundLevel(x, y, SND_GATE_PASSING);
       break;
 
     case EL_SWITCHGATE_OPEN:
@@ -5901,8 +6168,7 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
-
+      PlaySoundLevelElementAction(x, y, element, SND_ACTION_PASSING);
       break;
 
     case EL_SP_PORT1_LEFT:
@@ -5944,7 +6210,7 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
+      PlaySoundLevel(x, y, SND_SP_PORT_PASSING);
       break;
 
     case EL_TUBE_CROSS:
@@ -5985,6 +6251,8 @@ int DigField(struct PlayerInfo *player,
 
        if (!(tube_enter_directions[i][1] & move_direction))
          return MF_NO_ACTION;  /* tube has no opening in this direction */
+
+       PlaySoundLevel(x, y, SND_TUBE_PASSING);
       }
       break;
 
@@ -5998,7 +6266,7 @@ int DigField(struct PlayerInfo *player,
       if (mode == DF_SNAP)
        return MF_NO_ACTION;
 
-      PlaySoundLevel(x, y, SND_BUING);
+      PlaySoundLevel(x, y, SND_EXIT_ENTERING);
 
       break;
 
@@ -6006,7 +6274,7 @@ int DigField(struct PlayerInfo *player,
       Feld[x][y] = EL_BIRNE_EIN;
       local_player->lights_still_needed--;
       DrawLevelField(x, y);
-      PlaySoundLevel(x, y, SND_DENG);
+      PlaySoundLevel(x, y, SND_LAMP_ACTIVATING);
       return MF_ACTION;
       break;
 
@@ -6015,15 +6283,15 @@ int DigField(struct PlayerInfo *player,
       TimeLeft += 10;
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       DrawLevelField(x, y);
-      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MAX_RIGHT);
       return MF_ACTION;
       break;
 
     case EL_SOKOBAN_FELD_LEER:
       break;
 
-    case EL_SOKOBAN_FELD_VOLL:
     case EL_SOKOBAN_OBJEKT:
+    case EL_SOKOBAN_FELD_VOLL:
     case EL_SONDE:
     case EL_SP_DISK_YELLOW:
     case EL_BALLOON:
@@ -6077,32 +6345,37 @@ int DigField(struct PlayerInfo *player,
          Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
          local_player->sokobanfields_still_needed--;
          if (element == EL_SOKOBAN_OBJEKT)
-           PlaySoundLevel(x, y, SND_DENG);
+           PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_FILLING);
+         else
+           PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
        }
        else
+       {
          Feld[x+dx][y+dy] = EL_SOKOBAN_OBJEKT;
+         if (element == EL_SOKOBAN_FELD_VOLL)
+           PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_CLEARING);
+         else
+           PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
+       }
       }
       else
       {
        RemoveField(x, y);
        Feld[x+dx][y+dy] = element;
+       PlaySoundLevelElementAction(x, y, element, SND_ACTION_PUSHING);
       }
 
       player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
 
       DrawLevelField(x, y);
       DrawLevelField(x+dx, y+dy);
-      if (element == EL_BALLOON)
-       PlaySoundLevel(x+dx, y+dy, SND_SCHLURF);
-      else
-       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
 
       if (IS_SB_ELEMENT(element) &&
          local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
       {
        player->LevelSolved = player->GameOver = TRUE;
-       PlaySoundLevel(x, y, SND_BUING);
+       PlaySoundLevel(x, y, SND_SOKOBAN_GAME_SOLVING);
       }
 
       break;
@@ -6134,6 +6407,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   if (!dx && !dy)
   {
+    if (player->MovPos == 0)
+      player->Pushing = FALSE;
+
     player->snapped = FALSE;
     return FALSE;
   }
@@ -6187,6 +6463,8 @@ boolean PlaceBomb(struct PlayerInfo *player)
       else
        DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
     }
+
+    PlaySoundLevel(jx, jy, SND_DYNAMITE_PLACING);
   }
   else
   {
@@ -6195,47 +6473,71 @@ boolean PlaceBomb(struct PlayerInfo *player)
     player->dynabombs_left--;
     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNABOMB);
+
+    PlaySoundLevel(jx, jy, SND_DYNABOMB_PLACING);
   }
 
   return TRUE;
 }
 
-void PlaySoundLevel(int x, int y, int sound_nr)
+void PlaySoundLevel(int x, int y, int nr)
 {
+  static int loop_sound_frame[NUM_SOUND_EFFECTS];
+  static int loop_sound_volume[NUM_SOUND_EFFECTS];
   int sx = SCREENX(x), sy = SCREENY(y);
-  int volume, stereo;
-  int silence_distance = 8;
+  int volume, stereo_position;
+  int max_distance = 8;
+  int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
 
-  if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
-      (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
+  if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
+      (!setup.sound_loops && IS_LOOP_SOUND(nr)))
     return;
 
   if (!IN_LEV_FIELD(x, y) ||
-      sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
-      sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
+      sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
+      sy < -max_distance || sy >= SCR_FIELDY + max_distance)
     return;
 
-  volume = PSND_MAX_VOLUME;
-
-#if !defined(PLATFORM_MSDOS)
-  stereo = (sx - SCR_FIELDX/2) * 12;
-#else
-  stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
-  if (stereo > PSND_MAX_RIGHT)
-    stereo = PSND_MAX_RIGHT;
-  if (stereo < PSND_MAX_LEFT)
-    stereo = PSND_MAX_LEFT;
-#endif
+  volume = SOUND_MAX_VOLUME;
 
   if (!IN_SCR_FIELD(sx, sy))
   {
-    int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
-    int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
+    int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
+    int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
 
-    volume -= volume * (dx > dy ? dx : dy) / silence_distance;
+    volume -= volume * (dx > dy ? dx : dy) / max_distance;
   }
 
-  PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
+  stereo_position = (SOUND_MAX_LEFT +
+                    (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
+                    (SCR_FIELDX + 2 * max_distance));
+
+  if (IS_LOOP_SOUND(nr))
+  {
+    /* This assures that quieter loop sounds do not overwrite louder ones,
+       while restarting sound volume comparison with each new game frame. */
+
+    if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
+      return;
+
+    loop_sound_volume[nr] = volume;
+    loop_sound_frame[nr] = FrameCounter;
+  }
+
+  PlaySoundExt(nr, volume, stereo_position, type);
+}
+
+void PlaySoundLevelAction(int x, int y, int sound_action)
+{
+  PlaySoundLevelElementAction(x, y, Feld[x][y], sound_action);
+}
+
+void PlaySoundLevelElementAction(int x, int y, int element, int sound_action)
+{
+  int sound_effect = element_action_sound[element][sound_action];
+
+  if (sound_effect != -1)
+    PlaySoundLevel(x, y, sound_effect);
 }
 
 void RaiseScore(int value)
@@ -6291,6 +6593,31 @@ void RaiseScoreElement(int element)
   }
 }
 
+void RequestQuitGame(boolean ask_if_really_quit)
+{
+  if (AllPlayersGone ||
+      !ask_if_really_quit ||
+      level_editor_test_game ||
+      Request("Do you really want to quit the game ?",
+             REQ_ASK | REQ_STAY_CLOSED))
+  {
+#if defined(PLATFORM_UNIX)
+    if (options.network)
+      SendToServer_StopPlaying();
+    else
+#endif
+    {
+      game_status = MAINMENU;
+      DrawMainMenu();
+    }
+  }
+  else
+  {
+    OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+  }
+}
+
+
 /* ---------- new game button stuff ---------------------------------------- */
 
 /* graphic position values for game buttons */
@@ -6439,30 +6766,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
-      if (AllPlayersGone)
-      {
-       CloseDoor(DOOR_CLOSE_1);
-       game_status = MAINMENU;
-       DrawMainMenu();
-       break;
-      }
-
-      if (level_editor_test_game ||
-         Request("Do you really want to quit the game ?",
-                 REQ_ASK | REQ_STAY_CLOSED))
-      { 
-#if defined(PLATFORM_UNIX)
-       if (options.network)
-         SendToServer_StopPlaying();
-       else
-#endif
-       {
-         game_status = MAINMENU;
-         DrawMainMenu();
-       }
-      }
-      else
-       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+      RequestQuitGame(TRUE);
       break;
 
     case GAME_CTRL_ID_PAUSE:
@@ -6476,7 +6780,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 #endif
       }
       else
-       TapeTogglePause();
+       TapeTogglePause(TAPE_TOGGLE_MANUAL);
       break;
 
     case GAME_CTRL_ID_PLAY:
@@ -6500,11 +6804,10 @@ static void HandleGameButtons(struct GadgetInfo *gi)
        setup.sound_music = FALSE;
        FadeMusic();
       }
-      else if (audio.loops_available)
+      else if (audio.music_available)
       { 
        setup.sound = setup.sound_music = TRUE;
-       if (num_bg_loops)
-         PlayMusic(level_nr % num_bg_loops);
+       PlayMusic(level_nr);
       }
       break;
 
index 9bf009eb7ac98878af0e0f3739369e0b08404dd2..607e34e2bf97836b7d88d4a42db730b5141d861c 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -30,6 +30,8 @@
 #define SC_ZEITBONUS           10
 
 void GetPlayerConfig(void);
+void DrawGameDoorValues(void);
+void InitGameSound();
 void InitGame(void);
 void InitMovDir(int, int);
 void InitAmoebaNr(int, int);
@@ -89,9 +91,9 @@ void RemoveHero(struct PlayerInfo *);
 int DigField(struct PlayerInfo *, int, int, int, int, int);
 boolean SnapField(struct PlayerInfo *, int, int);
 boolean PlaceBomb(struct PlayerInfo *);
-void PlaySoundLevel(int, int, int);
 void RaiseScore(int);
 void RaiseScoreElement(int);
+void RequestQuitGame(boolean);
 
 void CreateGameButtons();
 void UnmapGameButtons();
index 2d6017f3708d77e5e124a9413e137c0b1a3e8a42..0e1fc339e647669f08750afa9b4a67888a66e916 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "tape.h"
 #include "tools.h"
 #include "files.h"
-#include "joystick.h"
 #include "network.h"
 #include "netserv.h"
+#include "cartoons.h"
+#include "config.h"
 
+static char *image_filename[NUM_PICTURES] =
+{
+  "RocksScreen.pcx",
+  "RocksDoor.pcx",
+  "RocksHeroes.pcx",
+  "RocksToons.pcx",
+  "RocksSP.pcx",
+  "RocksDC.pcx",
+  "RocksMore.pcx",
+  "RocksFont.pcx",
+  "RocksFont2.pcx",
+  "RocksFont3.pcx"
+}; 
+
+static void InitSetup(void);
 static void InitPlayerInfo(void);
 static void InitLevelInfo(void);
+static void InitArtworkInfo(void);
+static void InitLevelArtworkInfo(void);
 static void InitNetworkServer(void);
+static void InitMixer(void);
 static void InitSound(void);
 static void InitGfx(void);
 static void InitGfxBackground(void);
 static void InitGadgets(void);
 static void InitElementProperties(void);
+static void Execute_Debug_Command(char *);
 
 void OpenAll(void)
 {
+  if (options.debug_command)
+  {
+    Execute_Debug_Command(options.debug_command);
+
+    exit(0);
+  }
+
   if (options.serveronly)
   {
 #if defined(PLATFORM_UNIX)
@@ -47,14 +74,17 @@ void OpenAll(void)
   }
 
   InitProgramInfo(UNIX_USERDATA_DIRECTORY,
-                 PROGRAM_TITLE_STRING, WINDOW_TITLE_STRING,
+                 PROGRAM_TITLE_STRING, getWindowTitleString(),
                  ICON_TITLE_STRING, X11_ICON_FILENAME, X11_ICONMASK_FILENAME,
-                 MSDOS_POINTER_FILENAME);
+                 MSDOS_POINTER_FILENAME,
+                 COOKIE_PREFIX, FILENAME_PREFIX, GAME_VERSION_ACTUAL);
 
+  InitSetup();
   InitPlayerInfo();
+  InitArtworkInfo();           /* needed before loading gfx, sound & music */
 
   InitCounter();
-  InitSound();
+  InitMixer();
   InitJoysticks();
   InitRND(NEW_RANDOMIZE);
 
@@ -68,14 +98,23 @@ void OpenAll(void)
   InitElementProperties();     /* initializes IS_CHAR() for el2gfx() */
 
   InitLevelInfo();
+  InitLevelArtworkInfo();
   InitGadgets();               /* needs to know number of level series */
+  InitSound();                 /* needs to know current level directory */
 
   InitGfxBackground();
+  InitToons();
+
   DrawMainMenu();
 
   InitNetworkServer();
 }
 
+void InitSetup()
+{
+  LoadSetup();                                 /* global setup info */
+}
+
 void InitPlayerInfo()
 {
   int i;
@@ -84,14 +123,9 @@ void InitPlayerInfo()
   local_player = &stored_player[0];
 
   for (i=0; i<MAX_PLAYERS; i++)
-  {
-    stored_player[i].joystick_fd = -1; /* joystick device closed */
     stored_player[i].connected = FALSE;
-  }
 
   local_player->connected = TRUE;
-
-  LoadSetup();                                 /* global setup info */
 }
 
 void InitLevelInfo()
@@ -101,6 +135,16 @@ void InitLevelInfo()
   LoadLevelSetup_SeriesInfo();                 /* last played level info */
 }
 
+void InitArtworkInfo()
+{
+  LoadArtworkInfo();
+}
+
+void InitLevelArtworkInfo()
+{
+  LoadLevelArtworkInfo();
+}
+
 void InitNetworkServer()
 {
 #if defined(PLATFORM_UNIX)
@@ -124,182 +168,33 @@ void InitNetworkServer()
 #endif
 }
 
-void InitSound()
+static void InitMixer()
 {
-  int i;
-
   OpenAudio();
+  InitSoundList(sound_effects, NUM_SOUND_EFFECTS);
 
-  for(i=0; i<NUM_SOUNDS; i++)
-  {
-    if (!LoadSound(sound_name[i]))
-    {
-      audio.sound_available = FALSE;
-      audio.loops_available = FALSE;
-      audio.sound_enabled = FALSE;
-
-      return;
-    }
-  }
-
-  num_bg_loops = LoadMusic();
-
-  StartSoundserver();
+  StartMixer();
 }
 
-void InitJoysticks()
+static void InitSound()
 {
-#if defined(TARGET_SDL)
-  static boolean sdl_joystick_subsystem_initialized = FALSE;
-#endif
-
-  int i;
-
-  if (global_joystick_status == JOYSTICK_OFF)
-    return;
-
-  joystick_status = JOYSTICK_OFF;
-
-#if defined(TARGET_SDL)
-
-  if (!sdl_joystick_subsystem_initialized)
-  {
-    sdl_joystick_subsystem_initialized = TRUE;
-
-    if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
-    {
-      Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
-      return;
-    }
-  }
-
-  for (i=0; i<MAX_PLAYERS; i++)
-  {
-    char *device_name = setup.input[i].joy.device_name;
-    int joystick_nr = getJoystickNrFromDeviceName(device_name);
-
-    if (joystick_nr >= SDL_NumJoysticks())
-      joystick_nr = -1;
-
-    /* misuse joystick file descriptor variable to store joystick number */
-    stored_player[i].joystick_fd = joystick_nr;
-
-    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
-    if (Check_SDL_JoystickOpened(joystick_nr))
-      Close_SDL_Joystick(joystick_nr);
-
-    if (!setup.input[i].use_joystick)
-      continue;
-
-    if (!Open_SDL_Joystick(joystick_nr))
-    {
-      Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
-      continue;
-    }
-
-    joystick_status = JOYSTICK_AVAILABLE;
-  }
-
-#else /* !TARGET_SDL */
-
-#if defined(PLATFORM_UNIX)
-  for (i=0; i<MAX_PLAYERS; i++)
-  {
-    char *device_name = setup.input[i].joy.device_name;
-
-    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
-    if (stored_player[i].joystick_fd != -1)
-    {
-      close(stored_player[i].joystick_fd);
-      stored_player[i].joystick_fd = -1;
-    }
-
-    if (!setup.input[i].use_joystick)
-      continue;
-
-    if (access(device_name, R_OK) != 0)
-    {
-      Error(ERR_WARN, "cannot access joystick device '%s'", device_name);
-      continue;
-    }
-
-    if ((stored_player[i].joystick_fd = open(device_name, O_RDONLY)) < 0)
-    {
-      Error(ERR_WARN, "cannot open joystick device '%s'", device_name);
-      continue;
-    }
-
-    joystick_status = JOYSTICK_AVAILABLE;
-  }
-
-#else /* !PLATFORM_UNIX */
-
-  /* try to access two joysticks; if that fails, try to access just one */
-  if (install_joystick(JOY_TYPE_2PADS) == 0 ||
-      install_joystick(JOY_TYPE_AUTODETECT) == 0)
-    joystick_status = JOYSTICK_AVAILABLE;
-
-  /*
-  load_joystick_data(JOYSTICK_FILENAME);
-  */
-
-  for (i=0; i<MAX_PLAYERS; i++)
-  {
-    char *device_name = setup.input[i].joy.device_name;
-    int joystick_nr = getJoystickNrFromDeviceName(device_name);
-
-    if (joystick_nr >= num_joysticks)
-      joystick_nr = -1;
-
-    /* misuse joystick file descriptor variable to store joystick number */
-    stored_player[i].joystick_fd = joystick_nr;
-  }
-#endif
+  /* load custom sounds and music */
+  InitReloadSounds(artwork.snd_current->name);
+  InitReloadMusic(artwork.mus_current->name);
 
-#endif /* !TARGET_SDL */
+  /* initialize sound effect lookup table for element actions */
+  InitGameSound();
 }
 
-void InitGfx()
+static void InitTileClipmasks()
 {
-  int i;
-
 #if defined(TARGET_X11)
-  GC copy_clipmask_gc;
   XGCValues clip_gc_values;
   unsigned long clip_gc_valuemask;
-#endif
-
-#if !defined(PLATFORM_MSDOS)
-  static char *image_filename[NUM_PICTURES] =
-  {
-    "RocksScreen.pcx",
-    "RocksDoor.pcx",
-    "RocksHeroes.pcx",
-    "RocksToons.pcx",
-    "RocksSP.pcx",
-    "RocksDC.pcx",
-    "RocksMore.pcx",
-    "RocksFont.pcx",
-    "RocksFont2.pcx",
-    "RocksFont3.pcx"
-  }; 
-#else
-  static char *image_filename[NUM_PICTURES] =
-  {
-    "Screen.pcx",
-    "Door.pcx",
-    "Heroes.pcx",
-    "Toons.pcx",
-    "SP.pcx",
-    "DC.pcx",
-    "More.pcx",
-    "Font.pcx",
-    "Font2.pcx",
-    "Font3.pcx"
-  }; 
-#endif
 
 #if defined(TARGET_X11_NATIVE)
+  GC copy_clipmask_gc;
+
   static struct
   {
     int start;
@@ -351,45 +246,10 @@ void InitGfx()
     { GFX2_SHIELD_ACTIVE, 3 },
     { -1, 0 }
   };
-#endif
-
-  /* initialize some global variables */
-  global.frames_per_second = 0;
-  global.fps_slowdown = FALSE;
-  global.fps_slowdown_factor = 1;
-
-  /* initialize screen properties */
-  InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
-                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
-  InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
-  InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
-  InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
-
-  /* create additional image buffers for double-buffering */
-  pix[PIX_DB_DOOR] = CreateBitmap(3 * DXSIZE, DYSIZE + VYSIZE, DEFAULT_DEPTH);
-  pix[PIX_DB_FIELD] = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
-
-  pix[PIX_SMALLFONT] = LoadImage(image_filename[PIX_SMALLFONT]);
-  InitFontInfo(NULL, NULL, pix[PIX_SMALLFONT]);
-
-  DrawInitText(WINDOW_TITLE_STRING, 20, FC_YELLOW);
-  DrawInitText(WINDOW_SUBTITLE_STRING, 50, FC_RED);
-#if defined(PLATFORM_MSDOS)
-  DrawInitText(PROGRAM_DOS_PORT_STRING, 210, FC_BLUE);
-  rest(200);
-#endif
-  DrawInitText("Loading graphics:",120,FC_GREEN);
-
-  for(i=0; i<NUM_PICTURES; i++)
-  {
-    if (i != PIX_SMALLFONT)
-    {
-      DrawInitText(image_filename[i], 150, FC_YELLOW);
-      pix[i] = LoadImage(image_filename[i]);
-    }
-  }
+#endif /* TARGET_X11_NATIVE */
+#endif /* TARGET_X11 */
 
-  InitFontInfo(pix[PIX_BIGFONT], pix[PIX_MEDIUMFONT], pix[PIX_SMALLFONT]);
+  int i;
 
   /* initialize pixmap array for special X11 tile clipping to Pixmap 'None' */
   for(i=0; i<NUM_TILES; i++)
@@ -401,13 +261,6 @@ void InitGfx()
      To prevent this, create small (tile-sized) mask Pixmaps which will then
      be set much faster with XSetClipOrigin() and speed things up a lot. */
 
-  /* create graphic context structures needed for clipping */
-  clip_gc_values.graphics_exposures = False;
-  clip_gc_valuemask = GCGraphicsExposures;
-  copy_clipmask_gc =
-    XCreateGC(display, pix[PIX_BACK]->clip_mask,
-             clip_gc_valuemask, &clip_gc_values);
-
   clip_gc_values.graphics_exposures = False;
   clip_gc_valuemask = GCGraphicsExposures;
   tile_clip_gc =
@@ -421,11 +274,19 @@ void InitGfx()
       clip_gc_values.clip_mask = pix[i]->clip_mask;
       clip_gc_valuemask = GCGraphicsExposures | GCClipMask;
       pix[i]->stored_clip_gc = XCreateGC(display, window->drawable,
-                                        clip_gc_valuemask,&clip_gc_values);
+                                        clip_gc_valuemask, &clip_gc_values);
     }
   }
 
 #if defined(TARGET_X11_NATIVE)
+
+  /* create graphic context structures needed for clipping */
+  clip_gc_values.graphics_exposures = False;
+  clip_gc_valuemask = GCGraphicsExposures;
+  copy_clipmask_gc =
+    XCreateGC(display, pix[PIX_BACK]->clip_mask,
+             clip_gc_valuemask, &clip_gc_values);
+
   /* create only those clipping Pixmaps we really need */
   for(i=0; tile_needs_clipping[i].start>=0; i++)
   {
@@ -449,10 +310,86 @@ void InitGfx()
                src_x, src_y, TILEX, TILEY, 0, 0);
     }
   }
+
+  XFreeGC(display, copy_clipmask_gc);
+
 #endif /* TARGET_X11_NATIVE */
 #endif /* TARGET_X11 */
 }
 
+void FreeTileClipmasks()
+{
+#if defined(TARGET_X11)
+  int i;
+
+  for(i=0; i<NUM_TILES; i++)
+  {
+    if (tile_clipmask[i] != None)
+    {
+      XFreePixmap(display, tile_clipmask[i]);
+      tile_clipmask[i] = None;
+    }
+  }
+
+  if (tile_clip_gc)
+    XFreeGC(display, tile_clip_gc);
+  tile_clip_gc = None;
+
+  for(i=0; i<NUM_BITMAPS; i++)
+  {
+    if (pix[i] != NULL && pix[i]->stored_clip_gc)
+    {
+      XFreeGC(display, pix[i]->stored_clip_gc);
+      pix[i]->stored_clip_gc = None;
+    }
+  }
+#endif /* TARGET_X11 */
+}
+
+void InitGfx()
+{
+  int i;
+
+  /* initialize some global variables */
+  global.frames_per_second = 0;
+  global.fps_slowdown = FALSE;
+  global.fps_slowdown_factor = 1;
+
+  /* initialize screen properties */
+  InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
+                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+  InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
+  InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
+  InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
+
+  /* create additional image buffers for double-buffering */
+  pix[PIX_DB_DOOR] = CreateBitmap(3 * DXSIZE, DYSIZE + VYSIZE, DEFAULT_DEPTH);
+  pix[PIX_DB_FIELD] = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+  pix[PIX_SMALLFONT] = LoadCustomImage(image_filename[PIX_SMALLFONT]);
+
+  InitFontInfo(NULL, NULL, pix[PIX_SMALLFONT]);
+
+  DrawInitText(WINDOW_TITLE_STRING, 20, FC_YELLOW);
+  DrawInitText(WINDOW_SUBTITLE_STRING, 50, FC_RED);
+
+  DrawInitText("Loading graphics:", 120, FC_GREEN);
+
+  for(i=0; i<NUM_PICTURES; i++)
+  {
+    if (i != PIX_SMALLFONT)
+    {
+      DrawInitText(image_filename[i], 150, FC_YELLOW);
+
+      pix[i] = LoadCustomImage(image_filename[i]);
+    }
+  }
+
+  InitFontInfo(pix[PIX_BIGFONT], pix[PIX_MEDIUMFONT], pix[PIX_SMALLFONT]);
+
+  InitTileClipmasks();
+}
+
 void InitGfxBackground()
 {
   int x, y;
@@ -461,9 +398,9 @@ void InitGfxBackground()
   fieldbuffer = pix[PIX_DB_FIELD];
   SetDrawtoField(DRAW_BACKBUFFER);
 
-  BlitBitmap(pix[PIX_BACK], backbuffer, 0,0, WIN_XSIZE,WIN_YSIZE, 0,0);
-  ClearRectangle(backbuffer, REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE);
-  ClearRectangle(pix[PIX_DB_DOOR], 0,0, 3*DXSIZE,DYSIZE+VYSIZE);
+  BlitBitmap(pix[PIX_BACK], backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+  ClearRectangle(backbuffer, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+  ClearRectangle(pix[PIX_DB_DOOR], 0, 0, 3 * DXSIZE, DYSIZE + VYSIZE);
 
   for(x=0; x<MAX_BUF_XSIZE; x++)
     for(y=0; y<MAX_BUF_YSIZE; y++)
@@ -472,6 +409,77 @@ void InitGfxBackground()
   redraw_mask = REDRAW_ALL;
 }
 
+void ReloadCustomArtwork()
+{
+  static char *leveldir_current_filename = NULL;
+  static boolean last_override_level_graphics = FALSE;
+  static boolean last_override_level_sounds = FALSE;
+  static boolean last_override_level_music = FALSE;
+
+  if (leveldir_current_filename != leveldir_current->filename)
+  {
+    char *filename_old = leveldir_current_filename;
+    char *filename_new = leveldir_current->filename;
+
+    /* force reload of custom artwork after new level series was selected,
+       but reload only that part of the artwork that really has changed */
+    if (getTreeInfoFromFilename(artwork.gfx_first, filename_old) !=
+       getTreeInfoFromFilename(artwork.gfx_first, filename_new))
+      artwork.graphics_set_current_name = NULL;
+    if (getTreeInfoFromFilename(artwork.snd_first, filename_old) !=
+       getTreeInfoFromFilename(artwork.snd_first, filename_new))
+      artwork.sounds_set_current_name = NULL;
+    if (getTreeInfoFromFilename(artwork.mus_first, filename_new) !=
+       getTreeInfoFromFilename(artwork.mus_first, filename_new))
+      artwork.music_set_current_name = NULL;
+
+    leveldir_current_filename = leveldir_current->filename;
+  }
+
+  if (artwork.graphics_set_current_name != artwork.gfx_current->name ||
+      last_override_level_graphics != setup.override_level_graphics)
+  {
+    int i;
+
+    ClearRectangle(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+    for(i=0; i<NUM_PICTURES; i++)
+    {
+      DrawInitText(image_filename[i], 150, FC_YELLOW);
+      ReloadCustomImage(pix[i], image_filename[i]);
+    }
+
+    FreeTileClipmasks();
+    InitTileClipmasks();
+    InitGfxBackground();
+
+    /* force redraw of (open or closed) door graphics */
+    SetDoorState(DOOR_OPEN_ALL);
+    CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
+
+    artwork.graphics_set_current_name = artwork.gfx_current->name;
+    last_override_level_graphics = setup.override_level_graphics;
+  }
+
+  if (artwork.sounds_set_current_name != artwork.snd_current->name ||
+      last_override_level_sounds != setup.override_level_sounds)
+  {
+    InitReloadSounds(artwork.snd_current->name);
+
+    artwork.sounds_set_current_name = artwork.snd_current->name;
+    last_override_level_sounds = setup.override_level_sounds;
+  }
+
+  if (artwork.music_set_current_name != artwork.mus_current->name ||
+      last_override_level_music != setup.override_level_music)
+  {
+    InitReloadMusic(artwork.mus_current->name);
+
+    artwork.music_set_current_name = artwork.mus_current->name;
+    last_override_level_music = setup.override_level_music;
+  }
+}
+
 void InitGadgets()
 {
   CreateLevelEditorGadgets();
@@ -492,7 +500,7 @@ void InitElementProperties()
     EL_AMOEBE_VOLL,
     EL_AMOEBE_BD
   };
-  static int ep_amoebalive_num = sizeof(ep_amoebalive)/sizeof(int);
+  static int ep_amoebalive_num = SIZEOF_ARRAY_INT(ep_amoebalive);
 
   static int ep_amoeboid[] =
   {
@@ -502,7 +510,7 @@ void InitElementProperties()
     EL_AMOEBE_VOLL,
     EL_AMOEBE_BD
   };
-  static int ep_amoeboid_num = sizeof(ep_amoeboid)/sizeof(int);
+  static int ep_amoeboid_num = SIZEOF_ARRAY_INT(ep_amoeboid);
 
   static int ep_schluessel[] =
   {
@@ -515,7 +523,7 @@ void InitElementProperties()
     EL_EM_KEY_3,
     EL_EM_KEY_4
   };
-  static int ep_schluessel_num = sizeof(ep_schluessel)/sizeof(int);
+  static int ep_schluessel_num = SIZEOF_ARRAY_INT(ep_schluessel);
 
   static int ep_pforte[] =
   {
@@ -536,9 +544,13 @@ void InitElementProperties()
     EL_EM_GATE_3X,
     EL_EM_GATE_4X,
     EL_SWITCHGATE_OPEN,
+    EL_SWITCHGATE_OPENING,
     EL_SWITCHGATE_CLOSED,
+    EL_SWITCHGATE_CLOSING,
     EL_TIMEGATE_OPEN,
+    EL_TIMEGATE_OPENING,
     EL_TIMEGATE_CLOSED,
+    EL_TIMEGATE_CLOSING,
     EL_TUBE_CROSS,
     EL_TUBE_VERTICAL,
     EL_TUBE_HORIZONTAL,
@@ -551,7 +563,7 @@ void InitElementProperties()
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN
   };
-  static int ep_pforte_num = sizeof(ep_pforte)/sizeof(int);
+  static int ep_pforte_num = SIZEOF_ARRAY_INT(ep_pforte);
 
   static int ep_solid[] =
   {
@@ -577,11 +589,15 @@ void InitElementProperties()
     EL_QUICKSAND_EMPTYING,
     EL_MAGIC_WALL_OFF,
     EL_MAGIC_WALL_EMPTY,
+    EL_MAGIC_WALL_EMPTYING,
+    EL_MAGIC_WALL_FILLING,
     EL_MAGIC_WALL_FULL,
     EL_MAGIC_WALL_DEAD,
     EL_MAGIC_WALL_BD_OFF,
     EL_MAGIC_WALL_BD_EMPTY,
+    EL_MAGIC_WALL_BD_EMPTYING,
     EL_MAGIC_WALL_BD_FULL,
+    EL_MAGIC_WALL_BD_FILLING,
     EL_MAGIC_WALL_BD_DEAD,
     EL_LIFE,
     EL_LIFE_ASYNC,
@@ -607,6 +623,7 @@ void InitElementProperties()
     EL_SP_HARD_BASE5,
     EL_SP_HARD_BASE6,
     EL_SP_TERMINAL,
+    EL_SP_TERMINAL_ACTIVE,
     EL_SP_EXIT,
     EL_INVISIBLE_STEEL,
     EL_BELT1_SWITCH_LEFT,
@@ -655,6 +672,30 @@ void InitElementProperties()
     EL_CRYSTAL,
     EL_WALL_PEARL,
     EL_WALL_CRYSTAL,
+    EL_PFORTE1,
+    EL_PFORTE2,
+    EL_PFORTE3,
+    EL_PFORTE4,
+    EL_PFORTE1X,
+    EL_PFORTE2X,
+    EL_PFORTE3X,
+    EL_PFORTE4X,
+    EL_EM_GATE_1,
+    EL_EM_GATE_2,
+    EL_EM_GATE_3,
+    EL_EM_GATE_4,
+    EL_EM_GATE_1X,
+    EL_EM_GATE_2X,
+    EL_EM_GATE_3X,
+    EL_EM_GATE_4X,
+    EL_SWITCHGATE_OPEN,
+    EL_SWITCHGATE_OPENING,
+    EL_SWITCHGATE_CLOSED,
+    EL_SWITCHGATE_CLOSING,
+    EL_TIMEGATE_OPEN,
+    EL_TIMEGATE_OPENING,
+    EL_TIMEGATE_CLOSED,
+    EL_TIMEGATE_CLOSING,
     EL_TUBE_CROSS,
     EL_TUBE_VERTICAL,
     EL_TUBE_HORIZONTAL,
@@ -667,7 +708,7 @@ void InitElementProperties()
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN
   };
-  static int ep_solid_num = sizeof(ep_solid)/sizeof(int);
+  static int ep_solid_num = SIZEOF_ARRAY_INT(ep_solid);
 
   static int ep_massive[] =
   {
@@ -678,26 +719,6 @@ void InitElementProperties()
     EL_BADEWANNE3,
     EL_BADEWANNE4,
     EL_BADEWANNE5,
-    EL_PFORTE1,
-    EL_PFORTE2,
-    EL_PFORTE3,
-    EL_PFORTE4,
-    EL_PFORTE1X,
-    EL_PFORTE2X,
-    EL_PFORTE3X,
-    EL_PFORTE4X,
-    EL_EM_GATE_1,
-    EL_EM_GATE_2,
-    EL_EM_GATE_3,
-    EL_EM_GATE_4,
-    EL_EM_GATE_1X,
-    EL_EM_GATE_2X,
-    EL_EM_GATE_3X,
-    EL_EM_GATE_4X,
-    EL_SWITCHGATE_OPEN,
-    EL_SWITCHGATE_CLOSED,
-    EL_TIMEGATE_OPEN,
-    EL_TIMEGATE_CLOSED,
     EL_SP_HARD_GRAY,
     EL_SP_HARD_GREEN,
     EL_SP_HARD_BLUE,
@@ -742,6 +763,30 @@ void InitElementProperties()
     EL_EMC_STEEL_WALL_3,
     EL_EMC_STEEL_WALL_4,
     EL_CRYSTAL,
+    EL_PFORTE1,
+    EL_PFORTE2,
+    EL_PFORTE3,
+    EL_PFORTE4,
+    EL_PFORTE1X,
+    EL_PFORTE2X,
+    EL_PFORTE3X,
+    EL_PFORTE4X,
+    EL_EM_GATE_1,
+    EL_EM_GATE_2,
+    EL_EM_GATE_3,
+    EL_EM_GATE_4,
+    EL_EM_GATE_1X,
+    EL_EM_GATE_2X,
+    EL_EM_GATE_3X,
+    EL_EM_GATE_4X,
+    EL_SWITCHGATE_OPEN,
+    EL_SWITCHGATE_OPENING,
+    EL_SWITCHGATE_CLOSED,
+    EL_SWITCHGATE_CLOSING,
+    EL_TIMEGATE_OPEN,
+    EL_TIMEGATE_OPENING,
+    EL_TIMEGATE_CLOSED,
+    EL_TIMEGATE_CLOSING,
     EL_TUBE_CROSS,
     EL_TUBE_VERTICAL,
     EL_TUBE_HORIZONTAL,
@@ -754,7 +799,7 @@ void InitElementProperties()
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN
   };
-  static int ep_massive_num = sizeof(ep_massive)/sizeof(int);
+  static int ep_massive_num = SIZEOF_ARRAY_INT(ep_massive);
 
   static int ep_slippery[] =
   {
@@ -791,7 +836,7 @@ void InitElementProperties()
     EL_PEARL,
     EL_CRYSTAL
   };
-  static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
+  static int ep_slippery_num = SIZEOF_ARRAY_INT(ep_slippery);
 
   static int ep_enemy[] =
   {
@@ -806,7 +851,7 @@ void InitElementProperties()
     EL_SP_SNIKSNAK,
     EL_SP_ELECTRON
   };
-  static int ep_enemy_num = sizeof(ep_enemy)/sizeof(int);
+  static int ep_enemy_num = SIZEOF_ARRAY_INT(ep_enemy);
 
   static int ep_mauer[] =
   {
@@ -855,6 +900,7 @@ void InitElementProperties()
     EL_SP_HARD_BASE5,
     EL_SP_HARD_BASE6,
     EL_SP_TERMINAL,
+    EL_SP_TERMINAL_ACTIVE,
     EL_SP_EXIT,
     EL_INVISIBLE_STEEL,
     EL_STEEL_SLANTED,
@@ -871,7 +917,7 @@ void InitElementProperties()
     EL_EMC_WALL_7,
     EL_EMC_WALL_8
   };
-  static int ep_mauer_num = sizeof(ep_mauer)/sizeof(int);
+  static int ep_mauer_num = SIZEOF_ARRAY_INT(ep_mauer);
 
   static int ep_can_fall[] =
   {
@@ -899,7 +945,7 @@ void InitElementProperties()
     EL_SPRING,
     EL_DX_SUPABOMB
   };
-  static int ep_can_fall_num = sizeof(ep_can_fall)/sizeof(int);
+  static int ep_can_fall_num = SIZEOF_ARRAY_INT(ep_can_fall);
 
   static int ep_can_smash[] =
   {
@@ -932,7 +978,7 @@ void InitElementProperties()
     EL_SPRING,
     EL_DX_SUPABOMB
   };
-  static int ep_can_smash_num = sizeof(ep_can_smash)/sizeof(int);
+  static int ep_can_smash_num = SIZEOF_ARRAY_INT(ep_can_smash);
 
   static int ep_can_change[] =
   {
@@ -945,7 +991,7 @@ void InitElementProperties()
     EL_EDELSTEIN_LILA,
     EL_DIAMANT
   };
-  static int ep_can_change_num = sizeof(ep_can_change)/sizeof(int);
+  static int ep_can_change_num = SIZEOF_ARRAY_INT(ep_can_change);
 
   static int ep_can_move[] =
   {
@@ -967,7 +1013,7 @@ void InitElementProperties()
     EL_BALLOON,
     EL_SPRING_MOVING
   };
-  static int ep_can_move_num = sizeof(ep_can_move)/sizeof(int);
+  static int ep_can_move_num = SIZEOF_ARRAY_INT(ep_can_move);
 
   static int ep_could_move[] =
   {
@@ -992,7 +1038,7 @@ void InitElementProperties()
     EL_PACMAN_LEFT,
     EL_PACMAN_DOWN
   };
-  static int ep_could_move_num = sizeof(ep_could_move)/sizeof(int);
+  static int ep_could_move_num = SIZEOF_ARRAY_INT(ep_could_move);
 
   static int ep_dont_touch[] =
   {
@@ -1001,7 +1047,7 @@ void InitElementProperties()
     EL_BUTTERFLY,
     EL_FIREFLY
   };
-  static int ep_dont_touch_num = sizeof(ep_dont_touch)/sizeof(int);
+  static int ep_dont_touch_num = SIZEOF_ARRAY_INT(ep_dont_touch);
 
   static int ep_dont_go_to[] =
   {
@@ -1021,7 +1067,7 @@ void InitElementProperties()
     EL_TRAP_ACTIVE,
     EL_LANDMINE
   };
-  static int ep_dont_go_to_num = sizeof(ep_dont_go_to)/sizeof(int);
+  static int ep_dont_go_to_num = SIZEOF_ARRAY_INT(ep_dont_go_to);
 
   static int ep_mampf2[] =
   {
@@ -1048,7 +1094,7 @@ void InitElementProperties()
     EL_PEARL,
     EL_CRYSTAL
   };
-  static int ep_mampf2_num = sizeof(ep_mampf2)/sizeof(int);
+  static int ep_mampf2_num = SIZEOF_ARRAY_INT(ep_mampf2);
 
   static int ep_bd_element[] =
   {
@@ -1077,7 +1123,7 @@ void InitElementProperties()
     EL_AMOEBE_BD,
     EL_CHAR_FRAGE
   };
-  static int ep_bd_element_num = sizeof(ep_bd_element)/sizeof(int);
+  static int ep_bd_element_num = SIZEOF_ARRAY_INT(ep_bd_element);
 
   static int ep_sb_element[] =
   {
@@ -1089,7 +1135,7 @@ void InitElementProperties()
     EL_SPIELFIGUR,
     EL_INVISIBLE_STEEL
   };
-  static int ep_sb_element_num = sizeof(ep_sb_element)/sizeof(int);
+  static int ep_sb_element_num = SIZEOF_ARRAY_INT(ep_sb_element);
 
   static int ep_gem[] =
   {
@@ -1100,7 +1146,7 @@ void InitElementProperties()
     EL_EDELSTEIN_LILA,
     EL_DIAMANT
   };
-  static int ep_gem_num = sizeof(ep_gem)/sizeof(int);
+  static int ep_gem_num = SIZEOF_ARRAY_INT(ep_gem);
 
   static int ep_inactive[] =
   {
@@ -1237,7 +1283,7 @@ void InitElementProperties()
     EL_EMC_WALL_7,
     EL_EMC_WALL_8
   };
-  static int ep_inactive_num = sizeof(ep_inactive)/sizeof(int);
+  static int ep_inactive_num = SIZEOF_ARRAY_INT(ep_inactive);
 
   static int ep_explosive[] =
   {
@@ -1264,7 +1310,7 @@ void InitElementProperties()
     EL_SP_ELECTRON,
     EL_DX_SUPABOMB
   };
-  static int ep_explosive_num = sizeof(ep_explosive)/sizeof(int);
+  static int ep_explosive_num = SIZEOF_ARRAY_INT(ep_explosive);
 
   static int ep_mampf3[] =
   {
@@ -1277,7 +1323,7 @@ void InitElementProperties()
     EL_PEARL,
     EL_CRYSTAL
   };
-  static int ep_mampf3_num = sizeof(ep_mampf3)/sizeof(int);
+  static int ep_mampf3_num = SIZEOF_ARRAY_INT(ep_mampf3);
 
   static int ep_pushable[] =
   {
@@ -1296,7 +1342,7 @@ void InitElementProperties()
     EL_SPRING,
     EL_DX_SUPABOMB
   };
-  static int ep_pushable_num = sizeof(ep_pushable)/sizeof(int);
+  static int ep_pushable_num = SIZEOF_ARRAY_INT(ep_pushable);
 
   static int ep_player[] =
   {
@@ -1306,7 +1352,7 @@ void InitElementProperties()
     EL_SPIELER3,
     EL_SPIELER4
   };
-  static int ep_player_num = sizeof(ep_player)/sizeof(int);
+  static int ep_player_num = SIZEOF_ARRAY_INT(ep_player);
 
   static int ep_has_content[] =
   {
@@ -1316,7 +1362,7 @@ void InitElementProperties()
     EL_AMOEBE_VOLL,
     EL_AMOEBE_BD
   };
-  static int ep_has_content_num = sizeof(ep_has_content)/sizeof(int);
+  static int ep_has_content_num = SIZEOF_ARRAY_INT(ep_has_content);
 
   static int ep_eatable[] =
   {
@@ -1326,7 +1372,7 @@ void InitElementProperties()
     EL_TRAP_INACTIVE,
     EL_SAND_INVISIBLE
   };
-  static int ep_eatable_num = sizeof(ep_eatable)/sizeof(int);
+  static int ep_eatable_num = SIZEOF_ARRAY_INT(ep_eatable);
 
   static int ep_sp_element[] =
   {
@@ -1375,7 +1421,7 @@ void InitElementProperties()
     /* more than one murphy in a level results in an inactive clone */
     EL_SP_MURPHY_CLONE
   };
-  static int ep_sp_element_num = sizeof(ep_sp_element)/sizeof(int);
+  static int ep_sp_element_num = SIZEOF_ARRAY_INT(ep_sp_element);
 
   static int ep_quick_gate[] =
   {
@@ -1401,7 +1447,7 @@ void InitElementProperties()
     EL_SWITCHGATE_OPEN,
     EL_TIMEGATE_OPEN
   };
-  static int ep_quick_gate_num = sizeof(ep_quick_gate)/sizeof(int);
+  static int ep_quick_gate_num = SIZEOF_ARRAY_INT(ep_quick_gate);
 
   static int ep_over_player[] =
   {
@@ -1428,7 +1474,7 @@ void InitElementProperties()
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN
   };
-  static int ep_over_player_num = sizeof(ep_over_player)/sizeof(int);
+  static int ep_over_player_num = SIZEOF_ARRAY_INT(ep_over_player);
 
   static int ep_active_bomb[] =
   {
@@ -1438,7 +1484,7 @@ void InitElementProperties()
     EL_DYNABOMB_ACTIVE_3,
     EL_DYNABOMB_ACTIVE_4
   };
-  static int ep_active_bomb_num = sizeof(ep_active_bomb)/sizeof(int);
+  static int ep_active_bomb_num = SIZEOF_ARRAY_INT(ep_active_bomb);
 
   static int ep_belt[] =
   {
@@ -1455,7 +1501,7 @@ void InitElementProperties()
     EL_BELT4_MIDDLE,
     EL_BELT4_RIGHT,
   };
-  static int ep_belt_num = sizeof(ep_belt)/sizeof(int);
+  static int ep_belt_num = SIZEOF_ARRAY_INT(ep_belt);
 
   static int ep_belt_switch[] =
   {
@@ -1472,7 +1518,7 @@ void InitElementProperties()
     EL_BELT4_SWITCH_MIDDLE,
     EL_BELT4_SWITCH_RIGHT,
   };
-  static int ep_belt_switch_num = sizeof(ep_belt_switch)/sizeof(int);
+  static int ep_belt_switch_num = SIZEOF_ARRAY_INT(ep_belt_switch);
 
   static int ep_tube[] =
   {
@@ -1488,7 +1534,7 @@ void InitElementProperties()
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN
   };
-  static int ep_tube_num = sizeof(ep_tube)/sizeof(int);
+  static int ep_tube_num = SIZEOF_ARRAY_INT(ep_tube);
 
   static long ep1_bit[] =
   {
@@ -1610,8 +1656,8 @@ void InitElementProperties()
     &ep_belt_switch_num,
     &ep_tube_num
   };
-  static int num_properties1 = sizeof(ep1_num)/sizeof(int *);
-  static int num_properties2 = sizeof(ep2_num)/sizeof(int *);
+  static int num_properties1 = SIZEOF_ARRAY(ep1_num, int *);
+  static int num_properties2 = SIZEOF_ARRAY(ep2_num, int *);
 
   for(i=0; i<MAX_ELEMENTS; i++)
   {
@@ -1630,18 +1676,57 @@ void InitElementProperties()
     Elementeigenschaften1[i] |= (EP_BIT_CHAR | EP_BIT_INACTIVE);
 }
 
+void Execute_Debug_Command(char *command)
+{
+  if (strcmp(command, "create graphicsinfo.conf") == 0)
+  {
+    printf("# (Currently only \"name\" and \"sort_priority\" recognized.)\n");
+    printf("\n");
+    printf("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
+    printf("\n");
+    printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+  }
+  else if (strcmp(command, "create soundsinfo.conf") == 0)
+  {
+    int i;
+
+    printf("# You can configure additional/alternative sound effects here\n");
+    printf("# (The sounds below are default and therefore commented out.)\n");
+    printf("\n");
+    printf("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
+    printf("\n");
+    printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+    printf("\n");
+
+    for (i=0; i<NUM_SOUND_EFFECTS; i++)
+      printf("# %s\n",
+            getFormattedSetupEntry(sound_effects[i].text,
+                                   sound_effects[i].default_filename));
+  }
+  else if (strcmp(command, "create musicinfo.conf") == 0)
+  {
+    printf("# (Currently only \"name\" and \"sort_priority\" recognized.)\n");
+    printf("\n");
+    printf("%s\n", getFormattedSetupEntry("name", "Classic Music"));
+    printf("\n");
+    printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+  }
+}
+
 void CloseAllAndExit(int exit_value)
 {
   int i;
 
   StopSounds();
-  FreeSounds(NUM_SOUNDS);
-  CloseAudio();
+  FreeAllSounds();
+  FreeAllMusic();
+  CloseAudio();                /* called after freeing sounds (needed for SDL) */
 
+  FreeTileClipmasks();
   for(i=0; i<NUM_BITMAPS; i++)
     FreeBitmap(pix[i]);
-  CloseVideoDisplay();
 
+  CloseVideoDisplay();
   ClosePlatformDependantStuff();
 
   exit(exit_value);
index 1088db2632b373420c87eb846ed75e3e4ae6def4..9eb9fb628c79aa82c6c8ca780bdc4e6f4c9a89f6 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -17,7 +17,7 @@
 #include "main.h"
 
 void OpenAll(void);
+void ReloadCustomArtwork();
 void CloseAllAndExit(int);
-void InitJoysticks(void);
 
 #endif
diff --git a/src/joystick.c b/src/joystick.c
deleted file mode 100644 (file)
index 2ad9cb8..0000000
+++ /dev/null
@@ -1,521 +0,0 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* joystick.c                                               *
-***********************************************************/
-
-#if defined(PLATFORM_FREEBSD)
-#include <machine/joystick.h>
-#endif
-
-#include "libgame/libgame.h"
-
-#include "joystick.h"
-
-#define TRANSLATE_JOYSYMBOL_TO_JOYNAME 0
-#define TRANSLATE_JOYNAME_TO_JOYSYMBOL 1
-
-void translate_joyname(int *joysymbol, char **name, int mode)
-{
-  static struct
-  {
-    int joysymbol;
-    char *name;
-  } translate_joy[] =
-  {
-    { JOY_LEFT,                "joystick_left" },
-    { JOY_RIGHT,       "joystick_right" },
-    { JOY_UP,          "joystick_up" },
-    { JOY_DOWN,                "joystick_down" },
-    { JOY_BUTTON_1,    "joystick_button_1" },
-    { JOY_BUTTON_2,    "joystick_button_2" },
-  };
-
-  int i;
-
-  if (mode == TRANSLATE_JOYSYMBOL_TO_JOYNAME)
-  {
-    *name = "[undefined]";
-
-    for (i=0; i<6; i++)
-    {
-      if (*joysymbol == translate_joy[i].joysymbol)
-      {
-       *name = translate_joy[i].name;
-       break;
-      }
-    }
-  }
-  else if (mode == TRANSLATE_JOYNAME_TO_JOYSYMBOL)
-  {
-    *joysymbol = 0;
-
-    for (i=0; i<6; i++)
-    {
-      if (strcmp(*name, translate_joy[i].name) == 0)
-      {
-       *joysymbol = translate_joy[i].joysymbol;
-       break;
-      }
-    }
-  }
-}
-
-char *getJoyNameFromJoySymbol(int joysymbol)
-{
-  char *name;
-
-  translate_joyname(&joysymbol, &name, TRANSLATE_JOYSYMBOL_TO_JOYNAME);
-  return name;
-}
-
-int getJoySymbolFromJoyName(char *name)
-{
-  int joysymbol;
-
-  translate_joyname(&joysymbol, &name, TRANSLATE_JOYNAME_TO_JOYSYMBOL);
-  return joysymbol;
-}
-
-int getJoystickNrFromDeviceName(char *device_name)
-{
-  char c;
-  int joystick_nr = 0;
-
-  if (device_name == NULL || device_name[0] == '\0')
-    return 0;
-
-  c = device_name[strlen(device_name) - 1];
-
-  if (c >= '0' && c <= '9')
-    joystick_nr = (int)(c - '0');
-
-  if (joystick_nr < 0 || joystick_nr >= MAX_PLAYERS)
-    joystick_nr = 0;
-
-  return joystick_nr;
-}
-
-#if !defined(PLATFORM_MSDOS)
-static int JoystickPosition(int middle, int margin, int actual)
-{
-  long range, pos;
-  int percentage;
-
-  if (margin < middle && actual > middle)
-    return 0;
-  if (margin > middle && actual < middle)
-    return 0;
-
-  range = ABS(margin - middle);
-  pos = ABS(actual - middle);
-  percentage = (int)(pos * 100 / range);
-
-  if (percentage > 100)
-    percentage = 100;
-
-  return percentage;
-}
-#endif
-
-#if defined(TARGET_SDL)
-
-static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
-static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-
-SDL_Joystick *Get_SDL_Joystick(int nr)
-{
-  return sdl_joystick[nr];
-}
-
-boolean Open_SDL_Joystick(int nr)
-{
-  if (nr < 0 || nr > MAX_PLAYERS)
-    return FALSE;
-
-  return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
-}
-
-void Close_SDL_Joystick(int nr)
-{
-  if (nr < 0 || nr > MAX_PLAYERS)
-    return;
-
-  SDL_JoystickClose(sdl_joystick[nr]);
-}
-
-boolean Check_SDL_JoystickOpened(int nr)
-{
-  if (nr < 0 || nr > MAX_PLAYERS)
-    return FALSE;
-
-  return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
-}
-
-void HandleJoystickEvent(Event *event)
-{
-  switch(event->type)
-  {
-    case SDL_JOYAXISMOTION:
-      if (event->jaxis.axis < 2)
-      {
-       sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
-
-#if 0
-       printf("js_%d %s-axis: %d\n",
-              event->jaxis.which,
-              (event->jaxis.axis == 0 ? "x" : "y"),
-              event->jaxis.value);
-#endif
-      }
-      break;
-
-    case SDL_JOYBUTTONDOWN:
-      if (event->jbutton.button < 2)
-      {
-       sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
-
-#if 0
-        printf("js_%d button %d: pressed\n",
-              event->jbutton.which,
-              event->jbutton.button);
-#endif
-      }
-      break;
-
-    case SDL_JOYBUTTONUP:
-      if (event->jbutton.button < 2)
-      {
-       sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
-
-#if 0
-        printf("js_%d button %d: released\n",
-              event->jbutton.which,
-              event->jbutton.button);
-#endif
-      }
-      break;
-
-    default:
-      break;
-  }
-}
-
-int Get_SDL_Joystick_Axis(int nr, int axis)
-{
-  if (nr < 0 || nr > MAX_PLAYERS)
-    return 0;
-
-  if (axis < 0 || axis > 1)
-    return 0;
-
-  return sdl_js_axis[nr][axis];
-}
-
-void CheckJoystickData()
-{
-}
-
-int Joystick(int player_nr)
-{
-  int joystick_nr = stored_player[player_nr].joystick_fd;
-  int js_x,js_y, js_b1,js_b2;
-  int left, right, up, down;
-  int result = 0;
-
-  if (joystick_status == JOYSTICK_OFF)
-    return 0;
-
-  if (game_status == SETUPINPUT)
-    return 0;
-
-  if (!setup.input[player_nr].use_joystick ||
-      !Check_SDL_JoystickOpened(joystick_nr))
-    return 0;
-
-  js_x  = sdl_js_axis[joystick_nr][0];
-  js_y  = sdl_js_axis[joystick_nr][1];
-
-  js_b1 = sdl_js_button[joystick_nr][0];
-  js_b2 = sdl_js_button[joystick_nr][1];
-
-
-
-#if 0
-  printf("JOYSTICK %d: js_x == %d, js_y == %d, js_b1 == %d, js_b2 == %d\n",
-        joystick_nr, js_x, js_y, js_b1, js_b2);
-#endif
-
-
-
-  left  = JoystickPosition(setup.input[player_nr].joy.xmiddle,
-                          setup.input[player_nr].joy.xleft,  js_x);
-  right = JoystickPosition(setup.input[player_nr].joy.xmiddle,
-                          setup.input[player_nr].joy.xright, js_x);
-  up    = JoystickPosition(setup.input[player_nr].joy.ymiddle,
-                          setup.input[player_nr].joy.yupper, js_y);
-  down  = JoystickPosition(setup.input[player_nr].joy.ymiddle,
-                          setup.input[player_nr].joy.ylower, js_y);
-
-  if (left > JOYSTICK_PERCENT)
-    result |= JOY_LEFT;
-  else if (right > JOYSTICK_PERCENT)
-    result |= JOY_RIGHT;
-  if (up > JOYSTICK_PERCENT)
-    result |= JOY_UP;
-  else if (down > JOYSTICK_PERCENT)
-    result |= JOY_DOWN;
-
-  if (js_b1)
-    result |= JOY_BUTTON_1;
-  if (js_b2)
-    result |= JOY_BUTTON_2;
-
-
-
-#if 0
-  printf("result == 0x%08x\n", result);
-#endif
-
-
-
-  return result;
-}
-
-#else /* !TARGET_SDL */
-
-void CheckJoystickData()
-{
-  int i;
-  int distance = 100;
-
-  for(i=0; i<MAX_PLAYERS; i++)
-  {
-    if (setup.input[i].joy.xmiddle <= distance)
-      setup.input[i].joy.xmiddle = distance;
-    if (setup.input[i].joy.ymiddle <= distance)
-      setup.input[i].joy.ymiddle = distance;
-
-    if (setup.input[i].joy.xleft >= setup.input[i].joy.xmiddle)
-      setup.input[i].joy.xleft = setup.input[i].joy.xmiddle - distance;
-    if (setup.input[i].joy.xright <= setup.input[i].joy.xmiddle)
-      setup.input[i].joy.xright = setup.input[i].joy.xmiddle + distance;
-
-    if (setup.input[i].joy.yupper >= setup.input[i].joy.ymiddle)
-      setup.input[i].joy.yupper = setup.input[i].joy.ymiddle - distance;
-    if (setup.input[i].joy.ylower <= setup.input[i].joy.ymiddle)
-      setup.input[i].joy.ylower = setup.input[i].joy.ymiddle + distance;
-  }
-}
-
-#if defined(PLATFORM_UNIX)
-int Joystick(int player_nr)
-{
-#ifdef __FreeBSD__
-  struct joystick joy_ctrl;
-#else
-  struct joystick_control
-  {
-    int buttons;
-    int x;
-    int y;
-  } joy_ctrl;
-#endif
-
-  int joystick_fd = stored_player[player_nr].joystick_fd;
-  int js_x,js_y, js_b1,js_b2;
-  int left, right, up, down;
-  int result = 0;
-
-  if (joystick_status == JOYSTICK_OFF)
-    return 0;
-
-  if (game_status == SETUPINPUT)
-    return 0;
-
-  if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
-    return 0;
-
-  if (read(joystick_fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
-  {
-    Error(ERR_WARN, "cannot read joystick device '%s'",
-         setup.input[player_nr].joy.device_name);
-    joystick_status = JOYSTICK_OFF;
-    return 0;
-  }
-
-  js_x  = joy_ctrl.x;
-  js_y  = joy_ctrl.y;
-
-#ifdef __FreeBSD__
-  js_b1 = joy_ctrl.b1;
-  js_b2 = joy_ctrl.b2;
-#else
-  js_b1 = joy_ctrl.buttons & 1;
-  js_b2 = joy_ctrl.buttons & 2;
-#endif
-
-  left  = JoystickPosition(setup.input[player_nr].joy.xmiddle,
-                          setup.input[player_nr].joy.xleft,  js_x);
-  right = JoystickPosition(setup.input[player_nr].joy.xmiddle,
-                          setup.input[player_nr].joy.xright, js_x);
-  up    = JoystickPosition(setup.input[player_nr].joy.ymiddle,
-                          setup.input[player_nr].joy.yupper, js_y);
-  down  = JoystickPosition(setup.input[player_nr].joy.ymiddle,
-                          setup.input[player_nr].joy.ylower, js_y);
-
-  if (left > JOYSTICK_PERCENT)
-    result |= JOY_LEFT;
-  else if (right > JOYSTICK_PERCENT)
-    result |= JOY_RIGHT;
-  if (up > JOYSTICK_PERCENT)
-    result |= JOY_UP;
-  else if (down > JOYSTICK_PERCENT)
-    result |= JOY_DOWN;
-
-  if (js_b1)
-    result |= JOY_BUTTON_1;
-  if (js_b2)
-    result |= JOY_BUTTON_2;
-
-  return result;
-}
-
-#else /* PLATFORM_MSDOS */
-
-/* allegro global variables for joystick control */
-extern int num_joysticks;
-extern JOYSTICK_INFO joy[];
-
-int Joystick(int player_nr)
-{
-  int joystick_nr = stored_player[player_nr].joystick_fd;
-  int result = 0;
-
-  if (joystick_status == JOYSTICK_OFF)
-    return 0;
-
-  if (game_status == SETUPINPUT)
-    return 0;
-
-  if (joystick_nr < 0)
-    return 0;
-
-  /* the allegro global variable 'num_joysticks' contains the number
-     of joysticks found at initialization under MS-DOS / Windows */
-
-#if 0
-  if (joystick_nr >= num_joysticks || !setup.input[player_nr].use_joystick)
-    return 0;
-#else
-
-#if 1
-  if (joystick_nr >= num_joysticks ||
-      (game_status == PLAYING && !setup.input[player_nr].use_joystick))
-    return 0;
-#else
-  if (joystick_nr >= num_joysticks)
-    return 0;
-#endif
-
-#endif
-
-  poll_joystick();
-
-  if (joy[joystick_nr].stick[0].axis[0].d1)
-    result |= JOY_LEFT;
-  else if (joy[joystick_nr].stick[0].axis[0].d2)
-    result |= JOY_RIGHT;
-  if (joy[joystick_nr].stick[0].axis[1].d1)
-    result |= JOY_UP;
-  else if (joy[joystick_nr].stick[0].axis[1].d2)
-    result |= JOY_DOWN;
-
-  if (joy[joystick_nr].button[0].b)
-    result |= JOY_BUTTON_1;
-  if (joy[joystick_nr].button[1].b)
-    result |= JOY_BUTTON_2;
-
-  return result;
-}
-#endif /* PLATFORM_MSDOS */
-
-#endif /* !TARGET_SDL */
-
-int JoystickButton(int player_nr)
-{
-  static int last_joy_button[MAX_PLAYERS] = { 0, 0, 0, 0 };
-  int joy_button = (Joystick(player_nr) & JOY_BUTTON);
-  int result;
-
-  if (joy_button)
-  {
-    if (last_joy_button[player_nr])
-      result = JOY_BUTTON_PRESSED;
-    else
-      result = JOY_BUTTON_NEW_PRESSED;
-  }
-  else
-  {
-    if (last_joy_button[player_nr])
-      result = JOY_BUTTON_NEW_RELEASED;
-    else
-      result = JOY_BUTTON_NOT_PRESSED;
-  }
-
-  last_joy_button[player_nr] = joy_button;
-  return result;
-}
-
-int AnyJoystick()
-{
-  int i;
-  int result = 0;
-
-  for (i=0; i<MAX_PLAYERS; i++)
-  {
-
-    /*
-    if (!setup.input[i].use_joystick)
-      continue;
-      */
-
-
-    result |= Joystick(i);
-  }
-
-  return result;
-}
-
-int AnyJoystickButton()
-{
-  int i;
-  int result;
-
-  for (i=0; i<MAX_PLAYERS; i++)
-  {
-
-    /*
-    if (!setup.input[i].use_joystick)
-      continue;
-      */
-
-    /*
-    result |= JoystickButton(i);
-    */
-
-    result = JoystickButton(i);
-    if (result != JOY_BUTTON_NOT_PRESSED)
-      break;
-  }
-
-  return result;
-}
diff --git a/src/joystick.h b/src/joystick.h
deleted file mode 100644 (file)
index 00fb483..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* joystick.h                                               *
-***********************************************************/
-
-#ifndef JOYSTICK_H
-#define JOYSTICK_H
-
-#include "main.h"
-
-/* values for the joystick */
-#define JOYSTICK_OFF           0
-#define        JOYSTICK_AVAILABLE      1
-
-#ifdef __FreeBSD__
-#include <machine/joystick.h>
-#define DEV_JOYSTICK_0         "/dev/joy0"
-#define DEV_JOYSTICK_1         "/dev/joy1"
-#define DEV_JOYSTICK_2         "/dev/joy2"
-#define DEV_JOYSTICK_3         "/dev/joy3"
-#else
-#define DEV_JOYSTICK_0         "/dev/js0"
-#define DEV_JOYSTICK_1         "/dev/js1"
-#define DEV_JOYSTICK_2         "/dev/js2"
-#define DEV_JOYSTICK_3         "/dev/js3"
-#endif
-
-/* get these values from the program 'js' from the joystick package, */
-/* set JOYSTICK_PERCENT to a threshold appropriate for your joystick */
-
-#ifdef TARGET_SDL
-#define JOYSTICK_XLEFT         -32767
-#define JOYSTICK_XMIDDLE       0
-#define JOYSTICK_XRIGHT                32767
-#define JOYSTICK_YUPPER                -32767
-#define JOYSTICK_YMIDDLE       0
-#define JOYSTICK_YLOWER                32767
-#else
-#define JOYSTICK_XLEFT         30
-#define JOYSTICK_XMIDDLE       530
-#define JOYSTICK_XRIGHT                1250
-#define JOYSTICK_YUPPER                40
-#define JOYSTICK_YMIDDLE       680
-#define JOYSTICK_YLOWER                1440
-#endif
-
-#define JOYSTICK_PERCENT       25
-
-#define JOY_LEFT               MV_LEFT
-#define JOY_RIGHT              MV_RIGHT
-#define JOY_UP                 MV_UP
-#define JOY_DOWN               MV_DOWN
-#define JOY_BUTTON_1           (1<<4)
-#define JOY_BUTTON_2           (1<<5)
-#define JOY_BUTTON             (JOY_BUTTON_1 | JOY_BUTTON_2)
-
-#define JOY_BUTTON_NOT_PRESSED 0
-#define JOY_BUTTON_PRESSED     1
-#define JOY_BUTTON_NEW_PRESSED 2
-#define JOY_BUTTON_NEW_RELEASED        3
-
-#ifdef NO_JOYSTICK
-#define JOYSTICK_STATUS                JOYSTICK_OFF
-#else
-#define JOYSTICK_STATUS                JOYSTICK_AVAILABLE
-#endif
-
-
-#if defined(TARGET_SDL)
-SDL_Joystick *Get_SDL_Joystick(int);
-boolean Open_SDL_Joystick(int);
-void Close_SDL_Joystick(int);
-boolean Check_SDL_JoystickOpened(int);
-void HandleJoystickEvent(Event *);
-int Get_SDL_Joystick_Axis(int, int);
-#endif
-
-void CheckJoystickData(void);
-int Joystick(int);
-int JoystickButton(int);
-int AnyJoystick(void);
-int AnyJoystickButton(void);
-
-#endif /* JOYSTICK_H */
index b756b27d4f830429fc44b5b1498ba889f1d454be..f9f306b6345144cd1b914e50c347a70348a99a51 100644 (file)
@@ -1,15 +1,18 @@
 #=============================================================================#
 # Makefile for Artsoft Retro-Game Library                                     #
-# (c) 1994-2000 Holger Schemel, info@artsoft.org                              #
+# (c) 1994-2002 Holger Schemel, info@artsoft.org                              #
 #=============================================================================#
 
 SRCS = system.c        \
        gadgets.c       \
        text.c          \
        sound.c         \
+       joystick.c      \
+       toons.c         \
        pcx.c           \
        image.c         \
        random.c        \
+       setup.c         \
        misc.c          \
        msdos.c         \
        x11.c           \
@@ -19,9 +22,12 @@ OBJS =       system.o        \
        gadgets.o       \
        text.o          \
        sound.o         \
+       joystick.o      \
+       toons.o         \
        pcx.o           \
        image.o         \
        random.o        \
+       setup.o         \
        misc.o          \
        msdos.o         \
        x11.o           \
index a099f0b5d7366bb65ce58ebd2d7f9fa209affa65..9d709f3aba55d9359ae99c636d5a60f483ad2ebc 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -82,11 +82,6 @@ static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my)
 
 static void default_callback_info(void *ptr)
 {
-#if 0
-  if (game_status == LEVELED)
-    HandleEditorGadgetInfoText(ptr);
-#endif
-
   return;
 }
 
@@ -309,7 +304,9 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        break;
 
       case GDI_CHECKED:
-       gi->checked = va_arg(ap, boolean);
+       /* take care here: "boolean" is typedef'ed as "unsigned char",
+          which gets promoted to "int" */
+       gi->checked = (boolean)va_arg(ap, int);
        break;
 
       case GDI_RADIO_NR:
@@ -849,15 +846,27 @@ void HandleGadgets(int mx, int my, int button)
   if (last_info_gi != new_gi ||
       (new_gi && new_gi->type == GD_TYPE_DRAWING_AREA && changed_position))
   {
-    last_info_gi = new_gi;
-
     if (new_gi != NULL && (button == 0 || new_gi == last_gi))
     {
-      new_gi->event.type = 0;
+      new_gi->event.type = GD_EVENT_INFO_ENTERING;
       new_gi->callback_info(new_gi);
     }
-    else
+    else if (last_info_gi != NULL)
+    {
+      last_info_gi->event.type = GD_EVENT_INFO_LEAVING;
+      last_info_gi->callback_info(last_info_gi);
+
+#if 0
       default_callback_info(NULL);
+
+      printf("It seems that we are leaving gadget [%s]!\n",
+            (last_info_gi != NULL &&
+             last_info_gi->info_text != NULL ?
+             last_info_gi->info_text : ""));
+#endif
+    }
+
+    last_info_gi = new_gi;
   }
 
   if (gadget_pressed)
index 8210000852d29545de0fe182471ca004db5b1109..783e454f0a23f58bbe8a7ef30e2574dafd528957 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -45,6 +45,8 @@
 #define GD_EVENT_OFF_BORDERS           (1 << 4)
 #define GD_EVENT_TEXT_RETURN           (1 << 5)
 #define GD_EVENT_TEXT_LEAVING          (1 << 6)
+#define GD_EVENT_INFO_ENTERING         (1 << 7)
+#define GD_EVENT_INFO_LEAVING          (1 << 8)
 
 /* gadget button states */
 #define GD_BUTTON_UNPRESSED            0
index 836f28435b4b2e2c6380cccd5e61aa3da5d80f09..8299ad2b400e1ec20907442d04f575582d9ac830 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 Image *newImage(unsigned int width, unsigned int height, unsigned int depth)
 {
   Image *image;
-  const unsigned int bytes_per_pixel = 1;
+  unsigned int bytes_per_pixel = (depth + 7) / 8;
   int i;
 
+#if 0
   if (depth > 8)
     Error(ERR_EXIT, "images with more than 256 colors are not supported");
 
   depth = 8;
+#endif
+
   image = checked_malloc(sizeof(Image));
   image->data = checked_malloc(width * height * bytes_per_pixel);
   image->width = width;
   image->height = height;
   image->depth = depth;
+  image->bytes_per_pixel = bytes_per_pixel;
+  image->bytes_per_row = width * bytes_per_pixel;
+
   image->rgb.used = 0;
   for (i=0; i<MAX_COLORS; i++)
     image->rgb.color_used[i] = FALSE;
 
+  image->type = (depth < 8 ? IMAGETYPE_BITMAP :
+                depth > 8 ? IMAGETYPE_TRUECOLOR : IMAGETYPE_RGB);
+
   return image;
 }
 
@@ -53,26 +62,39 @@ void freeImage(Image *image)
 /* extra colors to try allocating in private color maps to minimize flashing */
 #define NOFLASH_COLORS 256
 
-/* architecture independent value-to-memory conversion
+/* architecture independent value <-> memory conversions;
    note: the internal format is big endian */
 
-#define value_to_memory(value, ptr, length) (                          \
-(length) == 1 ? (*( (byte *)(ptr)   ) = ( value     ) ) :              \
-(length) == 2 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>> 8),        \
-                *(((byte *)(ptr))+1) = ( value     ) ) :               \
-(length) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>16),        \
-                *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8), \
-                *(((byte *)(ptr))+2) = ( value     ) ) :               \
-                (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>24),        \
-                *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16), \
-                *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8), \
-                *(((byte *)(ptr))+3) = ( value     ) ))
+#define memory_to_value(ptr, len) (                                        \
+(len) == 1 ? (unsigned long)(                 *( (byte *)(ptr))         ) : \
+(len) == 2 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<< 8)   \
+                         + (                 *(((byte *)(ptr))+1)      ) : \
+(len) == 3 ? (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<<16)   \
+                         + (((unsigned long)(*(((byte *)(ptr))+1)))<< 8)   \
+                         + (                 *(((byte *)(ptr))+2)      ) : \
+            (unsigned long)(((unsigned long)(*( (byte *)(ptr))   ))<<24)   \
+                         + (((unsigned long)(*(((byte *)(ptr))+1)))<<16)   \
+                         + (((unsigned long)(*(((byte *)(ptr))+2)))<< 8)   \
+                         + (                 *(((byte *)(ptr))+3)      ) )
+
+
+#define value_to_memory(value, ptr, len) (                             \
+(len) == 1 ? (*( (byte *)(ptr)   ) = ( value     ) ) :                 \
+(len) == 2 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>> 8),   \
+             *(((byte *)(ptr))+1) = ( value     ) ) :                  \
+(len) == 3 ? (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>16),   \
+             *(((byte *)(ptr))+1) = (((unsigned long)(value))>> 8),    \
+             *(((byte *)(ptr))+2) = ( value     ) ) :                  \
+             (*( (byte *)(ptr)   ) = (((unsigned long)(value))>>24),   \
+             *(((byte *)(ptr))+1) = (((unsigned long)(value))>>16),    \
+             *(((byte *)(ptr))+2) = (((unsigned long)(value))>> 8),    \
+             *(((byte *)(ptr))+3) = ( value     ) ))
 
 static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
 {
   byte *src_ptr, *dst_ptr, *dst_ptr2;
   unsigned int bytes_per_row;
-  unsigned int x, y;
+  unsigned int x, y, i;
   byte bitmask;
   byte *mask_data;
   Pixmap mask_pixmap;
@@ -95,8 +117,9 @@ static Pixmap Image_to_Mask(Image *image, Display *display, Window window)
 
     for (x=0; x<image->width; x++)
     {
-      if (*src_ptr++)          /* source pixel solid? (pixel index != 0)  */
-       *dst_ptr2 |= bitmask;   /* then write a bit into the image mask    */
+      for (i=0; i<image->bytes_per_pixel; i++)
+       if (*src_ptr++)         /* source pixel solid? (pixel index != 0)  */
+         *dst_ptr2 |= bitmask; /* then write a bit into the image mask    */
 
       if ((bitmask <<= 1) == 0)        /* bit at rightmost byte position reached? */
       {
@@ -146,11 +169,19 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
   static int num_cmap_entries, free_cmap_entries;
   static boolean private_cmap = FALSE;
   Pixel *redvalue, *greenvalue, *bluevalue;
-  unsigned int a, c = 0, x, y, bytes_per_pixel, bits_per_pixel;
+  unsigned int display_bytes_per_pixel, display_bits_per_pixel;
+  unsigned int a, c = 0, x, y;
   XColor xcolor;
   XImage *ximage;
   XImageInfo *ximageinfo;
   byte *src_ptr, *dst_ptr;
+  char *error = "Image_to_Pixmap(): %s";
+
+  if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
+  {
+    SetError(error, "cannot handle true-color images on 8-bit display");
+    return NULL;
+  }
 
   if (!global_cmap)
   {
@@ -214,6 +245,7 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
       bluestep = 256 / bluecolors;
       redbottom = greenbottom = bluebottom = 0;
       redtop = greentop = bluetop = 0;
+
       for (a=0; a<visual->map_entries; a++)
       {
        if (redbottom < 256)
@@ -243,11 +275,13 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
 
          /* something completely unexpected happened */
 
-         fprintf(stderr, "imageToXImage: XAllocColor failed on a TrueColor/Directcolor visual\n");
+         fprintf(stderr, "Image_to_Pixmap: XAllocColor failed on a TrueColor/Directcolor visual\n");
+
           free(redvalue);
           free(greenvalue);
           free(bluevalue);
           free(ximageinfo);
+
          return NULL;
        }
 
@@ -260,6 +294,7 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
        while ((bluebottom < 256) && (bluebottom < bluetop))
          bluevalue[bluebottom++] = xcolor.pixel & visual->blue_mask;
       }
+
       break;
     }
 
@@ -365,7 +400,10 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
          }
 
          if (!color_found)             /* no more free color cells */
-           Error(ERR_EXIT, "cannot allocate enough color cells");
+         {
+           SetError(error, "cannot allocate enough color cells");
+           return NULL;
+         }
 
          xcolor.pixel = xcolor2.pixel;
          xcolor_private[xcolor.pixel] = xcolor;
@@ -385,9 +423,10 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
       break;
   
     default:
-      Error(ERR_RETURN, "display class not supported");
-      Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
-      break;
+      Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
+      SetError(error, "display class not supported");
+
+      return NULL;
   }
 
 #if DEBUG_TIMING
@@ -396,14 +435,14 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
 
   /* create XImage from internal image structure and convert it to Pixmap */
 
-  bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
-  bytes_per_pixel = (bits_per_pixel + 7) / 8;
+  display_bits_per_pixel = bitsPerPixelAtDepth(display, screen, depth);
+  display_bytes_per_pixel = (display_bits_per_pixel + 7) / 8;
 
   ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
                        NULL, image->width, image->height,
-                       8, image->width * bytes_per_pixel);
+                       8, image->width * display_bytes_per_pixel);
   ximage->data =
-    checked_malloc(image->width * image->height * bytes_per_pixel);
+    checked_malloc(image->width * image->height * display_bytes_per_pixel);
   ximage->byte_order = MSBFirst;
 
   src_ptr = image->data;
@@ -416,25 +455,60 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
     {
       Pixel pixval;
 
-      for (y=0; y<image->height; y++)          /* general case */
+      switch (image->type)
       {
-       for (x=0; x<image->width; x++)
+        case IMAGETYPE_RGB:
+       {
+         for (y=0; y<image->height; y++)               /* general case */
+         {
+           for (x=0; x<image->width; x++)
+           {
+             pixval = *src_ptr++;
+             pixval =
+               redvalue[image->rgb.red[pixval] >> 8] |
+               greenvalue[image->rgb.green[pixval] >> 8] |
+               bluevalue[image->rgb.blue[pixval] >> 8];
+             value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
+             dst_ptr += display_bytes_per_pixel;
+           }
+         }
+
+         break;
+       }
+
+        case IMAGETYPE_TRUECOLOR:
        {
-         pixval = *src_ptr++;
-         pixval =
-           redvalue[image->rgb.red[pixval] >> 8] |
-           greenvalue[image->rgb.green[pixval] >> 8] |
-           bluevalue[image->rgb.blue[pixval] >> 8];
-         value_to_memory(pixval, dst_ptr, bytes_per_pixel);
-         dst_ptr += bytes_per_pixel;
+         for (y=0; y<image->height; y++)               /* general case */
+         {
+           for (x=0; x<image->width; x++)
+           {
+             pixval = memory_to_value(src_ptr, image->bytes_per_pixel);
+             pixval =
+               redvalue[TRUECOLOR_RED(pixval)] |
+               greenvalue[TRUECOLOR_GREEN(pixval)] |
+               bluevalue[TRUECOLOR_BLUE(pixval)];
+             value_to_memory(pixval, dst_ptr, display_bytes_per_pixel);
+             src_ptr += image->bytes_per_pixel;
+             dst_ptr += display_bytes_per_pixel;
+           }
+         }
+
+         break;
        }
+
+        default:
+         Error(ERR_RETURN, "RGB or TrueColor image needed");
+         SetError(error, "image type not supported");
+
+         return NULL;
       }
+
       break;
     }
 
     case PseudoColor:
     {
-      if (bytes_per_pixel == 1)                        /* (common) special case */
+      if (display_bytes_per_pixel == 1)                /* special case */
       {
        for (y=0; y<image->height; y++)
          for (x=0; x<image->width; x++)
@@ -447,18 +521,20 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
          for (x=0; x<image->width; x++)
          {
            value_to_memory(ximageinfo->index[c + *src_ptr++],
-                           dst_ptr, bytes_per_pixel);
-           dst_ptr += bytes_per_pixel;
+                           dst_ptr, display_bytes_per_pixel);
+           dst_ptr += display_bytes_per_pixel;
          }
        }
       }
+
       break;
     }
 
     default:
-      Error(ERR_RETURN, "display class not supported");
-      Error(ERR_EXIT, "DirectColor, TrueColor or PseudoColor display needed");
-      break;
+      Error(ERR_RETURN,"DirectColor, TrueColor or PseudoColor display needed");
+      SetError(error, "display class not supported");
+
+      return NULL;
   }
 
   if (redvalue)
@@ -479,11 +555,9 @@ XImageInfo *Image_to_Pixmap(Display *display, int screen, Visual *visual,
   XPutImage(ximageinfo->display, ximageinfo->pixmap, gc,
            ximage, 0, 0, 0, 0, ximage->width, ximage->height);
 
-  free(ximage->data);
-  ximage->data = NULL;
   XDestroyImage(ximage);
 
-  return(ximageinfo);
+  return ximageinfo;
 }
 
 void freeXImage(Image *image, XImageInfo *ximageinfo)
@@ -528,7 +602,11 @@ int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
   /* convert image structure to X11 Pixmap */
   if (!(ximageinfo = Image_to_Pixmap(display, screen, visual,
                                     window, gc, depth, image)))
-    Error(ERR_EXIT, "cannot convert Image to Pixmap");
+  {
+    freeImage(image);
+
+    return PCX_OtherError;
+  }
 
   /* if a private colormap has been created, install it */
   if (ximageinfo->cmap != DefaultColormap(display, screen))
@@ -548,6 +626,10 @@ int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
   *pixmap = ximageinfo->pixmap;
   *pixmap_mask = ximageinfo->pixmap_mask;
 
+  /* free generic image and ximageinfo after native Pixmap has been created */
+  free(ximageinfo);
+  freeImage(image);
+
   return PCX_Success;
 }
 
index 1e1b6cb5d50fc2672c6a313f6acf9bf0e00d9f9a..6f473beeaf1c48b69b7b907dc2853c441affa216 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -45,13 +45,25 @@ struct RGBMap
 
 typedef struct
 {
+  unsigned int  type;          /* type of image (True-Color etc.)     */
   struct RGBMap rgb;           /* RGB map of image if IRGB type       */
   unsigned int  width;         /* width of image in pixels            */
   unsigned int  height;                /* height of image in pixels           */
   unsigned int  depth;         /* depth of image in bits if IRGB type */
+  unsigned int  bytes_per_pixel;/* (depth + 7) / 8                     */
+  unsigned int  bytes_per_row; /* width * bytes_per_pixel             */
   byte         *data;          /* image data                          */
 } Image;
 
+#define IMAGETYPE_BITMAP       0       /* monochrome bitmap       */
+#define IMAGETYPE_RGB          1       /* RGB image with colormap */
+#define IMAGETYPE_TRUECOLOR    2       /* true-color image        */
+
+#define TRUECOLOR_RED(pixel)   (((unsigned long)((pixel) & 0xff0000)) >> 16)
+#define TRUECOLOR_GREEN(pixel) (((unsigned long)((pixel) & 0xff00)) >> 8)
+#define TRUECOLOR_BLUE(pixel)  ( (unsigned long)((pixel) & 0xff))
+#define RGB_TO_TRUECOLOR(r,g,b)        ((((unsigned long)((r) & 0xff00)) << 8) | ((g) & 0xff00) | (((unsigned short)(b)) >> 8))
+
 Image *newImage(unsigned int, unsigned int, unsigned int);
 void freeImage(Image *);
 void freeXImage(Image *, XImageInfo *);
diff --git a/src/libgame/joystick.c b/src/libgame/joystick.c
new file mode 100644 (file)
index 0000000..06ac654
--- /dev/null
@@ -0,0 +1,360 @@
+/***********************************************************
+* Artsoft Retro-Game Library                               *
+*----------------------------------------------------------*
+* (c) 1995-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* joystick.c                                               *
+***********************************************************/
+
+#if defined(PLATFORM_FREEBSD)
+#include <machine/joystick.h>
+#endif
+
+#include "joystick.h"
+#include "misc.h"
+
+
+/* ========================================================================= */
+/* platform dependant joystick functions                                     */
+/* ========================================================================= */
+
+#if defined(PLATFORM_UNIX) && !defined(TARGET_SDL)
+void UnixInitJoysticks()
+{
+  int i;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    char *device_name = setup.input[i].joy.device_name;
+
+    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
+    if (joystick.fd[i] != -1)
+    {
+      close(joystick.fd[i]);
+      joystick.fd[i] = -1;
+    }
+
+    if (!setup.input[i].use_joystick)
+      continue;
+
+    if (access(device_name, R_OK) != 0)
+    {
+      Error(ERR_WARN, "cannot access joystick device '%s'", device_name);
+      continue;
+    }
+
+    if ((joystick.fd[i] = open(device_name, O_RDONLY)) < 0)
+    {
+      Error(ERR_WARN, "cannot open joystick device '%s'", device_name);
+      continue;
+    }
+
+    joystick.status = JOYSTICK_ACTIVATED;
+  }
+}
+
+boolean UnixReadJoystick(int fd, int *x, int *y, boolean *b1, boolean *b2)
+{
+#if defined(PLATFORM_FREEBSD)
+  struct joystick joy_ctrl;
+#else
+  struct joystick_control
+  {
+    int buttons;
+    int x;
+    int y;
+  } joy_ctrl;
+#endif
+
+  if (read(fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
+    return FALSE;
+
+  if (x != NULL)
+    *x = joy_ctrl.x;
+  if (y != NULL)
+    *y = joy_ctrl.y;
+
+#if defined(PLATFORM_FREEBSD)
+  if (b1 != NULL)
+    *b1 = joy_ctrl.b1;
+  if (b2 != NULL)
+    *b2 = joy_ctrl.b2;
+#else
+  if (b1 != NULL)
+    *b1 = joy_ctrl.buttons & 1;
+  if (b2 != NULL)
+    *b2 = joy_ctrl.buttons & 2;
+#endif
+
+  return TRUE;
+}
+#endif /* PLATFORM_UNIX && !TARGET_SDL */
+
+
+/* ========================================================================= */
+/* platform independant joystick functions                                   */
+/* ========================================================================= */
+
+#define TRANSLATE_JOYSYMBOL_TO_JOYNAME 0
+#define TRANSLATE_JOYNAME_TO_JOYSYMBOL 1
+
+void translate_joyname(int *joysymbol, char **name, int mode)
+{
+  static struct
+  {
+    int joysymbol;
+    char *name;
+  } translate_joy[] =
+  {
+    { JOY_LEFT,                "joystick_left" },
+    { JOY_RIGHT,       "joystick_right" },
+    { JOY_UP,          "joystick_up" },
+    { JOY_DOWN,                "joystick_down" },
+    { JOY_BUTTON_1,    "joystick_button_1" },
+    { JOY_BUTTON_2,    "joystick_button_2" },
+  };
+
+  int i;
+
+  if (mode == TRANSLATE_JOYSYMBOL_TO_JOYNAME)
+  {
+    *name = "[undefined]";
+
+    for (i=0; i<6; i++)
+    {
+      if (*joysymbol == translate_joy[i].joysymbol)
+      {
+       *name = translate_joy[i].name;
+       break;
+      }
+    }
+  }
+  else if (mode == TRANSLATE_JOYNAME_TO_JOYSYMBOL)
+  {
+    *joysymbol = 0;
+
+    for (i=0; i<6; i++)
+    {
+      if (strcmp(*name, translate_joy[i].name) == 0)
+      {
+       *joysymbol = translate_joy[i].joysymbol;
+       break;
+      }
+    }
+  }
+}
+
+char *getJoyNameFromJoySymbol(int joysymbol)
+{
+  char *name;
+
+  translate_joyname(&joysymbol, &name, TRANSLATE_JOYSYMBOL_TO_JOYNAME);
+  return name;
+}
+
+int getJoySymbolFromJoyName(char *name)
+{
+  int joysymbol;
+
+  translate_joyname(&joysymbol, &name, TRANSLATE_JOYNAME_TO_JOYSYMBOL);
+  return joysymbol;
+}
+
+int getJoystickNrFromDeviceName(char *device_name)
+{
+  char c;
+  int joystick_nr = 0;
+
+  if (device_name == NULL || device_name[0] == '\0')
+    return 0;
+
+  c = device_name[strlen(device_name) - 1];
+
+  if (c >= '0' && c <= '9')
+    joystick_nr = (int)(c - '0');
+
+  if (joystick_nr < 0 || joystick_nr >= MAX_PLAYERS)
+    joystick_nr = 0;
+
+  return joystick_nr;
+}
+
+char *getDeviceNameFromJoystickNr(int joystick_nr)
+{
+  static char *joystick_device_name[MAX_PLAYERS] =
+  {
+    DEV_JOYSTICK_0,
+    DEV_JOYSTICK_1,
+    DEV_JOYSTICK_2,
+    DEV_JOYSTICK_3
+  };
+
+  return (joystick_nr >= 0 && joystick_nr <= 3 ?
+         joystick_device_name[joystick_nr] : "");
+}
+
+static int JoystickPositionPercent(int center, int border, int actual)
+{
+  long range, position;
+  int percent;
+
+  if (border < center && actual > center)
+    return 0;
+  if (border > center && actual < center)
+    return 0;
+
+  range = ABS(border - center);
+  position = ABS(actual - center);
+
+  percent = (int)(position * 100 / range);
+
+  if (percent > 100)
+    percent = 100;
+
+  return percent;
+}
+
+void CheckJoystickData()
+{
+  int i;
+  int distance = 100;
+
+  for(i=0; i<MAX_PLAYERS; i++)
+  {
+    if (setup.input[i].joy.xleft >= setup.input[i].joy.xmiddle)
+      setup.input[i].joy.xleft = setup.input[i].joy.xmiddle - distance;
+    if (setup.input[i].joy.xright <= setup.input[i].joy.xmiddle)
+      setup.input[i].joy.xright = setup.input[i].joy.xmiddle + distance;
+
+    if (setup.input[i].joy.yupper >= setup.input[i].joy.ymiddle)
+      setup.input[i].joy.yupper = setup.input[i].joy.ymiddle - distance;
+    if (setup.input[i].joy.ylower <= setup.input[i].joy.ymiddle)
+      setup.input[i].joy.ylower = setup.input[i].joy.ymiddle + distance;
+  }
+}
+
+int Joystick(int player_nr)
+{
+  int joystick_fd = joystick.fd[player_nr];
+  int js_x, js_y;
+  boolean js_b1, js_b2;
+  int left, right, up, down;
+  int result = JOY_NO_ACTION;
+
+  if (joystick.status != JOYSTICK_ACTIVATED)
+    return JOY_NO_ACTION;
+
+  if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
+    return JOY_NO_ACTION;
+
+  if (!ReadJoystick(joystick_fd, &js_x, &js_y, &js_b1, &js_b2))
+  {
+    Error(ERR_WARN, "cannot read joystick device '%s'",
+         setup.input[player_nr].joy.device_name);
+
+    joystick.status = JOYSTICK_NOT_AVAILABLE;
+    return JOY_NO_ACTION;
+  }
+
+  left  = JoystickPositionPercent(setup.input[player_nr].joy.xmiddle,
+                                 setup.input[player_nr].joy.xleft,  js_x);
+  right = JoystickPositionPercent(setup.input[player_nr].joy.xmiddle,
+                                 setup.input[player_nr].joy.xright, js_x);
+  up    = JoystickPositionPercent(setup.input[player_nr].joy.ymiddle,
+                                 setup.input[player_nr].joy.yupper, js_y);
+  down  = JoystickPositionPercent(setup.input[player_nr].joy.ymiddle,
+                                 setup.input[player_nr].joy.ylower, js_y);
+
+  if (left > JOYSTICK_PERCENT)
+    result |= JOY_LEFT;
+  else if (right > JOYSTICK_PERCENT)
+    result |= JOY_RIGHT;
+  if (up > JOYSTICK_PERCENT)
+    result |= JOY_UP;
+  else if (down > JOYSTICK_PERCENT)
+    result |= JOY_DOWN;
+
+  if (js_b1)
+    result |= JOY_BUTTON_1;
+  if (js_b2)
+    result |= JOY_BUTTON_2;
+
+  return result;
+}
+
+int JoystickButton(int player_nr)
+{
+  static int last_joy_button[MAX_PLAYERS] = { 0, 0, 0, 0 };
+  int joy_button = (Joystick(player_nr) & JOY_BUTTON);
+  int result;
+
+  if (joy_button)
+  {
+    if (last_joy_button[player_nr])
+      result = JOY_BUTTON_PRESSED;
+    else
+      result = JOY_BUTTON_NEW_PRESSED;
+  }
+  else
+  {
+    if (last_joy_button[player_nr])
+      result = JOY_BUTTON_NEW_RELEASED;
+    else
+      result = JOY_BUTTON_NOT_PRESSED;
+  }
+
+  last_joy_button[player_nr] = joy_button;
+  return result;
+}
+
+int AnyJoystick()
+{
+  int i;
+  int result = 0;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+    result |= Joystick(i);
+
+  return result;
+}
+
+int AnyJoystickButton()
+{
+  int i;
+  int result = JOY_BUTTON_NOT_PRESSED;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    result = JoystickButton(i);
+    if (result != JOY_BUTTON_NOT_PRESSED)
+      break;
+  }
+
+  return result;
+}
+
+void DeactivateJoystick()
+{
+  /* Temporarily deactivate joystick. This is needed for calibration
+     screens, where the player has to select a joystick device that
+     should be calibrated. If there is a totally uncalibrated joystick
+     active, it may be impossible (due to messed up input from joystick)
+     to select the joystick device to calibrate even when trying to use
+     the mouse or keyboard to select the device. */
+
+  if (joystick.status & JOYSTICK_AVAILABLE)
+    joystick.status &= ~JOYSTICK_ACTIVE;
+}
+
+void ActivateJoystick()
+{
+  /* reactivate temporarily deactivated joystick */
+
+  if (joystick.status & JOYSTICK_AVAILABLE)
+    joystick.status |= JOYSTICK_ACTIVE;
+}
diff --git a/src/libgame/joystick.h b/src/libgame/joystick.h
new file mode 100644 (file)
index 0000000..d571a13
--- /dev/null
@@ -0,0 +1,92 @@
+/***********************************************************
+* Artsoft Retro-Game Library                               *
+*----------------------------------------------------------*
+* (c) 1995-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* joystick.h                                               *
+***********************************************************/
+
+#ifndef JOYSTICK_H
+#define JOYSTICK_H
+
+#include "system.h"
+
+#define JOYSTICK_NOT_AVAILABLE 0
+#define        JOYSTICK_AVAILABLE      (1 << 0)
+#define        JOYSTICK_ACTIVE         (1 << 1)
+#define JOYSTICK_ACTIVATED     (JOYSTICK_AVAILABLE | JOYSTICK_ACTIVE)
+
+#if defined(PLATFORM_FREEBSD)
+#define DEV_JOYSTICK_0         "/dev/joy0"
+#define DEV_JOYSTICK_1         "/dev/joy1"
+#define DEV_JOYSTICK_2         "/dev/joy2"
+#define DEV_JOYSTICK_3         "/dev/joy3"
+#else
+#define DEV_JOYSTICK_0         "/dev/js0"
+#define DEV_JOYSTICK_1         "/dev/js1"
+#define DEV_JOYSTICK_2         "/dev/js2"
+#define DEV_JOYSTICK_3         "/dev/js3"
+#endif
+
+/* get these values from the program 'js' from the joystick package, */
+/* set JOYSTICK_PERCENT to a threshold appropriate for your joystick */
+
+#ifdef TARGET_SDL
+#define JOYSTICK_XLEFT         -32767
+#define JOYSTICK_XMIDDLE       0
+#define JOYSTICK_XRIGHT                32767
+#define JOYSTICK_YUPPER                -32767
+#define JOYSTICK_YMIDDLE       0
+#define JOYSTICK_YLOWER                32767
+#else
+#define JOYSTICK_XLEFT         1
+#define JOYSTICK_XMIDDLE       128
+#define JOYSTICK_XRIGHT                255
+#define JOYSTICK_YUPPER                1
+#define JOYSTICK_YMIDDLE       128
+#define JOYSTICK_YLOWER                255
+#endif
+
+#define JOYSTICK_PERCENT       25
+
+#define JOY_NO_ACTION          0
+#define JOY_LEFT               MV_LEFT
+#define JOY_RIGHT              MV_RIGHT
+#define JOY_UP                 MV_UP
+#define JOY_DOWN               MV_DOWN
+#define JOY_BUTTON_1           KEY_BUTTON_1
+#define JOY_BUTTON_2           KEY_BUTTON_2
+#define JOY_MOTION             KEY_MOTION
+#define JOY_BUTTON             KEY_BUTTON
+#define JOY_ACTION             KEY_ACTION
+
+#define JOY_BUTTON_NOT_PRESSED 0
+#define JOY_BUTTON_PRESSED     1
+#define JOY_BUTTON_NEW_PRESSED 2
+#define JOY_BUTTON_NEW_RELEASED        3
+
+#if defined(PLATFORM_UNIX)
+void UnixInitJoysticks(void);
+boolean UnixReadJoystick(int, int *, int *, boolean *, boolean *);
+#endif
+
+char *getJoyNameFromJoySymbol(int);
+int getJoySymbolFromJoyName(char *);
+int getJoystickNrFromDeviceName(char *);
+char *getDeviceNameFromJoystickNr(int);
+
+void CheckJoystickData(void);
+int Joystick(int);
+int JoystickButton(int);
+int AnyJoystick(void);
+int AnyJoystickButton(void);
+
+void DeactivateJoystick();
+void ActivateJoystick();
+
+#endif /* JOYSTICK_H */
index 9c70978eb0fcec24806efd390d9bf3cfd9c21798..6c1dd346f0cb0323124628dee840e9684b8e94d3 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "gadgets.h"
 #include "text.h"
 #include "sound.h"
+#include "joystick.h"
+#include "toons.h"
 #include "image.h"
 #include "pcx.h"
+#include "setup.h"
 #include "misc.h"
 
 #endif /* LIBGAME_H */
index 7a55d94de4e5ebf8ae1881ca330828937a9a4a73..acada2472e9754f5e15b775b0e74fae3832823f7 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -14,7 +14,6 @@
 #include <time.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <stdarg.h>
 #include <ctype.h>
 #include <string.h>
@@ -28,6 +27,7 @@
 #endif
 
 #include "misc.h"
+#include "setup.h"
 #include "random.h"
 
 
@@ -111,10 +111,12 @@ static void sleep_milliseconds(unsigned long milliseconds_delay)
 {
   boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
 
+#if 0
 #if defined(PLATFORM_MSDOS)
-  /* don't use select() to perform waiting operations under DOS/Windows
+  /* don't use select() to perform waiting operations under DOS
      environment; always use a busy loop for waiting instead */
   do_busy_waiting = TRUE;
+#endif
 #endif
 
   if (do_busy_waiting)
@@ -134,6 +136,8 @@ static void sleep_milliseconds(unsigned long milliseconds_delay)
   {
 #if defined(TARGET_SDL)
     SDL_Delay(milliseconds_delay);
+#elif defined(TARGET_ALLEGRO)
+    rest(milliseconds_delay);
 #else
     struct timeval delay;
 
@@ -156,12 +160,13 @@ boolean FrameReached(unsigned long *frame_counter_var,
 {
   unsigned long actual_frame_counter = FrameCounter;
 
-  if (actual_frame_counter < *frame_counter_var+frame_delay &&
+  if (actual_frame_counter < *frame_counter_var + frame_delay &&
       actual_frame_counter >= *frame_counter_var)
-    return(FALSE);
+    return FALSE;
 
   *frame_counter_var = actual_frame_counter;
-  return(TRUE);
+
+  return TRUE;
 }
 
 boolean DelayReached(unsigned long *counter_var,
@@ -171,10 +176,11 @@ boolean DelayReached(unsigned long *counter_var,
 
   if (actual_counter < *counter_var + delay &&
       actual_counter >= *counter_var)
-    return(FALSE);
+    return FALSE;
 
   *counter_var = actual_counter;
-  return(TRUE);
+
+  return TRUE;
 }
 
 void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
@@ -300,12 +306,19 @@ char *getLoginName()
 #if defined(PLATFORM_WIN32)
   return ANONYMOUS_NAME;
 #else
-  struct passwd *pwd;
+  static char *login_name = NULL;
 
-  if ((pwd = getpwuid(getuid())) == NULL)
-    return ANONYMOUS_NAME;
-  else
-    return pwd->pw_name;
+  if (login_name == NULL)
+  {
+    struct passwd *pwd;
+
+    if ((pwd = getpwuid(getuid())) == NULL)
+      login_name = ANONYMOUS_NAME;
+    else
+      login_name = getStringCopy(pwd->pw_name);
+  }
+
+  return login_name;
 #endif
 }
 
@@ -351,16 +364,16 @@ char *getHomeDir()
 #if defined(PLATFORM_UNIX)
   static char *home_dir = NULL;
 
-  if (!home_dir)
+  if (home_dir == NULL)
   {
-    if (!(home_dir = getenv("HOME")))
+    if ((home_dir = getenv("HOME")) == NULL)
     {
       struct passwd *pwd;
 
-      if ((pwd = getpwuid(getuid())))
-       home_dir = pwd->pw_dir;
-      else
+      if ((pwd = getpwuid(getuid())) == NULL)
        home_dir = ".";
+      else
+       home_dir = getStringCopy(pwd->pw_dir);
     }
   }
 
@@ -425,10 +438,14 @@ void GetOptions(char *argv[])
   options.ro_base_directory = RO_BASE_PATH;
   options.rw_base_directory = RW_BASE_PATH;
   options.level_directory = RO_BASE_PATH "/" LEVELS_DIRECTORY;
+  options.graphics_directory = RO_BASE_PATH "/" GRAPHICS_DIRECTORY;
+  options.sounds_directory = RO_BASE_PATH "/" SOUNDS_DIRECTORY;
+  options.music_directory = RO_BASE_PATH "/" MUSIC_DIRECTORY;
   options.serveronly = FALSE;
   options.network = FALSE;
   options.verbose = FALSE;
   options.debug = FALSE;
+  options.debug_command = NULL;
 
   while (*options_left)
   {
@@ -466,16 +483,23 @@ void GetOptions(char *argv[])
       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
     else if (strncmp(option, "-help", option_len) == 0)
     {
-      printf("Usage: %s [options] [server.name [port]]\n"
+      printf("Usage: %s [options] [<server host> [<server port>]]\n"
             "Options:\n"
-            "  -d, --display machine:0       X server display\n"
-            "  -b, --basepath directory      alternative base directory\n"
-            "  -l, --level directory         alternative level directory\n"
-            "  -s, --serveronly              only start network server\n"
+            "  -d, --display <host>[:<scr>]  X server display\n"
+            "  -b, --basepath <directory>    alternative base directory\n"
+            "  -l, --level <directory>       alternative level directory\n"
+            "  -g, --graphics <directory>    alternative graphics directory\n"
+            "  -s, --sounds <directory>      alternative sounds directory\n"
+            "  -m, --music <directory>       alternative music directory\n"
             "  -n, --network                 network multiplayer game\n"
+            "      --serveronly              only start network server\n"
             "  -v, --verbose                 verbose mode\n"
             "      --debug                   display debugging information\n",
             program.command_basename);
+
+      if (options.debug)
+       printf("      --debug-command <command> execute special command\n");
+
       exit(0);
     }
     else if (strncmp(option, "-display", option_len) == 0)
@@ -511,6 +535,33 @@ void GetOptions(char *argv[])
       if (option_arg == next_option)
        options_left++;
     }
+    else if (strncmp(option, "-graphics", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.graphics_directory = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+    }
+    else if (strncmp(option, "-sounds", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.sounds_directory = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+    }
+    else if (strncmp(option, "-music", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.music_directory = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+    }
     else if (strncmp(option, "-network", option_len) == 0)
     {
       options.network = TRUE;
@@ -527,6 +578,15 @@ void GetOptions(char *argv[])
     {
       options.debug = TRUE;
     }
+    else if (strncmp(option, "-debug-command", option_len) == 0)
+    {
+      if (option_arg == NULL)
+       Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
+
+      options.debug_command = option_arg;
+      if (option_arg == next_option)
+       options_left++;
+    }
     else if (*option == '-')
     {
       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
@@ -548,6 +608,23 @@ void GetOptions(char *argv[])
   }
 }
 
+/* used by SetError() and GetError() to store internal error messages */
+static char internal_error[1024];      /* this is bad */
+
+void SetError(char *format, ...)
+{
+  va_list ap;
+
+  va_start(ap, format);
+  vsprintf(internal_error, format, ap);
+  va_end(ap);
+}
+
+char *GetError()
+{
+  return internal_error;
+}
+
 void Error(int mode, char *format, ...)
 {
   char *process_name = "";
@@ -645,6 +722,26 @@ void *checked_realloc(void *ptr, unsigned long size)
   return ptr;
 }
 
+inline void swap_numbers(int *i1, int *i2)
+{
+  int help = *i1;
+
+  *i1 = *i2;
+  *i2 = help;
+}
+
+inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
+{
+  int help_x = *x1;
+  int help_y = *y1;
+
+  *x1 = *x2;
+  *x2 = help_x;
+
+  *y1 = *y2;
+  *y2 = help_y;
+}
+
 short getFile16BitInteger(FILE *file, int byte_order)
 {
   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
@@ -731,9 +828,33 @@ void putFileChunk(FILE *file, char *chunk_name, int chunk_size,
   }
 }
 
+int getFileVersion(FILE *file)
+{
+  int version_major, version_minor, version_patch;
+
+  version_major = fgetc(file);
+  version_minor = fgetc(file);
+  version_patch = fgetc(file);
+  fgetc(file);         /* not used */
+
+  return VERSION_IDENT(version_major, version_minor, version_patch);
+}
+
+void putFileVersion(FILE *file, int version)
+{
+  int version_major = VERSION_MAJOR(version);
+  int version_minor = VERSION_MINOR(version);
+  int version_patch = VERSION_PATCH(version);
+
+  fputc(version_major, file);
+  fputc(version_minor, file);
+  fputc(version_patch, file);
+  fputc(0, file);      /* not used */
+}
+
 void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
 {
-  while (bytes--)
+  while (bytes-- && !feof(file))
     fgetc(file);
 }
 
@@ -743,9 +864,15 @@ void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
     fputc(0, file);
 }
 
+
+/* ------------------------------------------------------------------------- */
+/* functions to translate key identifiers between different format           */
+/* ------------------------------------------------------------------------- */
+
 #define TRANSLATE_KEYSYM_TO_KEYNAME    0
 #define TRANSLATE_KEYSYM_TO_X11KEYNAME 1
-#define TRANSLATE_X11KEYNAME_TO_KEYSYM 2
+#define TRANSLATE_KEYNAME_TO_KEYSYM    2
+#define TRANSLATE_X11KEYNAME_TO_KEYSYM 3
 
 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
 {
@@ -871,8 +998,8 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
-    else if (key >= KSYM_F1 && key <= KSYM_F24)
-      sprintf(name_buffer, "function F%d", (int)(key - KSYM_F1 + 1));
+    else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
+      sprintf(name_buffer, "function F%d", (int)(key - KSYM_FKEY_FIRST + 1));
     else if (key == KSYM_UNDEFINED)
       strcpy(name_buffer, "(undefined)");
     else
@@ -908,8 +1035,8 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
-    else if (key >= KSYM_F1 && key <= KSYM_F24)
-      sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_F1 + 1));
+    else if (key >= KSYM_FKEY_FIRST && key <= KSYM_FKEY_LAST)
+      sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_FKEY_FIRST + 1));
     else if (key == KSYM_UNDEFINED)
       strcpy(name_buffer, "[undefined]");
     else
@@ -932,6 +1059,26 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
 
     *x11name = name_buffer;
   }
+  else if (mode == TRANSLATE_KEYNAME_TO_KEYSYM)
+  {
+    Key key = KSYM_UNDEFINED;
+
+    i = 0;
+    do
+    {
+      if (strcmp(translate_key[i].name, *name) == 0)
+      {
+       key = translate_key[i].key;
+       break;
+      }
+    }
+    while (translate_key[++i].x11name);
+
+    if (key == KSYM_UNDEFINED)
+      Error(ERR_WARN, "getKeyFromKeyName(): not completely implemented");
+
+    *keysym = key;
+  }
   else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
   {
     Key key = KSYM_UNDEFINED;
@@ -965,7 +1112,7 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
          ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
        d = atoi(&name_ptr[4]);
 
-      if (d >=1 && d <= 24)
+      if (d >= 1 && d <= KSYM_NUM_FKEYS)
        key = KSYM_F1 + (Key)(d - 1);
     }
     else if (strncmp(name_ptr, "XK_", 3) == 0)
@@ -1033,6 +1180,14 @@ char *getX11KeyNameFromKey(Key key)
   return x11name;
 }
 
+Key getKeyFromKeyName(char *name)
+{
+  Key key;
+
+  translate_keyname(&key, NULL, &name, TRANSLATE_KEYNAME_TO_KEYSYM);
+  return key;
+}
+
 Key getKeyFromX11KeyName(char *x11name)
 {
   Key key;
@@ -1056,648 +1211,56 @@ char getCharFromKey(Key key)
   return letter;
 }
 
-/* ------------------------------------------------------------------------- */
-/* some functions to handle lists of level directories                       */
-/* ------------------------------------------------------------------------- */
-
-struct LevelDirInfo *newLevelDirInfo()
-{
-  return checked_calloc(sizeof(struct LevelDirInfo));
-}
-
-void pushLevelDirInfo(struct LevelDirInfo **node_first,
-                     struct LevelDirInfo *node_new)
-{
-  node_new->next = *node_first;
-  *node_first = node_new;
-}
-
-int numLevelDirInfo(struct LevelDirInfo *node)
-{
-  int num = 0;
-
-  while (node)
-  {
-    num++;
-    node = node->next;
-  }
-
-  return num;
-}
-
-boolean validLevelSeries(struct LevelDirInfo *node)
-{
-  return (node != NULL && !node->node_group && !node->parent_link);
-}
-
-struct LevelDirInfo *getFirstValidLevelSeries(struct LevelDirInfo *node)
-{
-  if (node == NULL)
-  {
-    if (leveldir_first)                /* start with first level directory entry */
-      return getFirstValidLevelSeries(leveldir_first);
-    else
-      return NULL;
-  }
-  else if (node->node_group)   /* enter level group (step down into tree) */
-    return getFirstValidLevelSeries(node->node_group);
-  else if (node->parent_link)  /* skip start entry of level group */
-  {
-    if (node->next)            /* get first real level series entry */
-      return getFirstValidLevelSeries(node->next);
-    else                       /* leave empty level group and go on */
-      return getFirstValidLevelSeries(node->node_parent->next);
-  }
-  else                         /* this seems to be a regular level series */
-    return node;
-}
-
-struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *node)
-{
-  if (node == NULL)
-    return NULL;
-
-  if (node->node_parent == NULL)               /* top level group */
-    return leveldir_first;
-  else                                         /* sub level group */
-    return node->node_parent->node_group;
-}
-
-int numLevelDirInfoInGroup(struct LevelDirInfo *node)
-{
-  return numLevelDirInfo(getLevelDirInfoFirstGroupEntry(node));
-}
-
-int posLevelDirInfo(struct LevelDirInfo *node)
-{
-  struct LevelDirInfo *node_cmp = getLevelDirInfoFirstGroupEntry(node);
-  int pos = 0;
-
-  while (node_cmp)
-  {
-    if (node_cmp == node)
-      return pos;
-
-    pos++;
-    node_cmp = node_cmp->next;
-  }
-
-  return 0;
-}
-
-struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *node, int pos)
-{
-  struct LevelDirInfo *node_default = node;
-  int pos_cmp = 0;
-
-  while (node)
-  {
-    if (pos_cmp == pos)
-      return node;
-
-    pos_cmp++;
-    node = node->next;
-  }
-
-  return node_default;
-}
-
-struct LevelDirInfo *getLevelDirInfoFromFilenameExt(struct LevelDirInfo *node,
-                                                   char *filename)
-{
-  if (filename == NULL)
-    return NULL;
-
-  while (node)
-  {
-    if (node->node_group)
-    {
-      struct LevelDirInfo *node_group;
-
-      node_group = getLevelDirInfoFromFilenameExt(node->node_group, filename);
-
-      if (node_group)
-       return node_group;
-    }
-    else if (!node->parent_link)
-    {
-      if (strcmp(filename, node->filename) == 0)
-       return node;
-    }
-
-    node = node->next;
-  }
-
-  return NULL;
-}
-
-struct LevelDirInfo *getLevelDirInfoFromFilename(char *filename)
-{
-  return getLevelDirInfoFromFilenameExt(leveldir_first, filename);
-}
-
-void dumpLevelDirInfo(struct LevelDirInfo *node, int depth)
-{
-  int i;
-
-  while (node)
-  {
-    for (i=0; i<depth * 3; i++)
-      printf(" ");
-
-    printf("filename == '%s'\n", node->filename);
-
-    if (node->node_group != NULL)
-      dumpLevelDirInfo(node->node_group, depth + 1);
-
-    node = node->next;
-  }
-}
-
-void sortLevelDirInfo(struct LevelDirInfo **node_first,
-                     int (*compare_function)(const void *, const void *))
-{
-  int num_nodes = numLevelDirInfo(*node_first);
-  struct LevelDirInfo **sort_array;
-  struct LevelDirInfo *node = *node_first;
-  int i = 0;
-
-  if (num_nodes == 0)
-    return;
-
-  /* allocate array for sorting structure pointers */
-  sort_array = checked_calloc(num_nodes * sizeof(struct LevelDirInfo *));
-
-  /* writing structure pointers to sorting array */
-  while (i < num_nodes && node)                /* double boundary check... */
-  {
-    sort_array[i] = node;
-
-    i++;
-    node = node->next;
-  }
-
-  /* sorting the structure pointers in the sorting array */
-  qsort(sort_array, num_nodes, sizeof(struct LevelDirInfo *),
-       compare_function);
-
-  /* update the linkage of list elements with the sorted node array */
-  for (i=0; i<num_nodes - 1; i++)
-    sort_array[i]->next = sort_array[i + 1];
-  sort_array[num_nodes - 1]->next = NULL;
-
-  /* update the linkage of the main list anchor pointer */
-  *node_first = sort_array[0];
-
-  free(sort_array);
-
-  /* now recursively sort the level group structures */
-  node = *node_first;
-  while (node)
-  {
-    if (node->node_group != NULL)
-      sortLevelDirInfo(&node->node_group, compare_function);
-
-    node = node->next;
-  }
-}
-
-inline void swap_numbers(int *i1, int *i2)
-{
-  int help = *i1;
-
-  *i1 = *i2;
-  *i2 = help;
-}
-
-inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
-{
-  int help_x = *x1;
-  int help_y = *y1;
-
-  *x1 = *x2;
-  *x2 = help_x;
-
-  *y1 = *y2;
-  *y2 = help_y;
-}
-
 
 /* ========================================================================= */
-/* some stuff from "files.c"                                                 */
+/* functions for checking filenames                                          */
 /* ========================================================================= */
 
-#if defined(PLATFORM_WIN32)
-#ifndef S_IRGRP
-#define S_IRGRP S_IRUSR
-#endif
-#ifndef S_IROTH
-#define S_IROTH S_IRUSR
-#endif
-#ifndef S_IWGRP
-#define S_IWGRP S_IWUSR
-#endif
-#ifndef S_IWOTH
-#define S_IWOTH S_IWUSR
-#endif
-#ifndef S_IXGRP
-#define S_IXGRP S_IXUSR
-#endif
-#ifndef S_IXOTH
-#define S_IXOTH S_IXUSR
-#endif
-#ifndef S_IRWXG
-#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
-#endif
-#ifndef S_ISGID
-#define S_ISGID 0
-#endif
-#endif /* PLATFORM_WIN32 */
-
-/* file permissions for newly written files */
-#define MODE_R_ALL             (S_IRUSR | S_IRGRP | S_IROTH)
-#define MODE_W_ALL             (S_IWUSR | S_IWGRP | S_IWOTH)
-#define MODE_X_ALL             (S_IXUSR | S_IXGRP | S_IXOTH)
-
-#define MODE_W_PRIVATE         (S_IWUSR)
-#define MODE_W_PUBLIC          (S_IWUSR | S_IWGRP)
-#define MODE_W_PUBLIC_DIR      (S_IWUSR | S_IWGRP | S_ISGID)
-
-#define DIR_PERMS_PRIVATE      (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
-#define DIR_PERMS_PUBLIC       (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
-
-#define FILE_PERMS_PRIVATE     (MODE_R_ALL | MODE_W_PRIVATE)
-#define FILE_PERMS_PUBLIC      (MODE_R_ALL | MODE_W_PUBLIC)
-
-char *getUserDataDir(void)
-{
-  static char *userdata_dir = NULL;
-
-  if (!userdata_dir)
-  {
-    char *home_dir = getHomeDir();
-    char *data_dir = program.userdata_directory;
-
-    userdata_dir = getPath2(home_dir, data_dir);
-  }
-
-  return userdata_dir;
-}
-
-char *getSetupDir()
+boolean FileIsGraphic(char *filename)
 {
-  return getUserDataDir();
-}
-
-static mode_t posix_umask(mode_t mask)
-{
-#if defined(PLATFORM_UNIX)
-  return umask(mask);
-#else
-  return 0;
-#endif
-}
-
-static int posix_mkdir(const char *pathname, mode_t mode)
-{
-#if defined(PLATFORM_WIN32)
-  return mkdir(pathname);
-#else
-  return mkdir(pathname, mode);
-#endif
-}
-
-void createDirectory(char *dir, char *text, int permission_class)
-{
-  /* leave "other" permissions in umask untouched, but ensure group parts
-     of USERDATA_DIR_MODE are not masked */
-  mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
-                    DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
-  mode_t normal_umask = posix_umask(0);
-  mode_t group_umask = ~(dir_mode & S_IRWXG);
-  posix_umask(normal_umask & group_umask);
-
-  if (access(dir, F_OK) != 0)
-    if (posix_mkdir(dir, dir_mode) != 0)
-      Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
-
-  posix_umask(normal_umask);           /* reset normal umask */
-}
-
-void InitUserDataDirectory()
-{
-  createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
-}
-
-void SetFilePermissions(char *filename, int permission_class)
-{
-  chmod(filename, (permission_class == PERMS_PRIVATE ?
-                  FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
-}
-
-int getFileVersionFromCookieString(const char *cookie)
-{
-  const char *ptr_cookie1, *ptr_cookie2;
-  const char *pattern1 = "_FILE_VERSION_";
-  const char *pattern2 = "?.?";
-  const int len_cookie = strlen(cookie);
-  const int len_pattern1 = strlen(pattern1);
-  const int len_pattern2 = strlen(pattern2);
-  const int len_pattern = len_pattern1 + len_pattern2;
-  int version_major, version_minor;
-
-  if (len_cookie <= len_pattern)
-    return -1;
-
-  ptr_cookie1 = &cookie[len_cookie - len_pattern];
-  ptr_cookie2 = &cookie[len_cookie - len_pattern2];
-
-  if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
-    return -1;
-
-  if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
-      ptr_cookie2[1] != '.' ||
-      ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
-    return -1;
-
-  version_major = ptr_cookie2[0] - '0';
-  version_minor = ptr_cookie2[2] - '0';
-
-  return VERSION_IDENT(version_major, version_minor, 0);
-}
-
-boolean checkCookieString(const char *cookie, const char *template)
-{
-  const char *pattern = "_FILE_VERSION_?.?";
-  const int len_cookie = strlen(cookie);
-  const int len_template = strlen(template);
-  const int len_pattern = strlen(pattern);
-
-  if (len_cookie != len_template)
-    return FALSE;
-
-  if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
-    return FALSE;
-
-  return TRUE;
-}
-
-/* ------------------------------------------------------------------------- */
-/* setup file stuff                                                          */
-/* ------------------------------------------------------------------------- */
-
-static char *string_tolower(char *s)
-{
-  static char s_lower[100];
-  int i;
-
-  if (strlen(s) >= 100)
-    return s;
-
-  strcpy(s_lower, s);
-
-  for (i=0; i<strlen(s_lower); i++)
-    s_lower[i] = tolower(s_lower[i]);
-
-  return s_lower;
-}
-
-int get_string_integer_value(char *s)
-{
-  static char *number_text[][3] =
-  {
-    { "0", "zero", "null", },
-    { "1", "one", "first" },
-    { "2", "two", "second" },
-    { "3", "three", "third" },
-    { "4", "four", "fourth" },
-    { "5", "five", "fifth" },
-    { "6", "six", "sixth" },
-    { "7", "seven", "seventh" },
-    { "8", "eight", "eighth" },
-    { "9", "nine", "ninth" },
-    { "10", "ten", "tenth" },
-    { "11", "eleven", "eleventh" },
-    { "12", "twelve", "twelfth" },
-  };
-
-  int i, j;
-
-  for (i=0; i<13; i++)
-    for (j=0; j<3; j++)
-      if (strcmp(string_tolower(s), number_text[i][j]) == 0)
-       return i;
-
-  return atoi(s);
-}
-
-boolean get_string_boolean_value(char *s)
-{
-  if (strcmp(string_tolower(s), "true") == 0 ||
-      strcmp(string_tolower(s), "yes") == 0 ||
-      strcmp(string_tolower(s), "on") == 0 ||
-      get_string_integer_value(s) == 1)
+  if (strlen(filename) > 4 &&
+      strcmp(&filename[strlen(filename) - 4], ".pcx") == 0)
     return TRUE;
-  else
-    return FALSE;
-}
-
-char *getFormattedSetupEntry(char *token, char *value)
-{
-  int i;
-  static char entry[MAX_LINE_LEN];
-
-  sprintf(entry, "%s:", token);
-  for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
-    entry[i] = ' ';
-  entry[i] = '\0';
-
-  strcat(entry, value);
-
-  return entry;
-}
-
-void freeSetupFileList(struct SetupFileList *setup_file_list)
-{
-  if (!setup_file_list)
-    return;
 
-  if (setup_file_list->token)
-    free(setup_file_list->token);
-  if (setup_file_list->value)
-    free(setup_file_list->value);
-  if (setup_file_list->next)
-    freeSetupFileList(setup_file_list->next);
-  free(setup_file_list);
+  return FALSE;
 }
 
-static struct SetupFileList *newSetupFileList(char *token, char *value)
+boolean FileIsSound(char *basename)
 {
-  struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
-
-  new->token = checked_malloc(strlen(token) + 1);
-  strcpy(new->token, token);
-
-  new->value = checked_malloc(strlen(value) + 1);
-  strcpy(new->value, value);
-
-  new->next = NULL;
-
-  return new;
-}
-
-char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
-{
-  if (!setup_file_list)
-    return NULL;
-
-  if (strcmp(setup_file_list->token, token) == 0)
-    return setup_file_list->value;
-  else
-    return getTokenValue(setup_file_list->next, token);
-}
-
-static void setTokenValue(struct SetupFileList *setup_file_list,
-                         char *token, char *value)
-{
-  if (!setup_file_list)
-    return;
+  if (strlen(basename) > 4 &&
+      strcmp(&basename[strlen(basename) - 4], ".wav") == 0)
+    return TRUE;
 
-  if (strcmp(setup_file_list->token, token) == 0)
-  {
-    free(setup_file_list->value);
-    setup_file_list->value = checked_malloc(strlen(value) + 1);
-    strcpy(setup_file_list->value, value);
-  }
-  else if (setup_file_list->next == NULL)
-    setup_file_list->next = newSetupFileList(token, value);
-  else
-    setTokenValue(setup_file_list->next, token, value);
+  return FALSE;
 }
 
-#ifdef DEBUG
-static void printSetupFileList(struct SetupFileList *setup_file_list)
+boolean FileIsMusic(char *basename)
 {
-  if (!setup_file_list)
-    return;
+  /* "music" can be a WAV (loop) file or (if compiled with SDL) a MOD file */
 
-  printf("token: '%s'\n", setup_file_list->token);
-  printf("value: '%s'\n", setup_file_list->value);
+  if (FileIsSound(basename))
+    return TRUE;
 
-  printSetupFileList(setup_file_list->next);
-}
+#if defined(TARGET_SDL)
+  if (strlen(basename) > 4 &&
+      (strcmp(&basename[strlen(basename) - 4], ".mod") == 0 ||
+       strcmp(&basename[strlen(basename) - 4], ".MOD") == 0 ||
+       strncmp(basename, "mod.", 4) == 0 ||
+       strncmp(basename, "MOD.", 4) == 0))
+    return TRUE;
 #endif
 
-struct SetupFileList *loadSetupFileList(char *filename)
-{
-  int line_len;
-  char line[MAX_LINE_LEN];
-  char *token, *value, *line_ptr;
-  struct SetupFileList *setup_file_list = newSetupFileList("", "");
-  struct SetupFileList *first_valid_list_entry;
-
-  FILE *file;
-
-  if (!(file = fopen(filename, MODE_READ)))
-  {
-    Error(ERR_WARN, "cannot open configuration file '%s'", filename);
-    return NULL;
-  }
-
-  while(!feof(file))
-  {
-    /* read next line of input file */
-    if (!fgets(line, MAX_LINE_LEN, file))
-      break;
-
-    /* cut trailing comment or whitespace from input line */
-    for (line_ptr = line; *line_ptr; line_ptr++)
-    {
-      if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
-      {
-       *line_ptr = '\0';
-       break;
-      }
-    }
-
-    /* cut trailing whitespaces from input line */
-    for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
-      if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
-       *line_ptr = '\0';
-
-    /* ignore empty lines */
-    if (*line == '\0')
-      continue;
-
-    line_len = strlen(line);
-
-    /* cut leading whitespaces from token */
-    for (token = line; *token; token++)
-      if (*token != ' ' && *token != '\t')
-       break;
-
-    /* find end of token */
-    for (line_ptr = token; *line_ptr; line_ptr++)
-    {
-      if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
-      {
-       *line_ptr = '\0';
-       break;
-      }
-    }
-
-    if (line_ptr < line + line_len)
-      value = line_ptr + 1;
-    else
-      value = "\0";
-
-    /* cut leading whitespaces from value */
-    for (; *value; value++)
-      if (*value != ' ' && *value != '\t')
-       break;
-
-    if (*token && *value)
-      setTokenValue(setup_file_list, token, value);
-  }
-
-  fclose(file);
-
-  first_valid_list_entry = setup_file_list->next;
-
-  /* free empty list header */
-  setup_file_list->next = NULL;
-  freeSetupFileList(setup_file_list);
-
-  if (first_valid_list_entry == NULL)
-    Error(ERR_WARN, "configuration file '%s' is empty", filename);
-
-  return first_valid_list_entry;
+  return FALSE;
 }
 
-void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
-                                 char *identifier)
+boolean FileIsArtworkType(char *basename, int type)
 {
-  if (!setup_file_list)
-    return;
-
-  if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
-  {
-    if (strcmp(setup_file_list->value, identifier) != 0)
-    {
-      Error(ERR_WARN, "configuration file has wrong version");
-      return;
-    }
-    else
-      return;
-  }
+  if ((type == TREE_TYPE_GRAPHICS_DIR && FileIsGraphic(basename)) ||
+      (type == TREE_TYPE_SOUNDS_DIR && FileIsSound(basename)) ||
+      (type == TREE_TYPE_MUSIC_DIR && FileIsMusic(basename)))
+    return TRUE;
 
-  if (setup_file_list->next)
-    checkSetupFileListIdentifier(setup_file_list->next, identifier);
-  else
-  {
-    Error(ERR_WARN, "configuration file has no version information");
-    return;
-  }
+  return FALSE;
 }
 
 
index 6213ae1becb60e190251707fabe826ba1c3d5bdd..e7dd846f9ef6c517f350004ee79e59eea8bc8bce 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "system.h"
 
 
-/* functions for version handling */
-#define VERSION_IDENT(x,y,z)   ((x) * 10000 + (y) * 100 + (z))
-#define VERSION_MAJOR(x)       ((x) / 10000)
-#define VERSION_MINOR(x)       (((x) % 10000) / 100)
-#define VERSION_PATCH(x)       ((x) % 100)
-
 /* values for InitCounter() and Counter() */
 #define INIT_COUNTER                   0
 #define READ_COUNTER                   1
 #define MAX_FILENAME_LEN               256
 #define MAX_LINE_LEN                   1000
 
-/* values for setup file stuff */
-#define TYPE_BOOLEAN                   1
-#define TYPE_SWITCH                    2
-#define TYPE_KEY                       3
-#define TYPE_INTEGER                   4
-#define TYPE_STRING                    5
-
-#define TOKEN_STR_FILE_IDENTIFIER      "file_identifier"
-
-#define TOKEN_VALUE_POSITION           30
-
-struct SetupFileList
-{
-  char *token;
-  char *value;
-  struct SetupFileList *next;
-};
-
-struct TokenInfo
-{
-  int type;
-  void *value;
-  char *text;
-};
-
 void InitCounter(void);
 unsigned long Counter(void);
 void Delay(unsigned long);
@@ -101,60 +70,53 @@ char *getPath2(char *, char *);
 char *getPath3(char *, char *, char*);
 char *getStringCopy(char *);
 char *getStringToLower(char *);
+
 void GetOptions(char **);
+
+void SetError(char *, ...);
+char *GetError(void);
 void Error(int, char *, ...);
+
 void *checked_malloc(unsigned long);
 void *checked_calloc(unsigned long);
 void *checked_realloc(void *, unsigned long);
+inline void swap_numbers(int *, int *);
+inline void swap_number_pairs(int *, int *, int *, int *);
+
 short getFile16BitInteger(FILE *, int);
 void putFile16BitInteger(FILE *, short, int);
 int getFile32BitInteger(FILE *, int);
 void putFile32BitInteger(FILE *, int, int);
 boolean getFileChunk(FILE *, char *, int *, int);
 void putFileChunk(FILE *, char *, int, int);
+int getFileVersion(FILE *);
+void putFileVersion(FILE *, int);
 void ReadUnusedBytesFromFile(FILE *, unsigned long);
 void WriteUnusedBytesToFile(FILE *, unsigned long);
 
+#define getFile16BitBE(f)     getFile16BitInteger(f,BYTE_ORDER_BIG_ENDIAN)
+#define getFile16BitLE(f)     getFile16BitInteger(f,BYTE_ORDER_LITTLE_ENDIAN)
+#define putFile16BitBE(f,x)   putFile16BitInteger(f,x,BYTE_ORDER_BIG_ENDIAN)
+#define putFile16BitLE(f,x)   putFile16BitInteger(f,x,BYTE_ORDER_LITTLE_ENDIAN)
+#define getFile32BitBE(f)     getFile32BitInteger(f,BYTE_ORDER_BIG_ENDIAN)
+#define getFile32BitLE(f)     getFile32BitInteger(f,BYTE_ORDER_LITTLE_ENDIAN)
+#define putFile32BitBE(f,x)   putFile32BitInteger(f,x,BYTE_ORDER_BIG_ENDIAN)
+#define putFile32BitLE(f,x)   putFile32BitInteger(f,x,BYTE_ORDER_LITTLE_ENDIAN)
+#define getFileChunkBE(f,s,x) getFileChunk(f,s,x,BYTE_ORDER_BIG_ENDIAN)
+#define getFileChunkLE(f,s,x) getFileChunk(f,s,x,BYTE_ORDER_LITTLE_ENDIAN)
+#define putFileChunkBE(f,s,x) putFileChunk(f,s,x,BYTE_ORDER_BIG_ENDIAN)
+#define putFileChunkLE(f,s,x) putFileChunk(f,s,x,BYTE_ORDER_LITTLE_ENDIAN)
+
 char *getKeyNameFromKey(Key);
 char *getX11KeyNameFromKey(Key);
+Key getKeyFromKeyName(char *);
 Key getKeyFromX11KeyName(char *);
 char getCharFromKey(Key);
-char *getJoyNameFromJoySymbol(int);
-int getJoySymbolFromJoyName(char *);
-int getJoystickNrFromDeviceName(char *);
-
-struct LevelDirInfo *newLevelDirInfo();
-void pushLevelDirInfo(struct LevelDirInfo **, struct LevelDirInfo *);
-int numLevelDirInfo(struct LevelDirInfo *);
-boolean validLevelSeries(struct LevelDirInfo *);
-struct LevelDirInfo *getFirstValidLevelSeries(struct LevelDirInfo *);
-struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *);
-int numLevelDirInfoInGroup(struct LevelDirInfo *);
-int posLevelDirInfo(struct LevelDirInfo *);
-struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *, int);
-struct LevelDirInfo *getLevelDirInfoFromFilename(char *);
-void dumpLevelDirInfo(struct LevelDirInfo *, int);
-void sortLevelDirInfo(struct LevelDirInfo **,
-                     int (*compare_function)(const void *, const void *));
-
-inline void swap_numbers(int *, int *);
-inline void swap_number_pairs(int *, int *, int *, int *);
 
-char *getUserDataDir(void);
-char *getSetupDir(void);
-void createDirectory(char *, char *, int);
-void InitUserDataDirectory(void);
-void SetFilePermissions(char *, int);
-int getFileVersionFromCookieString(const char *);
-boolean checkCookieString(const char *, const char *);
-
-int get_string_integer_value(char *);
-boolean get_string_boolean_value(char *);
-char *getFormattedSetupEntry(char *, char *);
-void freeSetupFileList(struct SetupFileList *);
-char *getTokenValue(struct SetupFileList *, char *);
-struct SetupFileList *loadSetupFileList(char *);
-void checkSetupFileListIdentifier(struct SetupFileList *, char *);
+boolean FileIsGraphic(char *);
+boolean FileIsSound(char *);
+boolean FileIsMusic(char *);
+boolean FileIsArtworkType(char *, int);
 
 #if !defined(PLATFORM_UNIX)
 void initErrorFile();
index 2e0d278085cec4a689fd429ca03fd5efba081819..29cc036028c7981817f400fa1f6e40037a342974 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -17,7 +17,9 @@
 #if defined(PLATFORM_MSDOS)
 
 #include "sound.h"
+#include "joystick.h"
 #include "misc.h"
+#include "setup.h"
 #include "pcx.h"
 
 #define AllegroDefaultScreen() (display->screens[display->default_screen])
@@ -52,12 +54,6 @@ static int global_colormap_entries_used = 0;
 
 boolean wait_for_vsync;
 
-/*
-extern int playing_sounds;
-extern struct SoundControl playlist[MAX_SOUNDS_PLAYING];
-extern struct SoundControl emptySoundControl;
-*/
-
 static BITMAP *Read_PCX_to_AllegroBitmap(char *);
 
 static void allegro_init_drivers()
@@ -90,7 +86,7 @@ static void allegro_init_drivers()
 
 static boolean allegro_init_audio()
 {
-  reserve_voices(MAX_SOUNDS_PLAYING, 0);
+  reserve_voices(NUM_MIXER_CHANNELS, 0);
 
   if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL) == -1)
     if (install_sound(DIGI_SB, MIDI_NONE, NULL) == -1)
@@ -301,9 +297,9 @@ Display *XOpenDisplay(char *display_name)
   Screen *screen;
   Display *display;
   BITMAP *mouse_bitmap = NULL;
+  char *mouse_filename =getCustomImageFilename(program.msdos_pointer_filename);
 
-  mouse_bitmap = Read_PCX_to_AllegroBitmap(program.msdos_pointer_filename);
-  if (mouse_bitmap == NULL)
+  if ((mouse_bitmap = Read_PCX_to_AllegroBitmap(mouse_filename)) == NULL)
     return NULL;
 
   screen = malloc(sizeof(Screen));
@@ -311,13 +307,8 @@ Display *XOpenDisplay(char *display_name)
 
   screen[0].cmap = 0;
   screen[0].root = 0;
-#if 0
-  screen[0].white_pixel = 0xFF;
-  screen[0].black_pixel = 0x00;
-#else
   screen[0].white_pixel = AllegroAllocColorCell(0xFFFF, 0xFFFF, 0xFFFF);
   screen[0].black_pixel = AllegroAllocColorCell(0x0000, 0x0000, 0x0000);
-#endif
   screen[0].video_bitmap = NULL;
 
   display->default_screen = 0;
@@ -510,12 +501,10 @@ static BITMAP *Image_to_AllegroBitmap(Image *image)
   byte *src_ptr = image->data;
   byte pixel_mapping[MAX_COLORS];
   unsigned int depth = 8;
-
-#if 0
-  int i, j, x, y;
-#else
   int i, x, y;
-#endif
+
+  if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
+    Error(ERR_EXIT, "cannot handle true-color images on 8-bit display");
 
   /* allocate new allegro bitmap structure */
   if ((bitmap = create_bitmap_ex(depth, image->width, image->height)) == NULL)
@@ -529,48 +518,12 @@ static BITMAP *Image_to_AllegroBitmap(Image *image)
   /* try to use existing colors from the global colormap */
   for (i=0; i<MAX_COLORS; i++)
   {
-
-#if 0
-    int r, g, b;
-#endif
-
     if (!image->rgb.color_used[i])
       continue;
 
-
-#if 0
-    r = image->rgb.red[i] >> 10;
-    g = image->rgb.green[i] >> 10;
-    b = image->rgb.blue[i] >> 10;
-
-    for (j=0; j<global_colormap_entries_used; j++)
-    {
-      if (r == global_colormap[j].r &&
-         g == global_colormap[j].g &&
-         b == global_colormap[j].b)            /* color found */
-      {
-       pixel_mapping[i] = j;
-       break;
-      }
-    }
-
-    if (j == global_colormap_entries_used)     /* color not found */
-    {
-      if (global_colormap_entries_used < MAX_COLORS)
-       global_colormap_entries_used++;
-
-      global_colormap[j].r = r;
-      global_colormap[j].g = g;
-      global_colormap[j].b = b;
-
-      pixel_mapping[i] = j;
-    }
-#else
     pixel_mapping[i] = AllegroAllocColorCell(image->rgb.red[i],
                                             image->rgb.green[i],
                                             image->rgb.blue[i]);
-#endif
-
   }
 
   /* copy bitmap data */
@@ -608,17 +561,10 @@ int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
     return errno_pcx;
 
   *pixmap = (Pixmap)bitmap;
-#if 0
-  *pixmap_mask = (Pixmap)bitmap;
-  /* !!! two pointers on same bitmap => second free() fails !!! */
-#else
+
   /* pixmap_mask will never be used in Allegro (which uses masked_blit()),
      so use non-NULL dummy pointer to empty Pixmap */
-  /*
-  *pixmap_mask = (Pixmap)checked_calloc(sizeof(Pixmap));
-  */
   *pixmap_mask = (Pixmap)DUMMY_MASK;
-#endif
 
   return PCX_Success;
 }
@@ -979,9 +925,13 @@ void MSDOSOpenAudio(void)
     audio.music_available = TRUE;
     audio.loops_available = TRUE;
     audio.sound_enabled = TRUE;
-  }
 
-  InitPlaylist();
+    audio.num_channels = NUM_MIXER_CHANNELS;
+    audio.music_channel = MUSIC_CHANNEL;
+    audio.first_sound_channel = FIRST_SOUND_CHANNEL;
+
+    Mixer_InitChannels();
+  }
 }
 
 void MSDOSCloseAudio(void)
@@ -994,4 +944,57 @@ void NetworkServer(int port, int serveronly)
   Error(ERR_WARN, "networking not supported in DOS version");
 }
 
+
+/* ========================================================================= */
+/* joystick functions                                                        */
+/* ========================================================================= */
+
+void MSDOSInitJoysticks()
+{
+  int i;
+
+  /* start from scratch */
+  remove_joystick();
+
+  /* try to access two joysticks; if that fails, try to access just one */
+  if (install_joystick(JOY_TYPE_2PADS) == 0 ||
+      install_joystick(JOY_TYPE_AUTODETECT) == 0)
+    joystick.status = JOYSTICK_ACTIVATED;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    char *device_name = setup.input[i].joy.device_name;
+    int joystick_nr = getJoystickNrFromDeviceName(device_name);
+
+    if (joystick_nr >= num_joysticks)
+      joystick_nr = -1;
+
+    /* misuse joystick file descriptor variable to store joystick number */
+    joystick.fd[i] = joystick_nr;
+  }
+}
+
+boolean MSDOSReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
+{
+  /* the allegro global variable 'num_joysticks' contains the number
+     of joysticks found at initialization under MS-DOS / Windows */
+
+  if (nr < 0 || nr >= num_joysticks)
+    return FALSE;
+
+  poll_joystick();
+
+  if (x != NULL)
+    *x = joy[nr].stick[0].axis[0].pos;
+  if (y != NULL)
+    *y = joy[nr].stick[0].axis[1].pos;
+
+  if (b1 != NULL)
+    *b1 = joy[nr].button[0].b;
+  if (b2 != NULL)
+    *b2 = joy[nr].button[1].b;
+
+  return TRUE;
+}
+
 #endif /* PLATFORM_MSDOS */
index 6e16b214baf12d3c627fc02178def2e1c4d7654f..5ddb3eef42aea353ed8e7ab3eeafd492d01143f3 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 
 
 /* symbol 'window' is defined in DJGPP cross-compiler in libc.a(conio.o) */
-#define window window_djgpp
+#define window window_internal
 
 /* symbol 'font' is defined in "allegro.h" */
-#define font font_allegro
+#define font font_internal
 
 /* system dependent definitions */
 
 #define XRES   800
 #define YRES   600
 
+/* allegro defines some macros that bother the rest of the program */
+#ifdef joy_x
+#undef joy_x
+#undef joy_y
+#undef joy_left
+#undef joy_right
+#undef joy_up
+#undef joy_down
+#undef joy_b1
+#undef joy_b2
+#endif
+
 /* additional Allegro keyboard mapping */
 
 /* The following are all undefined in Allegro */
 
 /* end of X11 keyboard mapping */
 
-#define JOYSTICK_FILENAME      "joystick.cnf"
 
 #define screen myscreen
 
@@ -741,4 +752,7 @@ void MSDOSCloseAudio(void);
 
 void NetworkServer(int, int);
 
+void MSDOSInitJoysticks();
+boolean MSDOSReadJoystick(int, int *, int *, boolean *, boolean *);
+
 #endif /* MSDOS_H */
index a43e67d5ff4ea2d5ccbbe17857c80fc7c2d3249c..7f6d7f2da42ad9407a0a6fa7b4d779aaabf9ea92 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #define PCX_DEBUG              FALSE
 
 #define PCX_MAGIC              0x0a    /* first byte in a PCX image file    */
-#define PCX_LAST_VERSION       5       /* last acceptable version number    */
+#define PCX_SUPPORTED_VERSION  5       /* last acceptable version number    */
 #define PCX_ENCODING           1       /* PCX encoding method               */
 #define PCX_256COLORS_MAGIC    0x0c    /* first byte of a PCX 256 color map */
-#define PCX_MAXDEPTH           8       /* supports up to 8 bits per pixel   */
 #define PCX_MAXCOLORS          256     /* maximum number of colors          */
 
 #define PCX_HEADER_SIZE                128
@@ -36,7 +35,7 @@ struct PCX_Header
   unsigned char signature;     /* PCX file identifier                 */
   unsigned char version;       /* version compatibility level         */
   unsigned char encoding;      /* encoding method                     */
-  unsigned char bits_per_pixel;        /* bits per pixel, or depth            */
+  unsigned char bits_per_pixel;        /* bits per pixel (not depth!)         */
   unsigned short xmin;         /* X position of left edge             */
   unsigned short ymin;         /* Y position of top edge              */
   unsigned short xmax;         /* X position of right edge            */
@@ -45,7 +44,7 @@ struct PCX_Header
   unsigned short vres;         /* Y screen resolution of source image */
   unsigned char palette[16][3];        /* PCX color map                       */
   unsigned char reserved;      /* should be 0, 1 if std res fax       */
-  unsigned char color_planes;  /* bit planes in image                 */
+  unsigned char color_planes;  /* "color planes" in image             */
   unsigned short bytes_per_line;/* byte delta between scanlines        */
   unsigned short palette_type; /* 0 = undef, 1 = color, 2 = grayscale */
   unsigned char filler[58];    /* fill to struct size of 128          */
@@ -54,6 +53,7 @@ struct PCX_Header
 /* global PCX error value */
 int errno_pcx = PCX_Success;
 
+#if 0
 static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
 {
   /* Run Length Encoding: If the two high bits are set,
@@ -97,7 +97,99 @@ static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
   /* return current buffer position for next decoding function */
   return buffer_ptr;
 }
+#endif
+
+static boolean PCX_ReadBitmap(FILE *file, struct PCX_Header *pcx, Image *image)
+{
+  int width = image->width;
+  int height = image->height;
+  int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
+  int bytes_per_row = pcx->color_planes * pcx->bytes_per_line;
+  byte *row_buffer = checked_malloc(bytes_per_row);
+  byte *bitmap_ptr = image->data;
+  int y;
+
+  for (y = 0; y < height; y++)
+  {
+    /* decode a scan line into a temporary buffer first */
+    byte *dst_ptr = (pcx_depth == 8) ? bitmap_ptr : row_buffer;
+    byte value = 0, count = 0;
+    int value_int;
+    int i;
+
+    for (i = 0; i < bytes_per_row; i++)
+    {
+      if (count == 0)
+      {
+       if ((value_int = fgetc(file)) == EOF)
+         return FALSE;
+       value = (byte)value_int;
+
+       if ((value & 0xc0) == 0xc0)     /* this is a repeat count byte */
+       {
+         count = value & 0x3f;         /* extract repeat count from byte */
+         if ((value_int = fgetc(file)) == EOF)
+           return FALSE;
+         value = (byte)value_int;
+       }
+       else
+         count = 1;
+      }
+
+      dst_ptr[i] = value;
+      count--;
+
+      if (pcx_depth == 8)
+       image->rgb.color_used[value] = TRUE;
+    }
+
+    if (pcx_depth <= 4)                        /* expand planes to 1 byte/pixel */
+    {
+      byte *src_ptr = row_buffer;
+      int plane;
+
+      for (plane = 0; plane < pcx->color_planes; plane++)
+      {
+       int i, j, x = 0;
+
+       for(i = 0; i < pcx->bytes_per_line; i++)
+       {
+         byte value = *src_ptr++;
+
+         for(j = 7; j >= 0; j--)
+         {
+           byte bit = (value >> j) & 1;
+
+           bitmap_ptr[x++] |= bit << plane;
+         }
+       }
+      }
+    }
+    else if (pcx_depth == 24)          /* de-interlace planes */
+    {
+      byte *src_ptr = row_buffer;
+      int plane;
+
+      for(plane = 0; plane < pcx->color_planes; plane++)
+      {
+       int x;
+
+       dst_ptr = bitmap_ptr + plane;
+       for(x = 0; x < width; x++)
+       {
+         *dst_ptr = *src_ptr++;
+         dst_ptr += pcx->color_planes;
+       }
+      }
+    }
+
+    bitmap_ptr += image->bytes_per_row;
+  }
 
+  return TRUE;
+}
+
+#if 0
 static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
 {
   int i, magic;
@@ -124,16 +216,59 @@ static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
   /* return current buffer position for next decoding function */
   return buffer_ptr;
 }
+#endif
+
+static boolean PCX_ReadColormap(FILE *file,struct PCX_Header *pcx,Image *image)
+{
+  int pcx_depth = pcx->bits_per_pixel * pcx->color_planes;
+  int num_colors = (1 << pcx_depth);
+  int i;
+
+  if (image->depth != 8)
+    return TRUE;
+
+  if (pcx_depth == 8)
+  {
+    byte value;
+    int value_int;
+
+    /* look for a 256-colour palette */
+    do
+    {
+      if ((value_int = fgetc(file)) == EOF)
+       return FALSE;
+      value = (byte)value_int;
+    }
+    while (value != PCX_256COLORS_MAGIC);
+
+    /* read 256 colors from PCX colormap */
+    for(i = 0; i < PCX_MAXCOLORS; i++)
+    {
+      image->rgb.red[i]   = (byte)fgetc(file) << 8;
+      image->rgb.green[i] = (byte)fgetc(file) << 8;
+      image->rgb.blue[i]  = (byte)fgetc(file) << 8;
+    }
+  }
+  else
+  {
+    for(i = 0; i < num_colors; i++)
+    {
+      image->rgb.red[i]   = pcx->palette[i][0] << 8;
+      image->rgb.green[i] = pcx->palette[i][1] << 8;
+      image->rgb.blue[i]  = pcx->palette[i][2] << 8;
+    }
+  }
+
+  return TRUE;
+}
 
 Image *Read_PCX_to_Image(char *filename)
 {
   FILE *file;
-  byte *file_buffer;
-  byte *buffer_ptr, *buffer_last;
-  unsigned int file_length;
+  byte header_buffer[PCX_HEADER_SIZE];
   struct PCX_Header pcx;
   Image *image;
-  int width, height, depth;
+  int width, height, depth, pcx_depth;
   int i;
 
   errno_pcx = PCX_Success;
@@ -144,57 +279,40 @@ Image *Read_PCX_to_Image(char *filename)
     return NULL;
   }
 
-  if (fseek(file, 0, SEEK_END) == -1)
-  {
-    fclose(file);
-    errno_pcx = PCX_ReadFailed;
-    return NULL;
-  }
-
-  file_length = ftell(file);
-  rewind(file);
-
-  if (file_length < PCX_HEADER_SIZE)
+  if (fread(header_buffer, 1, PCX_HEADER_SIZE, file) != PCX_HEADER_SIZE)
   {
-    /* PCX file is too short to contain a valid PCX header */
     fclose(file);
 
-    errno_pcx = PCX_FileInvalid;
-    return NULL;
-  }
-
-  file_buffer = checked_malloc(file_length);
-
-  if (fread(file_buffer, 1, file_length, file) != file_length)
-  {
-    fclose(file);
     errno_pcx = PCX_ReadFailed;
     return NULL;
   }
 
-  fclose(file);
-
-  pcx.signature      = file_buffer[0];
-  pcx.version        = file_buffer[1];
-  pcx.encoding       = file_buffer[2];
-  pcx.bits_per_pixel = file_buffer[3];
-  pcx.xmin           = file_buffer[4]  + 256 * file_buffer[5];
-  pcx.ymin           = file_buffer[6]  + 256 * file_buffer[7];
-  pcx.xmax           = file_buffer[8]  + 256 * file_buffer[9];
-  pcx.ymax           = file_buffer[10] + 256 * file_buffer[11];
-  pcx.color_planes   = file_buffer[65];
-  pcx.bytes_per_line = file_buffer[66] + 256 * file_buffer[67];
-  pcx.palette_type   = file_buffer[68] + 256 * file_buffer[69];
+  pcx.signature      = header_buffer[0];
+  pcx.version        = header_buffer[1];
+  pcx.encoding       = header_buffer[2];
+  pcx.bits_per_pixel = header_buffer[3];
+  pcx.xmin           = (header_buffer[5]  << 8) | header_buffer[4];
+  pcx.ymin           = (header_buffer[7]  << 8) | header_buffer[6];
+  pcx.xmax           = (header_buffer[9]  << 8) | header_buffer[8];
+  pcx.ymax           = (header_buffer[11] << 8) | header_buffer[10];
+  pcx.color_planes   = header_buffer[65];
+  pcx.bytes_per_line = (header_buffer[67] << 8) | header_buffer[66];
+  pcx.palette_type   = (header_buffer[69] << 8) | header_buffer[68];
+
+  for (i = 0; i < 48; i++)
+    pcx.palette[i / 3][i % 3] = header_buffer[16 + i];
 
   width  = pcx.xmax - pcx.xmin + 1;
   height = pcx.ymax - pcx.ymin + 1;
-  depth  = pcx.bits_per_pixel;
+  pcx_depth = pcx.bits_per_pixel * pcx.color_planes;
+  depth = ((pcx_depth + 7) / 8) * 8;
 
-  if (pcx.signature != PCX_MAGIC || pcx.version > PCX_LAST_VERSION ||
-      pcx.encoding != PCX_ENCODING || pcx.color_planes > PCX_MAXDEPTH ||
+  if (pcx.signature != PCX_MAGIC ||
+      pcx.version != PCX_SUPPORTED_VERSION ||
+      pcx.encoding != PCX_ENCODING ||
       width < 0 || height < 0)
   {
-    free(file_buffer);
+    fclose(file);
 
     errno_pcx = PCX_FileInvalid;
     return NULL;
@@ -203,10 +321,9 @@ Image *Read_PCX_to_Image(char *filename)
 #if PCX_DEBUG
   if (options.verbose)
   {
-    printf("%s is a %dx%d PC Paintbrush image with %d bitplanes\n",
-          filename, width, height,
-          pcx.color_planes);
-    printf("depth: %d\n", pcx.bits_per_pixel);
+    printf("%s is a %dx%d PC Paintbrush image\n", filename, width, height);
+    printf("depth: %d\n", depth);
+    printf("bits_per_pixel: %d\n", pcx.bits_per_pixel);
     printf("color_planes: %d\n", pcx.color_planes);
     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
     printf("palette type: %s\n",
@@ -218,43 +335,36 @@ Image *Read_PCX_to_Image(char *filename)
   /* allocate new image structure */
   image = newImage(width, height, depth);
 
-  buffer_ptr  = file_buffer + PCX_HEADER_SIZE;
-  buffer_last = file_buffer + file_length;
-
   /* read compressed bitmap data */
-  if ((buffer_ptr = PCX_ReadBitmap(image, buffer_ptr, buffer_last)) == NULL)
+  if (!PCX_ReadBitmap(file, &pcx, image))
   {
-    free(file_buffer);
+    fclose(file);
     freeImage(image);
 
     errno_pcx = PCX_FileInvalid;
     return NULL;
   }
 
-  if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
-  {
-    /* PCX file is too short to contain a valid 256 colors colormap */
-    fclose(file);
-    errno_pcx = PCX_ColorFailed;
-    return NULL;
-  }
-
   /* read colormap data */
-  if (!PCX_ReadColormap(image, buffer_ptr, buffer_last))
+  if (!PCX_ReadColormap(file, &pcx, image))
   {
-    free(file_buffer);
+    fclose(file);
     freeImage(image);
+
     errno_pcx = PCX_ColorFailed;
     return NULL;
   }
 
-  free(file_buffer);
+  fclose(file);
 
-  /* determine number of used colormap entries */
-  image->rgb.used = 0;
-  for (i=0; i<PCX_MAXCOLORS; i++)
-    if (image->rgb.color_used[i])
-      image->rgb.used++;
+  if (pcx_depth == 8)
+  {
+    /* determine number of used colormap entries for 8-bit PCX images */
+    image->rgb.used = 0;
+    for (i=0; i<PCX_MAXCOLORS; i++)
+      if (image->rgb.color_used[i])
+       image->rgb.used++;
+  }
 
 #if PCX_DEBUG
   if (options.verbose)
index 9c345dc136ddd34839a6c9d05ae10653febdb3b1..bf5bacfab5a535343569b87d111f7e83324bc26e 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -26,6 +26,7 @@
 #define        PCX_FileInvalid         -3
 #define PCX_NoMemory           -4
 #define PCX_ColorFailed                -5
+#define PCX_OtherError         -6
 
 /* global PCX error value */
 extern int errno_pcx;
index 352e3562ebf33c0214388eb4f6cc317040af0d80..94c5c4a8bc994d0785b49e359f4c46ed67fdf14d 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -14,7 +14,9 @@
 #ifndef PLATFORM_H
 #define PLATFORM_H
 
-/* define keywords for supported main platforms */
+/* ========================================================================= */
+/* define keywords for supported main platforms (Unix, DOS and Windows)      */
+/* ========================================================================= */
 
 #if defined(MSDOS)
 #define PLATFORM_MSDOS
 #define PLATFORM_NETBSD
 #endif
 
+#if defined(__bsdi__)
+#define PLATFORM_BSDI
+#endif
+
+#if defined(sparc) || defined(sun)
+#define PLATFORM_SOLARIS
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define PLATFORM_MACOSX
+#endif
+
 /* detecting HP-UX by the following compiler keyword definitions:
    - in K&R mode (the default), the HP C compiler defines "hpux"
    - in ANSI mode (-Aa or -Ae), the HP C compiler defines "__hpux"
index d65bc532da49414c55a5b79f548742c2f19d0946..08626fcab08584cc38957324a2a5624e8fff9bbe 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
index 6464286cb294f75c4e7212b37b517dfd08f8f910..540bc6679c885cf910b8f0f7418ad2493b7fdfea 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
index a2d82fab1d2aa370a617e720fb830a0d0dc032cd..212ce1b86c59493753cf8262050c547b9e3b1ae1 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -13,6 +13,7 @@
 
 #include "system.h"
 #include "sound.h"
+#include "joystick.h"
 #include "misc.h"
 
 
@@ -828,21 +829,33 @@ Bitmap *SDLLoadImage(char *filename)
 
   /* load image to temporary surface */
   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
-    Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
+  {
+    SetError("IMG_Load(): %s", SDL_GetError());
+    return NULL;
+  }
 
   /* create native non-transparent surface for current image */
   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
-    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
+  {
+    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+    return NULL;
+  }
 
   /* create native transparent surface for current image */
   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
                  SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
-    Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
+  {
+    SetError("SDL_DisplayFormat(): %s", SDL_GetError());
+    return NULL;
+  }
 
   /* free temporary surface */
   SDL_FreeSurface(sdl_image_tmp);
 
+  new_bitmap->width = new_bitmap->surface->w;
+  new_bitmap->height = new_bitmap->surface->h;
+
   return new_bitmap;
 }
 
@@ -859,8 +872,8 @@ inline void SDLOpenAudio(void)
     return;
   }
 
-  if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, AUDIO_S16,
-                   AUDIO_STEREO_CHANNELS,
+  if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
+                   AUDIO_NUM_CHANNELS_STEREO,
                    DEFAULT_AUDIO_FRAGMENT_SIZE) < 0)
   {
     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
@@ -872,19 +885,12 @@ inline void SDLOpenAudio(void)
   audio.loops_available = TRUE;
   audio.sound_enabled = TRUE;
 
-  /* determine number of available channels */
-  audio.channels = Mix_AllocateChannels(MIX_CHANNELS);
-
-  if (!audio.mods_available)   /* reserve first channel for music loops */
-  {
-    if (Mix_ReserveChannels(1) == 1)
-      audio.music_channel = 0;
-    else
-      audio.music_available = FALSE;
-  }
+  /* set number of available mixer channels */
+  audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
+  audio.music_channel = MUSIC_CHANNEL;
+  audio.first_sound_channel = FIRST_SOUND_CHANNEL;
 
-  Mix_Volume(-1, SOUND_MAX_VOLUME);
-  Mix_VolumeMusic(SOUND_MAX_VOLUME);
+  Mixer_InitChannels();
 }
 
 inline void SDLCloseAudio(void)
@@ -932,4 +938,123 @@ inline void SDLNextEvent(Event *event)
 #endif
 }
 
+
+/* ========================================================================= */
+/* joystick functions                                                        */
+/* ========================================================================= */
+
+static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
+static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+
+static boolean SDLOpenJoystick(int nr)
+{
+  if (nr < 0 || nr > MAX_PLAYERS)
+    return FALSE;
+
+  return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
+}
+
+static void SDLCloseJoystick(int nr)
+{
+  if (nr < 0 || nr > MAX_PLAYERS)
+    return;
+
+  SDL_JoystickClose(sdl_joystick[nr]);
+}
+
+static boolean SDLCheckJoystickOpened(int nr)
+{
+  if (nr < 0 || nr > MAX_PLAYERS)
+    return FALSE;
+
+  return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
+}
+
+void HandleJoystickEvent(Event *event)
+{
+  switch(event->type)
+  {
+    case SDL_JOYAXISMOTION:
+      if (event->jaxis.axis < 2)
+       sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
+      break;
+
+    case SDL_JOYBUTTONDOWN:
+      if (event->jbutton.button < 2)
+       sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
+      break;
+
+    case SDL_JOYBUTTONUP:
+      if (event->jbutton.button < 2)
+       sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
+      break;
+
+    default:
+      break;
+  }
+}
+
+void SDLInitJoysticks()
+{
+  static boolean sdl_joystick_subsystem_initialized = FALSE;
+  int i;
+
+  if (!sdl_joystick_subsystem_initialized)
+  {
+    sdl_joystick_subsystem_initialized = TRUE;
+
+    if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
+    {
+      Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
+      return;
+    }
+  }
+
+  for (i=0; i<MAX_PLAYERS; i++)
+  {
+    char *device_name = setup.input[i].joy.device_name;
+    int joystick_nr = getJoystickNrFromDeviceName(device_name);
+
+    if (joystick_nr >= SDL_NumJoysticks())
+      joystick_nr = -1;
+
+    /* misuse joystick file descriptor variable to store joystick number */
+    joystick.fd[i] = joystick_nr;
+
+    /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
+    if (SDLCheckJoystickOpened(joystick_nr))
+      SDLCloseJoystick(joystick_nr);
+
+    if (!setup.input[i].use_joystick)
+      continue;
+
+    if (!SDLOpenJoystick(joystick_nr))
+    {
+      Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
+      continue;
+    }
+
+    joystick.status = JOYSTICK_ACTIVATED;
+  }
+}
+
+boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
+{
+  if (nr < 0 || nr >= MAX_PLAYERS)
+    return FALSE;
+
+  if (x != NULL)
+    *x = sdl_js_axis[nr][0];
+  if (y != NULL)
+    *y = sdl_js_axis[nr][1];
+
+  if (b1 != NULL)
+    *b1 = sdl_js_button[nr][0];
+  if (b2 != NULL)
+    *b2 = sdl_js_button[nr][1];
+
+  return TRUE;
+}
+
 #endif /* TARGET_SDL */
index fcc566edbc849673b42d7707593ac00c6ed6eeec..64db7795c9df1094f8a18a5ff5e942b29d919a5f 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -60,6 +60,9 @@ typedef int                   Colormap;
 
 struct SDLSurfaceInfo
 {
+  char *source_filename;
+
+  int width, height;
   SDL_Surface *surface;
   SDL_Surface *surface_masked;
   GC gc;
@@ -311,6 +314,10 @@ struct XY
 #define KSYM_F23               KSYM_UNDEFINED
 #define KSYM_F24               KSYM_UNDEFINED
 
+#define KSYM_FKEY_FIRST                KSYM_F1
+#define KSYM_FKEY_LAST         KSYM_F15
+#define KSYM_NUM_FKEYS         (KSYM_FKEY_LAST - KSYM_FKEY_FIRST + 1)
+
 
 /* SDL function definitions */
 
@@ -330,4 +337,8 @@ inline void SDLCloseAudio(void);
 
 inline void SDLNextEvent(Event *);
 
+void HandleJoystickEvent(Event *);
+void SDLInitJoysticks(void);
+boolean SDLReadJoystick(int, int *, int *, boolean *, boolean *);
+
 #endif /* SDL_H */
diff --git a/src/libgame/setup.c b/src/libgame/setup.c
new file mode 100644 (file)
index 0000000..c6450a6
--- /dev/null
@@ -0,0 +1,2276 @@
+/***********************************************************
+* Artsoft Retro-Game Library                               *
+*----------------------------------------------------------*
+* (c) 1994-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* setup.c                                                  *
+***********************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "setup.h"
+#include "joystick.h"
+#include "text.h"
+#include "misc.h"
+
+/* file names and filename extensions */
+#if !defined(PLATFORM_MSDOS)
+#define LEVELSETUP_DIRECTORY   "levelsetup"
+#define SETUP_FILENAME         "setup.conf"
+#define LEVELSETUP_FILENAME    "levelsetup.conf"
+#define LEVELINFO_FILENAME     "levelinfo.conf"
+#define GRAPHICSINFO_FILENAME  "graphicsinfo.conf"
+#define SOUNDSINFO_FILENAME    "soundsinfo.conf"
+#define MUSICINFO_FILENAME     "musicinfo.conf"
+#define LEVELFILE_EXTENSION    "level"
+#define TAPEFILE_EXTENSION     "tape"
+#define SCOREFILE_EXTENSION    "score"
+#else
+#define LEVELSETUP_DIRECTORY   "lvlsetup"
+#define SETUP_FILENAME         "setup.cnf"
+#define LEVELSETUP_FILENAME    "lvlsetup.cnf"
+#define LEVELINFO_FILENAME     "lvlinfo.cnf"
+#define GRAPHICSINFO_FILENAME  "gfxinfo.cnf"
+#define SOUNDSINFO_FILENAME    "sndinfo.cnf"
+#define MUSICINFO_FILENAME     "musinfo.cnf"
+#define LEVELFILE_EXTENSION    "lvl"
+#define TAPEFILE_EXTENSION     "tap"
+#define SCOREFILE_EXTENSION    "sco"
+#endif
+
+#define NUM_LEVELCLASS_DESC    8
+static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
+{
+  "Tutorial Levels",
+  "Classic Originals",
+  "Contributions",
+  "Private Levels",
+  "Boulderdash",
+  "Emerald Mine",
+  "Supaplex",
+  "DX Boulderdash"
+};
+
+#define LEVELCOLOR(n)  (IS_LEVELCLASS_TUTORIAL(n) ?            FC_BLUE : \
+                        IS_LEVELCLASS_CLASSICS(n) ?            FC_RED : \
+                        IS_LEVELCLASS_BD(n) ?                  FC_GREEN : \
+                        IS_LEVELCLASS_EM(n) ?                  FC_YELLOW : \
+                        IS_LEVELCLASS_SP(n) ?                  FC_GREEN : \
+                        IS_LEVELCLASS_DX(n) ?                  FC_YELLOW : \
+                        IS_LEVELCLASS_CONTRIBUTION(n) ?        FC_GREEN : \
+                        IS_LEVELCLASS_USER(n) ?                FC_RED : \
+                        FC_BLUE)
+
+#define LEVELSORTING(n)        (IS_LEVELCLASS_TUTORIAL(n) ?            0 : \
+                        IS_LEVELCLASS_CLASSICS(n) ?            1 : \
+                        IS_LEVELCLASS_BD(n) ?                  2 : \
+                        IS_LEVELCLASS_EM(n) ?                  3 : \
+                        IS_LEVELCLASS_SP(n) ?                  4 : \
+                        IS_LEVELCLASS_DX(n) ?                  5 : \
+                        IS_LEVELCLASS_CONTRIBUTION(n) ?        6 : \
+                        IS_LEVELCLASS_USER(n) ?                7 : \
+                        9)
+
+#define ARTWORKCOLOR(n)        (IS_ARTWORKCLASS_CLASSICS(n) ?          FC_RED : \
+                        IS_ARTWORKCLASS_CONTRIBUTION(n) ?      FC_YELLOW : \
+                        IS_ARTWORKCLASS_LEVEL(n) ?             FC_GREEN : \
+                        IS_ARTWORKCLASS_USER(n) ?              FC_RED : \
+                        FC_BLUE)
+
+#define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ?       0 : \
+                        IS_ARTWORKCLASS_CONTRIBUTION(n) ?      1 : \
+                        IS_ARTWORKCLASS_LEVEL(n) ?             2 : \
+                        IS_ARTWORKCLASS_USER(n) ?              3 : \
+                        9)
+
+#define TOKEN_VALUE_POSITION           40
+#define TOKEN_COMMENT_POSITION         60
+
+#define MAX_COOKIE_LEN                 256
+
+#define ARTWORKINFO_FILENAME(type)     ((type) == TREE_TYPE_GRAPHICS_DIR ? \
+                                        GRAPHICSINFO_FILENAME :            \
+                                        (type) == TREE_TYPE_SOUNDS_DIR ?   \
+                                        SOUNDSINFO_FILENAME :              \
+                                        (type) == TREE_TYPE_MUSIC_DIR ?    \
+                                        MUSICINFO_FILENAME : "")
+
+#define ARTWORK_DIRECTORY(type)                ((type) == TREE_TYPE_GRAPHICS_DIR ? \
+                                        GRAPHICS_DIRECTORY :               \
+                                        (type) == TREE_TYPE_SOUNDS_DIR ?   \
+                                        SOUNDS_DIRECTORY :                 \
+                                        (type) == TREE_TYPE_MUSIC_DIR ?    \
+                                        MUSIC_DIRECTORY : "")
+
+#define OPTIONS_ARTWORK_DIRECTORY(type)        ((type) == TREE_TYPE_GRAPHICS_DIR ? \
+                                        options.graphics_directory :       \
+                                        (type) == TREE_TYPE_SOUNDS_DIR ?   \
+                                        options.sounds_directory :         \
+                                        (type) == TREE_TYPE_MUSIC_DIR ?    \
+                                        options.music_directory : "")
+
+
+/* ------------------------------------------------------------------------- */
+/* file functions                                                            */
+/* ------------------------------------------------------------------------- */
+
+static char *getLevelClassDescription(TreeInfo *ldi)
+{
+  int position = ldi->sort_priority / 100;
+
+  if (position >= 0 && position < NUM_LEVELCLASS_DESC)
+    return levelclass_desc[position];
+  else
+    return "Unknown Level Class";
+}
+
+static char *getUserLevelDir(char *level_subdir)
+{
+  static char *userlevel_dir = NULL;
+  char *data_dir = getUserDataDir();
+  char *userlevel_subdir = LEVELS_DIRECTORY;
+
+  if (userlevel_dir)
+    free(userlevel_dir);
+
+  if (level_subdir != NULL)
+    userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
+  else
+    userlevel_dir = getPath2(data_dir, userlevel_subdir);
+
+  return userlevel_dir;
+}
+
+static char *getTapeDir(char *level_subdir)
+{
+  static char *tape_dir = NULL;
+  char *data_dir = getUserDataDir();
+  char *tape_subdir = TAPES_DIRECTORY;
+
+  if (tape_dir)
+    free(tape_dir);
+
+  if (level_subdir != NULL)
+    tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
+  else
+    tape_dir = getPath2(data_dir, tape_subdir);
+
+  return tape_dir;
+}
+
+static char *getScoreDir(char *level_subdir)
+{
+  static char *score_dir = NULL;
+  char *data_dir = options.rw_base_directory;
+  char *score_subdir = SCORES_DIRECTORY;
+
+  if (score_dir)
+    free(score_dir);
+
+  if (level_subdir != NULL)
+    score_dir = getPath3(data_dir, score_subdir, level_subdir);
+  else
+    score_dir = getPath2(data_dir, score_subdir);
+
+  return score_dir;
+}
+
+static char *getLevelSetupDir(char *level_subdir)
+{
+  static char *levelsetup_dir = NULL;
+  char *data_dir = getUserDataDir();
+  char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
+
+  if (levelsetup_dir)
+    free(levelsetup_dir);
+
+  if (level_subdir != NULL)
+    levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
+  else
+    levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
+
+  return levelsetup_dir;
+}
+
+static char *getLevelDirFromTreeInfo(TreeInfo *node)
+{
+  static char *level_dir = NULL;
+
+  if (node == NULL)
+    return options.level_directory;
+
+  if (level_dir)
+    free(level_dir);
+
+  level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
+                       options.level_directory), node->fullpath);
+
+  return level_dir;
+}
+
+static char *getCurrentLevelDir()
+{
+  return getLevelDirFromTreeInfo(leveldir_current);
+}
+
+static char *getDefaultGraphicsDir(char *graphics_subdir)
+{
+  static char *graphics_dir = NULL;
+
+  if (graphics_subdir == NULL)
+    return options.graphics_directory;
+
+  if (graphics_dir)
+    free(graphics_dir);
+
+  graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
+
+  return graphics_dir;
+}
+
+static char *getDefaultSoundsDir(char *sounds_subdir)
+{
+  static char *sounds_dir = NULL;
+
+  if (sounds_subdir == NULL)
+    return options.sounds_directory;
+
+  if (sounds_dir)
+    free(sounds_dir);
+
+  sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
+
+  return sounds_dir;
+}
+
+static char *getDefaultMusicDir(char *music_subdir)
+{
+  static char *music_dir = NULL;
+
+  if (music_subdir == NULL)
+    return options.music_directory;
+
+  if (music_dir)
+    free(music_dir);
+
+  music_dir = getPath2(options.music_directory, music_subdir);
+
+  return music_dir;
+}
+
+static char *getUserGraphicsDir()
+{
+  static char *usergraphics_dir = NULL;
+
+  if (usergraphics_dir == NULL)
+    usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
+
+  return usergraphics_dir;
+}
+
+static char *getUserSoundsDir()
+{
+  static char *usersounds_dir = NULL;
+
+  if (usersounds_dir == NULL)
+    usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
+
+  return usersounds_dir;
+}
+
+static char *getUserMusicDir()
+{
+  static char *usermusic_dir = NULL;
+
+  if (usermusic_dir == NULL)
+    usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
+
+  return usermusic_dir;
+}
+
+char *getLevelFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
+
+  if (filename != NULL)
+    free(filename);
+
+  sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
+  filename = getPath2(getCurrentLevelDir(), basename);
+
+  return filename;
+}
+
+char *getTapeFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
+
+  if (filename != NULL)
+    free(filename);
+
+  sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
+  filename = getPath2(getTapeDir(leveldir_current->filename), basename);
+
+  return filename;
+}
+
+char *getScoreFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
+
+  if (filename != NULL)
+    free(filename);
+
+  sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
+  filename = getPath2(getScoreDir(leveldir_current->filename), basename);
+
+  return filename;
+}
+
+char *getSetupFilename()
+{
+  static char *filename = NULL;
+
+  if (filename != NULL)
+    free(filename);
+
+  filename = getPath2(getSetupDir(), SETUP_FILENAME);
+
+  return filename;
+}
+
+static char *getSetupArtworkDir(TreeInfo *ti)
+{
+  static char *artwork_dir = NULL;
+
+  if (artwork_dir != NULL)
+    free(artwork_dir);
+
+  artwork_dir = getPath2(ti->basepath, ti->fullpath);
+
+  return artwork_dir;
+}
+
+static char *getCorrectedImageBasename(char *basename)
+{
+  char *result = basename;
+
+#if defined(PLATFORM_MSDOS)
+  if (program.filename_prefix != NULL)
+  {
+    int prefix_len = strlen(program.filename_prefix);
+
+    if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
+      result = &basename[prefix_len];
+  }
+#endif
+
+  return result;
+}
+
+static boolean fileExists(char *filename)
+{
+#if 0
+  printf("checking file '%s'\n", filename);
+#endif
+
+  return (access(filename, F_OK) == 0);
+}
+
+char *getCustomImageFilename(char *basename)
+{
+  static char *filename = NULL;
+
+  if (filename != NULL)
+    free(filename);
+
+  basename = getCorrectedImageBasename(basename);
+
+  if (!setup.override_level_graphics)
+  {
+    /* 1st try: look for special artwork in current level series directory */
+    filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
+    if (fileExists(filename))
+      return filename;
+  }
+
+  /* 2nd try: look for special artwork in configured artwork directory */
+  filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
+  if (fileExists(filename))
+    return filename;
+
+  /* 3rd try: look for default artwork in new default artwork directory */
+  filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
+  if (fileExists(filename))
+    return filename;
+
+  /* 4th try: look for default artwork in old default artwork directory */
+  filename = getPath2(options.graphics_directory, basename);
+  if (fileExists(filename))
+    return filename;
+
+  return NULL;         /* cannot find specified artwork file anywhere */
+}
+
+char *getCustomSoundFilename(char *basename)
+{
+  static char *filename = NULL;
+
+  if (filename != NULL)
+    free(filename);
+
+  if (!setup.override_level_sounds)
+  {
+    /* 1st try: look for special artwork in current level series directory */
+    filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
+    if (fileExists(filename))
+      return filename;
+  }
+
+  /* 2nd try: look for special artwork in configured artwork directory */
+  filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
+  if (fileExists(filename))
+    return filename;
+
+  /* 3rd try: look for default artwork in new default artwork directory */
+  filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
+  if (fileExists(filename))
+    return filename;
+
+  /* 4th try: look for default artwork in old default artwork directory */
+  filename = getPath2(options.sounds_directory, basename);
+  if (fileExists(filename))
+    return filename;
+
+  return NULL;         /* cannot find specified artwork file anywhere */
+}
+
+char *getCustomSoundConfigFilename()
+{
+  return getCustomSoundFilename(SOUNDSINFO_FILENAME);
+}
+
+char *getCustomMusicDirectory(void)
+{
+  static char *directory = NULL;
+
+  if (directory != NULL)
+    free(directory);
+
+  if (!setup.override_level_music)
+  {
+    /* 1st try: look for special artwork in current level series directory */
+    directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
+    if (fileExists(directory))
+      return directory;
+  }
+
+  /* 2nd try: look for special artwork in configured artwork directory */
+  directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
+  if (fileExists(directory))
+    return directory;
+
+  /* 3rd try: look for default artwork in new default artwork directory */
+  directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
+  if (fileExists(directory))
+    return directory;
+
+  /* 4th try: look for default artwork in old default artwork directory */
+  directory = getStringCopy(options.music_directory);
+  if (fileExists(directory))
+    return directory;
+
+  return NULL;         /* cannot find specified artwork file anywhere */
+}
+
+void InitTapeDirectory(char *level_subdir)
+{
+  createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
+  createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
+  createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
+}
+
+void InitScoreDirectory(char *level_subdir)
+{
+  createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
+  createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
+}
+
+static void SaveUserLevelInfo();
+
+void InitUserLevelDirectory(char *level_subdir)
+{
+  if (access(getUserLevelDir(level_subdir), F_OK) != 0)
+  {
+    createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
+    createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
+    createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
+
+    SaveUserLevelInfo();
+  }
+}
+
+void InitLevelSetupDirectory(char *level_subdir)
+{
+  createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
+  createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
+  createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* some functions to handle lists of level directories                       */
+/* ------------------------------------------------------------------------- */
+
+TreeInfo *newTreeInfo()
+{
+  return checked_calloc(sizeof(TreeInfo));
+}
+
+void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
+{
+  node_new->next = *node_first;
+  *node_first = node_new;
+}
+
+int numTreeInfo(TreeInfo *node)
+{
+  int num = 0;
+
+  while (node)
+  {
+    num++;
+    node = node->next;
+  }
+
+  return num;
+}
+
+boolean validLevelSeries(TreeInfo *node)
+{
+  return (node != NULL && !node->node_group && !node->parent_link);
+}
+
+TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
+{
+  if (node == NULL)
+    return NULL;
+
+  if (node->node_group)                /* enter level group (step down into tree) */
+    return getFirstValidTreeInfoEntry(node->node_group);
+  else if (node->parent_link)  /* skip start entry of level group */
+  {
+    if (node->next)            /* get first real level series entry */
+      return getFirstValidTreeInfoEntry(node->next);
+    else                       /* leave empty level group and go on */
+      return getFirstValidTreeInfoEntry(node->node_parent->next);
+  }
+  else                         /* this seems to be a regular level series */
+    return node;
+}
+
+TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
+{
+  if (node == NULL)
+    return NULL;
+
+  if (node->node_parent == NULL)               /* top level group */
+    return *node->node_top;
+  else                                         /* sub level group */
+    return node->node_parent->node_group;
+}
+
+int numTreeInfoInGroup(TreeInfo *node)
+{
+  return numTreeInfo(getTreeInfoFirstGroupEntry(node));
+}
+
+int posTreeInfo(TreeInfo *node)
+{
+  TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
+  int pos = 0;
+
+  while (node_cmp)
+  {
+    if (node_cmp == node)
+      return pos;
+
+    pos++;
+    node_cmp = node_cmp->next;
+  }
+
+  return 0;
+}
+
+TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
+{
+  TreeInfo *node_default = node;
+  int pos_cmp = 0;
+
+  while (node)
+  {
+    if (pos_cmp == pos)
+      return node;
+
+    pos_cmp++;
+    node = node->next;
+  }
+
+  return node_default;
+}
+
+TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
+{
+  if (filename == NULL)
+    return NULL;
+
+  while (node)
+  {
+    if (node->node_group)
+    {
+      TreeInfo *node_group;
+
+      node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
+
+      if (node_group)
+       return node_group;
+    }
+    else if (!node->parent_link)
+    {
+      if (strcmp(filename, node->filename) == 0)
+       return node;
+
+      /* special case when looking for level series artwork:
+        node->name_short contains level series filename */
+      if (strcmp(node->filename, ".") == 0 &&
+         strcmp(filename, node->name_short) == 0)
+       return node;
+    }
+
+    node = node->next;
+  }
+
+  return NULL;
+}
+
+TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
+{
+  return getTreeInfoFromFilenameExt(ti, filename);
+}
+
+void dumpTreeInfo(TreeInfo *node, int depth)
+{
+  int i;
+
+  printf("Dumping TreeInfo:\n");
+
+  while (node)
+  {
+    for (i=0; i<(depth + 1) * 3; i++)
+      printf(" ");
+
+    printf("filename == '%s' (%s) [%s] (%d)\n",
+          node->filename, node->name, node->name_short, node->sort_priority);
+
+    if (node->node_group != NULL)
+      dumpTreeInfo(node->node_group, depth + 1);
+
+    node = node->next;
+  }
+}
+
+void sortTreeInfo(TreeInfo **node_first,
+                 int (*compare_function)(const void *, const void *))
+{
+  int num_nodes = numTreeInfo(*node_first);
+  TreeInfo **sort_array;
+  TreeInfo *node = *node_first;
+  int i = 0;
+
+  if (num_nodes == 0)
+    return;
+
+  /* allocate array for sorting structure pointers */
+  sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
+
+  /* writing structure pointers to sorting array */
+  while (i < num_nodes && node)                /* double boundary check... */
+  {
+    sort_array[i] = node;
+
+    i++;
+    node = node->next;
+  }
+
+  /* sorting the structure pointers in the sorting array */
+  qsort(sort_array, num_nodes, sizeof(TreeInfo *),
+       compare_function);
+
+  /* update the linkage of list elements with the sorted node array */
+  for (i=0; i<num_nodes - 1; i++)
+    sort_array[i]->next = sort_array[i + 1];
+  sort_array[num_nodes - 1]->next = NULL;
+
+  /* update the linkage of the main list anchor pointer */
+  *node_first = sort_array[0];
+
+  free(sort_array);
+
+  /* now recursively sort the level group structures */
+  node = *node_first;
+  while (node)
+  {
+    if (node->node_group != NULL)
+      sortTreeInfo(&node->node_group, compare_function);
+
+    node = node->next;
+  }
+}
+
+
+/* ========================================================================= */
+/* some stuff from "files.c"                                                 */
+/* ========================================================================= */
+
+#if defined(PLATFORM_WIN32)
+#ifndef S_IRGRP
+#define S_IRGRP S_IRUSR
+#endif
+#ifndef S_IROTH
+#define S_IROTH S_IRUSR
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP S_IWUSR
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH S_IWUSR
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP S_IXUSR
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH S_IXUSR
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+#endif
+#ifndef S_ISGID
+#define S_ISGID 0
+#endif
+#endif /* PLATFORM_WIN32 */
+
+/* file permissions for newly written files */
+#define MODE_R_ALL             (S_IRUSR | S_IRGRP | S_IROTH)
+#define MODE_W_ALL             (S_IWUSR | S_IWGRP | S_IWOTH)
+#define MODE_X_ALL             (S_IXUSR | S_IXGRP | S_IXOTH)
+
+#define MODE_W_PRIVATE         (S_IWUSR)
+#define MODE_W_PUBLIC          (S_IWUSR | S_IWGRP)
+#define MODE_W_PUBLIC_DIR      (S_IWUSR | S_IWGRP | S_ISGID)
+
+#define DIR_PERMS_PRIVATE      (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
+#define DIR_PERMS_PUBLIC       (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
+
+#define FILE_PERMS_PRIVATE     (MODE_R_ALL | MODE_W_PRIVATE)
+#define FILE_PERMS_PUBLIC      (MODE_R_ALL | MODE_W_PUBLIC)
+
+char *getUserDataDir(void)
+{
+  static char *userdata_dir = NULL;
+
+  if (!userdata_dir)
+  {
+    char *home_dir = getHomeDir();
+    char *data_dir = program.userdata_directory;
+
+    userdata_dir = getPath2(home_dir, data_dir);
+  }
+
+  return userdata_dir;
+}
+
+char *getSetupDir()
+{
+  return getUserDataDir();
+}
+
+static mode_t posix_umask(mode_t mask)
+{
+#if defined(PLATFORM_UNIX)
+  return umask(mask);
+#else
+  return 0;
+#endif
+}
+
+static int posix_mkdir(const char *pathname, mode_t mode)
+{
+#if defined(PLATFORM_WIN32)
+  return mkdir(pathname);
+#else
+  return mkdir(pathname, mode);
+#endif
+}
+
+void createDirectory(char *dir, char *text, int permission_class)
+{
+  /* leave "other" permissions in umask untouched, but ensure group parts
+     of USERDATA_DIR_MODE are not masked */
+  mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
+                    DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
+  mode_t normal_umask = posix_umask(0);
+  mode_t group_umask = ~(dir_mode & S_IRWXG);
+  posix_umask(normal_umask & group_umask);
+
+  if (access(dir, F_OK) != 0)
+    if (posix_mkdir(dir, dir_mode) != 0)
+      Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
+
+  posix_umask(normal_umask);           /* reset normal umask */
+}
+
+void InitUserDataDirectory()
+{
+  createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
+}
+
+void SetFilePermissions(char *filename, int permission_class)
+{
+  chmod(filename, (permission_class == PERMS_PRIVATE ?
+                  FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
+}
+
+char *getCookie(char *file_type)
+{
+  static char cookie[MAX_COOKIE_LEN + 1];
+
+  if (strlen(program.cookie_prefix) + 1 +
+      strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
+    return "[COOKIE ERROR]";   /* should never happen */
+
+  sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
+         program.cookie_prefix, file_type,
+         program.version_major, program.version_minor);
+
+  return cookie;
+}
+
+int getFileVersionFromCookieString(const char *cookie)
+{
+  const char *ptr_cookie1, *ptr_cookie2;
+  const char *pattern1 = "_FILE_VERSION_";
+  const char *pattern2 = "?.?";
+  const int len_cookie = strlen(cookie);
+  const int len_pattern1 = strlen(pattern1);
+  const int len_pattern2 = strlen(pattern2);
+  const int len_pattern = len_pattern1 + len_pattern2;
+  int version_major, version_minor;
+
+  if (len_cookie <= len_pattern)
+    return -1;
+
+  ptr_cookie1 = &cookie[len_cookie - len_pattern];
+  ptr_cookie2 = &cookie[len_cookie - len_pattern2];
+
+  if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
+    return -1;
+
+  if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
+      ptr_cookie2[1] != '.' ||
+      ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
+    return -1;
+
+  version_major = ptr_cookie2[0] - '0';
+  version_minor = ptr_cookie2[2] - '0';
+
+  return VERSION_IDENT(version_major, version_minor, 0);
+}
+
+boolean checkCookieString(const char *cookie, const char *template)
+{
+  const char *pattern = "_FILE_VERSION_?.?";
+  const int len_cookie = strlen(cookie);
+  const int len_template = strlen(template);
+  const int len_pattern = strlen(pattern);
+
+  if (len_cookie != len_template)
+    return FALSE;
+
+  if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* ------------------------------------------------------------------------- */
+/* setup file list handling functions                                        */
+/* ------------------------------------------------------------------------- */
+
+int get_string_integer_value(char *s)
+{
+  static char *number_text[][3] =
+  {
+    { "0", "zero", "null", },
+    { "1", "one", "first" },
+    { "2", "two", "second" },
+    { "3", "three", "third" },
+    { "4", "four", "fourth" },
+    { "5", "five", "fifth" },
+    { "6", "six", "sixth" },
+    { "7", "seven", "seventh" },
+    { "8", "eight", "eighth" },
+    { "9", "nine", "ninth" },
+    { "10", "ten", "tenth" },
+    { "11", "eleven", "eleventh" },
+    { "12", "twelve", "twelfth" },
+  };
+
+  int i, j;
+  char *s_lower = getStringToLower(s);
+  int result = -1;
+
+  for (i=0; i<13; i++)
+    for (j=0; j<3; j++)
+      if (strcmp(s_lower, number_text[i][j]) == 0)
+       result = i;
+
+  if (result == -1)
+    result = atoi(s);
+
+  free(s_lower);
+
+  return result;
+}
+
+boolean get_string_boolean_value(char *s)
+{
+  char *s_lower = getStringToLower(s);
+  boolean result = FALSE;
+
+  if (strcmp(s_lower, "true") == 0 ||
+      strcmp(s_lower, "yes") == 0 ||
+      strcmp(s_lower, "on") == 0 ||
+      get_string_integer_value(s) == 1)
+    result = TRUE;
+
+  free(s_lower);
+
+  return result;
+}
+
+char *getFormattedSetupEntry(char *token, char *value)
+{
+  int i;
+  static char entry[MAX_LINE_LEN];
+
+  /* start with the token and some spaces to format output line */
+  sprintf(entry, "%s:", token);
+  for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
+    strcat(entry, " ");
+
+  /* continue with the token's value */
+  strcat(entry, value);
+
+  return entry;
+}
+
+void freeSetupFileList(struct SetupFileList *setup_file_list)
+{
+  if (!setup_file_list)
+    return;
+
+  if (setup_file_list->token)
+    free(setup_file_list->token);
+  if (setup_file_list->value)
+    free(setup_file_list->value);
+  if (setup_file_list->next)
+    freeSetupFileList(setup_file_list->next);
+  free(setup_file_list);
+}
+
+static struct SetupFileList *newSetupFileList(char *token, char *value)
+{
+  struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
+
+  new->token = checked_malloc(strlen(token) + 1);
+  strcpy(new->token, token);
+
+  new->value = checked_malloc(strlen(value) + 1);
+  strcpy(new->value, value);
+
+  new->next = NULL;
+
+  return new;
+}
+
+char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
+{
+  if (!setup_file_list)
+    return NULL;
+
+  if (strcmp(setup_file_list->token, token) == 0)
+    return setup_file_list->value;
+  else
+    return getTokenValue(setup_file_list->next, token);
+}
+
+static void setTokenValue(struct SetupFileList *setup_file_list,
+                         char *token, char *value)
+{
+  if (!setup_file_list)
+    return;
+
+  if (strcmp(setup_file_list->token, token) == 0)
+  {
+    free(setup_file_list->value);
+    setup_file_list->value = checked_malloc(strlen(value) + 1);
+    strcpy(setup_file_list->value, value);
+  }
+  else if (setup_file_list->next == NULL)
+    setup_file_list->next = newSetupFileList(token, value);
+  else
+    setTokenValue(setup_file_list->next, token, value);
+}
+
+#ifdef DEBUG
+static void printSetupFileList(struct SetupFileList *setup_file_list)
+{
+  if (!setup_file_list)
+    return;
+
+  printf("token: '%s'\n", setup_file_list->token);
+  printf("value: '%s'\n", setup_file_list->value);
+
+  printSetupFileList(setup_file_list->next);
+}
+#endif
+
+struct SetupFileList *loadSetupFileList(char *filename)
+{
+  int line_len;
+  char line[MAX_LINE_LEN];
+  char *token, *value, *line_ptr;
+  struct SetupFileList *setup_file_list = newSetupFileList("", "");
+  struct SetupFileList *first_valid_list_entry;
+
+  FILE *file;
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    Error(ERR_WARN, "cannot open configuration file '%s'", filename);
+    return NULL;
+  }
+
+  while(!feof(file))
+  {
+    /* read next line of input file */
+    if (!fgets(line, MAX_LINE_LEN, file))
+      break;
+
+    /* cut trailing comment or whitespace from input line */
+    for (line_ptr = line; *line_ptr; line_ptr++)
+    {
+      if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
+      {
+       *line_ptr = '\0';
+       break;
+      }
+    }
+
+    /* cut trailing whitespaces from input line */
+    for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
+      if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
+       *line_ptr = '\0';
+
+    /* ignore empty lines */
+    if (*line == '\0')
+      continue;
+
+    line_len = strlen(line);
+
+    /* cut leading whitespaces from token */
+    for (token = line; *token; token++)
+      if (*token != ' ' && *token != '\t')
+       break;
+
+    /* find end of token */
+    for (line_ptr = token; *line_ptr; line_ptr++)
+    {
+      if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
+      {
+       *line_ptr = '\0';
+       break;
+      }
+    }
+
+    if (line_ptr < line + line_len)
+      value = line_ptr + 1;
+    else
+      value = "\0";
+
+    /* cut leading whitespaces from value */
+    for (; *value; value++)
+      if (*value != ' ' && *value != '\t')
+       break;
+
+    if (*token && *value)
+      setTokenValue(setup_file_list, token, value);
+  }
+
+  fclose(file);
+
+  first_valid_list_entry = setup_file_list->next;
+
+  /* free empty list header */
+  setup_file_list->next = NULL;
+  freeSetupFileList(setup_file_list);
+
+  if (first_valid_list_entry == NULL)
+    Error(ERR_WARN, "configuration file '%s' is empty", filename);
+
+  return first_valid_list_entry;
+}
+
+void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
+                                 char *identifier)
+{
+  if (!setup_file_list)
+    return;
+
+  if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
+  {
+    if (!checkCookieString(setup_file_list->value, identifier))
+    {
+      Error(ERR_WARN, "configuration file has wrong file identifier");
+      return;
+    }
+    else
+      return;
+  }
+
+  if (setup_file_list->next)
+    checkSetupFileListIdentifier(setup_file_list->next, identifier);
+  else
+  {
+    Error(ERR_WARN, "configuration file has no file identifier");
+    return;
+  }
+}
+
+
+/* ========================================================================= */
+/* setup file stuff                                                          */
+/* ========================================================================= */
+
+#define TOKEN_STR_LAST_LEVEL_SERIES    "last_level_series"
+#define TOKEN_STR_LAST_PLAYED_LEVEL    "last_played_level"
+#define TOKEN_STR_HANDICAP_LEVEL       "handicap_level"
+
+/* level directory info */
+#define LEVELINFO_TOKEN_NAME           0
+#define LEVELINFO_TOKEN_NAME_SHORT     1
+#define LEVELINFO_TOKEN_NAME_SORTING   2
+#define LEVELINFO_TOKEN_AUTHOR         3
+#define LEVELINFO_TOKEN_IMPORTED_FROM  4
+#define LEVELINFO_TOKEN_LEVELS         5
+#define LEVELINFO_TOKEN_FIRST_LEVEL    6
+#define LEVELINFO_TOKEN_SORT_PRIORITY  7
+#define LEVELINFO_TOKEN_LEVEL_GROUP    8
+#define LEVELINFO_TOKEN_READONLY       9
+
+#define NUM_LEVELINFO_TOKENS           10
+
+static LevelDirTree ldi;
+
+static struct TokenInfo levelinfo_tokens[] =
+{
+  /* level directory info */
+  { TYPE_STRING,  &ldi.name,           "name"          },
+  { TYPE_STRING,  &ldi.name_short,     "name_short"    },
+  { TYPE_STRING,  &ldi.name_sorting,   "name_sorting"  },
+  { TYPE_STRING,  &ldi.author,         "author"        },
+  { TYPE_STRING,  &ldi.imported_from,  "imported_from" },
+  { TYPE_INTEGER, &ldi.levels,         "levels"        },
+  { TYPE_INTEGER, &ldi.first_level,    "first_level"   },
+  { TYPE_INTEGER, &ldi.sort_priority,  "sort_priority" },
+  { TYPE_BOOLEAN, &ldi.level_group,    "level_group"   },
+  { TYPE_BOOLEAN, &ldi.readonly,       "readonly"      }
+};
+
+static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
+{
+  ldi->type = type;
+
+  ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
+                  ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
+                  ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
+                  ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
+                  NULL);
+
+  ldi->node_parent = NULL;
+  ldi->node_group = NULL;
+  ldi->next = NULL;
+
+  ldi->cl_first = -1;
+  ldi->cl_cursor = -1;
+
+  ldi->filename = NULL;
+  ldi->fullpath = NULL;
+  ldi->basepath = NULL;
+  ldi->name = getStringCopy(ANONYMOUS_NAME);
+  ldi->name_short = NULL;
+  ldi->name_sorting = NULL;
+  ldi->author = getStringCopy(ANONYMOUS_NAME);
+
+  ldi->sort_priority = LEVELCLASS_UNDEFINED;   /* default: least priority */
+  ldi->parent_link = FALSE;
+  ldi->user_defined = FALSE;
+  ldi->color = 0;
+  ldi->class_desc = NULL;
+
+  if (ldi->type == TREE_TYPE_LEVEL_DIR)
+  {
+    ldi->imported_from = NULL;
+    ldi->levels = 0;
+    ldi->first_level = 0;
+    ldi->last_level = 0;
+    ldi->level_group = FALSE;
+    ldi->handicap_level = 0;
+    ldi->readonly = TRUE;
+  }
+}
+
+static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
+{
+  if (parent == NULL)
+  {
+    Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
+
+    setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
+    return;
+  }
+
+  /* first copy all values from the parent structure ... */
+  *ldi = *parent;
+
+  /* ... then set all fields to default that cannot be inherited from parent.
+     This is especially important for all those fields that can be set from
+     the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
+     calls 'free()' for all already set token values which requires that no
+     other structure's pointer may point to them!
+  */
+
+  ldi->filename = NULL;
+  ldi->fullpath = NULL;
+  ldi->basepath = NULL;
+  ldi->name = getStringCopy(ANONYMOUS_NAME);
+  ldi->name_short = NULL;
+  ldi->name_sorting = NULL;
+  ldi->author = getStringCopy(parent->author);
+  ldi->imported_from = getStringCopy(parent->imported_from);
+
+  ldi->level_group = FALSE;
+  ldi->parent_link = FALSE;
+
+  ldi->node_top = parent->node_top;
+  ldi->node_parent = parent;
+  ldi->node_group = NULL;
+  ldi->next = NULL;
+}
+
+void setSetupInfo(struct TokenInfo *token_info,
+                 int token_nr, char *token_value)
+{
+  int token_type = token_info[token_nr].type;
+  void *setup_value = token_info[token_nr].value;
+
+  if (token_value == NULL)
+    return;
+
+  /* set setup field to corresponding token value */
+  switch (token_type)
+  {
+    case TYPE_BOOLEAN:
+    case TYPE_SWITCH:
+      *(boolean *)setup_value = get_string_boolean_value(token_value);
+      break;
+
+    case TYPE_KEY:
+      *(Key *)setup_value = getKeyFromKeyName(token_value);
+      break;
+
+    case TYPE_KEY_X11:
+      *(Key *)setup_value = getKeyFromX11KeyName(token_value);
+      break;
+
+    case TYPE_INTEGER:
+      *(int *)setup_value = get_string_integer_value(token_value);
+      break;
+
+    case TYPE_STRING:
+      if (*(char **)setup_value != NULL)
+       free(*(char **)setup_value);
+      *(char **)setup_value = getStringCopy(token_value);
+      break;
+
+    default:
+      break;
+  }
+}
+
+static int compareTreeInfoEntries(const void *object1, const void *object2)
+{
+  const TreeInfo *entry1 = *((TreeInfo **)object1);
+  const TreeInfo *entry2 = *((TreeInfo **)object2);
+  int class_sorting1, class_sorting2;
+  int compare_result;
+
+  if (entry1->type == TREE_TYPE_LEVEL_DIR)
+  {
+    class_sorting1 = LEVELSORTING(entry1);
+    class_sorting2 = LEVELSORTING(entry2);
+  }
+  else
+  {
+    class_sorting1 = ARTWORKSORTING(entry1);
+    class_sorting2 = ARTWORKSORTING(entry2);
+  }
+
+  if (entry1->parent_link || entry2->parent_link)
+    compare_result = (entry1->parent_link ? -1 : +1);
+  else if (entry1->sort_priority == entry2->sort_priority)
+  {
+    char *name1 = getStringToLower(entry1->name_sorting);
+    char *name2 = getStringToLower(entry2->name_sorting);
+
+    compare_result = strcmp(name1, name2);
+
+    free(name1);
+    free(name2);
+  }
+  else if (class_sorting1 == class_sorting2)
+    compare_result = entry1->sort_priority - entry2->sort_priority;
+  else
+    compare_result = class_sorting1 - class_sorting2;
+
+  return compare_result;
+}
+
+static void createParentTreeInfoNode(TreeInfo *node_parent)
+{
+  TreeInfo *ti_new;
+
+  if (node_parent == NULL)
+    return;
+
+  ti_new = newTreeInfo();
+  setTreeInfoToDefaults(ti_new, node_parent->type);
+
+  ti_new->node_parent = node_parent;
+  ti_new->parent_link = TRUE;
+
+  ti_new->name = ".. (parent directory)";
+  ti_new->name_short = getStringCopy(ti_new->name);
+  ti_new->name_sorting = getStringCopy(ti_new->name);
+
+  ti_new->filename = "..";
+  ti_new->fullpath = getStringCopy(node_parent->fullpath);
+
+  ti_new->sort_priority = node_parent->sort_priority;
+  ti_new->class_desc = getLevelClassDescription(ti_new);
+
+  pushTreeInfo(&node_parent->node_group, ti_new);
+}
+
+/* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
+static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
+
+static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
+                                         TreeInfo *node_parent,
+                                         char *level_directory,
+                                         char *directory_name)
+{
+  char *directory_path = getPath2(level_directory, directory_name);
+  char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
+  struct SetupFileList *setup_file_list = loadSetupFileList(filename);
+  LevelDirTree *leveldir_new = NULL;
+  int i;
+
+  if (setup_file_list == NULL)
+  {
+    Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
+
+    free(directory_path);
+    free(filename);
+
+    return FALSE;
+  }
+
+  leveldir_new = newTreeInfo();
+
+  if (node_parent)
+    setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
+  else
+    setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
+
+  leveldir_new->filename = getStringCopy(directory_name);
+
+  checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
+
+  /* set all structure fields according to the token/value pairs */
+  ldi = *leveldir_new;
+  for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
+    setSetupInfo(levelinfo_tokens, i,
+                getTokenValue(setup_file_list, levelinfo_tokens[i].text));
+  *leveldir_new = ldi;
+
+  if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
+  {
+    free(leveldir_new->name);
+    leveldir_new->name = getStringCopy(leveldir_new->filename);
+  }
+
+  DrawInitText(leveldir_new->name, 150, FC_YELLOW);
+
+  if (leveldir_new->name_short == NULL)
+    leveldir_new->name_short = getStringCopy(leveldir_new->name);
+
+  if (leveldir_new->name_sorting == NULL)
+    leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
+
+  if (node_parent == NULL)             /* top level group */
+  {
+    leveldir_new->basepath = level_directory;
+    leveldir_new->fullpath = leveldir_new->filename;
+  }
+  else                                 /* sub level group */
+  {
+    leveldir_new->basepath = node_parent->basepath;
+    leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
+  }
+
+  if (leveldir_new->levels < 1)
+    leveldir_new->levels = 1;
+
+  leveldir_new->last_level =
+    leveldir_new->first_level + leveldir_new->levels - 1;
+
+  leveldir_new->user_defined =
+    (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
+
+  leveldir_new->color = LEVELCOLOR(leveldir_new);
+  leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
+
+  leveldir_new->handicap_level =       /* set handicap to default value */
+    (leveldir_new->user_defined ?
+     leveldir_new->last_level :
+     leveldir_new->first_level);
+
+  pushTreeInfo(node_first, leveldir_new);
+
+  freeSetupFileList(setup_file_list);
+
+  if (leveldir_new->level_group)
+  {
+    /* create node to link back to current level directory */
+    createParentTreeInfoNode(leveldir_new);
+
+    /* step into sub-directory and look for more level series */
+    LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
+                             leveldir_new, directory_path);
+  }
+
+  free(directory_path);
+  free(filename);
+
+  return TRUE;
+}
+
+static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
+                                     TreeInfo *node_parent,
+                                     char *level_directory)
+{
+  DIR *dir;
+  struct dirent *dir_entry;
+  boolean valid_entry_found = FALSE;
+
+  if ((dir = opendir(level_directory)) == NULL)
+  {
+    Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+    return;
+  }
+
+  while ((dir_entry = readdir(dir)) != NULL)   /* loop until last dir entry */
+  {
+    struct stat file_status;
+    char *directory_name = dir_entry->d_name;
+    char *directory_path = getPath2(level_directory, directory_name);
+
+    /* skip entries for current and parent directory */
+    if (strcmp(directory_name, ".")  == 0 ||
+       strcmp(directory_name, "..") == 0)
+    {
+      free(directory_path);
+      continue;
+    }
+
+    /* find out if directory entry is itself a directory */
+    if (stat(directory_path, &file_status) != 0 ||     /* cannot stat file */
+       (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
+    {
+      free(directory_path);
+      continue;
+    }
+
+    free(directory_path);
+
+    if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
+       strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
+       strcmp(directory_name, MUSIC_DIRECTORY) == 0)
+      continue;
+
+    valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
+                                                   level_directory,
+                                                   directory_name);
+  }
+
+  closedir(dir);
+
+  if (!valid_entry_found)
+  {
+    /* check if this directory directly contains a file "levelinfo.conf" */
+    valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
+                                                   level_directory, ".");
+  }
+
+  if (!valid_entry_found)
+    Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
+         level_directory);
+}
+
+void LoadLevelInfo()
+{
+  InitUserLevelDirectory(getLoginName());
+
+  DrawInitText("Loading level series:", 120, FC_GREEN);
+
+  LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
+  LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
+
+  /* before sorting, the first entries will be from the user directory */
+  leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
+
+  if (leveldir_first == NULL)
+    Error(ERR_EXIT, "cannot find any valid level series in any directory");
+
+  sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
+
+#if 0
+  dumpTreeInfo(leveldir_first, 0);
+#endif
+}
+
+static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
+                                             TreeInfo *node_parent,
+                                             char *base_directory,
+                                             char *directory_name, int type)
+{
+  char *directory_path = getPath2(base_directory, directory_name);
+  char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
+  struct SetupFileList *setup_file_list = NULL;
+  TreeInfo *artwork_new = NULL;
+  int i;
+
+  if (access(filename, F_OK) == 0)             /* file exists */
+    setup_file_list = loadSetupFileList(filename);
+
+  if (setup_file_list == NULL) /* no config file -- look for artwork files */
+  {
+    DIR *dir;
+    struct dirent *dir_entry;
+    boolean valid_file_found = FALSE;
+
+    if ((dir = opendir(directory_path)) != NULL)
+    {
+      while ((dir_entry = readdir(dir)) != NULL)
+      {
+       char *entry_name = dir_entry->d_name;
+
+       if (FileIsArtworkType(entry_name, type))
+       {
+         valid_file_found = TRUE;
+         break;
+       }
+      }
+
+      closedir(dir);
+    }
+
+    if (!valid_file_found)
+    {
+      if (strcmp(directory_name, ".") != 0)
+       Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
+
+      free(directory_path);
+      free(filename);
+
+      return FALSE;
+    }
+  }
+
+  artwork_new = newTreeInfo();
+
+  if (node_parent)
+    setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
+  else
+    setTreeInfoToDefaults(artwork_new, type);
+
+  artwork_new->filename = getStringCopy(directory_name);
+
+  if (setup_file_list) /* (before defining ".color" and ".class_desc") */
+  {
+#if 0
+    checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
+#endif
+
+    /* set all structure fields according to the token/value pairs */
+    ldi = *artwork_new;
+    for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
+      setSetupInfo(levelinfo_tokens, i,
+                  getTokenValue(setup_file_list, levelinfo_tokens[i].text));
+    *artwork_new = ldi;
+
+    if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
+    {
+      free(artwork_new->name);
+      artwork_new->name = getStringCopy(artwork_new->filename);
+    }
+
+#if 0
+    DrawInitText(artwork_new->name, 150, FC_YELLOW);
+#endif
+
+    if (artwork_new->name_short == NULL)
+      artwork_new->name_short = getStringCopy(artwork_new->name);
+
+    if (artwork_new->name_sorting == NULL)
+      artwork_new->name_sorting = getStringCopy(artwork_new->name);
+  }
+
+  if (node_parent == NULL)             /* top level group */
+  {
+    artwork_new->basepath = getStringCopy(base_directory);
+    artwork_new->fullpath = getStringCopy(artwork_new->filename);
+  }
+  else                                 /* sub level group */
+  {
+    artwork_new->basepath = getStringCopy(node_parent->basepath);
+    artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
+  }
+
+  artwork_new->user_defined =
+    (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
+
+  /* (may use ".sort_priority" from "setup_file_list" above) */
+  artwork_new->color = ARTWORKCOLOR(artwork_new);
+  artwork_new->class_desc = getLevelClassDescription(artwork_new);
+
+  if (setup_file_list == NULL) /* (after determining ".user_defined") */
+  {
+    if (artwork_new->name != NULL)
+      free(artwork_new->name);
+
+    if (strcmp(artwork_new->filename, ".") == 0)
+    {
+      if (artwork_new->user_defined)
+      {
+       artwork_new->name = getStringCopy("private");
+       artwork_new->sort_priority = ARTWORKCLASS_USER;
+      }
+      else
+      {
+       artwork_new->name = getStringCopy("classic");
+       artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
+      }
+
+      artwork_new->color = ARTWORKCOLOR(artwork_new);
+      artwork_new->class_desc = getLevelClassDescription(artwork_new);
+    }
+    else
+      artwork_new->name = getStringCopy(artwork_new->filename);
+
+    artwork_new->name_short = getStringCopy(artwork_new->name);
+    artwork_new->name_sorting = getStringCopy(artwork_new->name);
+  }
+
+  DrawInitText(artwork_new->name, 150, FC_YELLOW);
+
+  pushTreeInfo(node_first, artwork_new);
+
+  freeSetupFileList(setup_file_list);
+
+  free(directory_path);
+  free(filename);
+
+  return TRUE;
+}
+
+static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
+                                         TreeInfo *node_parent,
+                                         char *base_directory, int type)
+{
+  DIR *dir;
+  struct dirent *dir_entry;
+  boolean valid_entry_found = FALSE;
+
+  if ((dir = opendir(base_directory)) == NULL)
+  {
+    if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
+      Error(ERR_WARN, "cannot read directory '%s'", base_directory);
+    return;
+  }
+
+  while ((dir_entry = readdir(dir)) != NULL)   /* loop until last dir entry */
+  {
+    struct stat file_status;
+    char *directory_name = dir_entry->d_name;
+    char *directory_path = getPath2(base_directory, directory_name);
+
+    /* skip entries for current and parent directory */
+    if (strcmp(directory_name, ".")  == 0 ||
+       strcmp(directory_name, "..") == 0)
+    {
+      free(directory_path);
+      continue;
+    }
+
+    /* find out if directory entry is itself a directory */
+    if (stat(directory_path, &file_status) != 0 ||     /* cannot stat file */
+       (file_status.st_mode & S_IFMT) != S_IFDIR)      /* not a directory */
+    {
+      free(directory_path);
+      continue;
+    }
+
+    free(directory_path);
+
+    /* check if this directory contains artwork with or without config file */
+    valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
+                                                       base_directory,
+                                                       directory_name, type);
+  }
+
+  closedir(dir);
+
+  /* check if this directory directly contains artwork itself */
+  valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
+                                                     base_directory, ".",
+                                                     type);
+  if (!valid_entry_found)
+    Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
+         base_directory);
+}
+
+static TreeInfo *getDummyArtworkInfo(int type)
+{
+  /* this is only needed when there is completely no artwork available */
+  TreeInfo *artwork_new = newTreeInfo();
+
+  setTreeInfoToDefaults(artwork_new, type);
+
+  artwork_new->filename = getStringCopy(NOT_AVAILABLE);
+  artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
+  artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
+
+  if (artwork_new->name != NULL)
+    free(artwork_new->name);
+
+  artwork_new->name         = getStringCopy(NOT_AVAILABLE);
+  artwork_new->name_short   = getStringCopy(NOT_AVAILABLE);
+  artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
+
+  return artwork_new;
+}
+
+void LoadArtworkInfo()
+{
+  DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
+
+  LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
+                               options.graphics_directory,
+                               TREE_TYPE_GRAPHICS_DIR);
+  LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
+                               getUserGraphicsDir(),
+                               TREE_TYPE_GRAPHICS_DIR);
+
+  LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
+                               options.sounds_directory,
+                               TREE_TYPE_SOUNDS_DIR);
+  LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
+                               getUserSoundsDir(),
+                               TREE_TYPE_SOUNDS_DIR);
+
+  LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
+                               options.music_directory,
+                               TREE_TYPE_MUSIC_DIR);
+  LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
+                               getUserMusicDir(),
+                               TREE_TYPE_MUSIC_DIR);
+
+  if (artwork.gfx_first == NULL)
+    artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
+  if (artwork.snd_first == NULL)
+    artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
+  if (artwork.mus_first == NULL)
+    artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
+
+  /* before sorting, the first entries will be from the user directory */
+  artwork.gfx_current =
+    getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
+  if (artwork.gfx_current == NULL)
+    artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
+
+  artwork.snd_current =
+    getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
+  if (artwork.snd_current == NULL)
+    artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
+
+  artwork.mus_current =
+    getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
+  if (artwork.mus_current == NULL)
+    artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
+
+  artwork.graphics_set_current_name = artwork.gfx_current->name;
+  artwork.sounds_set_current_name = artwork.snd_current->name;
+  artwork.music_set_current_name = artwork.mus_current->name;
+
+#if 0
+  printf("graphics set == %s\n\n", artwork.graphics_set_current_name);
+  printf("sounds set == %s\n\n", artwork.sounds_set_current_name);
+  printf("music set == %s\n\n", artwork.music_set_current_name);
+#endif
+
+  sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
+  sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
+  sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
+
+#if 0
+  dumpTreeInfo(artwork.gfx_first, 0);
+  dumpTreeInfo(artwork.snd_first, 0);
+  dumpTreeInfo(artwork.mus_first, 0);
+#endif
+}
+
+void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
+                                 LevelDirTree *level_node)
+{
+  /* recursively check all level directories for artwork sub-directories */
+
+  while (level_node)
+  {
+    char *path = getPath2(getLevelDirFromTreeInfo(level_node),
+                         ARTWORK_DIRECTORY((*artwork_node)->type));
+
+#if 0
+    if (!level_node->parent_link)
+      printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
+            level_node->filename, level_node->name);
+#endif
+
+    if (!level_node->parent_link)
+    {
+      TreeInfo *topnode_last = *artwork_node;
+
+      LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
+                                   (*artwork_node)->type);
+
+      if (topnode_last != *artwork_node)
+      {
+       free((*artwork_node)->name);
+       free((*artwork_node)->name_sorting);
+       free((*artwork_node)->name_short);
+
+       (*artwork_node)->name         = getStringCopy(level_node->name);
+       (*artwork_node)->name_sorting = getStringCopy(level_node->name);
+       (*artwork_node)->name_short   = getStringCopy(level_node->filename);
+
+       (*artwork_node)->sort_priority = level_node->sort_priority;
+       (*artwork_node)->color = LEVELCOLOR((*artwork_node));
+      }
+    }
+
+    free(path);
+
+    if (level_node->node_group != NULL)
+      LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
+
+    level_node = level_node->next;
+  }
+}
+
+void LoadLevelArtworkInfo()
+{
+  DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
+
+  LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
+  LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
+  LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
+
+  sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
+  sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
+  sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
+
+#if 0
+  dumpTreeInfo(artwork.gfx_first, 0);
+  dumpTreeInfo(artwork.snd_first, 0);
+  dumpTreeInfo(artwork.mus_first, 0);
+#endif
+}
+
+static void SaveUserLevelInfo()
+{
+  char *filename;
+  FILE *file;
+  int i;
+
+  filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write level info file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  /* always start with reliable default values */
+  setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
+
+  ldi.name = getStringCopy(getLoginName());
+  ldi.author = getStringCopy(getRealName());
+  ldi.levels = 100;
+  ldi.first_level = 1;
+  ldi.sort_priority = LEVELCLASS_USER_START;
+  ldi.readonly = FALSE;
+
+  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
+                                                getCookie("LEVELINFO")));
+
+  for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
+    if (i != LEVELINFO_TOKEN_NAME_SHORT &&
+       i != LEVELINFO_TOKEN_NAME_SORTING &&
+       i != LEVELINFO_TOKEN_IMPORTED_FROM)
+      fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
+
+  fclose(file);
+  free(filename);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+char *getSetupValue(int type, void *value)
+{
+  static char value_string[MAX_LINE_LEN];
+
+  if (value == NULL)
+    return NULL;
+
+  switch (type)
+  {
+    case TYPE_BOOLEAN:
+      strcpy(value_string, (*(boolean *)value ? "true" : "false"));
+      break;
+
+    case TYPE_SWITCH:
+      strcpy(value_string, (*(boolean *)value ? "on" : "off"));
+      break;
+
+    case TYPE_YES_NO:
+      strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
+      break;
+
+    case TYPE_KEY:
+      strcpy(value_string, getKeyNameFromKey(*(Key *)value));
+      break;
+
+    case TYPE_KEY_X11:
+      strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
+      break;
+
+    case TYPE_INTEGER:
+      sprintf(value_string, "%d", *(int *)value);
+      break;
+
+    case TYPE_STRING:
+      strcpy(value_string, *(char **)value);
+      break;
+
+    default:
+      value_string[0] = '\0';
+      break;
+  }
+
+  return value_string;
+}
+
+char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
+{
+  int i;
+  char *line;
+  static char token_string[MAX_LINE_LEN];
+  int token_type = token_info[token_nr].type;
+  void *setup_value = token_info[token_nr].value;
+  char *token_text = token_info[token_nr].text;
+  char *value_string = getSetupValue(token_type, setup_value);
+
+  /* build complete token string */
+  sprintf(token_string, "%s%s", prefix, token_text);
+
+  /* build setup entry line */
+  line = getFormattedSetupEntry(token_string, value_string);
+
+  if (token_type == TYPE_KEY_X11)
+  {
+    Key key = *(Key *)setup_value;
+    char *keyname = getKeyNameFromKey(key);
+
+    /* add comment, if useful */
+    if (strcmp(keyname, "(undefined)") != 0 &&
+       strcmp(keyname, "(unknown)") != 0)
+    {
+      /* add at least one whitespace */
+      strcat(line, " ");
+      for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
+       strcat(line, " ");
+
+      strcat(line, "# ");
+      strcat(line, keyname);
+    }
+  }
+
+  return line;
+}
+
+void LoadLevelSetup_LastSeries()
+{
+  char *filename;
+  struct SetupFileList *level_setup_list = NULL;
+
+  /* always start with reliable default values */
+  leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
+
+  /* ----------------------------------------------------------------------- */
+  /* ~/.<program>/levelsetup.conf                                            */
+  /* ----------------------------------------------------------------------- */
+
+  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+
+  if ((level_setup_list = loadSetupFileList(filename)))
+  {
+    char *last_level_series =
+      getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
+
+    leveldir_current = getTreeInfoFromFilename(leveldir_first,
+                                              last_level_series);
+    if (leveldir_current == NULL)
+      leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
+
+    checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
+
+    freeSetupFileList(level_setup_list);
+  }
+  else
+    Error(ERR_WARN, "using default setup values");
+
+  free(filename);
+}
+
+void SaveLevelSetup_LastSeries()
+{
+  char *filename;
+  char *level_subdir = leveldir_current->filename;
+  FILE *file;
+
+  /* ----------------------------------------------------------------------- */
+  /* ~/.<program>/levelsetup.conf                                            */
+  /* ----------------------------------------------------------------------- */
+
+  InitUserDataDirectory();
+
+  filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write setup file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
+                                                getCookie("LEVELSETUP")));
+  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
+                                              level_subdir));
+
+  fclose(file);
+  free(filename);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+}
+
+static void checkSeriesInfo()
+{
+  static char *level_directory = NULL;
+  DIR *dir;
+  struct dirent *dir_entry;
+
+  /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
+
+  level_directory = getPath2((leveldir_current->user_defined ?
+                             getUserLevelDir(NULL) :
+                             options.level_directory),
+                            leveldir_current->fullpath);
+
+  if ((dir = opendir(level_directory)) == NULL)
+  {
+    Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+    return;
+  }
+
+  while ((dir_entry = readdir(dir)) != NULL)   /* last directory entry */
+  {
+    if (strlen(dir_entry->d_name) > 4 &&
+       dir_entry->d_name[3] == '.' &&
+       strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
+    {
+      char levelnum_str[4];
+      int levelnum_value;
+
+      strncpy(levelnum_str, dir_entry->d_name, 3);
+      levelnum_str[3] = '\0';
+
+      levelnum_value = atoi(levelnum_str);
+
+      if (levelnum_value < leveldir_current->first_level)
+      {
+       Error(ERR_WARN, "additional level %d found", levelnum_value);
+       leveldir_current->first_level = levelnum_value;
+      }
+      else if (levelnum_value > leveldir_current->last_level)
+      {
+       Error(ERR_WARN, "additional level %d found", levelnum_value);
+       leveldir_current->last_level = levelnum_value;
+      }
+    }
+  }
+
+  closedir(dir);
+}
+
+void LoadLevelSetup_SeriesInfo()
+{
+  char *filename;
+  struct SetupFileList *level_setup_list = NULL;
+  char *level_subdir = leveldir_current->filename;
+
+  /* always start with reliable default values */
+  level_nr = leveldir_current->first_level;
+
+  checkSeriesInfo(leveldir_current);
+
+  /* ----------------------------------------------------------------------- */
+  /* ~/.<program>/levelsetup/<level series>/levelsetup.conf                  */
+  /* ----------------------------------------------------------------------- */
+
+  level_subdir = leveldir_current->filename;
+
+  filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
+
+  if ((level_setup_list = loadSetupFileList(filename)))
+  {
+    char *token_value;
+
+    token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
+
+    if (token_value)
+    {
+      level_nr = atoi(token_value);
+
+      if (level_nr < leveldir_current->first_level)
+       level_nr = leveldir_current->first_level;
+      if (level_nr > leveldir_current->last_level)
+       level_nr = leveldir_current->last_level;
+    }
+
+    token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
+
+    if (token_value)
+    {
+      int level_nr = atoi(token_value);
+
+      if (level_nr < leveldir_current->first_level)
+       level_nr = leveldir_current->first_level;
+      if (level_nr > leveldir_current->last_level + 1)
+       level_nr = leveldir_current->last_level;
+
+      if (leveldir_current->user_defined)
+       level_nr = leveldir_current->last_level;
+
+      leveldir_current->handicap_level = level_nr;
+    }
+
+    checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
+
+    freeSetupFileList(level_setup_list);
+  }
+  else
+    Error(ERR_WARN, "using default setup values");
+
+  free(filename);
+}
+
+void SaveLevelSetup_SeriesInfo()
+{
+  char *filename;
+  char *level_subdir = leveldir_current->filename;
+  char *level_nr_str = int2str(level_nr, 0);
+  char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
+  FILE *file;
+
+  /* ----------------------------------------------------------------------- */
+  /* ~/.<program>/levelsetup/<level series>/levelsetup.conf                  */
+  /* ----------------------------------------------------------------------- */
+
+  InitLevelSetupDirectory(level_subdir);
+
+  filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
+
+  if (!(file = fopen(filename, MODE_WRITE)))
+  {
+    Error(ERR_WARN, "cannot write setup file '%s'", filename);
+    free(filename);
+    return;
+  }
+
+  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
+                                                getCookie("LEVELSETUP")));
+  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
+                                              level_nr_str));
+  fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
+                                              handicap_level_str));
+
+  fclose(file);
+  free(filename);
+
+  SetFilePermissions(filename, PERMS_PRIVATE);
+}
diff --git a/src/libgame/setup.h b/src/libgame/setup.h
new file mode 100644 (file)
index 0000000..f299f3d
--- /dev/null
@@ -0,0 +1,231 @@
+/***********************************************************
+* Artsoft Retro-Game Library                               *
+*----------------------------------------------------------*
+* (c) 1994-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* setup.h                                                  *
+***********************************************************/
+
+#ifndef SETUP_H
+#define SETUP_H
+
+#include "system.h"
+
+
+/* values for setup file handling */
+#define TYPE_BOOLEAN                   (1 << 0)
+#define TYPE_SWITCH                    (1 << 1)
+#define TYPE_YES_NO                    (1 << 2)
+#define TYPE_KEY                       (1 << 3)
+#define TYPE_KEY_X11                   (1 << 4)
+#define TYPE_INTEGER                   (1 << 5)
+#define TYPE_STRING                    (1 << 6)
+
+#define TYPE_BOOLEAN_STYLE             (TYPE_BOOLEAN | \
+                                        TYPE_SWITCH  | \
+                                        TYPE_YES_NO)
+
+/* additional values for setup screen */
+#define TYPE_ENTER_MENU                        (1 << 7)
+#define TYPE_LEAVE_MENU                        (1 << 8)
+#define TYPE_EMPTY                     (1 << 9)
+#define TYPE_KEYTEXT                   (1 << 10)
+
+#define TYPE_GHOSTED                   (1 << 11)
+#define TYPE_QUERY                     (1 << 12)
+
+#define TYPE_VALUE                     (TYPE_BOOLEAN_STYLE | \
+                                        TYPE_KEY | \
+                                        TYPE_KEY_X11 | \
+                                        TYPE_INTEGER | \
+                                        TYPE_STRING)
+
+#define TYPE_SKIP_ENTRY                        (TYPE_EMPTY | \
+                                        TYPE_KEY | \
+                                        TYPE_STRING)
+
+#define TYPE_ENTER_OR_LEAVE_MENU       (TYPE_ENTER_MENU | \
+                                        TYPE_LEAVE_MENU)
+
+/* cookie token for file identifier and version number */
+#define TOKEN_STR_FILE_IDENTIFIER      "file_identifier"
+
+/* structures for setup file handling */
+struct SetupFileList
+{
+  char *token;
+  char *value;
+  struct SetupFileList *next;
+};
+
+struct TokenInfo
+{
+  int type;
+  void *value;
+  char *text;
+};
+
+/* sort priorities of level series (also used as level series classes) */
+#define LEVELCLASS_TUTORIAL_START      10
+#define LEVELCLASS_TUTORIAL_END                99
+#define LEVELCLASS_CLASSICS_START      100
+#define LEVELCLASS_CLASSICS_END                199
+#define LEVELCLASS_CONTRIBUTION_START  200
+#define LEVELCLASS_CONTRIBUTION_END    299
+#define LEVELCLASS_USER_START          300
+#define LEVELCLASS_USER_END            399
+#define LEVELCLASS_BD_START            400
+#define LEVELCLASS_BD_END              499
+#define LEVELCLASS_EM_START            500
+#define LEVELCLASS_EM_END              599
+#define LEVELCLASS_SP_START            600
+#define LEVELCLASS_SP_END              699
+#define LEVELCLASS_DX_START            700
+#define LEVELCLASS_DX_END              799
+
+#define LEVELCLASS_TUTORIAL            LEVELCLASS_TUTORIAL_START
+#define LEVELCLASS_CLASSICS            LEVELCLASS_CLASSICS_START
+#define LEVELCLASS_CONTRIBUTION                LEVELCLASS_CONTRIBUTION_START
+#define LEVELCLASS_USER                        LEVELCLASS_USER_START
+#define LEVELCLASS_BD                  LEVELCLASS_BD_START
+#define LEVELCLASS_EM                  LEVELCLASS_EM_START
+#define LEVELCLASS_SP                  LEVELCLASS_SP_START
+#define LEVELCLASS_DX                  LEVELCLASS_DX_START
+
+#define LEVELCLASS_UNDEFINED           999
+
+#define IS_LEVELCLASS_TUTORIAL(p) \
+       ((p)->sort_priority >= LEVELCLASS_TUTORIAL_START && \
+        (p)->sort_priority <= LEVELCLASS_TUTORIAL_END)
+#define IS_LEVELCLASS_CLASSICS(p) \
+       ((p)->sort_priority >= LEVELCLASS_CLASSICS_START && \
+        (p)->sort_priority <= LEVELCLASS_CLASSICS_END)
+#define IS_LEVELCLASS_CONTRIBUTION(p) \
+       ((p)->sort_priority >= LEVELCLASS_CONTRIBUTION_START && \
+        (p)->sort_priority <= LEVELCLASS_CONTRIBUTION_END)
+#define IS_LEVELCLASS_USER(p) \
+       ((p)->sort_priority >= LEVELCLASS_USER_START && \
+        (p)->sort_priority <= LEVELCLASS_USER_END)
+#define IS_LEVELCLASS_BD(p) \
+       ((p)->sort_priority >= LEVELCLASS_BD_START && \
+        (p)->sort_priority <= LEVELCLASS_BD_END)
+#define IS_LEVELCLASS_EM(p) \
+       ((p)->sort_priority >= LEVELCLASS_EM_START && \
+        (p)->sort_priority <= LEVELCLASS_EM_END)
+#define IS_LEVELCLASS_SP(p) \
+       ((p)->sort_priority >= LEVELCLASS_SP_START && \
+        (p)->sort_priority <= LEVELCLASS_SP_END)
+#define IS_LEVELCLASS_DX(p) \
+       ((p)->sort_priority >= LEVELCLASS_DX_START && \
+        (p)->sort_priority <= LEVELCLASS_DX_END)
+
+#define LEVELCLASS(n)  (IS_LEVELCLASS_TUTORIAL(n) ? LEVELCLASS_TUTORIAL : \
+                        IS_LEVELCLASS_CLASSICS(n) ? LEVELCLASS_CLASSICS : \
+                        IS_LEVELCLASS_CONTRIBUTION(n) ? LEVELCLASS_CONTRIBUTION : \
+                        IS_LEVELCLASS_USER(n) ? LEVELCLASS_USER : \
+                        IS_LEVELCLASS_BD(n) ? LEVELCLASS_BD : \
+                        IS_LEVELCLASS_EM(n) ? LEVELCLASS_EM : \
+                        IS_LEVELCLASS_SP(n) ? LEVELCLASS_SP : \
+                        IS_LEVELCLASS_DX(n) ? LEVELCLASS_DX : \
+                        LEVELCLASS_UNDEFINED)
+
+/* sort priorities of artwork */
+#define ARTWORKCLASS_CLASSICS_START    100
+#define ARTWORKCLASS_CLASSICS_END      199
+#define ARTWORKCLASS_CONTRIBUTION_START        200
+#define ARTWORKCLASS_CONTRIBUTION_END  299
+#define ARTWORKCLASS_LEVEL_START       300
+#define ARTWORKCLASS_LEVEL_END         399
+#define ARTWORKCLASS_USER_START                400
+#define ARTWORKCLASS_USER_END          499
+
+#define ARTWORKCLASS_CLASSICS          ARTWORKCLASS_CLASSICS_START
+#define ARTWORKCLASS_CONTRIBUTION      ARTWORKCLASS_CONTRIBUTION_START
+#define ARTWORKCLASS_LEVEL             ARTWORKCLASS_LEVEL_START
+#define ARTWORKCLASS_USER              ARTWORKCLASS_USER_START
+
+#define ARTWORKCLASS_UNDEFINED         999
+
+#define IS_ARTWORKCLASS_CLASSICS(p) \
+       ((p)->sort_priority >= ARTWORKCLASS_CLASSICS_START && \
+        (p)->sort_priority <= ARTWORKCLASS_CLASSICS_END)
+#define IS_ARTWORKCLASS_CONTRIBUTION(p) \
+       ((p)->sort_priority >= ARTWORKCLASS_CONTRIBUTION_START && \
+        (p)->sort_priority <= ARTWORKCLASS_CONTRIBUTION_END)
+#define IS_ARTWORKCLASS_LEVEL(p) \
+       ((p)->sort_priority >= ARTWORKCLASS_LEVEL_START && \
+        (p)->sort_priority <= ARTWORKCLASS_LEVEL_END)
+#define IS_ARTWORKCLASS_USER(p) \
+       ((p)->sort_priority >= ARTWORKCLASS_USER_START && \
+        (p)->sort_priority <= ARTWORKCLASS_USER_END)
+
+#define ARTWORKCLASS(n)        (IS_ARTWORKCLASS_CLASSICS(n) ? ARTWORKCLASS_CLASSICS :\
+                        IS_ARTWORKCLASS_CONTRIBUTION(n) ? ARTWORKCLASS_CONTRIBUTION : \
+                        IS_ARTWORKCLASS_LEVEL(n) ? ARTWORKCLASS_LEVEL : \
+                        IS_ARTWORKCLASS_USER(n) ? ARTWORKCLASS_USER : \
+                        ARTWORKCLASS_UNDEFINED)
+
+
+char *getLevelFilename(int);
+char *getTapeFilename(int);
+char *getScoreFilename(int);
+char *getSetupFilename(void);
+char *getImageFilename(char *);
+char *getCustomImageFilename(char *);
+char *getCustomSoundFilename(char *);
+char *getCustomSoundConfigFilename(void);
+char *getCustomMusicDirectory(void);
+
+void InitTapeDirectory(char *);
+void InitScoreDirectory(char *);
+void InitUserLevelDirectory(char *);
+void InitLevelSetupDirectory(char *);
+
+TreeInfo *newTreeInfo();
+void pushTreeInfo(TreeInfo **, TreeInfo *);
+int numTreeInfo(TreeInfo *);
+boolean validLevelSeries(TreeInfo *);
+TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *);
+TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *);
+int numTreeInfoInGroup(TreeInfo *);
+int posTreeInfo(TreeInfo *);
+TreeInfo *getTreeInfoFromPos(TreeInfo *, int);
+TreeInfo *getTreeInfoFromFilename(TreeInfo *, char *);
+void dumpTreeInfo(TreeInfo *, int);
+void sortTreeInfo(TreeInfo **,
+                 int (*compare_function)(const void *, const void *));
+
+char *getUserDataDir(void);
+char *getSetupDir(void);
+void createDirectory(char *, char *, int);
+void InitUserDataDirectory(void);
+void SetFilePermissions(char *, int);
+
+char *getCookie(char *);
+int getFileVersionFromCookieString(const char *);
+boolean checkCookieString(const char *, const char *);
+
+char *getFormattedSetupEntry(char *, char *);
+void freeSetupFileList(struct SetupFileList *);
+char *getTokenValue(struct SetupFileList *, char *);
+struct SetupFileList *loadSetupFileList(char *);
+void checkSetupFileListIdentifier(struct SetupFileList *, char *);
+void setSetupInfo(struct TokenInfo *, int, char *);
+char *getSetupValue(int, void *);
+char *getSetupLine(struct TokenInfo *, char *, int);
+
+void LoadLevelInfo(void);
+void LoadArtworkInfo(void);
+void LoadLevelArtworkInfo(void);
+
+void LoadLevelSetup_LastSeries(void);
+void SaveLevelSetup_LastSeries(void);
+void LoadLevelSetup_SeriesInfo(void);
+void SaveLevelSetup_SeriesInfo(void);
+
+#endif /* MISC_H */
index 088df1276eeb3618188e7365f6620454a6616280..cd34b284473e3126a0191fc3074f7005793fd0e0 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 * sound.c                                                  *
 ***********************************************************/
 
-#include <string.h>
+#include <sys/types.h>
 #include <sys/time.h>
+#include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <dirent.h>
 #include <signal.h>
+#include <math.h>
+
+#include "platform.h"
+
+#if defined(PLATFORM_LINUX)
+#include <sys/ioctl.h>
+#include <linux/soundcard.h>
+#elif defined(PLATFORM_FREEBSD)
+#include <machine/soundcard.h>
+#elif defined(PLATFORM_NETBSD)
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#elif defined(PLATFORM_HPUX)
+#include <sys/audio.h>
+#endif
 
 #include "system.h"
 #include "sound.h"
 #include "misc.h"
+#include "setup.h"
+#include "text.h"
 
 
-static int num_sounds = 0, num_music = 0;
-static struct SampleInfo *Sound = NULL;
-#if defined(TARGET_SDL)
-static int num_mods = 0;
-static struct SampleInfo *Mod = NULL;
+/* expiration time (in milliseconds) for sound loops */
+#define SOUND_LOOP_EXPIRATION_TIME     200
+
+/* one second fading interval == 1000 ticks (milliseconds) */
+#define SOUND_FADING_INTERVAL          1000
+
+#if defined(AUDIO_STREAMING_DSP)
+#define SOUND_FADING_VOLUME_STEP       (SOUND_MAX_VOLUME / 40)
+#define SOUND_FADING_VOLUME_THRESHOLD  (SOUND_FADING_VOLUME_STEP * 2)
+#endif
+
+#if !defined(PLATFORM_HPUX)
+#define SND_BLOCKSIZE                  4096
+#else
+#define SND_BLOCKSIZE                  32768
 #endif
 
+#define SND_TYPE_NONE                  0
+#define SND_TYPE_WAV                   1
 
-/* ========================================================================= */
-/* THE STUFF BELOW IS ONLY USED BY THE SOUND SERVER CHILD PROCESS            */
+#define MUS_TYPE_NONE                  0
+#define MUS_TYPE_WAV                   1
+#define MUS_TYPE_MOD                   2
+
+#define DEVICENAME_DSP                 "/dev/dsp"
+#define DEVICENAME_AUDIO               "/dev/audio"
+#define DEVICENAME_AUDIOCTL            "/dev/audioCtl"
 
-static int playing_sounds = 0;
-static struct SoundControl playlist[MAX_SOUNDS_PLAYING];
-static struct SoundControl emptySoundControl =
+#define SOUND_VOLUME_LEFT(x)           (stereo_volume[x])
+#define SOUND_VOLUME_RIGHT(x)          (stereo_volume[SOUND_MAX_LEFT2RIGHT-x])
+
+#define SAME_SOUND_NR(x,y)             ((x).nr == (y).nr)
+#define SAME_SOUND_DATA(x,y)           ((x).data_ptr == (y).data_ptr)
+
+#if 0
+struct SoundHeader_SUN
 {
-  -1,0,0, FALSE,FALSE,FALSE,FALSE,FALSE, 0,0L,0L,NULL
+  unsigned long magic;
+  unsigned long hdr_size;
+  unsigned long data_size;
+  unsigned long encoding;
+  unsigned long sample_rate;
+  unsigned long channels;
 };
 
-#if defined(PLATFORM_UNIX)
-static int stereo_volume[PSND_MAX_LEFT2RIGHT+1];
-static char premix_first_buffer[SND_BLOCKSIZE];
-#if defined(AUDIO_STREAMING_DSP)
-static char premix_left_buffer[SND_BLOCKSIZE];
-static char premix_right_buffer[SND_BLOCKSIZE];
-static int premix_last_buffer[SND_BLOCKSIZE];
+struct SoundHeader_8SVX
+{
+  char magic_FORM[4];
+  unsigned long chunk_size;
+  char magic_8SVX[4];
+};
 #endif
-static unsigned char playing_buffer[SND_BLOCKSIZE];
+
+#if defined(AUDIO_UNIX_NATIVE)
+struct SoundHeader_WAV
+{
+  unsigned short compression_code;
+  unsigned short num_channels;
+  unsigned long  sample_rate;
+  unsigned long  bytes_per_second;
+  unsigned short block_align;
+  unsigned short bits_per_sample;
+};
 #endif
 
-/* forward declaration of internal functions */
-#if defined(AUDIO_STREAMING_DSP)
-static void SoundServer_InsertNewSound(struct SoundControl);
-#elif defined(PLATFORM_UNIX)
+struct AudioFormatInfo
+{
+  boolean stereo;              /* availability of stereo sound */
+  int format;                  /* size and endianess of sample data */
+  int sample_rate;             /* sample frequency */
+  int fragment_size;           /* audio device fragment size in bytes */
+};
+
+struct SampleInfo
+{
+  char *source_filename;
+  int num_references;
+
+  int type;
+  int format;
+  void *data_ptr;              /* pointer to first sample (8 or 16 bit) */
+  long data_len;               /* number of samples, NOT number of bytes */
+};
+typedef struct SampleInfo SoundInfo;
+typedef struct SampleInfo MusicInfo;
+
+struct SoundControl
+{
+  boolean active;
+
+  int nr;
+  int volume;
+  int stereo_position;
+
+  int state;
+
+  unsigned long playing_starttime;
+  unsigned long playing_pos;
+
+  int type;
+  int format;
+  void *data_ptr;              /* pointer to first sample (8 or 16 bit) */
+  long data_len;               /* number of samples, NOT number of bytes */
+
+#if defined(TARGET_ALLEGRO)
+  int voice;
+#endif
+};
+typedef struct SoundControl SoundControl;
+
+struct ListNode
+{
+  char *key;
+  void *content;
+  struct ListNode *next;
+};
+typedef struct ListNode ListNode;
+
+static ListNode *newListNode(void);
+static void addNodeToList(ListNode **, char *, void *);
+static void deleteNodeFromList(ListNode **, char *, void (*function)(void *));
+static ListNode *getNodeFromKey(ListNode *, char *);
+static int getNumNodes(ListNode *);
+
+
+static struct SoundEffectInfo *sound_effect;
+static ListNode *SoundFileList = NULL;
+static SoundInfo **Sound = NULL;
+static MusicInfo **Music = NULL;
+static int num_sounds = 0, num_music = 0;
+static int stereo_volume[SOUND_MAX_LEFT2RIGHT + 1];
+
+
+/* ========================================================================= */
+/* THE STUFF BELOW IS ONLY USED BY THE SOUND SERVER CHILD PROCESS            */
+
+static struct SoundControl mixer[NUM_MIXER_CHANNELS];
+static int mixer_active_channels = 0;
+
+#if defined(AUDIO_UNIX_NATIVE)
+static struct AudioFormatInfo afmt;
+
+static void Mixer_Main(void);
+#if !defined(AUDIO_STREAMING_DSP)
 static unsigned char linear_to_ulaw(int);
 static int ulaw_to_linear(unsigned char);
 #endif
-
-#if defined(AUDIO_LINUX_IOCTL)
-static boolean InitAudioDevice_Linux();
-#elif defined(PLATFORM_NETBSD)
-static boolean InitAudioDevice_NetBSD();
-#elif defined(PLATFORM_HPUX)
-static boolean InitAudioDevice_HPUX();
-#elif defined(PLATFORM_MSDOS)
-static void SoundServer_InsertNewSound(struct SoundControl);
-static void SoundServer_StopSound(int);
-static void SoundServer_StopAllSounds();
 #endif
 
-#if defined(PLATFORM_UNIX)
+static void ReloadCustomSounds();
+static void ReloadCustomMusic();
+static void FreeSound(void *);
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for native (non-SDL) Unix audio/mixer support                   */
+/* ------------------------------------------------------------------------- */
+
+#if defined(AUDIO_UNIX_NATIVE)
+
 static int OpenAudioDevice(char *audio_device_name)
 {
-  int audio_fd;
+  int audio_device_fd;
 
   /* check if desired audio device is accessible */
   if (access(audio_device_name, W_OK) != 0)
     return -1;
 
   /* try to open audio device in non-blocking mode */
-  if ((audio_fd = open(audio_device_name, O_WRONLY | O_NONBLOCK)) < 0)
-    return audio_fd;
+  if ((audio_device_fd = open(audio_device_name, O_WRONLY | O_NONBLOCK)) < 0)
+    return audio_device_fd;
 
   /* re-open audio device in blocking mode */
-  close(audio_fd);
-  audio_fd = open(audio_device_name, O_WRONLY);
+  close(audio_device_fd);
+  audio_device_fd = open(audio_device_name, O_WRONLY);
 
-  return audio_fd;
+  return audio_device_fd;
+}
+
+static void CloseAudioDevice(int *audio_device_fd)
+{
+  if (*audio_device_fd == 0)
+    return;
+
+  close(*audio_device_fd);
+  *audio_device_fd = -1;
 }
 
 static boolean TestAudioDevices(void)
@@ -99,55 +236,48 @@ static boolean TestAudioDevices(void)
     DEVICENAME_DSP,
     DEVICENAME_AUDIO
   };
-  int audio_fd = -1;
+  int audio_device_fd = -1;
   int i;
 
   /* look for available audio devices, starting with preferred ones */
   for (i=0; i<sizeof(audio_device_name)/sizeof(char *); i++)
-    if ((audio_fd = OpenAudioDevice(audio_device_name[i])) >= 0)
+    if ((audio_device_fd = OpenAudioDevice(audio_device_name[i])) >= 0)
       break;
 
-  if (audio_fd < 0)
+  if (audio_device_fd < 0)
   {
     Error(ERR_WARN, "cannot open audio device -- no sound");
     return FALSE;
   }
 
-  close(audio_fd);
+  close(audio_device_fd);
 
   audio.device_name = audio_device_name[i];
 
   return TRUE;
 }
 
-#if !defined(TARGET_SDL)
 static boolean ForkAudioProcess(void)
 {
-  if (pipe(audio.soundserver_pipe) < 0)
+  if (pipe(audio.mixer_pipe) < 0)
   {
     Error(ERR_WARN, "cannot create pipe -- no sounds");
     return FALSE;
   }
 
-  if ((audio.soundserver_pid = fork()) < 0)
+  if ((audio.mixer_pid = fork()) < 0)
   {       
     Error(ERR_WARN, "cannot create sound server process -- no sounds");
     return FALSE;
   }
 
-  if (audio.soundserver_pid == 0)      /* we are child */
-  {
-    SoundServer();
-
-    /* never reached */
-    exit(0);
-  }
-  else                                 /* we are parent */
-    close(audio.soundserver_pipe[0]); /* no reading from pipe needed */
+  if (IS_CHILD_PROCESS(audio.mixer_pid))
+    Mixer_Main();                      /* this function never returns */
+  else
+    close(audio.mixer_pipe[0]);                /* no reading from pipe needed */
 
   return TRUE;
 }
-#endif
 
 void UnixOpenAudio(void)
 {
@@ -161,6 +291,10 @@ void UnixOpenAudio(void)
   audio.music_available = TRUE;
   audio.loops_available = TRUE;
 #endif
+
+  audio.num_channels = NUM_MIXER_CHANNELS;
+  audio.music_channel = MUSIC_CHANNEL;
+  audio.first_sound_channel = FIRST_SOUND_CHANNEL;
 }
 
 void UnixCloseAudio(void)
@@ -168,637 +302,1107 @@ void UnixCloseAudio(void)
   if (audio.device_fd)
     close(audio.device_fd);
 
-  if (audio.soundserver_pid)
-    kill(audio.soundserver_pid, SIGTERM);
+  if (IS_PARENT_PROCESS(audio.mixer_pid))
+    kill(audio.mixer_pid, SIGTERM);
 }
-#endif /* PLATFORM_UNIX */
 
-void InitPlaylist(void)
+
+/* ------------------------------------------------------------------------- */
+/* functions for platform specific audio device initialization               */
+/* ------------------------------------------------------------------------- */
+
+#if defined(AUDIO_LINUX_IOCTL)
+static void InitAudioDevice_Linux(struct AudioFormatInfo *afmt)
 {
+  /* "ioctl()" expects pointer to 'int' value for stereo flag
+     (boolean is defined as 'char', which will not work here) */
+  unsigned int fragment_spec = 0;
+  int fragment_size_query;
+  int stereo = TRUE;
+  struct
+  {
+    int format_ioctl;
+    int format_result;
+  }
+  formats[] =
+  {
+    /* supported audio format in preferred order */
+    { AFMT_S16_LE,     AUDIO_FORMAT_S16 | AUDIO_FORMAT_LE },
+    { AFMT_S16_BE,     AUDIO_FORMAT_S16 | AUDIO_FORMAT_BE },
+    { AFMT_U8,         AUDIO_FORMAT_U8                    },
+    { -1,              -1 }
+  };
   int i;
 
-  for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-    playlist[i] = emptySoundControl;
-  playing_sounds = 0;
+  /* determine logarithm (log2) of the fragment size */
+  while ((1 << fragment_spec) < afmt->fragment_size)
+    fragment_spec++;
+
+  /* use two fragments (play one fragment, prepare the other);
+     one fragment would result in interrupted audio output, more
+     than two fragments would raise audio output latency to much */
+  fragment_spec |= 0x00020000;
+
+  /* Example for fragment specification:
+     - 2 buffers / 512 bytes (giving 1/16 second resolution for 8 kHz)
+     - (with stereo the effective buffer size will shrink to 256)
+     => fragment_size = 0x00020009 */
+
+  if (ioctl(audio.device_fd, SNDCTL_DSP_SETFRAGMENT, &fragment_spec) < 0)
+    Error(ERR_EXIT_SOUND_SERVER,
+         "cannot set fragment size of /dev/dsp -- no sounds");
+
+  i = 0;
+  afmt->format = 0;
+  while (formats[i].format_result != -1)
+  {
+    unsigned int audio_format = formats[i].format_ioctl;
+    if (ioctl(audio.device_fd, SNDCTL_DSP_SETFMT, &audio_format) == 0)
+    {
+      afmt->format = formats[i].format_result;
+      break;
+    }
+  }
+
+  if (afmt->format == 0)       /* no supported audio format found */
+    Error(ERR_EXIT_SOUND_SERVER,
+         "cannot set audio format of /dev/dsp -- no sounds");
+
+  /* try if we can use stereo sound */
+  afmt->stereo = TRUE;
+  if (ioctl(audio.device_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
+    afmt->stereo = FALSE;
+
+  if (ioctl(audio.device_fd, SNDCTL_DSP_SPEED, &afmt->sample_rate) < 0)
+    Error(ERR_EXIT_SOUND_SERVER,
+         "cannot set sample rate of /dev/dsp -- no sounds");
+
+  /* get the real fragmentation size; this should return 512 */
+  if (ioctl(audio.device_fd, SNDCTL_DSP_GETBLKSIZE, &fragment_size_query) < 0)
+    Error(ERR_EXIT_SOUND_SERVER,
+         "cannot get fragment size of /dev/dsp -- no sounds");
+  if (fragment_size_query != afmt->fragment_size)
+    Error(ERR_EXIT_SOUND_SERVER,
+         "cannot set fragment size of /dev/dsp -- no sounds");
 }
+#endif /* AUDIO_LINUX_IOCTL */
 
-void StartSoundserver(void)
+#if defined(PLATFORM_NETBSD)
+static void InitAudioDevice_NetBSD(struct AudioFormatInfo *afmt)
 {
-  if (!audio.sound_available)
-    return;
+  audio_info_t a_info;
+  boolean stereo = TRUE;
 
-#if defined(PLATFORM_UNIX) && !defined(TARGET_SDL)
-  if (!ForkAudioProcess())
-    audio.sound_available = FALSE;
+  AUDIO_INITINFO(&a_info);
+  a_info.play.encoding = AUDIO_ENCODING_LINEAR8;
+  a_info.play.precision = 8;
+  a_info.play.channels = 2;
+  a_info.play.sample_rate = sample_rate;
+  a_info.blocksize = fragment_size;
+
+  afmt->format = AUDIO_FORMAT_U8;
+  afmt->stereo = TRUE;
+
+  if (ioctl(audio.device_fd, AUDIO_SETINFO, &a_info) < 0)
+  {
+    /* try to disable stereo */
+    a_info.play.channels = 1;
+
+    afmt->stereo = FALSE;
+
+    if (ioctl(audio.device_fd, AUDIO_SETINFO, &a_info) < 0)
+      Error(ERR_EXIT_SOUND_SERVER,
+           "cannot set sample rate of /dev/audio -- no sounds");
+  }
+}
+#endif /* PLATFORM_NETBSD */
+
+#if defined(PLATFORM_HPUX)
+static void InitAudioDevice_HPUX(struct AudioFormatInfo *afmt)
+{
+  struct audio_describe ainfo;
+  int audio_ctl;
+
+  audio_ctl = open("/dev/audioCtl", O_WRONLY | O_NDELAY);
+  if (audio_ctl == -1)
+    Error(ERR_EXIT_SOUND_SERVER, "cannot open /dev/audioCtl -- no sounds");
+
+  if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
+    Error(ERR_EXIT_SOUND_SERVER, "no audio info -- no sounds");
+
+  if (ioctl(audio_ctl, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ULAW) == -1)
+    Error(ERR_EXIT_SOUND_SERVER, "ulaw audio not available -- no sounds");
+
+  ioctl(audio_ctl, AUDIO_SET_CHANNELS, 1);
+  ioctl(audio_ctl, AUDIO_SET_SAMPLE_RATE, 8000);
+
+  afmt->format = AUDIO_FORMAT_U8;
+  afmt->stereo = FALSE;
+  afmt->sample_rate = 8000;
+
+  close(audio_ctl);
+}
+#endif /* PLATFORM_HPUX */
+
+static void InitAudioDevice(struct AudioFormatInfo *afmt)
+{
+  afmt->stereo = TRUE;
+  afmt->format = AUDIO_FORMAT_UNKNOWN;
+  afmt->sample_rate = DEFAULT_AUDIO_SAMPLE_RATE;
+  afmt->fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
+
+#if defined(AUDIO_LINUX_IOCTL)
+  InitAudioDevice_Linux(afmt);
+#elif defined(PLATFORM_NETBSD)
+  InitAudioDevice_NetBSD(afmt);
+#elif defined(PLATFORM_HPUX)
+  InitAudioDevice_HPUX(afmt);
+#else
+  /* generic /dev/audio stuff might be placed here */
 #endif
 }
 
-#if defined(PLATFORM_UNIX)
-void SoundServer(void)
+
+/* ------------------------------------------------------------------------- */
+/* functions for communication between main process and sound mixer process  */
+/* ------------------------------------------------------------------------- */
+
+static void SendSoundControlToMixerProcess(SoundControl *snd_ctrl)
+{
+  if (IS_CHILD_PROCESS(audio.mixer_pid))
+    return;
+
+  if (write(audio.mixer_pipe[1], snd_ctrl, sizeof(SoundControl)) < 0)
+  {
+    Error(ERR_WARN, "cannot pipe to child process -- no sounds");
+    audio.sound_available = audio.sound_enabled = FALSE;
+    return;
+  }
+}
+
+static void ReadSoundControlFromMainProcess(SoundControl *snd_ctrl)
+{
+  if (IS_PARENT_PROCESS(audio.mixer_pid))
+    return;
+
+  if (read(audio.mixer_pipe[0], snd_ctrl, sizeof(SoundControl))
+      != sizeof(SoundControl))
+    Error(ERR_EXIT_SOUND_SERVER, "broken pipe -- no sounds");
+}
+
+static void WriteReloadInfoToPipe(char *set_name, int type)
+{
+  SoundControl snd_ctrl;
+  TreeInfo *ti = (type == SND_CTRL_RELOAD_SOUNDS ? artwork.snd_current :
+                 artwork.mus_current);
+  unsigned long str_size1 = strlen(leveldir_current->fullpath) + 1;
+  unsigned long str_size2 = strlen(ti->basepath) + 1;
+  unsigned long str_size3 = strlen(ti->fullpath) + 1;
+  boolean override_level_artwork = (type == SND_CTRL_RELOAD_SOUNDS ?
+                                   setup.override_level_sounds :
+                                   setup.override_level_music);
+
+  if (IS_CHILD_PROCESS(audio.mixer_pid))
+    return;
+
+  if (leveldir_current == NULL)                /* should never happen */
+    Error(ERR_EXIT, "leveldir_current == NULL");
+
+  snd_ctrl.active = FALSE;
+  snd_ctrl.state = type;
+  snd_ctrl.data_len = strlen(set_name) + 1;
+
+  if (write(audio.mixer_pipe[1], &snd_ctrl,
+           sizeof(snd_ctrl)) < 0 ||
+      write(audio.mixer_pipe[1], set_name,
+           snd_ctrl.data_len) < 0 ||
+      write(audio.mixer_pipe[1], &override_level_artwork,
+           sizeof(boolean)) < 0 ||
+      write(audio.mixer_pipe[1], leveldir_current,
+           sizeof(TreeInfo)) < 0 ||
+      write(audio.mixer_pipe[1], ti,
+           sizeof(TreeInfo)) < 0 ||
+      write(audio.mixer_pipe[1], &str_size1,
+           sizeof(unsigned long)) < 0 ||
+      write(audio.mixer_pipe[1], &str_size2,
+           sizeof(unsigned long)) < 0 ||
+      write(audio.mixer_pipe[1], &str_size3,
+           sizeof(unsigned long)) < 0 ||
+      write(audio.mixer_pipe[1], leveldir_current->fullpath,
+           str_size1) < 0 ||
+      write(audio.mixer_pipe[1], ti->basepath,
+           str_size2) < 0 ||
+      write(audio.mixer_pipe[1], ti->fullpath,
+           str_size3) < 0)
+  {
+    Error(ERR_WARN, "cannot pipe to child process -- no sounds");
+    audio.sound_available = audio.sound_enabled = FALSE;
+    return;
+  }
+}
+
+static void ReadReloadInfoFromPipe(SoundControl *snd_ctrl)
+{
+  TreeInfo **ti_ptr = ((snd_ctrl->state & SND_CTRL_RELOAD_SOUNDS) ?
+                      &artwork.snd_current : &artwork.mus_current);
+  TreeInfo *ti = *ti_ptr;
+  unsigned long str_size1, str_size2, str_size3;
+  static char *set_name = NULL;
+  boolean *override_level_artwork = (snd_ctrl->state & SND_CTRL_RELOAD_SOUNDS ?
+                                    &setup.override_level_sounds :
+                                    &setup.override_level_music);
+
+  if (set_name)
+    free(set_name);
+
+  set_name = checked_malloc(snd_ctrl->data_len);
+
+  if (leveldir_current == NULL)
+    leveldir_current = checked_calloc(sizeof(TreeInfo));
+  if (ti == NULL)
+    ti = *ti_ptr = checked_calloc(sizeof(TreeInfo));
+  if (leveldir_current->fullpath != NULL)
+    free(leveldir_current->fullpath);
+  if (ti->basepath != NULL)
+    free(ti->basepath);
+  if (ti->fullpath != NULL)
+    free(ti->fullpath);
+
+  if (read(audio.mixer_pipe[0], set_name,
+          snd_ctrl->data_len) != snd_ctrl->data_len ||
+      read(audio.mixer_pipe[0], override_level_artwork,
+          sizeof(boolean)) != sizeof(boolean) ||
+      read(audio.mixer_pipe[0], leveldir_current,
+          sizeof(TreeInfo)) != sizeof(TreeInfo) ||
+      read(audio.mixer_pipe[0], ti,
+          sizeof(TreeInfo)) != sizeof(TreeInfo) ||
+      read(audio.mixer_pipe[0], &str_size1,
+          sizeof(unsigned long)) != sizeof(unsigned long) ||
+      read(audio.mixer_pipe[0], &str_size2,
+          sizeof(unsigned long)) != sizeof(unsigned long) ||
+      read(audio.mixer_pipe[0], &str_size3,
+          sizeof(unsigned long)) != sizeof(unsigned long))
+    Error(ERR_EXIT_SOUND_SERVER, "broken pipe -- no sounds");
+
+  leveldir_current->fullpath = checked_calloc(str_size1);
+  ti->basepath = checked_calloc(str_size2);
+  ti->fullpath = checked_calloc(str_size3);
+
+  if (read(audio.mixer_pipe[0], leveldir_current->fullpath,
+          str_size1) != str_size1 ||
+      read(audio.mixer_pipe[0], ti->basepath,
+          str_size2) != str_size2 ||
+      read(audio.mixer_pipe[0], ti->fullpath,
+          str_size3) != str_size3)
+    Error(ERR_EXIT_SOUND_SERVER, "broken pipe -- no sounds");
+
+  if (snd_ctrl->state & SND_CTRL_RELOAD_SOUNDS)
+    artwork.sounds_set_current_name = set_name;
+  else
+    artwork.music_set_current_name = set_name;
+}
+
+#endif /* AUDIO_UNIX_NATIVE */
+
+
+/* ------------------------------------------------------------------------- */
+/* mixer functions                                                           */
+/* ------------------------------------------------------------------------- */
+
+void Mixer_InitChannels()
 {
   int i;
 
-  struct SoundControl snd_ctrl;
-  fd_set sound_fdset;
+  for(i=0; i<audio.num_channels; i++)
+    mixer[i].active = FALSE;
+  mixer_active_channels = 0;
+}
+
+static void Mixer_ResetChannelExpiration(int channel)
+{
+  mixer[channel].playing_starttime = Counter();
+
+#if defined(TARGET_SDL)
+  if (IS_LOOP(mixer[channel]) && !IS_MUSIC(mixer[channel]))
+    Mix_ExpireChannel(channel, SOUND_LOOP_EXPIRATION_TIME);
+#endif
+}
 
-  close(audio.soundserver_pipe[1]);    /* no writing into pipe needed */
+static boolean Mixer_ChannelExpired(int channel)
+{
+  if (!mixer[channel].active)
+    return TRUE;
 
-  InitPlaylist();
+  if (IS_LOOP(mixer[channel]) && !IS_MUSIC(mixer[channel]) &&
+      DelayReached(&mixer[channel].playing_starttime,
+                  SOUND_LOOP_EXPIRATION_TIME))
+    return TRUE;
 
-  stereo_volume[PSND_MAX_LEFT2RIGHT] = 0;
-  for(i=0;i<PSND_MAX_LEFT2RIGHT;i++)
-    stereo_volume[i] =
-      (int)sqrt((float)(PSND_MAX_LEFT2RIGHT*PSND_MAX_LEFT2RIGHT-i*i));
+#if defined(TARGET_SDL)
 
-#if defined(PLATFORM_HPUX)
-  InitAudioDevice_HPUX();
+  if (!Mix_Playing(channel))
+    return TRUE;
+
+#elif defined(TARGET_ALLEGRO)
+
+  mixer[channel].playing_pos = voice_get_position(mixer[channel].voice);
+  mixer[channel].volume = voice_get_volume(mixer[channel].voice);
+
+  /* sound sample has completed playing or was completely faded out */
+  if (mixer[channel].playing_pos == -1 || mixer[channel].volume == 0)
+    return TRUE;
+
+#endif /* TARGET_ALLEGRO */
+
+  return FALSE;
+}
+
+static boolean Mixer_AllocateChannel(int channel)
+{
+#if defined(TARGET_ALLEGRO)
+  mixer[channel].voice = allocate_voice((SAMPLE *)mixer[channel].data_ptr);
+  if (mixer[channel].voice < 0)
+    return FALSE;
 #endif
 
-  FD_ZERO(&sound_fdset); 
-  FD_SET(audio.soundserver_pipe[0], &sound_fdset);
+  return TRUE;
+}
 
-  while(1)     /* wait for sound playing commands from client */
+static void Mixer_SetChannelProperties(int channel)
+{
+#if defined(TARGET_SDL)
+  Mix_Volume(channel, mixer[channel].volume);
+  Mix_SetPanning(channel,
+                SOUND_VOLUME_LEFT(mixer[channel].stereo_position),
+                SOUND_VOLUME_RIGHT(mixer[channel].stereo_position));
+#elif defined(TARGET_ALLEGRO)
+  voice_set_volume(mixer[channel].voice, mixer[channel].volume);
+  voice_set_pan(mixer[channel].voice, mixer[channel].stereo_position);
+#endif
+}
+
+static void Mixer_StartChannel(int channel)
+{
+#if defined(TARGET_SDL)
+  Mix_PlayChannel(channel, mixer[channel].data_ptr,
+                 IS_LOOP(mixer[channel]) ? -1 : 0);
+#elif defined(TARGET_ALLEGRO)
+  if (IS_LOOP(mixer[channel]))
+    voice_set_playmode(mixer[channel].voice, PLAYMODE_LOOP);
+
+  voice_start(mixer[channel].voice);       
+#endif
+}
+
+static void Mixer_PlayChannel(int channel)
+{
+  /* start with inactive channel in case something goes wrong */
+  mixer[channel].active = FALSE;
+
+  if (mixer[channel].type != MUS_TYPE_WAV)
+    return;
+
+  if (!Mixer_AllocateChannel(channel))
+    return;
+
+  Mixer_SetChannelProperties(channel);
+  Mixer_StartChannel(channel);
+
+  Mixer_ResetChannelExpiration(channel);
+
+  mixer[channel].playing_pos = 0;
+  mixer[channel].active = TRUE;
+  mixer_active_channels++;
+}
+
+static void Mixer_PlayMusicChannel()
+{
+  Mixer_PlayChannel(audio.music_channel);
+
+#if defined(TARGET_SDL)
+  if (mixer[audio.music_channel].type != MUS_TYPE_WAV)
   {
-    FD_SET(audio.soundserver_pipe[0], &sound_fdset);
-    select(audio.soundserver_pipe[0] + 1, &sound_fdset, NULL, NULL, NULL);
-    if (!FD_ISSET(audio.soundserver_pipe[0], &sound_fdset))
-      continue;
-    if (read(audio.soundserver_pipe[0], &snd_ctrl, sizeof(snd_ctrl))
-       != sizeof(snd_ctrl))
-      Error(ERR_EXIT_SOUND_SERVER, "broken pipe -- no sounds");
+    /* Mix_VolumeMusic() must be called _after_ Mix_PlayMusic() --
+       this looks like a bug in the SDL_mixer library */
+    Mix_PlayMusic(mixer[audio.music_channel].data_ptr, -1);
+    Mix_VolumeMusic(SOUND_MAX_VOLUME);
+  }
+#endif
+}
 
-#if defined(AUDIO_STREAMING_DSP)
+static void Mixer_StopChannel(int channel)
+{
+  if (!mixer[channel].active)
+    return;
 
-    if (snd_ctrl.fade_sound)
-    {
-      if (!playing_sounds)
-       continue;
+#if defined(TARGET_SDL)
+  Mix_HaltChannel(channel);
+#elif defined(TARGET_ALLEGRO)
+  voice_set_volume(mixer[channel].voice, 0);
+  deallocate_voice(mixer[channel].voice);
+#endif
 
-      for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-       if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
-         playlist[i].fade_sound = TRUE;
-    }
-    else if (snd_ctrl.stop_all_sounds)
+  mixer[channel].active = FALSE;
+  mixer_active_channels--;
+}
+
+static void Mixer_StopMusicChannel()
+{
+  Mixer_StopChannel(audio.music_channel);
+
+#if defined(TARGET_SDL)
+  Mix_HaltMusic();
+#endif
+}
+
+static void Mixer_FadeChannel(int channel)
+{
+  if (!mixer[channel].active)
+    return;
+
+  mixer[channel].state |= SND_CTRL_FADE;
+
+#if defined(TARGET_SDL)
+  Mix_FadeOutChannel(channel, SOUND_FADING_INTERVAL);
+#elif defined(TARGET_ALLEGRO)
+  if (voice_check(mixer[channel].voice))
+    voice_ramp_volume(mixer[channel].voice, SOUND_FADING_INTERVAL, 0);
+#endif
+}
+
+static void Mixer_FadeMusicChannel()
+{
+  Mixer_FadeChannel(audio.music_channel);
+
+#if defined(TARGET_SDL)
+  Mix_FadeOutMusic(SOUND_FADING_INTERVAL);
+#endif
+}
+
+static void Mixer_UnFadeChannel(int channel)
+{
+  if (!mixer[channel].active || !IS_FADING(mixer[channel]))
+    return;
+
+  mixer[channel].state &= ~SND_CTRL_FADE;
+  mixer[channel].volume = SOUND_MAX_VOLUME;
+
+#if defined(TARGET_SDL)
+  Mix_ExpireChannel(channel, -1);
+  Mix_Volume(channel, mixer[channel].volume);
+#elif defined(TARGET_ALLEGRO)
+  voice_stop_volumeramp(mixer[channel].voice);
+  voice_ramp_volume(mixer[channel].voice, SOUND_FADING_INTERVAL,
+                   mixer[channel].volume);
+#endif
+}
+
+static void Mixer_InsertSound(SoundControl snd_ctrl)
+{
+  SoundInfo *snd_info;
+  int i, k;
+
+#if 0
+  printf("NEW SOUND %d HAS ARRIVED [%d]\n", snd_ctrl.nr, num_sounds);
+#endif
+
+#if 0
+  printf("%d ACTIVE CHANNELS\n", mixer_active_channels);
+#endif
+
+  if (IS_MUSIC(snd_ctrl))
+  {
+    if (num_music == 0)
+      return;
+
+    snd_ctrl.nr = snd_ctrl.nr % num_music;
+  }
+  else if (snd_ctrl.nr >= num_sounds)
+    return;
+
+  snd_info = (IS_MUSIC(snd_ctrl) ? Music[snd_ctrl.nr] : Sound[snd_ctrl.nr]);
+  if (snd_info == NULL)
+    return;
+
+  /* copy sound sample and format information */
+  snd_ctrl.type     = snd_info->type;
+  snd_ctrl.format   = snd_info->format;
+  snd_ctrl.data_ptr = snd_info->data_ptr;
+  snd_ctrl.data_len = snd_info->data_len;
+
+  /* play music samples on a dedicated music channel */
+  if (IS_MUSIC(snd_ctrl))
+  {
+#if 0
+    printf("PLAY MUSIC WITH VOLUME/STEREO %d/%d\n",
+          snd_ctrl.volume, snd_ctrl.stereo_position);
+#endif
+
+    mixer[audio.music_channel] = snd_ctrl;
+    Mixer_PlayMusicChannel();
+
+    return;
+  }
+
+  /* check if (and how often) this sound sample is already playing */
+  for (k=0, i=audio.first_sound_channel; i<audio.num_channels; i++)
+    if (mixer[i].active && SAME_SOUND_DATA(mixer[i], snd_ctrl))
+      k++;
+
+#if 0
+  printf("SOUND %d [CURRENTLY PLAYING %d TIMES]\n", snd_ctrl.nr, k);
+#endif
+
+  /* reset expiration delay for already playing loop sounds */
+  if (k > 0 && IS_LOOP(snd_ctrl))
+  {
+    for(i=audio.first_sound_channel; i<audio.num_channels; i++)
     {
-      if (!playing_sounds)
-       continue;
+      if (mixer[i].active && SAME_SOUND_DATA(mixer[i], snd_ctrl))
+      {
+#if 0
+       printf("RESETTING EXPIRATION FOR SOUND %d\n", snd_ctrl.nr);
+#endif
+
+       if (IS_FADING(mixer[i]))
+         Mixer_UnFadeChannel(i);
 
-      for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-       playlist[i]=emptySoundControl;
-      playing_sounds=0;
+       /* restore settings like volume and stereo position */
+       mixer[i].volume = snd_ctrl.volume;
+       mixer[i].stereo_position = snd_ctrl.stereo_position;
 
-      close(audio.device_fd);
+       Mixer_SetChannelProperties(i);
+       Mixer_ResetChannelExpiration(i);
+
+#if 0
+       printf("RESETTING VOLUME/STEREO FOR SOUND %d TO %d/%d\n",
+              snd_ctrl.nr, snd_ctrl.volume, snd_ctrl.stereo_position);
+#endif
+      }
     }
-    else if (snd_ctrl.stop_sound)
+
+    return;
+  }
+
+#if 0
+  printf("PLAYING NEW SOUND %d\n", snd_ctrl.nr);
+#endif
+
+  /* don't play sound more than n times simultaneously (with n == 2 for now) */
+  if (k >= 2)
+  {
+    unsigned long playing_current = Counter();
+    int longest = 0, longest_nr = audio.first_sound_channel;
+
+    /* look for oldest equal sound */
+    for(i=audio.first_sound_channel; i<audio.num_channels; i++)
     {
-      if (!playing_sounds)
+      int playing_time = playing_current - mixer[i].playing_starttime;
+      int actual;
+
+      if (!mixer[i].active || !SAME_SOUND_NR(mixer[i], snd_ctrl))
        continue;
 
-      for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-       if (playlist[i].nr == snd_ctrl.nr)
-       {
-         playlist[i]=emptySoundControl;
-         playing_sounds--;
-       }
+      actual = 1000 * playing_time / mixer[i].data_len;
 
-      if (!playing_sounds)
-       close(audio.device_fd);
+      if (actual >= longest)
+      {
+       longest = actual;
+       longest_nr = i;
+      }
     }
 
-    if (playing_sounds || snd_ctrl.active)
+    Mixer_StopChannel(longest_nr);
+  }
+
+  /* If all (non-music) channels are active, stop the channel that has
+     played its sound sample most completely (in percent of the sample
+     length). As we cannot currently get the actual playing position
+     of the channel's sound sample when compiling with the SDL mixer
+     library, we use the current playing time (in milliseconds) instead. */
+
+  if (mixer_active_channels ==
+      audio.num_channels - (mixer[audio.music_channel].active ? 0 : 1))
+  {
+    unsigned long playing_current = Counter();
+    int longest = 0, longest_nr = audio.first_sound_channel;
+
+    for (i=audio.first_sound_channel; i<audio.num_channels; i++)
     {
-      struct timeval delay = { 0, 0 };
-      byte *sample_ptr;
-      long sample_size;
-      static long max_sample_size = 0;
-      static long fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
-      int sample_rate = DEFAULT_AUDIO_SAMPLE_RATE;
-      boolean stereo = TRUE;
-
-      if (playing_sounds ||
-         (audio.device_fd = OpenAudioDevice(audio.device_name)) >= 0)
+      int playing_time = playing_current - mixer[i].playing_starttime;
+      int actual = 1000 * playing_time / mixer[i].data_len;
+
+      if (!IS_LOOP(mixer[i]) && actual > longest)
       {
-       if (!playing_sounds)    /* we just opened the audio device */
-       {
-#if defined(AUDIO_LINUX_IOCTL)
-         stereo = InitAudioDevice_Linux(fragment_size, sample_rate);
-#elif defined(PLATFORM_NETBSD)
-         stereo = InitAudioDevice_NetBSD(fragment_size, sample_rate);
-#endif
-         max_sample_size = fragment_size / (stereo ? 2 : 1);
-       }
-
-       if (snd_ctrl.active)    /* new sound has arrived */
-         SoundServer_InsertNewSound(snd_ctrl);
-
-       while(playing_sounds &&
-             select(audio.soundserver_pipe[0] + 1,
-                    &sound_fdset, NULL, NULL, &delay) < 1)
-       {       
-         FD_SET(audio.soundserver_pipe[0], &sound_fdset);
-
-         /* first clear the last premixing buffer */
-         memset(premix_last_buffer, 0, fragment_size * sizeof(int));
-
-         for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-         {
-           int j;
-
-           if (!playlist[i].active)
-             continue;
-
-           /* get pointer and size of the actual sound sample */
-           sample_ptr = playlist[i].data_ptr+playlist[i].playingpos;
-           sample_size =
-             MIN(max_sample_size,playlist[i].data_len-playlist[i].playingpos);
-           playlist[i].playingpos += sample_size;
-
-           /* fill the first mixing buffer with original sample */
-           memcpy(premix_first_buffer,sample_ptr,sample_size);
-
-           /* are we about to restart a looping sound? */
-           if (playlist[i].loop && sample_size<max_sample_size)
-           {
-             playlist[i].playingpos = max_sample_size-sample_size;
-             memcpy(premix_first_buffer+sample_size,
-                    playlist[i].data_ptr,max_sample_size-sample_size);
-             sample_size = max_sample_size;
-           }
-
-           /* decrease volume if sound is fading out */
-           if (playlist[i].fade_sound &&
-               playlist[i].volume >= SOUND_FADING_VOLUME_THRESHOLD)
-             playlist[i].volume -= SOUND_FADING_VOLUME_STEP;
-
-           /* adjust volume of actual sound sample */
-           if (playlist[i].volume != PSND_MAX_VOLUME)
-             for(j=0;j<sample_size;j++)
-               premix_first_buffer[j] =
-                 (playlist[i].volume * (int)premix_first_buffer[j])
-                   >> PSND_MAX_VOLUME_BITS;
-
-           /* fill the last mixing buffer with stereo or mono sound */
-           if (stereo)
-           {
-             int middle_pos = PSND_MAX_LEFT2RIGHT/2;
-             int left_volume = stereo_volume[middle_pos+playlist[i].stereo];
-             int right_volume = stereo_volume[middle_pos-playlist[i].stereo];
-
-             for(j=0;j<sample_size;j++)
-             {
-               premix_left_buffer[j] =
-                 (left_volume * (int)premix_first_buffer[j])
-                   >> PSND_MAX_LEFT2RIGHT_BITS;
-               premix_right_buffer[j] =
-                 (right_volume * (int)premix_first_buffer[j])
-                   >> PSND_MAX_LEFT2RIGHT_BITS;
-               premix_last_buffer[2*j+0] += premix_left_buffer[j];
-               premix_last_buffer[2*j+1] += premix_right_buffer[j];
-             }
-           }
-           else
-           {
-             for(j=0;j<sample_size;j++)
-               premix_last_buffer[j] += (int)premix_first_buffer[j];
-           }
-
-           /* delete completed sound entries from the playlist */
-           if (playlist[i].playingpos >= playlist[i].data_len)
-           {
-             if (playlist[i].loop)
-               playlist[i].playingpos = 0;
-             else
-             {
-               playlist[i] = emptySoundControl;
-               playing_sounds--;
-             }
-           }
-           else if (playlist[i].volume <= SOUND_FADING_VOLUME_THRESHOLD)
-           {
-             playlist[i] = emptySoundControl;
-             playing_sounds--;
-           }
-         }
-
-         /* put last mixing buffer to final playing buffer */
-         for(i=0; i<fragment_size; i++)
-         {
-           if (premix_last_buffer[i]<-255)
-             playing_buffer[i] = 0;
-           else if (premix_last_buffer[i]>255)
-             playing_buffer[i] = 255;
-           else
-             playing_buffer[i] = (premix_last_buffer[i]>>1)^0x80;
-         }
-
-         /* finally play the sound fragment */
-         write(audio.device_fd, playing_buffer, fragment_size);
-       }
-
-       /* if no sounds playing, free device for other sound programs */
-       if (!playing_sounds)
-         close(audio.device_fd);
+       longest = actual;
+       longest_nr = i;
       }
     }
 
-#else /* !AUDIO_STREAMING_DSP */
+    Mixer_StopChannel(longest_nr);
+  }
 
-    if (snd_ctrl.active && !snd_ctrl.loop)
+  /* add the new sound to the mixer */
+  for(i=0; i<audio.num_channels; i++)
+  {
+#if 0
+    printf("CHECKING CHANNEL %d FOR SOUND %d ...\n", i, snd_ctrl.nr);
+#endif
+
+    /*
+    if (!mixer[i].active ||
+       (IS_MUSIC(snd_ctrl) && i == audio.music_channel))
+    */
+    if ((i == audio.music_channel && IS_MUSIC(snd_ctrl)) ||
+       (i != audio.music_channel && !mixer[i].active))
     {
-      struct timeval delay = { 0, 0 };
-      byte *sample_ptr;
-      long sample_size, max_sample_size = SND_BLOCKSIZE;
-      long sample_rate = 8000; /* standard "/dev/audio" sampling rate */
-      int wait_percent = 90;   /* wait 90% of the real playing time */
-      int i;
+#if 0
+      printf("ADDING NEW SOUND %d TO MIXER\n", snd_ctrl.nr);
+#endif
 
-      if ((audio.device_fd = OpenAudioDevice(audio.device_name)) >= 0)
+#if 1
+#if defined(AUDIO_UNIX_NATIVE)
+      if (snd_info->data_len == 0)
       {
-       playing_sounds = 1;
-
-       while(playing_sounds &&
-             select(audio.soundserver_pipe[0] + 1,
-                    &sound_fdset, NULL, NULL, &delay) < 1)
-       {       
-         FD_SET(audio.soundserver_pipe[0], &sound_fdset);
-
-         /* get pointer and size of the actual sound sample */
-         sample_ptr = snd_ctrl.data_ptr + snd_ctrl.playingpos;
-         sample_size =
-           MIN(max_sample_size, snd_ctrl.data_len - snd_ctrl.playingpos);
-         snd_ctrl.playingpos += sample_size;
-
-         /* fill the first mixing buffer with original sample */
-         memcpy(premix_first_buffer,sample_ptr,sample_size);
-
-         /* adjust volume of actual sound sample */
-         if (snd_ctrl.volume != PSND_MAX_VOLUME)
-           for(i=0;i<sample_size;i++)
-             premix_first_buffer[i] =
-               (snd_ctrl.volume * (int)premix_first_buffer[i])
-                 >> PSND_MAX_VOLUME_BITS;
-
-         for(i=0;i<sample_size;i++)
-           playing_buffer[i] =
-             linear_to_ulaw(((int)premix_first_buffer[i]) << 8);
-
-         if (snd_ctrl.playingpos >= snd_ctrl.data_len)
-           playing_sounds = 0;
-
-         /* finally play the sound fragment */
-         write(audio.device_fd,playing_buffer,sample_size);
-
-         delay.tv_sec = 0;
-         delay.tv_usec = ((sample_size*10*wait_percent)/(sample_rate))*1000;
-       }
-       close(audio.device_fd);
+       printf("THIS SHOULD NEVER HAPPEN! [snd_info->data_len == 0]\n");
+      }
+#endif
+#endif
+
+#if 1
+      if (IS_MUSIC(snd_ctrl) && i == audio.music_channel && mixer[i].active)
+      {
+       printf("THIS SHOULD NEVER HAPPEN! [adding music twice]\n");
+
+#if 1
+       Mixer_StopChannel(i);
+#endif
       }
+#endif
+
+      mixer[i] = snd_ctrl;
+      Mixer_PlayChannel(i);
+
+      break;
     }
-#endif /* !AUDIO_STREAMING_DSP */
   }
 }
-#endif /* PLATFORM_UNIX */
 
-#if defined(PLATFORM_MSDOS)
-static void sound_handler(struct SoundControl snd_ctrl)
+static void HandleSoundRequest(SoundControl snd_ctrl)
+{
+  int i;
+
+#if defined(AUDIO_UNIX_NATIVE)
+  if (IS_PARENT_PROCESS(audio.mixer_pid))
+  {
+    SendSoundControlToMixerProcess(&snd_ctrl);
+    return;
+  }
+#endif
+
+  /* deactivate channels that have expired since the last request */
+  for (i=0; i<audio.num_channels; i++)
+    if (mixer[i].active && Mixer_ChannelExpired(i))
+      Mixer_StopChannel(i);
+
+  if (IS_RELOADING(snd_ctrl))          /* load new sound or music files */
+  {
+    Mixer_StopMusicChannel();
+    for(i=audio.first_sound_channel; i<audio.num_channels; i++)
+      Mixer_StopChannel(i);
+
+#if defined(AUDIO_UNIX_NATIVE)
+    CloseAudioDevice(&audio.device_fd);
+    ReadReloadInfoFromPipe(&snd_ctrl);
+#endif
+
+    if (snd_ctrl.state & SND_CTRL_RELOAD_SOUNDS)
+      ReloadCustomSounds();
+    else
+      ReloadCustomMusic();
+  }
+  else if (IS_FADING(snd_ctrl))                /* fade out existing sound or music */
+  {
+    if (IS_MUSIC(snd_ctrl))
+    {
+      Mixer_FadeMusicChannel();
+      return;
+    }
+
+    for(i=audio.first_sound_channel; i<audio.num_channels; i++)
+      if (SAME_SOUND_NR(mixer[i], snd_ctrl) || ALL_SOUNDS(snd_ctrl))
+       Mixer_FadeChannel(i);
+  }
+  else if (IS_STOPPING(snd_ctrl))      /* stop existing sound or music */
+  {
+    if (IS_MUSIC(snd_ctrl))
+    {
+      Mixer_StopMusicChannel();
+      return;
+    }
+
+    for(i=audio.first_sound_channel; i<audio.num_channels; i++)
+      if (SAME_SOUND_NR(mixer[i], snd_ctrl) || ALL_SOUNDS(snd_ctrl))
+       Mixer_StopChannel(i);
+
+#if defined(AUDIO_UNIX_NATIVE)
+    if (!mixer_active_channels)
+      CloseAudioDevice(&audio.device_fd);
+#endif
+  }
+  else if (snd_ctrl.active)            /* add new sound to mixer */
+  {
+    Mixer_InsertSound(snd_ctrl);
+  }
+}
+
+void StartMixer(void)
+{
+  int i;
+
+#if 0
+  SDL_version compile_version;
+  const SDL_version *link_version;
+  MIX_VERSION(&compile_version);
+  printf("compiled with SDL_mixer version: %d.%d.%d\n", 
+        compile_version.major,
+        compile_version.minor,
+        compile_version.patch);
+  link_version = Mix_Linked_Version();
+  printf("running with SDL_mixer version: %d.%d.%d\n", 
+        link_version->major,
+        link_version->minor,
+        link_version->patch);
+#endif
+
+  if (!audio.sound_available)
+    return;
+
+  /* initialize stereo position conversion information */
+  for(i=0; i<=SOUND_MAX_LEFT2RIGHT; i++)
+    stereo_volume[i] =
+      (int)sqrt((float)(SOUND_MAX_LEFT2RIGHT * SOUND_MAX_LEFT2RIGHT - i * i));
+
+#if defined(AUDIO_UNIX_NATIVE)
+  if (!ForkAudioProcess())
+    audio.sound_available = FALSE;
+#endif
+}
+
+#if defined(AUDIO_UNIX_NATIVE)
+
+static void CopySampleToMixingBuffer(SoundControl *snd_ctrl,
+                                    int sample_pos, int sample_size,
+                                    short *buffer_ptr)
 {
+  void *sample_ptr = snd_ctrl->data_ptr;
   int i;
 
-  if (snd_ctrl.fade_sound)
-  {
-    if (!playing_sounds)
-      return;
+  if (snd_ctrl->format == AUDIO_FORMAT_U8)
+    for (i=0; i<sample_size; i++)
+      *buffer_ptr++ =
+       ((short)(((byte *)sample_ptr)[sample_pos + i] ^ 0x80)) << 8;
+  else /* AUDIO_FORMAT_S16 */
+    for (i=0; i<sample_size; i++)
+      *buffer_ptr++ =
+       ((short *)sample_ptr)[sample_pos + i];
+}
 
-    for (i=0; i<MAX_SOUNDS_PLAYING; i++)
-      if ((snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr) &&
-         !playlist[i].fade_sound)
-      {
-       playlist[i].fade_sound = TRUE;
-       if (voice_check(playlist[i].voice))
-         voice_ramp_volume(playlist[i].voice, 1000, 0);
-       playlist[i].loop = PSND_NO_LOOP;
-      }
-  }
-  else if (snd_ctrl.stop_all_sounds)
-  {
-    if (!playing_sounds)
-      return;
-    SoundServer_StopAllSounds();
-  }
-  else if (snd_ctrl.stop_sound)
-  {
-    if (!playing_sounds)
-      return;
-    SoundServer_StopSound(snd_ctrl.nr);
-  }
+#if defined(AUDIO_STREAMING_DSP)
+static void Mixer_Main_DSP()
+{
+  static short premix_first_buffer[SND_BLOCKSIZE];
+  static short premix_left_buffer[SND_BLOCKSIZE];
+  static short premix_right_buffer[SND_BLOCKSIZE];
+  static long premix_last_buffer[SND_BLOCKSIZE];
+  static byte playing_buffer[SND_BLOCKSIZE];
+  boolean stereo;
+  int fragment_size;
+  int sample_bytes;
+  int max_sample_size;
+  int i, j;
+
+  if (!mixer_active_channels)
+    return;
 
-  for (i=0; i<MAX_SOUNDS_PLAYING; i++)
+  if (audio.device_fd < 0)
   {
-    if (!playlist[i].active || playlist[i].loop)
-      continue;
+    if ((audio.device_fd = OpenAudioDevice(audio.device_name)) < 0)
+      return;
 
-    playlist[i].playingpos = voice_get_position(playlist[i].voice);
-    playlist[i].volume = voice_get_volume(playlist[i].voice);
-    if (playlist[i].playingpos == -1 || !playlist[i].volume)
-    {
-      deallocate_voice(playlist[i].voice);
-      playlist[i] = emptySoundControl;
-      playing_sounds--;
-    }
+    InitAudioDevice(&afmt);
   }
 
-  if (snd_ctrl.active)
-    SoundServer_InsertNewSound(snd_ctrl);
-}
-#endif /* PLATFORM_MSDOS */
+  stereo = afmt.stereo;
+  fragment_size = afmt.fragment_size;
+  sample_bytes = (afmt.format & AUDIO_FORMAT_U8 ? 1 : 2);
+  max_sample_size = fragment_size / ((stereo ? 2 : 1) * sample_bytes);
 
-#if !defined(PLATFORM_WIN32)
-static void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
-{
-  int i, k;
+  /* first clear the last premixing buffer */
+  memset(premix_last_buffer, 0,
+        max_sample_size * (stereo ? 2 : 1) * sizeof(long));
 
-  /* if playlist is full, remove oldest sound */
-  if (playing_sounds==MAX_SOUNDS_PLAYING)
+  for(i=0; i<audio.num_channels; i++)
   {
-    int longest=0, longest_nr=0;
+    void *sample_ptr;
+    int sample_len;
+    int sample_pos;
+    int sample_size;
 
-    for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-    {
-#if !defined(PLATFORM_MSDOS)
-      int actual = 100 * playlist[i].playingpos / playlist[i].data_len;
-#else
-      int actual = playlist[i].playingpos;
-#endif
+    if (!mixer[i].active)
+      continue;
 
-      if (!playlist[i].loop && actual>longest)
-      {
-       longest=actual;
-       longest_nr=i;
-      }
+    if (Mixer_ChannelExpired(i))
+    {
+      Mixer_StopChannel(i);
+      continue;
     }
-#if defined(PLATFORM_MSDOS)
-    voice_set_volume(playlist[longest_nr].voice, 0);
-    deallocate_voice(playlist[longest_nr].voice);
-#endif
-    playlist[longest_nr] = emptySoundControl;
-    playing_sounds--;
-  }
 
-  /* check if sound is already being played (and how often) */
-  for(k=0,i=0;i<MAX_SOUNDS_PLAYING;i++)
-  {
-    if (playlist[i].nr == snd_ctrl.nr)
-      k++;
-  }
+    /* pointer, lenght and actual playing position of sound sample */
+    sample_ptr = mixer[i].data_ptr;
+    sample_len = mixer[i].data_len;
+    sample_pos = mixer[i].playing_pos;
+    sample_size = MIN(max_sample_size, sample_len - sample_pos);
+    mixer[i].playing_pos += sample_size;
 
-  /* restart loop sounds only if they are just fading out */
-  if (k>=1 && snd_ctrl.loop)
-  {
-    for(i=0;i<MAX_SOUNDS_PLAYING;i++)
+    /* copy original sample to first mixing buffer */
+    CopySampleToMixingBuffer(&mixer[i], sample_pos, sample_size,
+                            premix_first_buffer);
+
+    /* are we about to restart a looping sound? */
+    if (IS_LOOP(mixer[i]) && sample_size < max_sample_size)
     {
-      if (playlist[i].nr == snd_ctrl.nr && playlist[i].fade_sound)
+      while (sample_size < max_sample_size)
       {
-       playlist[i].fade_sound = FALSE;
-       playlist[i].volume = PSND_MAX_VOLUME;
-#if defined(PLATFORM_MSDOS)
-        playlist[i].loop = PSND_LOOP;
-        voice_stop_volumeramp(playlist[i].voice);
-        voice_ramp_volume(playlist[i].voice, playlist[i].volume, 1000);
-#endif
+       int restarted_sample_size =
+         MIN(max_sample_size - sample_size, sample_len);
+
+       if (mixer[i].format == AUDIO_FORMAT_U8)
+         for (j=0; j<restarted_sample_size; j++)
+           premix_first_buffer[sample_size + j] =
+             ((short)(((byte *)sample_ptr)[j] ^ 0x80)) << 8;
+       else
+         for (j=0; j<restarted_sample_size; j++)
+           premix_first_buffer[sample_size + j] =
+             ((short *)sample_ptr)[j];
+
+       mixer[i].playing_pos = restarted_sample_size;
+       sample_size += restarted_sample_size;
       }
     }
-    return;
-  }
 
-  /* don't play sound more than n times simultaneously (with n == 2 for now) */
-  if (k>=2)
-  {
-    int longest=0, longest_nr=0;
+    /* decrease volume if sound is fading out */
+    if (IS_FADING(mixer[i]) &&
+       mixer[i].volume >= SOUND_FADING_VOLUME_THRESHOLD)
+      mixer[i].volume -= SOUND_FADING_VOLUME_STEP;
 
-    /* look for oldest equal sound */
-    for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-    {
-      int actual;
+    /* adjust volume of actual sound sample */
+    if (mixer[i].volume != SOUND_MAX_VOLUME)
+      for(j=0; j<sample_size; j++)
+       premix_first_buffer[j] =
+         mixer[i].volume * (long)premix_first_buffer[j] / SOUND_MAX_VOLUME;
 
-      if (!playlist[i].active || playlist[i].nr != snd_ctrl.nr)
-       continue;
+    /* fill the last mixing buffer with stereo or mono sound */
+    if (stereo)
+    {
+      int left_volume  = SOUND_VOLUME_LEFT(mixer[i].stereo_position);
+      int right_volume = SOUND_VOLUME_RIGHT(mixer[i].stereo_position);
 
-#if !defined(PLATFORM_MSDOS)
-      actual = 100 * playlist[i].playingpos / playlist[i].data_len;
-#else
-      actual = playlist[i].playingpos;
-#endif
-      if (actual>=longest)
+      for(j=0; j<sample_size; j++)
       {
-       longest=actual;
-       longest_nr=i;
+       premix_left_buffer[j] =
+         left_volume  * premix_first_buffer[j] / SOUND_MAX_LEFT2RIGHT;
+       premix_right_buffer[j] =
+         right_volume * premix_first_buffer[j] / SOUND_MAX_LEFT2RIGHT;
+
+       premix_last_buffer[2 * j + 0] += premix_left_buffer[j];
+       premix_last_buffer[2 * j + 1] += premix_right_buffer[j];
       }
     }
+    else
+    {
+      for(j=0; j<sample_size; j++)
+       premix_last_buffer[j] += premix_first_buffer[j];
+    }
 
-#if defined(PLATFORM_MSDOS)
-    voice_set_volume(playlist[longest_nr].voice, 0);
-    deallocate_voice(playlist[longest_nr].voice);
-#endif
-    playlist[longest_nr] = emptySoundControl;
-    playing_sounds--;
+    /* delete completed sound entries from the mixer */
+    if (mixer[i].playing_pos >= mixer[i].data_len)
+    {
+      if (IS_LOOP(mixer[i]))
+       mixer[i].playing_pos = 0;
+      else
+       Mixer_StopChannel(i);
+    }
+    else if (mixer[i].volume <= SOUND_FADING_VOLUME_THRESHOLD)
+      Mixer_StopChannel(i);
   }
 
-  /* neuen Sound in Liste packen */
-  for(i=0;i<MAX_SOUNDS_PLAYING;i++)
+  /* prepare final playing buffer according to system audio format */
+  for(i=0; i<max_sample_size * (stereo ? 2 : 1); i++)
   {
-    if (!playlist[i].active)
-    {
-      playlist[i] = snd_ctrl;
-      playing_sounds++;
+    /* cut off at 17 bit value */
+    if (premix_last_buffer[i] < -65535)
+      premix_last_buffer[i] = -65535;
+    else if (premix_last_buffer[i] > 65535)
+      premix_last_buffer[i] = 65535;
 
-#if defined(PLATFORM_MSDOS)
-      playlist[i].voice = allocate_voice(Sound[snd_ctrl.nr].sample_ptr);
-      if(snd_ctrl.loop)
-        voice_set_playmode(playlist[i].voice, PLAYMODE_LOOP);
-      voice_set_volume(playlist[i].voice, snd_ctrl.volume);
-      voice_set_pan(playlist[i].voice, snd_ctrl.stereo);
-      voice_start(playlist[i].voice);       
-#endif
-      break;
+    /* shift to 16 bit value */
+    premix_last_buffer[i] >>= 1;
+
+    if (afmt.format & AUDIO_FORMAT_U8)
+    {
+      playing_buffer[i] = (premix_last_buffer[i] >> 8) ^ 0x80;
+    }
+    else if (afmt.format & AUDIO_FORMAT_LE)    /* 16 bit */
+    {
+      playing_buffer[2 * i + 0] = premix_last_buffer[i] & 0xff;
+      playing_buffer[2 * i + 1] = premix_last_buffer[i] >> 8;
+    }
+    else                                       /* big endian */
+    {
+      playing_buffer[2 * i + 0] = premix_last_buffer[i] >> 8;
+      playing_buffer[2 * i + 1] = premix_last_buffer[i] & 0xff;
     }
   }
-}
-#endif /* !PLATFORM_WIN32 */
-
-/*
-void SoundServer_FadeSound(int nr)
-{
-  int i;
 
-  if (!playing_sounds)
-    return;
+  /* finally play the sound fragment */
+  write(audio.device_fd, playing_buffer, fragment_size);
 
-  for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-    if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
-      playlist[i].fade_sound = TRUE;
+  if (!mixer_active_channels)
+    CloseAudioDevice(&audio.device_fd);
 }
-*/
-
-#if !defined(PLATFORM_WIN32)
-#if defined(PLATFORM_MSDOS)
-static void SoundServer_StopSound(int nr)
-{
-  int i;
 
-  if (!playing_sounds)
-    return;
+#else /* !AUDIO_STREAMING_DSP */
 
-  for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-    if (playlist[i].nr == nr)
-    {
-#if defined(PLATFORM_MSDOS)
-      voice_set_volume(playlist[i].voice, 0);
-      deallocate_voice(playlist[i].voice);
+static int Mixer_Main_SimpleAudio(SoundControl snd_ctrl)
+{
+  static short premix_first_buffer[SND_BLOCKSIZE];
+  static byte playing_buffer[SND_BLOCKSIZE];
+  int max_sample_size = SND_BLOCKSIZE;
+  void *sample_ptr;
+  int sample_len;
+  int sample_pos;
+  int sample_size;
+  int i, j;
+
+  i = 1;
+
+  /* pointer, lenght and actual playing position of sound sample */
+  sample_ptr = mixer[i].data_ptr;
+  sample_len = mixer[i].data_len;
+  sample_pos = mixer[i].playing_pos;
+  sample_size = MIN(max_sample_size, sample_len - sample_pos);
+  mixer[i].playing_pos += sample_size;
+
+  /* copy original sample to first mixing buffer */
+  CopySampleToMixingBuffer(&mixer[i], sample_pos, sample_size,
+                          premix_first_buffer);
+
+  /* adjust volume of actual sound sample */
+  if (mixer[i].volume != SOUND_MAX_VOLUME)
+    for(j=0; j<sample_size; j++)
+      premix_first_buffer[j] =
+       mixer[i].volume * (long)premix_first_buffer[j] / SOUND_MAX_VOLUME;
+
+  /* might be needed for u-law /dev/audio */
+#if 1
+  for(j=0; j<sample_size; j++)
+    playing_buffer[j] =
+      linear_to_ulaw(premix_first_buffer[j]);
 #endif
-      playlist[i] = emptySoundControl;
-      playing_sounds--;
-    }
 
-#if !defined(PLATFORM_MSDOS)
-  if (!playing_sounds)
-    close(audio.device_fd);
-#endif
-}
+  /* delete completed sound entries from the mixer */
+  if (mixer[i].playing_pos >= mixer[i].data_len)
+    Mixer_StopChannel(i);
 
-static void SoundServer_StopAllSounds()
-{
-  int i;
+  for(i=0; i<sample_size; i++)
+    playing_buffer[i] = (premix_first_buffer[i] >> 8) ^ 0x80;
 
-  for(i=0;i<MAX_SOUNDS_PLAYING;i++)
-  {
-#if defined(PLATFORM_MSDOS)
-    voice_set_volume(playlist[i].voice, 0);
-    deallocate_voice(playlist[i].voice);
-#endif
-    playlist[i]=emptySoundControl;
-  }
-  playing_sounds = 0;
+  /* finally play the sound fragment */
+  write(audio.device_fd, playing_buffer, sample_size);
 
-#if !defined(PLATFORM_MSDOS)
-  close(audio.device_fd);
-#endif
+  return sample_size;
 }
-#endif /* PLATFORM_MSDOS */
-#endif /* !PLATFORM_WIN32 */
-
-
-/* ------------------------------------------------------------------------- */
-/* platform dependant audio initialization code                              */
-/* ------------------------------------------------------------------------- */
+#endif /* !AUDIO_STREAMING_DSP */
 
-#if defined(AUDIO_LINUX_IOCTL)
-static boolean InitAudioDevice_Linux(long fragment_size, int sample_rate)
+void Mixer_Main()
 {
-  /* "ioctl()" expects pointer to 'int' value for stereo flag
-     (boolean is defined as 'char', which will not work here) */
-  int stereo = TRUE;
-  unsigned long fragment_spec = 0;
+  SoundControl snd_ctrl;
+  fd_set mixer_fdset;
 
-  /* determine logarithm (log2) of the fragment size */
-  for (fragment_spec=0; (1 << fragment_spec) < fragment_size;
-       fragment_spec++);
+  close(audio.mixer_pipe[1]);  /* no writing into pipe needed */
 
-  /* use two fragments (play one fragment, prepare the other);
-     one fragment would result in interrupted audio output, more
-     than two fragments would raise audio output latency to much */
-  fragment_spec |= 0x00020000;
+  Mixer_InitChannels();
 
-  /* Example for fragment specification:
-     - 2 buffers / 512 bytes (giving 1/16 second resolution for 8 kHz)
-     - (with stereo the effective buffer size will shrink to 256)
-     => fragment_size = 0x00020009 */
+#if defined(PLATFORM_HPUX)
+  InitAudioDevice(&afmt);
+#endif
 
-  if (ioctl(audio.device_fd, SNDCTL_DSP_SETFRAGMENT, &fragment_spec) < 0)
-    Error(ERR_EXIT_SOUND_SERVER,
-         "cannot set fragment size of /dev/dsp -- no sounds");
+  FD_ZERO(&mixer_fdset); 
+  FD_SET(audio.mixer_pipe[0], &mixer_fdset);
 
-  /* try if we can use stereo sound */
-  if (ioctl(audio.device_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
+  while(1)     /* wait for sound playing commands from client */
   {
-#ifdef DEBUG
-    static boolean reported = FALSE;
+    struct timeval delay = { 0, 0 };
 
-    if (!reported)
-    {
-      Error(ERR_RETURN, "cannot get stereo sound on /dev/dsp");
-      reported = TRUE;
-    }
-#endif
-    stereo = FALSE;
-  }
+    FD_SET(audio.mixer_pipe[0], &mixer_fdset);
+    select(audio.mixer_pipe[0] + 1, &mixer_fdset, NULL, NULL, NULL);
+    if (!FD_ISSET(audio.mixer_pipe[0], &mixer_fdset))
+      continue;
 
-  if (ioctl(audio.device_fd, SNDCTL_DSP_SPEED, &sample_rate) < 0)
-    Error(ERR_EXIT_SOUND_SERVER,
-         "cannot set sample rate of /dev/dsp -- no sounds");
+    ReadSoundControlFromMainProcess(&snd_ctrl);
 
-  /* get the real fragmentation size; this should return 512 */
-  if (ioctl(audio.device_fd, SNDCTL_DSP_GETBLKSIZE, &fragment_size) < 0)
-    Error(ERR_EXIT_SOUND_SERVER,
-         "cannot get fragment size of /dev/dsp -- no sounds");
+    HandleSoundRequest(snd_ctrl);
 
-  return (boolean)stereo;
-}
-#endif /* AUDIO_LINUX_IOCTL */
+#if defined(AUDIO_STREAMING_DSP)
 
-#if defined(PLATFORM_NETBSD)
-static boolean InitAudioDevice_NetBSD(long fragment_size, int sample_rate)
-{
-  audio_info_t a_info;
-  boolean stereo = TRUE;
+    while (mixer_active_channels &&
+          select(audio.mixer_pipe[0] + 1,
+                 &mixer_fdset, NULL, NULL, &delay) < 1)
+    {
+      FD_SET(audio.mixer_pipe[0], &mixer_fdset);
 
-  AUDIO_INITINFO(&a_info);
-  a_info.play.encoding = AUDIO_ENCODING_LINEAR8;
-  a_info.play.precision = 8;
-  a_info.play.channels = 2;
-  a_info.play.sample_rate = sample_rate;
-  a_info.blocksize = fragment_size;
+      Mixer_Main_DSP();
+    }
 
-  if (ioctl(audio.device_fd, AUDIO_SETINFO, &a_info) < 0)
-  {
-    /* try to disable stereo */
-    a_info.play.channels = 1;
-    stereo = FALSE;
+#else /* !AUDIO_STREAMING_DSP */
 
-    if (ioctl(audio.device_fd, AUDIO_SETINFO, &a_info) < 0)
-      Error(ERR_EXIT_SOUND_SERVER,
-           "cannot set sample rate of /dev/audio -- no sounds");
-  }
+    if (!snd_ctrl.active || IS_LOOP(snd_ctrl) ||
+       (audio.device_fd = OpenAudioDevice(audio.device_name)) < 0)
+      continue;
 
-  return stereo;
-}
-#endif /* PLATFORM_NETBSD */
+    InitAudioDevice(&afmt);
 
-#if defined(PLATFORM_HPUX)
-static boolean InitAudioDevice_HPUX()
-{
-  struct audio_describe ainfo;
-  int audio_ctl;
+    delay.tv_sec = 0;
+    delay.tv_usec = 0;
 
-  audio_ctl = open("/dev/audioCtl", O_WRONLY | O_NDELAY);
-  if (audio_ctl == -1)
-    Error(ERR_EXIT_SOUND_SERVER, "cannot open /dev/audioCtl -- no sounds");
+    while (mixer_active_channels &&
+          select(audio.mixer_pipe[0] + 1,
+                 &mixer_fdset, NULL, NULL, &delay) < 1)
+    {
+      int wait_percent = 90;   /* wait 90% of the real playing time */
+      int sample_size;
 
-  if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
-    Error(ERR_EXIT_SOUND_SERVER, "no audio info -- no sounds");
+      FD_SET(audio.mixer_pipe[0], &mixer_fdset);
 
-  if (ioctl(audio_ctl, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_ULAW) == -1)
-    Error(ERR_EXIT_SOUND_SERVER, "ulaw audio not available -- no sounds");
+      sample_size = Mixer_Main_SimpleAudio(snd_ctrl);
 
-  ioctl(audio_ctl, AUDIO_SET_CHANNELS, 1);
-  ioctl(audio_ctl, AUDIO_SET_SAMPLE_RATE, 8000);
+      delay.tv_sec = 0;
+      delay.tv_usec =
+       ((sample_size * 10 * wait_percent) / afmt.sample_rate) * 1000;
+    }
 
-  close(audio_ctl);
+    CloseAudioDevice(&audio.device_fd);
 
-  return TRUE; /* to provide common interface for InitAudioDevice_...() */
+    Mixer_InitChannels();      /* remove all sounds from mixer */
+
+#endif /* !AUDIO_STREAMING_DSP */
+  }
 }
-#endif /* PLATFORM_HPUX */
+#endif /* AUDIO_UNIX_NATIVE */
 
-#if defined(PLATFORM_UNIX) && !defined(AUDIO_STREAMING_DSP)
+
+#if defined(AUDIO_UNIX_NATIVE) && !defined(AUDIO_STREAMING_DSP)
 
 /* these two are stolen from "sox"... :) */
 
@@ -888,220 +1492,418 @@ static unsigned char linear_to_ulaw(int sample)
 ** Output: signed 16 bit linear sample
 */
 
-static int ulaw_to_linear(unsigned char ulawbyte)
-{
-  static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
-  int sign, exponent, mantissa, sample;
+static int ulaw_to_linear(unsigned char ulawbyte)
+{
+  static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
+  int sign, exponent, mantissa, sample;
+
+  ulawbyte = ~ ulawbyte;
+  sign = ( ulawbyte & 0x80 );
+  exponent = ( ulawbyte >> 4 ) & 0x07;
+  mantissa = ulawbyte & 0x0F;
+  sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
+  if (sign != 0)
+    sample = -sample;
+
+  return(sample);
+}
+#endif /* AUDIO_UNIX_NATIVE && !AUDIO_STREAMING_DSP */
+
+
+/* THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS            */
+/* ========================================================================= */
+/* THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS                          */
+
+#define CHUNK_ID_LEN            4       /* IFF style chunk id length */
+#define WAV_HEADER_SIZE                16      /* size of WAV file header */
+
+static SoundInfo *Load_WAV(char *filename)
+{
+  SoundInfo *snd_info;
+#if defined(AUDIO_UNIX_NATIVE)
+  struct SoundHeader_WAV header;
+#if 0
+  byte sound_header_buffer[WAV_HEADER_SIZE];
+  int i;
+#endif
+  char chunk_name[CHUNK_ID_LEN + 1];
+  int chunk_size;
+  FILE *file;
+#endif
+
+  if (!audio.sound_available)
+    return NULL;
+
+#if 0
+  printf("loading WAV file '%s'\n", filename);
+#endif
+
+  snd_info = checked_calloc(sizeof(SoundInfo));
+
+#if defined(TARGET_SDL)
+
+  if ((snd_info->data_ptr = Mix_LoadWAV(filename)) == NULL)
+  {
+    Error(ERR_WARN, "cannot read sound file '%s'", filename);
+    free(snd_info);
+    return NULL;
+  }
+
+  snd_info->data_len = ((Mix_Chunk *)snd_info->data_ptr)->alen;
+
+#elif defined(TARGET_ALLEGRO)
+
+  if ((snd_info->data_ptr = load_sample(filename)) == NULL)
+  {
+    Error(ERR_WARN, "cannot read sound file '%s'", filename);
+    free(snd_info);
+    return NULL;
+  }
+
+  snd_info->data_len = ((SAMPLE *)snd_info->data_ptr)->len;
+
+#else /* AUDIO_UNIX_NATIVE */
+
+  if ((file = fopen(filename, MODE_READ)) == NULL)
+  {
+    Error(ERR_WARN, "cannot open sound file '%s'", filename);
+    free(snd_info);
+    return NULL;
+  }
+
+  /* read chunk id "RIFF" */
+  getFileChunkLE(file, chunk_name, &chunk_size);
+  if (strcmp(chunk_name, "RIFF") != 0)
+  {
+    Error(ERR_WARN, "missing 'RIFF' chunk of sound file '%s'", filename);
+    fclose(file);
+    free(snd_info);
+    return NULL;
+  }
+
+  /* read "RIFF" type id "WAVE" */
+  getFileChunkLE(file, chunk_name, NULL);
+  if (strcmp(chunk_name, "WAVE") != 0)
+  {
+    Error(ERR_WARN, "missing 'WAVE' type ID of sound file '%s'", filename);
+    fclose(file);
+    free(snd_info);
+    return NULL;
+  }
+
+  while (getFileChunkLE(file, chunk_name, &chunk_size))
+  {
+    if (strcmp(chunk_name, "fmt ") == 0)
+    {
+      if (chunk_size < WAV_HEADER_SIZE)
+      {
+       Error(ERR_WARN, "sound file '%s': chunk 'fmt ' too short", filename);
+       fclose(file);
+       free(snd_info);
+       return NULL;
+      }
+
+      header.compression_code = getFile16BitLE(file);
+      header.num_channels = getFile16BitLE(file);
+      header.sample_rate = getFile32BitLE(file);
+      header.bytes_per_second = getFile32BitLE(file);
+      header.block_align = getFile16BitLE(file);
+      header.bits_per_sample = getFile16BitLE(file);
+
+      if (chunk_size > WAV_HEADER_SIZE)
+       ReadUnusedBytesFromFile(file, chunk_size - WAV_HEADER_SIZE);
+
+      if (header.compression_code != 1)
+      {
+       Error(ERR_WARN, "sound file '%s': compression code %d not supported",
+             filename, header.compression_code);
+       fclose(file);
+       free(snd_info);
+       return NULL;
+      }
+
+      if (header.num_channels != 1)
+      {
+       Error(ERR_WARN, "sound file '%s': number of %d channels not supported",
+             filename, header.num_channels);
+       fclose(file);
+       free(snd_info);
+       return NULL;
+      }
+
+      if (header.bits_per_sample != 8 && header.bits_per_sample != 16)
+      {
+       Error(ERR_WARN, "sound file '%s': %d bits per sample not supported",
+             filename, header.bits_per_sample);
+       fclose(file);
+       free(snd_info);
+       return NULL;
+      }
+
+      /* warn, but accept wrong sample rate (may be only slightly different) */
+      if (header.sample_rate != DEFAULT_AUDIO_SAMPLE_RATE)
+       Error(ERR_WARN, "sound file '%s': wrong sample rate %d instead of %d",
+             filename, header.sample_rate, DEFAULT_AUDIO_SAMPLE_RATE);
+
+#if 0
+      printf("WAV file: '%s'\n", filename);
+      printf("  Compression code: %d'\n", header.compression_code);
+      printf("  Number of channels: %d'\n", header.num_channels);
+      printf("  Sample rate: %ld'\n", header.sample_rate);
+      printf("  Average bytes per second: %ld'\n", header.bytes_per_second);
+      printf("  Block align: %d'\n", header.block_align);
+      printf("  Significant bits per sample: %d'\n", header.bits_per_sample);
+#endif
+    }
+    else if (strcmp(chunk_name, "data") == 0)
+    {
+      snd_info->data_len = chunk_size;
+      snd_info->data_ptr = checked_malloc(snd_info->data_len);
+
+      /* read sound data */
+      if (fread(snd_info->data_ptr, 1, snd_info->data_len, file) !=
+         snd_info->data_len)
+      {
+       Error(ERR_WARN,"cannot read 'data' chunk of sound file '%s'",filename);
+       fclose(file);
+       free(snd_info->data_ptr);
+       free(snd_info);
+       return NULL;
+      }
+
+      /* check for odd number of sample bytes (data chunk is word aligned) */
+      if ((chunk_size % 2) == 1)
+       ReadUnusedBytesFromFile(file, 1);
+    }
+    else       /* unknown chunk -- ignore */
+      ReadUnusedBytesFromFile(file, chunk_size);
+  }
 
-  ulawbyte = ~ ulawbyte;
-  sign = ( ulawbyte & 0x80 );
-  exponent = ( ulawbyte >> 4 ) & 0x07;
-  mantissa = ulawbyte & 0x0F;
-  sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
-  if (sign != 0)
-    sample = -sample;
+  fclose(file);
 
-  return(sample);
-}
-#endif /* PLATFORM_UNIX && !AUDIO_STREAMING_DSP */
+  if (snd_info->data_ptr == NULL)
+  {
+    Error(ERR_WARN, "missing 'data' chunk of sound file '%s'", filename);
+    free(snd_info);
+    return NULL;
+  }
 
+  if (header.bits_per_sample == 8)
+    snd_info->format = AUDIO_FORMAT_U8;
+  else                                 /* header.bits_per_sample == 16 */
+  {
+    snd_info->format = AUDIO_FORMAT_S16;
+    snd_info->data_len /= 2;           /* correct number of samples */
+  }
 
-/* THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS            */
-/* ========================================================================= */
-/* THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS                          */
+#endif /* AUDIO_UNIX_NATIVE */
 
+  snd_info->type = SND_TYPE_WAV;
+  snd_info->source_filename = getStringCopy(filename);
 
-#define CHUNK_ID_LEN            4       /* IFF style chunk id length */
-#define WAV_HEADER_SIZE                20      /* size of WAV file header */
+  return snd_info;
+}
 
-static boolean LoadSoundExt(char *sound_name, boolean is_music)
+static void deleteSoundEntry(SoundInfo **snd_info)
 {
-  struct SampleInfo *snd_info;
-  char filename[256];
-#if !defined(TARGET_SDL) && !defined(PLATFORM_MSDOS)
-  byte sound_header_buffer[WAV_HEADER_SIZE];
-  char chunk[CHUNK_ID_LEN + 1];
-  int chunk_size, dummy;
-  FILE *file;
-  int i;
-#endif
+  if (*snd_info)
+  {
+    char *filename = (*snd_info)->source_filename;
 
-  if (!audio.sound_available)
-    return FALSE;
+#if 0
+    printf("[decrementing reference counter of sound '%s']\n", filename);
+#endif
 
-  num_sounds++;
-  Sound = checked_realloc(Sound, num_sounds * sizeof(struct SampleInfo));
+    if (--(*snd_info)->num_references <= 0)
+    {
+#if 0
+      printf("[deleting sound '%s']\n", filename);
+#endif
 
-  snd_info = &Sound[num_sounds - 1];
-  snd_info->name = sound_name;
+      /*
+      FreeSound(*snd_info);
+      */
+      deleteNodeFromList(&SoundFileList, filename, FreeSound);
+    }
 
-  sprintf(filename, "%s/%s/%s", options.ro_base_directory,
-         (is_music ? MUSIC_DIRECTORY : SOUNDS_DIRECTORY), snd_info->name);
+    *snd_info = NULL;
+  }
+}
 
-#if defined(TARGET_SDL)
+static void replaceSoundEntry(SoundInfo **snd_info, char *filename)
+{
+  ListNode *node;
 
-  if ((snd_info->mix_chunk = Mix_LoadWAV(filename)) == NULL)
+  /* check if the old and the new sound file are the same */
+  if (*snd_info && strcmp((*snd_info)->source_filename, filename) == 0)
   {
-    Error(ERR_WARN, "cannot read sound file '%s' -- no sounds", filename);
-    return FALSE;
-  }
+    /* The old and new sound are the same (have the same filename and path).
+       This usually means that this sound does not exist in this sound set
+       and a fallback to the existing sound is done. */
 
-#elif defined(PLATFORM_UNIX)
+#if 0
+    printf("[sound '%s' already exists (same list entry)]\n", filename);
+#endif
 
-  if ((file = fopen(filename, MODE_READ)) == NULL)
-  {
-    Error(ERR_WARN, "cannot open sound file '%s' -- no sounds", filename);
-    return FALSE;
+    return;
   }
 
-  /* read chunk "RIFF" */
-  getFileChunk(file, chunk, &chunk_size, BYTE_ORDER_LITTLE_ENDIAN);
-  if (strcmp(chunk, "RIFF") != 0)
+  /* delete existing sound file entry */
+  deleteSoundEntry(snd_info);
+
+  /* check if the new sound file already exists in the list of sounds */
+  if ((node = getNodeFromKey(SoundFileList, filename)) != NULL)
   {
-    Error(ERR_WARN, "missing 'RIFF' chunk of sound file '%s'", filename);
-    fclose(file);
-    return FALSE;
-  }
+#if 0
+      printf("[sound '%s' already exists (other list entry)]\n", filename);
+#endif
 
-  /* read chunk "WAVE" */
-  getFileChunk(file, chunk, &dummy, BYTE_ORDER_LITTLE_ENDIAN);
-  if (strcmp(chunk, "WAVE") != 0)
+      *snd_info = (SoundInfo *)node->content;
+      (*snd_info)->num_references++;
+  }
+  else if ((*snd_info = Load_WAV(filename)) != NULL)   /* load new sound */
   {
-    Error(ERR_WARN, "missing 'WAVE' chunk of sound file '%s'", filename);
-    fclose(file);
-    return FALSE;
+    (*snd_info)->num_references = 1;
+    addNodeToList(&SoundFileList, (*snd_info)->source_filename, *snd_info);
   }
+}
+
+static void LoadCustomSound(SoundInfo **snd_info, char *basename)
+{
+  char *filename = getCustomSoundFilename(basename);
 
-  /* read header information */
-  for (i=0; i<WAV_HEADER_SIZE; i++)
-    sound_header_buffer[i] = fgetc(file);
+#if 0
+  printf("GOT CUSTOM SOUND FILE '%s'\n", filename);
+#endif
 
-  /* read chunk "data" */
-  getFileChunk(file, chunk, &chunk_size, BYTE_ORDER_LITTLE_ENDIAN);
-  if (strcmp(chunk, "data") != 0)
+  if (strcmp(basename, SND_FILE_UNDEFINED) == 0)
   {
-    Error(ERR_WARN, "missing 'data' chunk of sound file '%s'", filename);
-    fclose(file);
-    return FALSE;
+    deleteSoundEntry(snd_info);
+    return;
   }
 
-  snd_info->data_len = chunk_size;
-  snd_info->data_ptr = checked_malloc(snd_info->data_len);
-
-  /* read sound data */
-  if (fread(snd_info->data_ptr, 1, snd_info->data_len, file) !=
-      snd_info->data_len)
+  if (filename == NULL)
   {
-    Error(ERR_WARN, "cannot read sound file '%s' -- no sounds", filename);
-    fclose(file);
-    return FALSE;
+    Error(ERR_WARN, "cannot find sound file '%s'", basename);
+    return;
   }
 
-  fclose(file);
+  replaceSoundEntry(snd_info, filename);
+}
 
-  for (i=0; i<snd_info->data_len; i++)
-    snd_info->data_ptr[i] = snd_info->data_ptr[i] ^ 0x80;
+void InitSoundList(struct SoundEffectInfo *sounds_list, int num_list_entries)
+{
+  if (Sound == NULL)
+    Sound = checked_calloc(num_list_entries * sizeof(SoundInfo *));
 
-#else /* PLATFORM_MSDOS */
+  sound_effect = sounds_list;
+  num_sounds = num_list_entries;
+}
 
-  snd_info->sample_ptr = load_sample(filename);
-  if (!snd_info->sample_ptr)
-  {
-    Error(ERR_WARN, "cannot read sound file '%s' -- no sounds", filename);
-    return FALSE;
-  }
+void LoadSoundToList(char *basename, int list_pos)
+{
+  if (Sound == NULL || list_pos >= num_sounds)
+    return;
 
+#if 0
+  printf("loading sound '%s' ...  [%d]\n",
+        basename, getNumNodes(SoundFileList));
 #endif
 
-  return TRUE;
-}
+  LoadCustomSound(&Sound[list_pos], basename);
 
-boolean LoadSound(char *sound_name)
-{
-  return LoadSoundExt(sound_name, FALSE);
+#if 0
+  printf("loading sound '%s' done [%d]\n",
+        basename, getNumNodes(SoundFileList));
+#endif
 }
 
-boolean LoadMod(char *mod_name)
+static MusicInfo *Load_MOD(char *filename)
 {
 #if defined(TARGET_SDL)
-  struct SampleInfo *mod_info;
-  char filename[256];
+  MusicInfo *mod_info;
 
-  num_mods++;
-  Mod = checked_realloc(Mod, num_mods * sizeof(struct SampleInfo));
-
-  mod_info = &Mod[num_mods - 1];
-  mod_info->name = mod_name;
+  if (!audio.sound_available)
+    return NULL;
 
-  sprintf(filename, "%s/%s/%s", options.ro_base_directory,
-         MUSIC_DIRECTORY, mod_info->name);
+  mod_info = checked_calloc(sizeof(MusicInfo));
 
-  if ((mod_info->mix_music = Mix_LoadMUS(filename)) == NULL)
+  if ((mod_info->data_ptr = Mix_LoadMUS(filename)) == NULL)
   {
-    Error(ERR_WARN, "cannot read music file '%s' -- no music", filename);
-    return FALSE;
+    Error(ERR_WARN, "cannot read music file '%s'", filename);
+    free(mod_info);
+    return NULL;
   }
 
-  return TRUE;
+  mod_info->type = MUS_TYPE_MOD;
+  mod_info->source_filename = getStringCopy(filename);
+
+  return mod_info;
 #else
-  return FALSE;
+  return NULL;
 #endif
 }
 
-int LoadMusic(void)
+void LoadCustomMusic(void)
 {
+  static boolean draw_init_text = TRUE;                /* only draw at startup */
+  static char *last_music_directory = NULL;
+  char *music_directory = getCustomMusicDirectory();
   DIR *dir;
   struct dirent *dir_entry;
-  char *music_directory = getPath2(options.ro_base_directory, MUSIC_DIRECTORY);
-  int num_wav_music = 0;
-  int num_mod_music = 0;
 
   if (!audio.sound_available)
-    return 0;
+    return;
+
+  if (last_music_directory != NULL &&
+      strcmp(last_music_directory, music_directory) == 0)
+    return;    /* old and new music directory are the same */
+
+  last_music_directory = music_directory;
+
+  FreeAllMusic();
 
   if ((dir = opendir(music_directory)) == NULL)
   {
     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
     audio.music_available = FALSE;
-    free(music_directory);
-    return 0;
+    return;
   }
 
+  if (draw_init_text)
+    DrawInitText("Loading music:", 120, FC_GREEN);
+
   while ((dir_entry = readdir(dir)) != NULL)   /* loop until last dir entry */
   {
-    char *filename = dir_entry->d_name;
+    char *basename = dir_entry->d_name;
+    char *filename = getPath2(music_directory, basename);
+    MusicInfo *mus_info = NULL;
 
-    if (strlen(filename) > 4 &&
-       strcmp(&filename[strlen(filename) - 4], ".wav") == 0)
-    {
-      if (LoadSoundExt(filename, TRUE))
-       num_wav_music++;
-    }
-    else if (strlen(filename) > 4 &&
-            (strcmp(&filename[strlen(filename) - 4], ".mod") == 0 ||
-             strcmp(&filename[strlen(filename) - 4], ".MOD") == 0 ||
-             strncmp(filename, "mod.", 4) == 0 ||
-             strncmp(filename, "MOD.", 4) == 0))
+    if (draw_init_text)
+      DrawInitText(basename, 150, FC_YELLOW);
+
+    if (FileIsSound(basename))
+      mus_info = Load_WAV(filename);
+    else if (FileIsMusic(basename))
+      mus_info = Load_MOD(filename);
+
+    free(filename);
+
+    if (mus_info)
     {
-      if (LoadMod(filename))
-       num_mod_music++;
+      num_music++;
+      Music = checked_realloc(Music, num_music * sizeof(MusicInfo *));
+      Music[num_music -1] = mus_info;
     }
   }
 
   closedir(dir);
 
-  if (num_wav_music == 0 && num_mod_music == 0)
+  draw_init_text = FALSE;
+
+  if (num_music == 0)
     Error(ERR_WARN, "cannot find any valid music files in directory '%s'",
          music_directory);
-
-  free(music_directory);
-
-  num_music = (num_mod_music > 0 ? num_mod_music : num_wav_music);
-
-  audio.mods_available = (num_mod_music > 0);
-  audio.music_available = (num_music > 0);
-
-  return num_music;
 }
 
 void PlayMusic(int nr)
@@ -1109,202 +1911,389 @@ void PlayMusic(int nr)
   if (!audio.music_available)
     return;
 
-  if (!audio.mods_available)
-    nr = num_sounds - num_music + nr;
-
-#if defined(TARGET_SDL)
-  if (audio.mods_available)    /* play MOD music */
-  {
-    Mix_PlayMusic(Mod[nr].mix_music, -1);
-    Mix_VolumeMusic(SOUND_MAX_VOLUME); /* must be _after_ Mix_PlayMusic()! */
-  }
-  else                         /* play WAV music loop */
-  {
-    Mix_Volume(audio.music_channel, SOUND_MAX_VOLUME);
-    Mix_PlayChannel(audio.music_channel, Sound[nr].mix_chunk, -1);
-  }
-#else
-  audio.music_nr = nr;
-  PlaySoundLoop(nr);
-#endif
+  PlaySoundMusic(nr);
 }
 
 void PlaySound(int nr)
 {
-  PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_NO_LOOP);
+  PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_SOUND);
 }
 
-void PlaySoundStereo(int nr, int stereo)
+void PlaySoundStereo(int nr, int stereo_position)
 {
-  PlaySoundExt(nr, PSND_MAX_VOLUME, stereo, PSND_NO_LOOP);
+  PlaySoundExt(nr, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_SOUND);
 }
 
 void PlaySoundLoop(int nr)
 {
-  PlaySoundExt(nr, PSND_MAX_VOLUME, PSND_MIDDLE, PSND_LOOP);
+  PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_LOOP);
 }
 
-void PlaySoundExt(int nr, int volume, int stereo, boolean loop)
+void PlaySoundMusic(int nr)
 {
-  struct SoundControl snd_ctrl = emptySoundControl;
+  PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_MUSIC);
+}
+
+void PlaySoundExt(int nr, int volume, int stereo_position, int state)
+{
+  SoundControl snd_ctrl;
 
-  if (!audio.sound_available || !audio.sound_enabled)
+  if (!audio.sound_available ||
+      !audio.sound_enabled ||
+      audio.sound_deactivated)
     return;
 
-  if (volume<PSND_MIN_VOLUME)
-    volume = PSND_MIN_VOLUME;
-  else if (volume>PSND_MAX_VOLUME)
-    volume = PSND_MAX_VOLUME;
+  if (volume < SOUND_MIN_VOLUME)
+    volume = SOUND_MIN_VOLUME;
+  else if (volume > SOUND_MAX_VOLUME)
+    volume = SOUND_MAX_VOLUME;
 
-  if (stereo<PSND_MAX_LEFT)
-    stereo = PSND_MAX_LEFT;
-  else if (stereo>PSND_MAX_RIGHT)
-    stereo = PSND_MAX_RIGHT;
+  if (stereo_position < SOUND_MAX_LEFT)
+    stereo_position = SOUND_MAX_LEFT;
+  else if (stereo_position > SOUND_MAX_RIGHT)
+    stereo_position = SOUND_MAX_RIGHT;
 
-  snd_ctrl.nr          = nr;
-  snd_ctrl.volume      = volume;
-  snd_ctrl.stereo      = stereo;
-  snd_ctrl.loop                = loop;
-  snd_ctrl.active      = TRUE;
-  snd_ctrl.data_ptr    = Sound[nr].data_ptr;
-  snd_ctrl.data_len    = Sound[nr].data_len;
+  snd_ctrl.active = TRUE;
+  snd_ctrl.nr = nr;
+  snd_ctrl.volume = volume;
+  snd_ctrl.stereo_position = stereo_position;
+  snd_ctrl.state = state;
 
-#if defined(TARGET_SDL)
-  Mix_Volume(-1, SOUND_MAX_VOLUME);
-  Mix_PlayChannel(-1, Sound[nr].mix_chunk, (loop ? -1 : 0));
-#elif defined(PLATFORM_UNIX)
-  if (write(audio.soundserver_pipe[1], &snd_ctrl, sizeof(snd_ctrl)) < 0)
-  {
-    Error(ERR_WARN, "cannot pipe to child process -- no sounds");
-    audio.sound_available = audio.sound_enabled = FALSE;
-    return;
-  }
-#elif defined(PLATFORM_MSDOS)
-  sound_handler(snd_ctrl);
-#endif
+  HandleSoundRequest(snd_ctrl);
 }
 
 void FadeMusic(void)
 {
-#if defined(TARGET_SDL)
-  if (!audio.sound_available)
+  if (!audio.music_available)
     return;
 
-  if (audio.mods_available)
-    Mix_FadeOutMusic(SOUND_FADING_INTERVAL);
-  else
-    Mix_FadeOutChannel(audio.music_channel, SOUND_FADING_INTERVAL);
-#else
-  FadeSound(audio.music_nr);
-#endif
+  StopSoundExt(-1, SND_CTRL_FADE_MUSIC);
 }
 
 void FadeSound(int nr)
 {
-  StopSoundExt(nr, SSND_FADE_SOUND);
+  StopSoundExt(nr, SND_CTRL_FADE_SOUND);
 }
 
 void FadeSounds()
 {
   FadeMusic();
-  StopSoundExt(-1, SSND_FADE_ALL_SOUNDS);
+  StopSoundExt(-1, SND_CTRL_FADE_ALL);
 }
 
 void StopMusic(void)
 {
-#if defined(TARGET_SDL)
-  if (!audio.sound_available)
+  if (!audio.music_available)
     return;
 
-  if (audio.mods_available)
-    Mix_HaltMusic();
-  else
-    Mix_HaltChannel(audio.music_channel);
-#else
-  StopSound(audio.music_nr);
-#endif
+  StopSoundExt(-1, SND_CTRL_STOP_MUSIC);
 }
 
 void StopSound(int nr)
 {
-  StopSoundExt(nr, SSND_STOP_SOUND);
+  StopSoundExt(nr, SND_CTRL_STOP_SOUND);
 }
 
 void StopSounds()
 {
-  StopSoundExt(-1, SSND_STOP_ALL_SOUNDS);
+  StopMusic();
+  StopSoundExt(-1, SND_CTRL_STOP_ALL);
 }
 
-void StopSoundExt(int nr, int method)
+void StopSoundExt(int nr, int state)
 {
-  struct SoundControl snd_ctrl = emptySoundControl;
+  SoundControl snd_ctrl;
 
   if (!audio.sound_available)
     return;
 
-  if (SSND_FADING(method))
-    snd_ctrl.fade_sound = TRUE;
+  snd_ctrl.active = FALSE;
+  snd_ctrl.nr = nr;
+  snd_ctrl.state = state;
+
+  HandleSoundRequest(snd_ctrl);
+}
+
+ListNode *newListNode()
+{
+  return checked_calloc(sizeof(ListNode));
+}
+
+void addNodeToList(ListNode **node_first, char *key, void *content)
+{
+  ListNode *node_new = newListNode();
+
+#if 0
+  printf("LIST: adding node with key '%s'\n", key);
+#endif
+
+  node_new->key = getStringCopy(key);
+  node_new->content = content;
+  node_new->next = *node_first;
+  *node_first = node_new;
+}
+
+void deleteNodeFromList(ListNode **node_first, char *key,
+                       void (*destructor_function)(void *))
+{
+  if (node_first == NULL || *node_first == NULL)
+    return;
+
+#if 0
+  printf("[CHECKING LIST KEY '%s' == '%s']\n",
+        (*node_first)->key, key);
+#endif
+
+  if (strcmp((*node_first)->key, key) == 0)
+  {
+#if 0
+    printf("[DELETING LIST ENTRY]\n");
+#endif
+
+    free((*node_first)->key);
+    if (destructor_function)
+      destructor_function((*node_first)->content);
+    *node_first = (*node_first)->next;
+  }
+  else
+    deleteNodeFromList(&(*node_first)->next, key, destructor_function);
+}
+
+ListNode *getNodeFromKey(ListNode *node_first, char *key)
+{
+  if (node_first == NULL)
+    return NULL;
 
-  if (SSND_ALL(method))
-    snd_ctrl.stop_all_sounds = TRUE;
+  if (strcmp(node_first->key, key) == 0)
+    return node_first;
   else
+    return getNodeFromKey(node_first->next, key);
+}
+
+int getNumNodes(ListNode *node_first)
+{
+  return (node_first ? 1 + getNumNodes(node_first->next) : 0);
+}
+
+void dumpList(ListNode *node_first)
+{
+  ListNode *node = node_first;
+
+  while (node)
   {
-    snd_ctrl.nr = nr;
-    snd_ctrl.stop_sound = TRUE;
+    printf("['%s' (%d)]\n", node->key,
+          ((SoundInfo *)node->content)->num_references);
+    node = node->next;
   }
 
-#if defined(TARGET_SDL)
+  printf("[%d nodes]\n", getNumNodes(node_first));
+}
+
+static void LoadSoundsInfo()
+{
+  char *filename = getCustomSoundConfigFilename();
+  struct SetupFileList *setup_file_list;
+  int i;
+
+#if 0
+  printf("GOT CUSTOM SOUND CONFIG FILE '%s'\n", filename);
+#endif
+
+  /* always start with reliable default values */
+  for (i=0; i<num_sounds; i++)
+    sound_effect[i].filename = NULL;
+
+  if (filename == NULL)
+    return;
 
-  if (SSND_FADING(method))
+  if ((setup_file_list = loadSetupFileList(filename)))
   {
-    int i;
+    for (i=0; i<num_sounds; i++)
+      sound_effect[i].filename =
+       getStringCopy(getTokenValue(setup_file_list, sound_effect[i].text));
 
-    for (i=0; i<audio.channels; i++)
-      if (i != audio.music_channel || snd_ctrl.stop_all_sounds)
-       Mix_FadeOutChannel(i, SOUND_FADING_INTERVAL);
-    if (snd_ctrl.stop_all_sounds)
-      Mix_FadeOutMusic(SOUND_FADING_INTERVAL);
+    freeSetupFileList(setup_file_list);
+
+#if 0
+    for (i=0; i<num_sounds; i++)
+    {
+      printf("'%s' ", sound_effect[i].text);
+      if (sound_effect[i].filename)
+       printf("-> '%s'\n", sound_effect[i].filename);
+      else
+       printf("-> UNDEFINED [-> '%s']\n", sound_effect[i].default_filename);
+    }
+#endif
   }
-  else
+}
+
+static void ReloadCustomSounds()
+{
+  static boolean draw_init_text = TRUE;                /* only draw at startup */
+  int i;
+
+#if 0
+  printf("DEBUG: reloading sounds '%s' ...\n",artwork.sounds_set_current_name);
+#endif
+
+  LoadSoundsInfo();
+
+  if (draw_init_text)
+    DrawInitText("Loading sounds:", 120, FC_GREEN);
+
+#if 0
+  printf("DEBUG: reloading %d sounds ...\n", num_sounds);
+#endif
+
+  for(i=0; i<num_sounds; i++)
   {
-    int i;
+    if (draw_init_text)
+      DrawInitText(sound_effect[i].text, 150, FC_YELLOW);
 
-    for (i=0; i<audio.channels; i++)
-      if (i != audio.music_channel || snd_ctrl.stop_all_sounds)
-       Mix_HaltChannel(i);
-    if (snd_ctrl.stop_all_sounds)
-      Mix_HaltMusic();
+    if (sound_effect[i].filename)
+      LoadSoundToList(sound_effect[i].filename, i);
+    else
+      LoadSoundToList(sound_effect[i].default_filename, i);
   }
 
+  draw_init_text = FALSE;
+
+  /*
+  printf("list size == %d\n", getNumNodes(SoundFileList));
+  */
+
+#if 0
+  dumpList(SoundFileList);
+#endif
+}
+
+static void ReloadCustomMusic()
+{
+#if 0
+  printf("DEBUG: reloading music '%s' ...\n", artwork.music_set_current_name);
+#endif
+
+#if 0
+  /* this is done directly in LoadCustomMusic() now */
+  FreeAllMusic();
+#endif
+
+  LoadCustomMusic();
+}
+
+void InitReloadSounds(char *set_name)
+{
+  if (!audio.sound_available)
+    return;
+
+#if defined(AUDIO_UNIX_NATIVE)
+  WriteReloadInfoToPipe(set_name, SND_CTRL_RELOAD_SOUNDS);
 #else
-#if !defined(PLATFORM_MSDOS)
-  if (write(audio.soundserver_pipe[1], &snd_ctrl, sizeof(snd_ctrl)) < 0)
-  {
-    Error(ERR_WARN, "cannot pipe to child process -- no sounds");
-    audio.sound_available = audio.sound_enabled = FALSE;
+  ReloadCustomSounds();
+#endif
+}
+
+void InitReloadMusic(char *set_name)
+{
+  if (!audio.music_available)
     return;
-  }
+
+#if defined(AUDIO_UNIX_NATIVE)
+  WriteReloadInfoToPipe(set_name, SND_CTRL_RELOAD_MUSIC);
 #else
-  sound_handler(snd_ctrl);
+  ReloadCustomMusic();
+#endif
+}
+
+void FreeSound(void *ptr)
+{
+  SoundInfo *sound = (SoundInfo *)ptr;
+
+  if (sound == NULL)
+    return;
+
+  if (sound->data_ptr)
+  {
+#if defined(TARGET_SDL)
+    Mix_FreeChunk(sound->data_ptr);
+#elif defined(TARGET_ALLEGRO)
+    destroy_sample(sound->data_ptr);
+#else /* AUDIO_UNIX_NATIVE */
+    free(sound->data_ptr);
 #endif
+  }
+
+  if (sound->source_filename)
+    free(sound->source_filename);
+
+  free(sound);
+}
+
+void FreeMusic(MusicInfo *music)
+{
+  if (music == NULL)
+    return;
+
+  if (music->data_ptr)
+  {
+#if defined(TARGET_SDL)
+    if (music->type == MUS_TYPE_MOD)
+      Mix_FreeMusic(music->data_ptr);
+    else
+      Mix_FreeChunk(music->data_ptr);
+#elif defined(TARGET_ALLEGRO)
+    destroy_sample(music->data_ptr);
+#else /* AUDIO_UNIX_NATIVE */
+    free(music->data_ptr);
 #endif
+  }
+
+  free(music);
 }
 
-void FreeSounds(int num_sounds)
+void FreeAllSounds()
 {
   int i;
 
-  if (!audio.sound_available)
+  if (Sound == NULL)
     return;
 
+#if 0
+  printf("%s: FREEING SOUNDS ...\n",
+        IS_CHILD_PROCESS(audio.mixer_pid) ? "CHILD" : "PARENT");
+#endif
+
   for(i=0; i<num_sounds; i++)
-#if defined(TARGET_SDL)
-    free(Sound[i].mix_chunk);
-#elif !defined(PLATFORM_MSDOS)
-    free(Sound[i].data_ptr);
-#else
-    destroy_sample(Sound[i].sample_ptr);
+    deleteSoundEntry(&Sound[i]);
+  /*
+    FreeSound(Sound[i]);
+  */
+
+#if 0
+  printf("%s: FREEING SOUNDS -- DONE\n",
+        IS_CHILD_PROCESS(audio.mixer_pid) ? "CHILD" : "PARENT");
 #endif
+
+  free(Sound);
+
+  Sound = NULL;
+  num_sounds = 0;
+}
+
+void FreeAllMusic()
+{
+  int i;
+
+  if (Music == NULL)
+    return;
+
+  for(i=0; i<num_music; i++)
+    FreeMusic(Music[i]);
+
+  free(Music);
+
+  Music = NULL;
+  num_music = 0;
 }
 
 /* THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS                          */
index 07e73550085431f76d563cc7913d33840fe1d980..ec5a2a8b8c2a0a13d7382cb4a7e5a6dd73dfe72f 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #ifndef SOUND_H
 #define SOUND_H
 
-#include <math.h>
-
 #include "platform.h"
 
-#if defined(PLATFORM_LINUX)
-#include <sys/ioctl.h>
-#endif
 
-#if defined(PLATFORM_LINUX)
-#include <linux/soundcard.h>
-#elif defined(PLATFORM_FREEBSD)
-#include <machine/soundcard.h>
-#elif defined(PLATFORM_NETBSD)
-#include <sys/ioctl.h>
-#include <sys/audioio.h>
-#elif defined(PLATFORM_HPUX)
-#include <sys/audio.h>
+#if defined(PLATFORM_UNIX) && !defined(TARGET_SDL)
+#define AUDIO_UNIX_NATIVE
 #endif
 
-#include "system.h"
-
-
 #if defined(PLATFORM_LINUX) || defined(PLATFORM_FREEBSD) || defined(VOXWARE)
 #define AUDIO_LINUX_IOCTL
 #endif
 #define AUDIO_STREAMING_DSP
 #endif
 
-#define AUDIO_SAMPLE_RATE_8000                 8000
-#define AUDIO_SAMPLE_RATE_22050                        22050
+/* values for platform specific sound initialization */
+#define AUDIO_SAMPLE_RATE_8000         8000
+#define AUDIO_SAMPLE_RATE_22050                22050
 
-#define AUDIO_FRAGMENT_SIZE_512                        512
-#define AUDIO_FRAGMENT_SIZE_1024               1024
-#define AUDIO_FRAGMENT_SIZE_2048               2048
-#define AUDIO_FRAGMENT_SIZE_4096               4096
-
-#define AUDIO_MONO_CHANNEL                     1
-#define AUDIO_STEREO_CHANNELS                  2
-
-#if defined(TARGET_SDL)
-/* one second fading interval == 1000 ticks (milliseconds) */
-#define SOUND_FADING_INTERVAL                  1000
-#define SOUND_MAX_VOLUME                       SDL_MIX_MAXVOLUME
-#endif
+#define AUDIO_FRAGMENT_SIZE_512                512
+#define AUDIO_FRAGMENT_SIZE_1024       1024
+#define AUDIO_FRAGMENT_SIZE_2048       2048
+#define AUDIO_FRAGMENT_SIZE_4096       4096
 
-#if defined(AUDIO_STREAMING_DSP)
-#define SOUND_FADING_VOLUME_STEP               (PSND_MAX_VOLUME / 40)
-#define SOUND_FADING_VOLUME_THRESHOLD          (SOUND_FADING_VOLUME_STEP * 2)
-#endif
+#define AUDIO_NUM_CHANNELS_MONO                1
+#define AUDIO_NUM_CHANNELS_STEREO      2
 
-#define DEFAULT_AUDIO_SAMPLE_RATE              AUDIO_SAMPLE_RATE_22050
-#define DEFAULT_AUDIO_FRAGMENT_SIZE_UNIX       AUDIO_FRAGMENT_SIZE_512
-#define DEFAULT_AUDIO_FRAGMENT_SIZE_WIN32      AUDIO_FRAGMENT_SIZE_2048
+#define AUDIO_FORMAT_UNKNOWN           (0)
+#define AUDIO_FORMAT_U8                        (1 << 0)
+#define AUDIO_FORMAT_S16               (1 << 1)
+#define AUDIO_FORMAT_LE                        (1 << 2)
+#define AUDIO_FORMAT_BE                        (1 << 3)
 
-#if defined(PLATFORM_UNIX)
-#define DEFAULT_AUDIO_FRAGMENT_SIZE    DEFAULT_AUDIO_FRAGMENT_SIZE_UNIX
+#if defined(AUDIO_UNIX_NATIVE) && !defined(AUDIO_STREAMING_DSP)
+#define DEFAULT_AUDIO_SAMPLE_RATE      AUDIO_SAMPLE_RATE_8000
 #else
-#define DEFAULT_AUDIO_FRAGMENT_SIZE    DEFAULT_AUDIO_FRAGMENT_SIZE_WIN32
+#define DEFAULT_AUDIO_SAMPLE_RATE      AUDIO_SAMPLE_RATE_22050
 #endif
 
-#if !defined(PLATFORM_MSDOS)
-#define MAX_SOUNDS_PLAYING                     16
+#if defined(PLATFORM_WIN32)
+#define DEFAULT_AUDIO_FRAGMENT_SIZE    AUDIO_FRAGMENT_SIZE_2048
 #else
-#define MAX_SOUNDS_PLAYING                     8
+#define DEFAULT_AUDIO_FRAGMENT_SIZE    AUDIO_FRAGMENT_SIZE_512
 #endif
 
-#if !defined(PLATFORM_HPUX)
-#define SND_BLOCKSIZE 4096
+#if defined(TARGET_SDL)
+#define NUM_MIXER_CHANNELS             MIX_CHANNELS
 #else
-#define SND_BLOCKSIZE 32768
+#define NUM_MIXER_CHANNELS             8
 #endif
 
-/* some values for PlaySound(), StopSound() and friends */
-#if !defined(PLATFORM_MSDOS)
-#define PSND_SILENCE           0
-#define PSND_MAX_VOLUME_BITS   7
-#define PSND_MIN_VOLUME                0
-#define PSND_MAX_VOLUME                (1 << PSND_MAX_VOLUME_BITS)
-#define PSND_NO_LOOP           0
-#define PSND_LOOP              1
-#define PSND_MIDDLE            0
-#define PSND_MAX_STEREO_BITS   7
-#define PSND_MAX_STEREO                (1 << PSND_MAX_STEREO_BITS)
-#define PSND_MAX_LEFT          (-PSND_MAX_STEREO)
-#define PSND_MAX_RIGHT         (+PSND_MAX_STEREO)
-#define PSND_MAX_LEFT2RIGHT_BITS (PSND_MAX_STEREO_BITS+1)
-#define PSND_MAX_LEFT2RIGHT    (1 << PSND_MAX_LEFT2RIGHT_BITS)
-#else
-#define PSND_SILENCE           0
-#define PSND_MIN_VOLUME                0
-#define PSND_MAX_VOLUME                255
-#define PSND_NO_LOOP           0
-#define PSND_LOOP              1
-#define PSND_MAX_LEFT          0
-#define PSND_MAX_RIGHT         255
-#define PSND_MIDDLE            128
-#endif
+#define MUSIC_CHANNEL                  0
+#define FIRST_SOUND_CHANNEL            1
 
-#define SSND_FADE_SOUND                (1<<0)
-#define SSND_FADE_ALL_SOUNDS   (1<<1)
-#define SSND_FADING(x)         (x & (SSND_FADE_SOUND | SSND_FADE_ALL_SOUNDS))
-#define SSND_STOP_SOUND                (1<<2)
-#define SSND_STOP_ALL_SOUNDS   (1<<3)
-#define SSND_STOPPING(x)       (x & (SSND_STOP_SOUND | SSND_STOP_ALL_SOUNDS))
-#define SSND_ALL(x)            (x&(SSND_FADE_ALL_SOUNDS|SSND_STOP_ALL_SOUNDS))
-
-/* settings for sound path, sound device, etc. */
-#ifndef SND_PATH
-#define SND_PATH       "./sounds"
-#endif
 
-#define DEVICENAME_DSP         "/dev/dsp"
-#define DEVICENAME_AUDIO       "/dev/audio"
-#define DEVICENAME_AUDIOCTL    "/dev/audioCtl"
+/* values for PlaySound(), StopSound() and friends */
+#define SND_CTRL_NONE                  (0)
+#define SND_CTRL_MUSIC                 (1 << 0)
+#define SND_CTRL_LOOP                  (1 << 1)
+#define SND_CTRL_FADE                  (1 << 2)
+#define SND_CTRL_STOP                  (1 << 3)
+#define SND_CTRL_ALL_SOUNDS            (1 << 4)
+#define SND_CTRL_RELOAD_SOUNDS         (1 << 5)
+#define SND_CTRL_RELOAD_MUSIC          (1 << 6)
 
-#if 0
-#if defined(AUDIO_STREAMING_DSP)
-#define AUDIO_DEVICE           DEVICENAME_DSP
-#else
-#define AUDIO_DEVICE   DEVICENAME_AUDIO
-#endif
-#endif
+#define SND_CTRL_PLAY_SOUND            (SND_CTRL_NONE)
+#define SND_CTRL_PLAY_LOOP             (SND_CTRL_LOOP)
+#define SND_CTRL_PLAY_MUSIC            (SND_CTRL_LOOP | SND_CTRL_MUSIC)
 
-#if 0
-struct SoundHeader_SUN
-{
-  unsigned long magic;
-  unsigned long hdr_size;
-  unsigned long data_size;
-  unsigned long encoding;
-  unsigned long sample_rate;
-  unsigned long channels;
-};
-
-struct SoundHeader_8SVX
-{
-  char magic_FORM[4];
-  unsigned long chunk_size;
-  char magic_8SVX[4];
-};
-#endif
+#define SND_CTRL_FADE_SOUND            (SND_CTRL_FADE)
+#define SND_CTRL_FADE_MUSIC            (SND_CTRL_FADE | SND_CTRL_MUSIC)
+#define SND_CTRL_FADE_ALL              (SND_CTRL_FADE | SND_CTRL_ALL_SOUNDS)
 
-struct SampleInfo
-{ 
-  char *name;
-  byte *data_ptr;
-  long data_len;
+#define SND_CTRL_STOP_SOUND            (SND_CTRL_STOP)
+#define SND_CTRL_STOP_MUSIC            (SND_CTRL_STOP | SND_CTRL_MUSIC)
+#define SND_CTRL_STOP_ALL              (SND_CTRL_STOP | SND_CTRL_ALL_SOUNDS)
 
-#if defined(PLATFORM_MSDOS)
-  SAMPLE *sample_ptr;
-#endif
+#define IS_MUSIC(x)                    ((x).state & SND_CTRL_MUSIC)
+#define IS_LOOP(x)                     ((x).state & SND_CTRL_LOOP)
+#define IS_FADING(x)                   ((x).state & SND_CTRL_FADE)
+#define IS_STOPPING(x)                 ((x).state & SND_CTRL_STOP)
+#define IS_RELOADING(x)                        ((x).state & (SND_CTRL_RELOAD_SOUNDS |\
+                                                     SND_CTRL_RELOAD_MUSIC))
+#define ALL_SOUNDS(x)                  ((x).state & SND_CTRL_ALL_SOUNDS)
 
+#define SOUND_MIN_VOLUME               0
 #if defined(TARGET_SDL)
-  Mix_Chunk *mix_chunk;
-  Mix_Music *mix_music;
+#define SOUND_MAX_VOLUME               SDL_MIX_MAXVOLUME
+#elif defined(TARGET_ALLEGRO)
+#define SOUND_MAX_VOLUME               255
+#else
+#define SOUND_MAX_VOLUME               128
 #endif
-};
 
-struct SoundControl
+#define SOUND_MAX_LEFT                 0
+#define SOUND_MAX_RIGHT                        255
+#define SOUND_MAX_LEFT2RIGHT           255
+#define SOUND_MIDDLE                   (SOUND_MAX_LEFT2RIGHT / 2)
+
+/* value for undefined sound effect filename */
+#define SND_FILE_UNDEFINED             "NONE"
+
+
+struct SoundEffectInfo
 {
-  int nr;
-  int volume;
-  int stereo;
-  boolean active;
-  boolean loop;
-  boolean fade_sound;
-  boolean stop_sound;
-  boolean stop_all_sounds;
-  int playingtime;
-  long playingpos;
-  long data_len;
-  byte *data_ptr;
-
-#if defined(PLATFORM_MSDOS)
-  int voice;
-#endif
+  char *text;
+  char *default_filename;
+
+  char *filename;
 };
 
+
 /* general sound functions */
 void UnixOpenAudio(void);
 void UnixCloseAudio(void);
 
-/* sound server functions */ 
-void InitPlaylist(void);
-void StartSoundserver(void);
-void SoundServer(void);
+/* mixer functions */ 
+void Mixer_InitChannels(void);
+void StartMixer(void);
 
 /* sound client functions */
-boolean LoadSound(char *);
-boolean LoadMod(char *);
-int LoadMusic(void);
 void PlayMusic(int);
 void PlaySound(int);
 void PlaySoundStereo(int, int);
 void PlaySoundLoop(int);
-void PlaySoundExt(int, int, int, boolean);
+void PlaySoundMusic(int);
+void PlaySoundExt(int, int, int, int);
 void FadeMusic(void);
 void FadeSound(int);
 void FadeSounds(void);
@@ -219,6 +148,10 @@ void StopMusic(void);
 void StopSound(int);
 void StopSounds(void);
 void StopSoundExt(int, int);
-void FreeSounds(int);
+void InitSoundList(struct SoundEffectInfo *, int);
+void InitReloadSounds(char *);
+void InitReloadMusic(char *);
+void FreeAllSounds(void);
+void FreeAllMusic(void);
 
 #endif
index b4ac9ce4e83a3b8bb4e49a954e78f071a6ab4391..cc7dc2aa33fb4111ff5f737ca76a4a731437960c 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -22,6 +22,8 @@
 
 #include "system.h"
 #include "sound.h"
+#include "setup.h"
+#include "joystick.h"
 #include "misc.h"
 
 
@@ -34,26 +36,30 @@ struct OptionInfo   options;
 struct VideoSystemInfo video;
 struct AudioSystemInfo audio;
 struct GfxInfo         gfx;
+struct ArtworkInfo     artwork;
+struct JoystickInfo    joystick;
+struct SetupInfo       setup;
 
-struct LevelDirInfo    *leveldir_first = NULL;
-struct LevelDirInfo    *leveldir_current = NULL;
+LevelDirTree          *leveldir_first = NULL;
+LevelDirTree          *leveldir_current = NULL;
+int                    level_nr;
 
-Display        *display = NULL;
-Visual        *visual = NULL;
-int            screen = 0;
-Colormap       cmap = None;
+Display                       *display = NULL;
+Visual                *visual = NULL;
+int                    screen = 0;
+Colormap               cmap = None;
 
-DrawWindow     *window = NULL;
-DrawBuffer     *backbuffer = NULL;
-DrawBuffer     *drawto = NULL;
+DrawWindow            *window = NULL;
+DrawBuffer            *backbuffer = NULL;
+DrawBuffer            *drawto = NULL;
 
-int            button_status = MB_NOT_PRESSED;
-boolean                motion_status = FALSE;
+int                    button_status = MB_NOT_PRESSED;
+boolean                        motion_status = FALSE;
 
-int            redraw_mask = REDRAW_NONE;
-int            redraw_tiles = 0;
+int                    redraw_mask = REDRAW_NONE;
+int                    redraw_tiles = 0;
 
-int            FrameCounter = 0;
+int                    FrameCounter = 0;
 
 
 /* ========================================================================= */
@@ -106,16 +112,11 @@ void ClosePlatformDependantStuff(void)
 
 void InitProgramInfo(char *unix_userdata_directory, char *program_title,
                     char *window_title, char *icon_title,
-                    char *x11_icon_basename, char *x11_iconmask_basename,
-                    char *msdos_pointer_basename)
+                    char *x11_icon_filename, char *x11_iconmask_filename,
+                    char *msdos_pointer_filename,
+                    char *cookie_prefix, char *filename_prefix,
+                    int program_version)
 {
-  char *gfx_dir = getPath2(options.ro_base_directory, GRAPHICS_DIRECTORY);
-  char *x11_icon_filename = getPath2(gfx_dir, x11_icon_basename);
-  char *x11_iconmask_filename = getPath2(gfx_dir, x11_iconmask_basename);
-  char *msdos_pointer_filename = getPath2(gfx_dir, msdos_pointer_basename);
-
-  free(gfx_dir);
-
 #if defined(PLATFORM_UNIX)
   program.userdata_directory = unix_userdata_directory;
 #else
@@ -128,6 +129,13 @@ void InitProgramInfo(char *unix_userdata_directory, char *program_title,
   program.x11_icon_filename = x11_icon_filename;
   program.x11_iconmask_filename = x11_iconmask_filename;
   program.msdos_pointer_filename = msdos_pointer_filename;
+
+  program.cookie_prefix = cookie_prefix;
+  program.filename_prefix = filename_prefix;
+
+  program.version_major = VERSION_MAJOR(program_version);
+  program.version_minor = VERSION_MINOR(program_version);
+  program.version_patch = VERSION_PATCH(program_version);
 }
 
 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
@@ -142,6 +150,8 @@ void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
   gfx.real_sy = real_sy;
   gfx.full_sxsize = full_sxsize;
   gfx.full_sysize = full_sysize;
+
+  SetDrawDeactivationMask(REDRAW_NONE);                /* do not deactivate drawing */
 }
 
 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
@@ -167,6 +177,11 @@ void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
   gfx.scrollbuffer_height = scrollbuffer_height;
 }
 
+void SetDrawDeactivationMask(int draw_deactivation_mask)
+{
+  gfx.draw_deactivation_mask = draw_deactivation_mask;
+}
+
 
 /* ========================================================================= */
 /* video functions                                                           */
@@ -193,6 +208,7 @@ inline void CloseVideoDisplay(void)
 #if defined(TARGET_SDL)
   SDL_QuitSubSystem(SDL_INIT_VIDEO);
 #else
+
   if (display)
     XCloseDisplay(display);
 #endif
@@ -265,7 +281,7 @@ inline Bitmap *CreateBitmap(int width, int height, int depth)
   return new_bitmap;
 }
 
-inline void FreeBitmap(Bitmap *bitmap)
+inline static void FreeBitmapPointers(Bitmap *bitmap)
 {
   if (bitmap == NULL)
     return;
@@ -275,15 +291,49 @@ inline void FreeBitmap(Bitmap *bitmap)
     SDL_FreeSurface(bitmap->surface);
   if (bitmap->surface_masked)
     SDL_FreeSurface(bitmap->surface_masked);
+  bitmap->surface = NULL;
+  bitmap->surface_masked = NULL;
 #else
+  /* The X11 version seems to have a memory leak here -- although
+     "XFreePixmap()" is called, the correspondig memory seems not
+     to be freed (according to "ps"). The SDL version apparently
+     does not have this problem. */
+
   if (bitmap->drawable)
     XFreePixmap(display, bitmap->drawable);
   if (bitmap->clip_mask)
     XFreePixmap(display, bitmap->clip_mask);
   if (bitmap->stored_clip_gc)
     XFreeGC(display, bitmap->stored_clip_gc);
+  /* the other GCs are only pointers to GCs used elsewhere */
+  bitmap->drawable = None;
+  bitmap->clip_mask = None;
+  bitmap->stored_clip_gc = None;
 #endif
 
+  if (bitmap->source_filename)
+    free(bitmap->source_filename);
+  bitmap->source_filename = NULL;
+}
+
+inline static void TransferBitmapPointers(Bitmap *src_bitmap,
+                                         Bitmap *dst_bitmap)
+{
+  if (src_bitmap == NULL || dst_bitmap == NULL)
+    return;
+
+  FreeBitmapPointers(dst_bitmap);
+
+  *dst_bitmap = *src_bitmap;
+}
+
+inline void FreeBitmap(Bitmap *bitmap)
+{
+  if (bitmap == NULL)
+    return;
+
+  FreeBitmapPointers(bitmap);
+
   free(bitmap);
 }
 
@@ -300,11 +350,36 @@ inline void CloseWindow(DrawWindow *window)
 #endif
 }
 
+inline boolean DrawingDeactivated(int x, int y, int width, int height)
+{
+  if (gfx.draw_deactivation_mask != REDRAW_NONE)
+  {
+    if ((gfx.draw_deactivation_mask & REDRAW_FIELD) &&
+       x < gfx.sx + gfx.sxsize)
+      return TRUE;
+    else if ((gfx.draw_deactivation_mask & REDRAW_DOORS) &&
+            x > gfx.dx)
+    {
+      if ((gfx.draw_deactivation_mask & REDRAW_DOOR_1) &&
+         y < gfx.dy + gfx.dysize)
+       return TRUE;
+      else if ((gfx.draw_deactivation_mask & REDRAW_DOOR_2) &&
+              y > gfx.vy)
+       return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
 inline void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
                       int src_x, int src_y,
                       int width, int height,
                       int dst_x, int dst_y)
 {
+  if (DrawingDeactivated(dst_x, dst_y, width, height))
+    return;
+
 #ifdef TARGET_SDL
   SDLCopyArea(src_bitmap, dst_bitmap,
              src_x, src_y, width, height, dst_x, dst_y, SDLCOPYAREA_OPAQUE);
@@ -316,6 +391,9 @@ inline void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
 
 inline void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
 {
+  if (DrawingDeactivated(x, y, width, height))
+    return;
+
 #ifdef TARGET_SDL
   SDLFillRectangle(bitmap, x, y, width, height, 0x000000);
 #else
@@ -362,6 +440,9 @@ inline void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
                             int width, int height,
                             int dst_x, int dst_y)
 {
+  if (DrawingDeactivated(dst_x, dst_y, width, height))
+    return;
+
 #ifdef TARGET_SDL
   SDLCopyArea(src_bitmap, dst_bitmap,
              src_x, src_y, width, height, dst_x, dst_y, SDLCOPYAREA_MASKED);
@@ -578,11 +659,9 @@ inline boolean ChangeVideoModeIfNeeded(boolean fullscreen)
   return fullscreen;
 }
 
-Bitmap *LoadImage(char *basename)
+Bitmap *LoadImage(char *filename)
 {
   Bitmap *new_bitmap;
-  char *filename = getPath3(options.ro_base_directory, GRAPHICS_DIRECTORY,
-                           basename);
 
 #if defined(TARGET_SDL)
   new_bitmap = SDLLoadImage(filename);
@@ -590,11 +669,64 @@ Bitmap *LoadImage(char *basename)
   new_bitmap = X11LoadImage(filename);
 #endif
 
-  free(filename);
+  if (new_bitmap)
+    new_bitmap->source_filename = getStringCopy(filename);
+
+  return new_bitmap;
+}
+
+Bitmap *LoadCustomImage(char *basename)
+{
+  char *filename = getCustomImageFilename(basename);
+  Bitmap *new_bitmap;
+
+  if (filename == NULL)
+    Error(ERR_EXIT, "LoadCustomImage(): cannot find file '%s'", basename);
+
+  if ((new_bitmap = LoadImage(filename)) == NULL)
+    Error(ERR_EXIT, "LoadImage() failed: %s", GetError());
 
   return new_bitmap;
 }
 
+void ReloadCustomImage(Bitmap *bitmap, char *basename)
+{
+  char *filename = getCustomImageFilename(basename);
+  Bitmap *new_bitmap;
+
+  if (filename == NULL)                /* (should never happen) */
+  {
+    Error(ERR_WARN, "ReloadCustomImage(): cannot find file '%s'", basename);
+    return;
+  }
+
+  if (strcmp(filename, bitmap->source_filename) == 0)
+  {
+    /* The old and new image are the same (have the same filename and path).
+       This usually means that this image does not exist in this graphic set
+       and a fallback to the existing image is done. */
+
+    return;
+  }
+
+  if ((new_bitmap = LoadImage(filename)) == NULL)
+  {
+    Error(ERR_WARN, "LoadImage() failed: %s", GetError());
+    return;
+  }
+
+  if (bitmap->width != new_bitmap->width ||
+      bitmap->height != new_bitmap->height)
+  {
+    Error(ERR_WARN, "ReloadCustomImage: new image has wrong dimensions");
+    FreeBitmap(new_bitmap);
+    return;
+  }
+
+  TransferBitmapPointers(new_bitmap, bitmap);
+  free(new_bitmap);
+}
+
 
 /* ========================================================================= */
 /* audio functions                                                           */
@@ -606,24 +738,25 @@ inline void OpenAudio(void)
   audio.sound_available = FALSE;
   audio.music_available = FALSE;
   audio.loops_available = FALSE;
-  audio.mods_available = FALSE;
+
   audio.sound_enabled = FALSE;
+  audio.sound_deactivated = FALSE;
 
-  audio.soundserver_pipe[0] = audio.soundserver_pipe[1] = 0;
-  audio.soundserver_pid = 0;
+  audio.mixer_pipe[0] = audio.mixer_pipe[1] = 0;
+  audio.mixer_pid = -1;
   audio.device_name = NULL;
-  audio.device_fd = 0;
+  audio.device_fd = -1;
 
-  audio.channels = 0;
+  audio.num_channels = 0;
   audio.music_channel = 0;
-  audio.music_nr = 0;
+  audio.first_sound_channel = 0;
 
 #if defined(TARGET_SDL)
   SDLOpenAudio();
-#elif defined(PLATFORM_MSDOS)
-  MSDOSOpenAudio();
 #elif defined(PLATFORM_UNIX)
   UnixOpenAudio();
+#elif defined(PLATFORM_MSDOS)
+  MSDOSOpenAudio();
 #endif
 }
 
@@ -631,10 +764,10 @@ inline void CloseAudio(void)
 {
 #if defined(TARGET_SDL)
   SDLCloseAudio();
-#elif defined(PLATFORM_MSDOS)
-  MSDOSCloseAudio();
 #elif defined(PLATFORM_UNIX)
   UnixCloseAudio();
+#elif defined(PLATFORM_MSDOS)
+  MSDOSCloseAudio();
 #endif
 
   audio.sound_enabled = FALSE;
@@ -689,7 +822,9 @@ inline Key GetEventKey(KeyEvent *event, boolean with_modifiers)
         (int)SDL_GetModState());
 #endif
 
-  if (with_modifiers && event->keysym.unicode != 0)
+  if (with_modifiers &&
+      event->keysym.unicode > 0x0000 &&
+      event->keysym.unicode < 0x2000)
     return event->keysym.unicode;
   else
     return event->keysym.sym;
@@ -724,9 +859,39 @@ inline boolean CheckCloseWindowEvent(ClientMessageEvent *event)
 }
 
 
-inline void dummy(void)
+/* ========================================================================= */
+/* joystick functions                                                        */
+/* ========================================================================= */
+
+inline void InitJoysticks()
 {
-#ifdef TARGET_SDL
-#else
+  int i;
+
+#ifdef NO_JOYSTICK
+  return;      /* joysticks generally deactivated by compile-time directive */
+#endif
+
+  /* always start with reliable default values */
+  joystick.status = JOYSTICK_NOT_AVAILABLE;
+  for (i=0; i<MAX_PLAYERS; i++)
+    joystick.fd[i] = -1;               /* joystick device closed */
+
+#if defined(TARGET_SDL)
+  SDLInitJoysticks();
+#elif defined(PLATFORM_UNIX)
+  UnixInitJoysticks();
+#elif defined(PLATFORM_MSDOS)
+  MSDOSInitJoysticks();
+#endif
+}
+
+inline boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
+{
+#if defined(TARGET_SDL)
+  return SDLReadJoystick(nr, x, y, b1, b2);
+#elif defined(PLATFORM_UNIX)
+  return UnixReadJoystick(nr, x, y, b1, b2);
+#elif defined(PLATFORM_MSDOS)
+  return MSDOSReadJoystick(nr, x, y, b1, b2);
 #endif
 }
index 34a068a543b6f846f5ec1ebfe96dc6e19fbb7250..0f6d5e2b897b9387ce753301b57ca8e2d5025ef9 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "x11.h"
 #endif
 
+#if defined(PLATFORM_MACOSX)
+/* some symbols are already defined on Mac OS X */
+#define Delay Delay_internal
+#define DrawLine DrawLine_internal
+#define DrawText DrawText_internal
+#define GetPixel GetPixel_internal
+#endif
 
-/* contant definitions */
 
 /* the additional 'b' is needed for Win32 to open files in binary mode */
 #define MODE_READ              "rb"
 #define FULLSCREEN_NOT_AVAILABLE FALSE
 #define FULLSCREEN_AVAILABLE    TRUE
 
-/* values for button_status */
+/* default input keys */
+#define DEFAULT_KEY_LEFT       KSYM_Left
+#define DEFAULT_KEY_RIGHT      KSYM_Right
+#define DEFAULT_KEY_UP         KSYM_Up
+#define DEFAULT_KEY_DOWN       KSYM_Down
+#define DEFAULT_KEY_SNAP       KSYM_Shift_L
+#define DEFAULT_KEY_BOMB       KSYM_Shift_R
+#define DEFAULT_KEY_OKAY       KSYM_Return
+#define DEFAULT_KEY_CANCEL     KSYM_Escape
+
+/* default shortcut keys */
+#define DEFAULT_KEY_SAVE_GAME  KSYM_F1
+#define DEFAULT_KEY_LOAD_GAME  KSYM_F2
+#define DEFAULT_KEY_TOGGLE_PAUSE KSYM_space
+
+/* values for move directions and special "button" keys */
+#define MV_NO_MOVING           0
+#define MV_LEFT                        (1 << 0)
+#define MV_RIGHT               (1 << 1)
+#define MV_UP                  (1 << 2)
+#define MV_DOWN                        (1 << 3)
+#define KEY_BUTTON_1           (1 << 4)
+#define KEY_BUTTON_2           (1 << 5)
+#define KEY_MOTION             (MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN)
+#define KEY_BUTTON             (KEY_BUTTON_1 | KEY_BUTTON_2)
+#define KEY_ACTION             (KEY_MOTION | KEY_BUTTON)
+
+/* values for button status */
 #define MB_NOT_PRESSED         FALSE
 #define MB_NOT_RELEASED                TRUE
 #define MB_RELEASED            FALSE
@@ -48,6 +81,7 @@
 #define MB_MENU_CHOICE         FALSE
 #define MB_MENU_MARK           TRUE
 #define MB_MENU_INITIALIZE     (-1)
+#define MB_MENU_LEAVE          (-2)
 #define MB_LEFTBUTTON          1
 #define MB_MIDDLEBUTTON                2
 #define MB_RIGHTBUTTON         3
 #define REDRAW_FPS             (1 << 11)
 #define REDRAWTILES_THRESHOLD  (SCR_FIELDX * SCR_FIELDY / 2)
 
+/* maximum number of parallel players supported by libgame functions */
+#define MAX_PLAYERS            4
+
+/* maximum allowed length of player name */
+#define MAX_PLAYER_NAME_LEN    10
 
 /* default name for empty highscore entry */
 #define EMPTY_PLAYER_NAME      "no name"
 /* default name for unknown player names */
 #define ANONYMOUS_NAME         "anonymous"
 
+/* default text for non-existant artwork */
+#define NOT_AVAILABLE          "(not available)"
+
 /* default name for new levels */
 #define NAMELESS_LEVEL_NAME    "nameless level"
 
 #define RW_BASE_PATH           RW_GAME_DIR
 
 #define GRAPHICS_DIRECTORY     "graphics"
-#define MUSIC_DIRECTORY                "music"
 #define SOUNDS_DIRECTORY       "sounds"
+#define MUSIC_DIRECTORY                "music"
 #define LEVELS_DIRECTORY       "levels"
 #define TAPES_DIRECTORY                "tapes"
 #define SCORES_DIRECTORY       "scores"
 
+#if !defined(PLATFORM_MSDOS)
+#define GRAPHICS_SUBDIR                "gfx_classic"
+#define SOUNDS_SUBDIR          "snd_classic"
+#define MUSIC_SUBDIR           "mus_classic"
+#else
+#define GRAPHICS_SUBDIR                "gfx_orig"
+#define SOUNDS_SUBDIR          "snd_orig"
+#define MUSIC_SUBDIR           "mus_orig"
+#endif
+
 /* areas in bitmap PIX_DOOR */
 /* meaning in PIX_DB_DOOR: (3 PAGEs)
    PAGEX1: 1. buffer for DOOR_1
 #define DOOR_GFX_PAGEY1                (0)
 #define DOOR_GFX_PAGEY2                (gfx.dysize)
 
+/* functions for version handling */
+#define VERSION_IDENT(x,y,z)   ((x) * 10000 + (y) * 100 + (z))
+#define VERSION_MAJOR(x)       ((x) / 10000)
+#define VERSION_MINOR(x)       (((x) % 10000) / 100)
+#define VERSION_PATCH(x)       ((x) % 100)
+
+/* functions for parent/child process identification */
+#define IS_PARENT_PROCESS(pid) ((pid) > 0)
+#define IS_CHILD_PROCESS(pid)  ((pid) == 0)
 
-/* type definitions */
 
+/* type definitions */
 typedef int (*EventFilter)(const Event *);
 
 
@@ -141,10 +202,18 @@ struct ProgramInfo
   char *program_title;
   char *window_title;
   char *icon_title;
+
   char *x11_icon_filename;
   char *x11_iconmask_filename;
   char *msdos_pointer_filename;
 
+  char *cookie_prefix;
+  char *filename_prefix;       /* prefix to cut off from DOS filenames */
+
+  int version_major;
+  int version_minor;
+  int version_patch;
+
   void (*exit_function)(int);
 };
 
@@ -156,10 +225,14 @@ struct OptionInfo
   char *ro_base_directory;
   char *rw_base_directory;
   char *level_directory;
+  char *graphics_directory;
+  char *sounds_directory;
+  char *music_directory;
   boolean serveronly;
   boolean network;
   boolean verbose;
   boolean debug;
+  char *debug_command;
 };
 
 struct VideoSystemInfo
@@ -173,19 +246,20 @@ struct VideoSystemInfo
 struct AudioSystemInfo
 {
   boolean sound_available;
-  boolean music_available;
   boolean loops_available;
-  boolean mods_available;
+  boolean music_available;
+
   boolean sound_enabled;
+  boolean sound_deactivated;   /* for temporarily disabling sound */
 
-  int soundserver_pipe[2];
-  int soundserver_pid;
+  int mixer_pipe[2];
+  int mixer_pid;
   char *device_name;
   int device_fd;
 
-  int channels;
+  int num_channels;
   int music_channel;
-  int music_nr;
+  int first_sound_channel;
 };
 
 struct GfxInfo
@@ -201,10 +275,103 @@ struct GfxInfo
 
   int vx, vy;
   int vxsize, vysize;
+
+  boolean draw_deactivation_mask;
 };
 
-struct LevelDirInfo
+struct JoystickInfo
 {
+  int status;
+  int fd[MAX_PLAYERS];         /* file descriptor of player's joystick */
+};
+
+struct SetupJoystickInfo
+{
+  char *device_name;           /* device name of player's joystick */
+
+  int xleft, xmiddle, xright;
+  int yupper, ymiddle, ylower;
+  int snap;
+  int bomb;
+};
+
+struct SetupKeyboardInfo
+{
+  Key left;
+  Key right;
+  Key up;
+  Key down;
+  Key snap;
+  Key bomb;
+};
+
+struct SetupInputInfo
+{
+  boolean use_joystick;
+  struct SetupJoystickInfo joy;
+  struct SetupKeyboardInfo key;
+};
+
+struct SetupShortcutInfo
+{
+  Key save_game;
+  Key load_game;
+  Key toggle_pause;
+};
+
+struct SetupInfo
+{
+  char *player_name;
+
+  boolean sound;
+  boolean sound_loops;
+  boolean sound_music;
+  boolean sound_simple;
+  boolean toons;
+  boolean double_buffering;
+  boolean direct_draw;         /* !double_buffering (redundant!) */
+  boolean scroll_delay;
+  boolean soft_scrolling;
+  boolean fading;
+  boolean autorecord;
+  boolean quick_doors;
+  boolean team_mode;
+  boolean handicap;
+  boolean time_limit;
+  boolean fullscreen;
+  boolean ask_on_escape;
+
+  char *graphics_set;
+  char *sounds_set;
+  char *music_set;
+  boolean override_level_graphics;
+  boolean override_level_sounds;
+  boolean override_level_music;
+
+  struct SetupShortcutInfo shortcut;
+  struct SetupInputInfo input[MAX_PLAYERS];
+};
+
+#define TREE_TYPE_GENERIC              0
+#define TREE_TYPE_LEVEL_DIR            1
+#define TREE_TYPE_GRAPHICS_DIR         2
+#define TREE_TYPE_SOUNDS_DIR           3
+#define TREE_TYPE_MUSIC_DIR            4
+
+struct TreeInfo
+{
+  struct TreeInfo **node_top;          /* topmost node in tree */
+  struct TreeInfo *node_parent;                /* parent level directory info */
+  struct TreeInfo *node_group;         /* level group sub-directory info */
+  struct TreeInfo *next;               /* next level series structure node */
+
+  int cl_first;                /* internal control field for setup screen */
+  int cl_cursor;       /* internal control field for setup screen */
+
+  int type;            /* type of tree content */
+
+  /* fields for "type == TREE_TYPE_LEVEL_DIR" */
+
   char *filename;      /* level series single directory name */
   char *fullpath;      /* complete path relative to level directory */
   char *basepath;      /* absolute base path of level directory */
@@ -224,12 +391,27 @@ struct LevelDirInfo
   int color;           /* color to use on selection screen for this level */
   char *class_desc;    /* description of level series class */
   int handicap_level;  /* number of the lowest unsolved level */
-  int cl_first;                /* internal control field for "choose level" screen */
-  int cl_cursor;       /* internal control field for "choose level" screen */
+};
+
+typedef struct TreeInfo TreeInfo;
+typedef struct TreeInfo LevelDirTree;
+typedef struct TreeInfo ArtworkDirTree;
+typedef struct TreeInfo GraphicsDirTree;
+typedef struct TreeInfo SoundsDirTree;
+typedef struct TreeInfo MusicDirTree;
 
-  struct LevelDirInfo *node_parent;    /* parent level directory info */
-  struct LevelDirInfo *node_group;     /* level group sub-directory info */
-  struct LevelDirInfo *next;           /* next level series structure node */
+struct ArtworkInfo
+{
+  GraphicsDirTree *gfx_first;
+  GraphicsDirTree *gfx_current;
+  SoundsDirTree *snd_first;
+  SoundsDirTree *snd_current;
+  MusicDirTree *mus_first;
+  MusicDirTree *mus_current;
+
+  char *graphics_set_current_name;
+  char *sounds_set_current_name;
+  char *music_set_current_name;
 };
 
 
@@ -242,26 +424,30 @@ extern struct OptionInfo  options;
 extern struct VideoSystemInfo  video;
 extern struct AudioSystemInfo  audio;
 extern struct GfxInfo          gfx;
+extern struct ArtworkInfo      artwork;
+extern struct JoystickInfo     joystick;
+extern struct SetupInfo                setup;
 
-extern struct LevelDirInfo     *leveldir_first;
-extern struct LevelDirInfo     *leveldir_current;
+extern LevelDirTree           *leveldir_first;
+extern LevelDirTree           *leveldir_current;
+extern int                     level_nr;
 
-extern Display        *display;
-extern Visual         *visual;
-extern int             screen;
-extern Colormap                cmap;
+extern Display                *display;
+extern Visual                 *visual;
+extern int                     screen;
+extern Colormap                        cmap;
 
-extern DrawWindow      *window;
-extern DrawBuffer      *backbuffer;
-extern DrawBuffer      *drawto;
+extern DrawWindow             *window;
+extern DrawBuffer             *backbuffer;
+extern DrawBuffer             *drawto;
 
-extern int             button_status;
-extern boolean         motion_status;
+extern int                     button_status;
+extern boolean                 motion_status;
 
-extern int             redraw_mask;
-extern int             redraw_tiles;
+extern int                     redraw_mask;
+extern int                     redraw_tiles;
 
-extern int             FrameCounter;
+extern int                     FrameCounter;
 
 
 /* function definitions */
@@ -271,12 +457,14 @@ void InitExitFunction(void (*exit_function)(int));
 void InitPlatformDependantStuff(void);
 void ClosePlatformDependantStuff(void);
 
-void InitProgramInfo(char *, char *, char *, char *, char *, char *, char *);
+void InitProgramInfo(char *, char *, char *, char *, char *, char *, char *,
+                    char *, char *, int);
 
 void InitGfxFieldInfo(int, int, int, int, int, int, int, int);
 void InitGfxDoor1Info(int, int, int, int);
 void InitGfxDoor2Info(int, int, int, int);
 void InitGfxScrollbufferInfo(int, int);
+void SetDrawDeactivationMask(int );
 
 inline void InitVideoDisplay(void);
 inline void CloseVideoDisplay(void);
@@ -304,6 +492,8 @@ inline boolean SetVideoMode(boolean);
 inline boolean ChangeVideoModeIfNeeded(boolean);
 
 Bitmap *LoadImage(char *);
+Bitmap *LoadCustomImage(char *);
+void ReloadCustomImage(Bitmap *, char *);
 
 inline void OpenAudio(void);
 inline void CloseAudio(void);
@@ -315,4 +505,7 @@ inline void NextEvent(Event *event);
 inline Key GetEventKey(KeyEvent *, boolean);
 inline boolean CheckCloseWindowEvent(ClientMessageEvent *);
 
+inline void InitJoysticks();
+inline boolean ReadJoystick(int, int *, int *, boolean *, boolean *);
+
 #endif /* SYSTEM_H */
index 924381178be53926c7bd25bb112706ef304e310c..16381e494438e8872531972fb045c6eb1a884a76 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -147,17 +147,21 @@ void DrawTextExt(DrawBuffer *bitmap, int x, int y,
       c = 92;
     else if (c == 'ü' || c == 'Ü')
       c = 93;
+    else if (c == '[' || c == ']')     /* map to normal braces */
+      c = (c == '[' ? '(' : ')');
+    else if (c == '\\')                        /* bad luck ... */
+      c = '/';
 
-    if ((c >= 32 && c <= 95) || c == '°')
+    if ((c >= 32 && c <= 95) || c == '°' || c == '´')
     {
       int src_x = ((c - 32) % FONT_CHARS_PER_LINE) * font_width;
       int src_y = ((c - 32) / FONT_CHARS_PER_LINE) * font_height + font_start;
       int dest_x = x, dest_y = y;
 
-      if (c == '°')
+      if (c == '°' || c == '´')                /* map '°' and 'TM' signs */
       {
-       src_x = (FONT_CHARS_PER_LINE + 1) * font_width;
-       src_y = 3 * font_height + font_start;
+       src_x = FONT_CHARS_PER_LINE * font_width;
+       src_y = (c == '°' ? 1 : 2) * font_height + font_start;
       }
 
       if (print_inverse)
index f7f4ddb32bab9c90b618ebdd3c821763910ce929..f459e3fd9f392366c2fd37f176b51deb22568693 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
diff --git a/src/libgame/toons.c b/src/libgame/toons.c
new file mode 100644 (file)
index 0000000..2546975
--- /dev/null
@@ -0,0 +1,305 @@
+/***********************************************************
+* Artsoft Retro-Game Library                               *
+*----------------------------------------------------------*
+* (c) 1995-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* toons.c                                                  *
+***********************************************************/
+
+#include "toons.h"
+#include "misc.h"
+
+
+/* values for toon animation */
+#define ANIM_START     0
+#define ANIM_CONTINUE  1
+#define ANIM_STOP      2
+
+
+static struct ToonScreenInfo screen_info;
+
+void InitToonScreen(Bitmap **toon_bitmap_array,
+                   Bitmap *save_buffer,
+                   void (*update_function)(void),
+                   void (*prepare_backbuffer_function)(void),
+                   boolean (*redraw_needed_function)(void),
+                   struct ToonInfo *toons, int num_toons,
+                   int startx, int starty,
+                   int width, int height)
+{
+  screen_info.toon_bitmap_array = toon_bitmap_array;
+  screen_info.save_buffer = save_buffer;
+  screen_info.update_function = update_function;
+  screen_info.prepare_backbuffer_function = prepare_backbuffer_function;
+  screen_info.redraw_needed_function = redraw_needed_function;
+  screen_info.toons = toons;
+  screen_info.num_toons = num_toons;
+  screen_info.startx = startx;
+  screen_info.starty = starty;
+  screen_info.width = width;
+  screen_info.height = height;
+}
+
+void DrawAnim(Bitmap *toon_bitmap, GC toon_clip_gc,
+             int src_x, int src_y, int width, int height,
+             int dest_x, int dest_y, int pad_x, int pad_y)
+{
+  int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
+
+#if 1
+  /* special method to avoid flickering interference with BackToFront() */
+  BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
+            width+2*pad_x, height+2*pad_y, buf_x, buf_y);
+  SetClipOrigin(toon_bitmap, toon_clip_gc, dest_x-src_x, dest_y-src_y);
+  BlitBitmapMasked(toon_bitmap, backbuffer,
+                  src_x, src_y, width, height, dest_x, dest_y);
+  BlitBitmap(backbuffer, window, dest_x-pad_x, dest_y-pad_y,
+            width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
+
+  screen_info.update_function();
+
+  BlitBitmap(screen_info.save_buffer, backbuffer, buf_x, buf_y,
+           width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
+#else
+  /* normal method, causing flickering interference with BackToFront() */
+  BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
+            width+2*pad_x, height+2*pad_y, buf_x, buf_y);
+  SetClipOrigin(toon_bitmap,toon_clip_gc, buf_x-src_x+pad_x,buf_y-src_y+pad_y);
+  BlitBitmapMasked(toon_bitmap, screen_info.save_buffer,
+                  src_x, src_y, width, height, buf_x+pad_x, buf_y+pad_y);
+  BlitBitmap(screen_info.save_buffer, window, buf_x, buf_y,
+            width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
+#endif
+
+  FlushDisplay();
+}
+
+boolean AnimateToon(int toon_nr, boolean restart)
+{
+  static int pos_x = 0, pos_y = 0;
+  static int delta_x = 0, delta_y = 0;
+  static int frame = 0, frame_step = 1;
+  static boolean horiz_move, vert_move;
+  static unsigned long anim_delay = 0;
+  static unsigned long anim_delay_value = 0;
+  static int width,height;
+  static int pad_x,pad_y;
+  static int cut_x,cut_y;
+  static int src_x, src_y;
+  static int dest_x, dest_y;
+
+  struct ToonInfo *anim = &screen_info.toons[toon_nr];
+  int bitmap_nr = screen_info.toons[toon_nr].bitmap_nr;
+  Bitmap *anim_bitmap = screen_info.toon_bitmap_array[bitmap_nr];
+  GC anim_clip_gc = anim_bitmap->stored_clip_gc;
+
+  if (restart)
+  {
+    horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
+    vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
+    anim_delay_value = 1000/anim->frames_per_second;
+    frame = 0;
+
+    if (horiz_move)
+    {
+      if (anim->position == ANIMPOS_UP)
+       pos_y = 0;
+      else if (anim->position == ANIMPOS_DOWN)
+       pos_y = screen_info.height - anim->height;
+      else if (anim->position == ANIMPOS_UPPER)
+       pos_y = SimpleRND((screen_info.height - anim->height) / 2);
+      else
+       pos_y = SimpleRND(screen_info.height - anim->height);
+
+      if (anim->direction == ANIMDIR_RIGHT)
+      {
+       delta_x = anim->stepsize;
+       pos_x = -anim->width + delta_x;
+      }
+      else
+      {
+       delta_x = -anim->stepsize;
+       pos_x = screen_info.width + delta_x;
+      }
+      delta_y = 0;
+    }
+    else
+    {
+      if (anim->position == ANIMPOS_LEFT)
+       pos_x = 0;
+      else if (anim->position == ANIMPOS_RIGHT)
+       pos_x = screen_info.width - anim->width;
+      else
+       pos_x = SimpleRND(screen_info.width - anim->width);
+
+      if (anim->direction == ANIMDIR_DOWN)
+      {
+       delta_y = anim->stepsize;
+       pos_y = -anim->height + delta_y;
+      }
+      else
+      {
+       delta_y = -anim->stepsize;
+       pos_y = screen_info.width + delta_y;
+      }
+      delta_x = 0;
+    }
+  }
+
+  if (pos_x <= -anim->width        - anim->stepsize ||
+      pos_x >=  screen_info.width  + anim->stepsize ||
+      pos_y <= -anim->height       - anim->stepsize ||
+      pos_y >=  screen_info.height + anim->stepsize)
+    return TRUE;
+
+  if (!DelayReached(&anim_delay, anim_delay_value))
+  {
+    if (screen_info.redraw_needed_function() && !restart)
+      DrawAnim(anim_bitmap, anim_clip_gc,
+              src_x + cut_x, src_y + cut_y,
+              width, height,
+              screen_info.startx + dest_x,
+              screen_info.starty + dest_y,
+              pad_x, pad_y);
+
+    return FALSE;
+  }
+
+  if (pos_x < -anim->width)
+    pos_x = -anim->width;
+  else if (pos_x > screen_info.width)
+    pos_x = screen_info.width;
+  if (pos_y < -anim->height)
+    pos_y = -anim->height;
+  else if (pos_y > screen_info.height)
+    pos_y = screen_info.height;
+
+  pad_x = (horiz_move ? anim->stepsize : 0);
+  pad_y = (vert_move  ? anim->stepsize : 0);
+  src_x = anim->src_x + frame * anim->width;
+  src_y = anim->src_y;
+  dest_x = pos_x;
+  dest_y = pos_y;
+  cut_x = cut_y = 0;
+  width  = anim->width;
+  height = anim->height;
+
+  if (pos_x<0)
+  {
+    dest_x = 0;
+    width += pos_x;
+    cut_x = -pos_x;
+  }
+  else if (pos_x > screen_info.width - anim->width)
+    width -= (pos_x - (screen_info.width - anim->width));
+
+  if (pos_y<0)
+  {
+    dest_y = 0;
+    height += pos_y;
+    cut_y = -pos_y;
+  }
+  else if (pos_y > screen_info.height - anim->height)
+    height -= (pos_y - (screen_info.height - anim->height));
+
+  DrawAnim(anim_bitmap, anim_clip_gc,
+          src_x + cut_x, src_y + cut_y,
+          width, height,
+          screen_info.startx + dest_x,
+          screen_info.starty + dest_y,
+          pad_x, pad_y);
+
+  pos_x += delta_x;
+  pos_y += delta_y;
+  frame += frame_step;
+
+  if (frame<0 || frame>=anim->frames)
+  {
+    if (anim->pingpong)
+    {
+      frame_step *= -1;
+      frame = (frame<0 ? 1 : anim->frames-2);
+    }
+    else
+      frame = (frame<0 ? anim->frames-1 : 0);
+  }
+
+  return FALSE;
+}
+
+void HandleAnimation(int mode)
+{
+  static unsigned long animstart_delay = -1;
+  static unsigned long animstart_delay_value = 0;
+  static boolean anim_restart = TRUE;
+  static boolean reset_delay = TRUE;
+  static int toon_nr = 0;
+  int draw_mode;
+
+  if (!setup.toons)
+    return;
+
+  switch(mode)
+  {
+    case ANIM_START:
+      screen_info.prepare_backbuffer_function();
+      anim_restart = TRUE;
+      reset_delay = TRUE;
+
+      return;
+
+    case ANIM_CONTINUE:
+      break;
+
+    case ANIM_STOP:
+      redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
+
+      /* Redraw background even when in direct drawing mode */
+      draw_mode = setup.direct_draw;
+      setup.direct_draw = FALSE;
+      screen_info.update_function();
+      setup.direct_draw = draw_mode;
+
+      return;
+
+    default:
+      break;
+  }
+
+  if (reset_delay)
+  {
+    animstart_delay = Counter();
+    animstart_delay_value = SimpleRND(3000);
+    reset_delay = FALSE;
+  }
+
+  if (anim_restart)
+  {
+    if (!DelayReached(&animstart_delay, animstart_delay_value))
+      return;
+
+    toon_nr = SimpleRND(screen_info.num_toons);
+  }
+
+  anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
+}
+
+void InitAnimation()
+{
+  HandleAnimation(ANIM_START);
+}
+
+void StopAnimation()
+{
+  HandleAnimation(ANIM_STOP);
+}
+
+void DoAnimation()
+{
+  HandleAnimation(ANIM_CONTINUE);
+}
diff --git a/src/libgame/toons.h b/src/libgame/toons.h
new file mode 100644 (file)
index 0000000..f090b4d
--- /dev/null
@@ -0,0 +1,68 @@
+/***********************************************************
+* Artsoft Retro-Game Library                               *
+*----------------------------------------------------------*
+* (c) 1995-2002 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
+*----------------------------------------------------------*
+* toons.h                                                  *
+***********************************************************/
+
+#ifndef TOONS_H
+#define TOONS_H
+
+#include "system.h"
+
+
+/* values for toon animation */
+#define ANIMDIR_LEFT   1
+#define ANIMDIR_RIGHT  2
+#define ANIMDIR_UP     4
+#define ANIMDIR_DOWN   8
+
+#define ANIMPOS_ANY    0
+#define ANIMPOS_LEFT   1
+#define ANIMPOS_RIGHT  2
+#define ANIMPOS_UP     4
+#define ANIMPOS_DOWN   8
+#define ANIMPOS_UPPER  16
+
+
+struct ToonScreenInfo
+{
+  Bitmap **toon_bitmap_array;
+  Bitmap *save_buffer;
+  void (*update_function)(void);
+  void (*prepare_backbuffer_function)(void);
+  boolean (*redraw_needed_function)(void);
+
+  struct ToonInfo *toons;
+  int num_toons;
+
+  int startx, starty;
+  int width, height;
+};
+
+struct ToonInfo
+{
+  int bitmap_nr;
+  int width, height;
+  int src_x, src_y;
+  int frames;
+  int frames_per_second;
+  int stepsize;
+  boolean pingpong;
+  int direction;
+  int position;
+};
+
+
+void InitToonScreen();
+void InitAnimation(void);
+void StopAnimation(void);
+void DoAnimation(void);
+
+#endif /* TOONS_H */
index 76f1f9e02f1acdfcbe9ab28b89d8c8d03ea76ba1..2ee05af0c7e961cbe16555889488fb1f7d708712 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #ifndef TYPES_H
 #define TYPES_H
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
 typedef unsigned char boolean;
 typedef unsigned char byte;
 
@@ -38,4 +43,7 @@ typedef unsigned char byte;
 #define SIGN(a)                ((a) < 0 ? -1 : ((a)>0 ? 1 : 0))
 #endif
 
+#define SIZEOF_ARRAY(array, type)      (sizeof(array) / sizeof(type))
+#define SIZEOF_ARRAY_INT(array)                SIZEOF_ARRAY(array, int)
+
 #endif /* TYPES_H */
index 625a6c93a98d7afa3d8c4da1b298e34c4641ab73..4ef1bd16f258cbb3c6eb3cb206cdf98bdd870410 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -14,6 +14,7 @@
 #include "system.h"
 #include "pcx.h"
 #include "misc.h"
+#include "setup.h"
 
 
 #if defined(TARGET_X11)
@@ -73,17 +74,11 @@ static void X11InitDisplay()
 
   /* got appropriate visual? */
   if (depth < 8)
-  {
-    printf("Sorry, displays with less than 8 bits per pixel not supported.\n");
-    exit(-1);
-  }
+    Error(ERR_EXIT, "X11 display not supported (less than 8 bits per pixel)");
   else if ((depth ==8 && visual->class != PseudoColor) ||
           (depth > 8 && visual->class != TrueColor &&
            visual->class != DirectColor))
-  {
-    printf("Sorry, cannot get appropriate visual.\n");
-    exit(-1);
-  }
+    Error(ERR_EXIT, "X11 display not supported (inappropriate visual)");
 #endif /* !PLATFORM_MSDOS */
 }
 
@@ -146,24 +141,22 @@ static DrawWindow *X11InitWindow()
                    PropModePrepend, (unsigned char *) &delete_atom, 1);
 
 #if 0
-  sprintf(icon_filename, "%s/%s/%s",
-         options.ro_base_directory, GRAPHICS_DIRECTORY,
+  sprintf(icon_filename, "%s/%s", options.graphics_directory,
          icon_pic.picture_filename);
 #endif
   if (XReadBitmapFile(display, new_window->drawable,
-                     program.x11_icon_filename,
+                     getCustomImageFilename(program.x11_icon_filename),
                      &icon_width, &icon_height, &icon_pixmap,
                      &icon_hot_x, &icon_hot_y) != BitmapSuccess)
     Error(ERR_EXIT, "cannot read icon bitmap file '%s'",
          program.x11_icon_filename);
 
 #if 0
-  sprintf(icon_filename, "%s/%s/%s",
-         options.ro_base_directory, GRAPHICS_DIRECTORY,
+  sprintf(icon_filename, "%s/%s", options.graphics_directory,
          icon_pic.picturemask_filename);
 #endif
   if (XReadBitmapFile(display, new_window->drawable,
-                     program.x11_iconmask_filename,
+                     getCustomImageFilename(program.x11_iconmask_filename),
                      &icon_width, &icon_height, &iconmask_pixmap,
                      &icon_hot_x, &icon_hot_y) != BitmapSuccess)
     Error(ERR_EXIT, "cannot read icon bitmap file '%s'",
@@ -239,15 +232,29 @@ static DrawWindow *X11InitWindow()
   return new_window;
 }
 
+static void SetImageDimensions(Bitmap *bitmap)
+{
+#if defined(TARGET_ALLEGRO)
+  BITMAP *allegro_bitmap = (BITMAP *)(bitmap->drawable);
+
+  bitmap->width  = allegro_bitmap->w;
+  bitmap->height = allegro_bitmap->h;
+#else
+  Window root;
+  int x, y;
+  unsigned int border_width, depth;
+
+  XGetGeometry(display, bitmap->drawable, &root, &x, &y,
+              &bitmap->width, &bitmap->height, &border_width, &depth);
+#endif
+}
+
 Bitmap *X11LoadImage(char *filename)
 {
   Bitmap *new_bitmap = CreateBitmapStruct();
+  char *error = "Read_PCX_to_Pixmap(): %s '%s'";
   int pcx_err;
 
-#if defined(PLATFORM_MSDOS)
-  rest(100);
-#endif
-
   pcx_err = Read_PCX_to_Pixmap(display, window->drawable, window->gc, filename,
                               &new_bitmap->drawable, &new_bitmap->clip_mask);
   switch(pcx_err)
@@ -255,28 +262,46 @@ Bitmap *X11LoadImage(char *filename)
     case PCX_Success:
       break;
     case PCX_OpenFailed:
-      Error(ERR_EXIT, "cannot open PCX file '%s'", filename);
+      SetError(error, "cannot open PCX file", filename);
+      return NULL;
     case PCX_ReadFailed:
-      Error(ERR_EXIT, "cannot read PCX file '%s'", filename);
+      SetError(error, "cannot read PCX file", filename);
+      return NULL;
     case PCX_FileInvalid:
-      Error(ERR_EXIT, "invalid PCX file '%s'", filename);
+      SetError(error, "invalid PCX file", filename);
+      return NULL;
     case PCX_NoMemory:
-      Error(ERR_EXIT, "not enough memory for PCX file '%s'", filename);
+      SetError(error, "not enough memory for PCX file", filename);
+      return NULL;
     case PCX_ColorFailed:
-      Error(ERR_EXIT, "cannot get colors for PCX file '%s'", filename);
+      SetError(error, "cannot get colors for PCX file", filename);
+      return NULL;
+    case PCX_OtherError:
+      /* this should already have called SetError() */
+      return NULL;
     default:
-      break;
+      SetError(error, "unknown error reading PCX file", filename);
+      return NULL;
   }
 
   if (!new_bitmap->drawable)
-    Error(ERR_EXIT, "cannot get graphics for '%s'", filename);
+  {
+    SetError("X11LoadImage(): cannot get graphics for '%s'", filename);
+    return NULL;
+  }
 
   if (!new_bitmap->clip_mask)
-    Error(ERR_EXIT, "cannot get clipmask for '%s'", filename);
+  {
+    SetError("X11LoadImage(): cannot get clipmask for '%s'", filename);
+    return NULL;
+  }
 
   /* set GraphicContext inheritated from Window */
   new_bitmap->gc = window->gc;
 
+  /* set image width and height */
+  SetImageDimensions(new_bitmap);
+
   return new_bitmap;
 }
 
index 9903e8c977ca172acab32a7f74ed68a694e814c7..c42f25cc54fe4d896277a192c5348b01351e2d2f 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2001 Artsoft Entertainment                      *
+* (c) 1994-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -59,6 +59,9 @@ typedef XClientMessageEvent   ClientMessageEvent;
 
 struct X11DrawableInfo
 {
+  char *source_filename;
+
+  int width, height;
   Drawable drawable;
   Drawable clip_mask;
   GC gc;               /* GC for normal drawing (inheritated from 'window') */
@@ -283,6 +286,10 @@ struct XY
 #define KSYM_F23               XK_F23
 #define KSYM_F24               XK_F24
 
+#define KSYM_FKEY_FIRST                KSYM_F1
+#define KSYM_FKEY_LAST         KSYM_F24
+#define KSYM_NUM_FKEYS         (KSYM_FKEY_LAST - KSYM_FKEY_FIRST + 1)
+
 
 /* X11 function definitions */
 
index de38f3f093b91eafe334534cd3c19ff8e10eaa86..2a0d656655036ea0cbab99364b0d171b9245674f 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -17,7 +17,6 @@
 #include "init.h"
 #include "game.h"
 #include "events.h"
-#include "joystick.h"
 
 GC             tile_clip_gc;
 Bitmap        *pix[NUM_BITMAPS];
@@ -25,22 +24,11 @@ Pixmap              tile_clipmask[NUM_TILES];
 DrawBuffer     *fieldbuffer;
 DrawBuffer     *drawto_field;
 
-int            joystick_device = 0;
-char          *joystick_device_name[MAX_PLAYERS] =
-{
-  DEV_JOYSTICK_0,
-  DEV_JOYSTICK_1,
-  DEV_JOYSTICK_2,
-  DEV_JOYSTICK_3
-};
-
 int            game_status = MAINMENU;
 boolean                level_editor_test_game = FALSE;
 boolean                network_playing = FALSE;
 
 int            key_joystick_mapping = 0;
-int            global_joystick_status = JOYSTICK_STATUS;
-int            joystick_status = JOYSTICK_STATUS;
 
 boolean                redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
 int            redraw_x1 = 0, redraw_y1 = 0;
@@ -62,7 +50,6 @@ short         ExplodeField[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 unsigned long  Elementeigenschaften1[MAX_ELEMENTS];
 unsigned long  Elementeigenschaften2[MAX_ELEMENTS];
 
-int            level_nr;
 int            lev_fieldx,lev_fieldy, scroll_x,scroll_y;
 
 int            FX = SX, FY = SY, ScrollStepSize;
@@ -149,391 +136,592 @@ char *sound_name[NUM_SOUNDS] =
   "gate.wav"
 };
 
-/* background music */
-int background_loop[] =
+struct SoundEffectInfo sound_effects[] =
 {
+  /* sounds for Boulder Dash style elements and actions */
+  { "bd_empty_space.digging",          "empty.wav"             },
+  { "bd_sand.digging",                 "schlurf.wav"           },
+  { "bd_diamond.collecting",           "pong.wav"              },
+  { "bd_diamond.impact",               "pling.wav"             },
+  { "bd_rock.pushing",                 "pusch.wav"             },
+  { "bd_rock.impact",                  "klopf.wav"             },
+  { "bd_magic_wall.activating",                "quirk.wav"             },
+  { "bd_magic_wall.changing",          "quirk.wav"             },
+  { "bd_magic_wall.running",           "miep.wav"              },
+  { "bd_amoeba.waiting",               SND_FILE_UNDEFINED      },
+  { "bd_amoeba.creating",              "amoebe.wav"            },
+  { "bd_amoeba.turning_to_gem",                "pling.wav"             },
+  { "bd_amoeba.turning_to_rock",       "klopf.wav"             },
+  { "bd_butterfly.moving",             "klapper.wav"           },
+  { "bd_butterfly.waiting",            "klapper.wav"           },
+  { "bd_firefly.moving",               "roehr.wav"             },
+  { "bd_firefly.waiting",              "roehr.wav"             },
+  { "bd_exit.entering",                        "buing.wav"             },
+
+  /* sounds for Supaplex style elements and actions */
+  { "sp_empty_space.digging",          "empty.wav"             },
+  { "sp_base.digging",                 "base.wav"              },
+  { "sp_buggy_base.digging",           "base.wav"              },
+  { "sp_buggy_base.activating",                "bug.wav"               },
+  { "sp_infotron.collecting",          "infotron.wav"          },
+  { "sp_infotron.impact",              "pling.wav"             },
+  { "sp_zonk.pushing",                 "zonkpush.wav"          },
+  { "sp_zonk.impact",                  "zonkdown.wav"          },
+  { "sp_disk_red.collecting",          "infotron.wav"          },
+  { "sp_disk_orange.pushing",          "zonkpush.wav"          },
+  { "sp_disk_yellow.pushing",          "pusch.wav"             },
+  { "sp_port.passing",                 "gate.wav"              },
+  { "sp_exit.entering",                        "exit.wav"              },
+  { "sp_element.exploding",            "booom.wav"             },
+  { "sp_sniksnak.moving",              SND_FILE_UNDEFINED      },
+  { "sp_sniksnak.waiting",             SND_FILE_UNDEFINED      },
+  { "sp_electron.moving",              SND_FILE_UNDEFINED      },
+  { "sp_electron.waiting",             SND_FILE_UNDEFINED      },
+  { "sp_terminal.activating",          SND_FILE_UNDEFINED      },
+
+  /* sounds for Sokoban style elements and actions */
+  { "sokoban_object.pushing",          "pusch.wav"             },
+  { "sokoban_field.filling",           "deng.wav"              },
+  { "sokoban_field.clearing",          SND_FILE_UNDEFINED      },
+  { "sokoban_game.solving",            "buing.wav"             },
+
+  /* sounds for Emerald Mine style elements and actions */
+  { "empty_space.digging",             "empty.wav"             },
+  { "sand.digging",                    "schlurf.wav"           },
+  { "emerald.collecting",              "pong.wav"              },
+  { "emerald.impact",                  "pling.wav"             },
+  { "diamond.collecting",              "pong.wav"              },
+  { "diamond.impact",                  "pling.wav"             },
+  { "diamond.breaking",                        "quirk.wav"             },
+  { "rock.pushing",                    "pusch.wav"             },
+  { "rock.impact",                     "klopf.wav"             },
+  { "bomb.pushing",                    "pusch.wav"             },
+  { "nut.pushing",                     "knurk.wav"             },
+  { "nut.cracking",                    "knack.wav"             },
+  { "nut.impact",                      "klumpf.wav"            },
+  { "dynamite.collecting",             "pong.wav"              },
+  { "dynamite.placing",                        "deng.wav"              },
+  { "dynamite.burning",                        "zisch.wav"             },
+  { "key.collecting",                  "pong.wav"              },
+  { "gate.passing",                    "gate.wav"              },
+  { "bug.moving",                      "klapper.wav"           },
+  { "bug.waiting",                     "klapper.wav"           },
+  { "spaceship.moving",                        "roehr.wav"             },
+  { "spaceship.waiting",               "roehr.wav"             },
+  { "yamyam.moving",                   SND_FILE_UNDEFINED      },
+  { "yamyam.waiting",                  "njam.wav"              },
+  { "yamyam.eating_diamond",           SND_FILE_UNDEFINED      },
+  { "robot.stepping",                  "schlurf.wav"           },
+  { "robot.waiting",                   SND_FILE_UNDEFINED      },
+  { "robot_wheel.activating",          "deng.wav"              },
+  { "robot_wheel.running",             "miep.wav"              },
+  { "magic_wall.activating",           "quirk.wav"             },
+  { "magic_wall.changing",             "quirk.wav"             },
+  { "magic_wall.running",              "miep.wav"              },
+  { "amoeba.waiting",                  SND_FILE_UNDEFINED      },
+  { "amoeba.creating",                 "amoebe.wav"            },
+  { "amoeba.dropping",                 SND_FILE_UNDEFINED      },
+  { "acid.splashing",                  "blurb.wav"             },
+  { "quicksand.filling",               SND_FILE_UNDEFINED      },
+  { "quicksand.slipping_through",      SND_FILE_UNDEFINED      },
+  { "quicksand.emptying",              SND_FILE_UNDEFINED      },
+  { "exit.opening",                    "oeffnen.wav"           },
+  { "exit.entering",                   "buing.wav"             },
+
+  /* sounds for Emerald Mine Club style elements and actions */
+  { "balloon.moving",                  SND_FILE_UNDEFINED      },
+  { "balloon.waiting",                 SND_FILE_UNDEFINED      },
+  { "balloon.pushing",                 "schlurf.wav"           },
+  { "balloon_switch.activating",       SND_FILE_UNDEFINED      },
+  { "spring.moving",                   SND_FILE_UNDEFINED      },
+  { "spring.pushing",                  "pusch.wav"             },
+  { "spring.impact",                   "klopf.wav"             },
+  { "wall.growing",                    SND_FILE_UNDEFINED      },
+
+  /* sounds for Diamond Caves style elements and actions */
+  { "pearl.collecting",                        "pong.wav"              },
+  { "pearl.breaking",                  "knack.wav"             },
+  { "pearl.impact",                    "pling.wav"             },
+  { "crystal.collecting",              "pong.wav"              },
+  { "crystal.impact",                  "pling.wav"             },
+  { "envelope.collecting",             "pong.wav"              },
+  { "sand_invisible.digging",          "schlurf.wav"           },
+  { "shield_passive.collecting",       "pong.wav"              },
+  { "shield_passive.activated",                SND_FILE_UNDEFINED      },
+  { "shield_active.collecting",                "pong.wav"              },
+  { "shield_active.activated",         SND_FILE_UNDEFINED      },
+  { "extra_time.collecting",           "gong.wav"              },
+  { "mole.moving",                     SND_FILE_UNDEFINED      },
+  { "mole.waiting",                    SND_FILE_UNDEFINED      },
+  { "mole.eating_amoeba",              "blurb.wav"             },
+  { "switchgate_switch.activating",    SND_FILE_UNDEFINED      },
+  { "switchgate.opening",              "oeffnen.wav"           },
+  { "switchgate.closing",              "oeffnen.wav"           },
+  { "switchgate.passing",              "gate.wav"              },
+  { "timegate_wheel.activating",       "deng.wav"              },
+  { "timegate_wheel.running",          "miep.wav"              },
+  { "timegate.opening",                        "oeffnen.wav"           },
+  { "timegate.closing",                        "oeffnen.wav"           },
+  { "timegate.passing",                        "gate.wav"              },
+  { "conveyor_belt_switch.activating", SND_FILE_UNDEFINED      },
+  { "conveyor_belt.running",           SND_FILE_UNDEFINED      },
+  { "light_switch.activating",         SND_FILE_UNDEFINED      },
+  { "light_switch.deactivating",       SND_FILE_UNDEFINED      },
+
+  /* sounds for DX Boulderdash style elements and actions */
+  { "dx_bomb.pushing",                 "pusch.wav"             },
+  { "trap_inactive.digging",           "schlurf.wav"           },
+  { "trap.activating",                 SND_FILE_UNDEFINED      },
+  { "tube.passing",                    SND_FILE_UNDEFINED      },
+
+  /* sounds for Rocks'n'Diamonds style elements and actions */
+  { "amoeba.turning_to_gem",           "pling.wav"             },
+  { "amoeba.turning_to_rock",          "klopf.wav"             },
+  { "speed_pill.collecting",           "pong.wav"              },
+  { "dynabomb_nr.collecting",          "pong.wav"              },
+  { "dynabomb_sz.collecting",          "pong.wav"              },
+  { "dynabomb_xl.collecting",          "pong.wav"              },
+  { "dynabomb.placing",                        "deng.wav"              },
+  { "dynabomb.burning",                        "zisch.wav"             },
+  { "satellite.moving",                        SND_FILE_UNDEFINED      },
+  { "satellite.waiting",               SND_FILE_UNDEFINED      },
+  { "satellite.pushing",               "pusch.wav"             },
+  { "lamp.activating",                 "deng.wav"              },
+  { "lamp.deactivating",               "deng.wav"              },
+  { "time_orb_full.collecting",                "gong.wav"              },
+  { "time_orb_full.impact",            "deng.wav"              },
+  { "time_orb_empty.pushing",          "pusch.wav"             },
+  { "time_orb_empty.impact",           "deng.wav"              },
+  { "gameoflife.waiting",              SND_FILE_UNDEFINED      },
+  { "gameoflife.creating",             "amoebe.wav"            },
+  { "biomaze.waiting",                 SND_FILE_UNDEFINED      },
+  { "biomaze.creating",                        "amoebe.wav"            },
+  { "pacman.moving",                   SND_FILE_UNDEFINED      },
+  { "pacman.waiting",                  SND_FILE_UNDEFINED      },
+  { "pacman.eating_amoeba",            SND_FILE_UNDEFINED      },
+  { "dark_yamyam.moving",              SND_FILE_UNDEFINED      },
+  { "dark_yamyam.waiting",             "njam.wav"              },
+  { "dark_yamyam.eating_any",          SND_FILE_UNDEFINED      },
+  { "penguin.moving",                  SND_FILE_UNDEFINED      },
+  { "penguin.waiting",                 SND_FILE_UNDEFINED      },
+  { "penguin.entering_exit",           "buing.wav"             },
+  { "pig.moving",                      SND_FILE_UNDEFINED      },
+  { "pig.waiting",                     SND_FILE_UNDEFINED      },
+  { "pig.eating_gem",                  SND_FILE_UNDEFINED      },
+  { "dragon.moving",                   SND_FILE_UNDEFINED      },
+  { "dragon.waiting",                  SND_FILE_UNDEFINED      },
+  { "dragon.attacking",                        SND_FILE_UNDEFINED      },
+
+  /* sounds for generic elements and actions */
+  { "player.dying",                    "autsch.wav"            },
+  { "element.exploding",               "roaaar.wav"            },
+
+  /* sounds for other game actions */
+  { "game.starting",                   SND_FILE_UNDEFINED      },
+  { "game.running_out_of_time",                "gong.wav"              },
+  { "game.leveltime_bonus",            "sirr.wav"              },
+  { "game.losing",                     "lachen.wav"            },
+  { "game.winning",                    SND_FILE_UNDEFINED      },
+
+  /* sounds for other non-game actions */
+  { "menu.door_opening",               "oeffnen.wav"           },
+  { "menu.door_closing",               "oeffnen.wav"           },
+  { "menu.hall_of_fame",               "halloffame.wav"        },
+  { "menu.info_screen",                        "rhythmloop.wav"        },
+
 #if 0
-  SND_ALCHEMY,
-  SND_CHASE,
-  SND_NETWORK,
-  SND_CZARDASZ,
-  SND_TYGER,
-  SND_VOYAGER,
-  SND_TWILIGHT
+  { "[not used]",                      "antigrav.wav"          },
+  { "[not used]",                      "bong.wav"              },
+  { "[not used]",                      "fuel.wav"              },
+  { "[not used]",                      "holz.wav"              },
+  { "[not used]",                      "hui.wav"               },
+  { "[not used]",                      "kabumm.wav"            },
+  { "[not used]",                      "kink.wav"              },
+  { "[not used]",                      "kling.wav"             },
+  { "[not used]",                      "krach.wav"             },
+  { "[not used]",                      "laser.wav"             },
+  { "[not used]",                      "quiek.wav"             },
+  { "[not used]",                      "rumms.wav"             },
+  { "[not used]",                      "schlopp.wav"           },
+  { "[not used]",                      "schrff.wav"            },
+  { "[not used]",                      "schwirr.wav"           },
+  { "[not used]",                      "slurp.wav"             },
+  { "[not used]",                      "sproing.wav"           },
+  { "[not used]",                      "warnton.wav"           },
+  { "[not used]",                      "whoosh.wav"            },
+  { "[not used]",                      "boom.wav"              },
 #endif
 };
-int num_bg_loops = sizeof(background_loop)/sizeof(int);
 
-char *element_info[] =
+struct ElementInfo element_info[] =
 {
-  "empty space",                               /* 0 */
-  "sand",
-  "normal wall",
-  "round wall",
-  "rock",
-  "key",
-  "emerald",
-  "closed exit",
-  "player",
-  "bug",
-  "spaceship",                                 /* 10 */
-  "yam yam",
-  "robot",
-  "steel wall",
-  "diamond",
-  "dead amoeba",
-  "empty quicksand",
-  "quicksand with rock",
-  "amoeba drop",
-  "bomb",
-  "magic wall",                                        /* 20 */
-  "speed ball",
-  "acid pool",
-  "dropping amoeba",
-  "normal amoeba",
-  "nut with emerald",
-  "life wall",
-  "biomaze",
-  "burning dynamite",
-  "unknown",
-  "magic wheel",                               /* 30 */
-  "running wire",
-  "red key",
-  "yellow key",
-  "green key",
-  "blue key",
-  "red door",
-  "yellow door",
-  "green door",
-  "blue door",
-  "gray door (opened by red key)",             /* 40 */
-  "gray door (opened by yellow key)",
-  "gray door (opened by green key)",
-  "gray door (opened by blue key)",
-  "dynamite",
-  "pac man",
-  "invisible normal wall",
-  "light bulb (dark)",
-  "ligh bulb (glowing)",
-  "wall with emerald",
-  "wall with diamond",                         /* 50 */
-  "amoeba with content",
-  "amoeba (BD style)",
-  "time orb (full)",
-  "time orb (empty)",
-  "growing wall",
-  "diamond (BD style)",
-  "yellow emerald",
-  "wall with BD style diamond",
-  "wall with yellow emerald",
-  "dark yam yam",                              /* 60 */
-  "magic wall (BD style)",
-  "invisible steel wall",
-  "-",
-  "increases number of bombs",
-  "increases explosion size",
-  "increases power of explosion",
-  "sokoban object",
-  "sokoban empty field",
-  "sokoban field with object",
-  "butterfly (starts moving right)",           /* 70 */
-  "butterfly (starts moving up)",
-  "butterfly (starts moving left)",
-  "butterfly (starts moving down)",
-  "firefly (starts moving right)",
-  "firefly (starts moving up)",
-  "firefly (starts moving left)",
-  "firefly (starts moving down)",
-  "butterfly",
-  "firefly",
-  "yellow player",                             /* 80 */
-  "red player",
-  "green player",
-  "blue player",
-  "bug (starts moving right)",
-  "bug (starts moving up)",
-  "bug (starts moving left)",
-  "bug (starts moving down)",
-  "spaceship (starts moving right)",
-  "spaceship (starts moving up)",
-  "spaceship (starts moving left)",            /* 90 */
-  "spaceship (starts moving down)",
-  "pac man (starts moving right)",
-  "pac man (starts moving up)",
-  "pac man (starts moving left)",
-  "pac man (starts moving down)",
-  "red emerald",
-  "violet emerald",
-  "wall with red emerald",
-  "wall with violet emerald",
-  "unknown",                                   /* 100 */
-  "unknown",
-  "unknown",
-  "unknown",
-  "unknown",
-  "normal wall (BD style)",
-  "rock (BD style)",
-  "open exit",
-  "black orb bomb",
-  "amoeba",
-  "mole",                                      /* 110 */
-  "penguin",
-  "satellite",
-  "arrow left",
-  "arrow right",
-  "arrow up",
-  "arrow down",
-  "pig",
-  "fire breathing dragon",
-  "red key (EM style)",
-  "letter ' '",                                        /* 120 */
-  "letter '!'",
-  "letter '\"'",
-  "letter '#'",
-  "letter '$'",
-  "letter '%'",
-  "letter '&'",
-  "letter '''",
-  "letter '('",
-  "letter ')'",
-  "letter '*'",                                        /* 130 */
-  "letter '+'",
-  "letter ','",
-  "letter '-'",
-  "letter '.'",
-  "letter '/'",
-  "letter '0'",
-  "letter '1'",
-  "letter '2'",
-  "letter '3'",
-  "letter '4'",                                        /* 140 */
-  "letter '5'",
-  "letter '6'",
-  "letter '7'",
-  "letter '8'",
-  "letter '9'",
-  "letter ':'",
-  "letter ';'",
-  "letter '<'",
-  "letter '='",
-  "letter '>'",                                        /* 150 */
-  "letter '?'",
-  "letter '@'",
-  "letter 'A'",
-  "letter 'B'",
-  "letter 'C'",
-  "letter 'D'",
-  "letter 'E'",
-  "letter 'F'",
-  "letter 'G'",
-  "letter 'H'",                                        /* 160 */
-  "letter 'I'",
-  "letter 'J'",
-  "letter 'K'",
-  "letter 'L'",
-  "letter 'M'",
-  "letter 'N'",
-  "letter 'O'",
-  "letter 'P'",
-  "letter 'Q'",
-  "letter 'R'",                                        /* 170 */
-  "letter 'S'",
-  "letter 'T'",
-  "letter 'U'",
-  "letter 'V'",
-  "letter 'W'",
-  "letter 'X'",
-  "letter 'Y'",
-  "letter 'Z'",
-  "letter 'Ä'",
-  "letter 'Ö'",                                        /* 180 */
-  "letter 'Ü'",
-  "letter '^'",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",                                 /* 190 */
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "letter ''",
-  "growing wall (horizontal)",                 /* 200 */
-  "growing wall (vertical)",
-  "growing wall (all directions)",
-  "red door (EM style)",
-  "yellow door (EM style)",
-  "green door (EM style)",
-  "blue door (EM style)",
-  "yellow key (EM style)",
-  "green key (EM style)",
-  "blue key (EM style)",
-  "empty space",                               /* 210 */
-  "zonk",
-  "base",
-  "murphy",
-  "infotron",
-  "chip (single)",
-  "hardware",
-  "exit",
-  "orange disk",
-  "port (leading right)",
-  "port (leading down)",                       /* 220 */
-  "port (leading left)",
-  "port (leading up)",
-  "port (leading right)",
-  "port (leading down)",
-  "port (leading left)",
-  "port (leading up)",
-  "snik snak",
-  "yellow disk",
-  "terminal",
-  "red disk",                                  /* 230 */
-  "port (vertical)",
-  "port (horizontal)",
-  "port (all directions)",
-  "electron",
-  "buggy base",
-  "chip (left half)",
-  "chip (right half)",
-  "hardware",
-  "hardware",
-  "hardware",                                  /* 240 */
-  "hardware",
-  "hardware",
-  "hardware",
-  "hardware",
-  "hardware",
-  "hardware",
-  "hardware",
-  "chip (upper half)",
-  "chip (lower half)",
-  "gray door (EM style, red key)",             /* 250 */
-  "gray door (EM style, yellow key)",
-  "gray door (EM style, green key)",
-  "gray door (EM style, blue key)",
-  "unknown",
-  "unknown",
+  { "empty_space",             "empty space"                   },      /* 0 */
+  { "sand",                    "sand"                          },
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "round wall"                    },
+  { "rock",                    "rock"                          },
+  { "key",                     "key"                           },
+  { "emerald",                 "emerald"                       },
+  { "exit",                    "closed exit"                   },
+  { "player",                  "player"                        },
+  { "bug",                     "bug"                           },
+  { "spaceship",               "spaceship"                     },     /* 10 */
+  { "yamyam",                  "yam yam"                       },
+  { "robot",                   "robot"                         },
+  { "wall",                    "steel wall"                    },
+  { "diamond",                 "diamond"                       },
+  { "amoeba",                  "dead amoeba"                   },
+  { "quicksand",               "empty quicksand"               },
+  { "quicksand",               "quicksand with rock"           },
+  { "amoeba",                  "amoeba drop"                   },
+  { "bomb",                    "bomb"                          },
+  { "magic_wall",              "magic wall"                    },     /* 20 */
+  { "speed_pill",              "speed pill"                    },
+  { "acid",                    "acid pool"                     },
+  { "amoeba",                  "dropping amoeba"               },
+  { "amoeba",                  "normal amoeba"                 },
+  { "nut",                     "nut with emerald"              },
+  { "gameoflife",              "Conway's wall of life"         },
+  { "biomaze",                 "biomaze"                       },
+  { "dynamite",                        "burning dynamite"              },
+  { NULL,                      "unknown"                       },
+  { "robot_wheel",             "magic wheel"                   },     /* 30 */
+  { "robot_wheel",             "magic wheel (running)"         },
+  { "key",                     "red key"                       },
+  { "key",                     "yellow key"                    },
+  { "key",                     "green key"                     },
+  { "key",                     "blue key"                      },
+  { "gate",                    "red door"                      },
+  { "gate",                    "yellow door"                   },
+  { "gate",                    "green door"                    },
+  { "gate",                    "blue door"                     },
+  { "gate",                    "gray door (opened by red key)" },     /* 40 */
+  { "gate",                    "gray door (opened by yellow key)"},
+  { "gate",                    "gray door (opened by green key)"},
+  { "gate",                    "gray door (opened by blue key)"},
+  { "dynamite",                        "dynamite"                      },
+  { "pacman",                  "pac man"                       },
+  { "wall",                    "invisible normal wall"         },
+  { "lamp",                    "lamp (off)"                    },
+  { "lamp",                    "lamp (on)"                     },
+  { "wall",                    "wall with emerald"             },
+  { "wall",                    "wall with diamond"             },     /* 50 */
+  { "amoeba",                  "amoeba with content"           },
+  { "bd_amoeba",               "amoeba (BD style)"             },
+  { "time_orb_full",           "time orb (full)"               },
+  { "time_orb_empty",          "time orb (empty)"              },
+  { "wall",                    "growing wall"                  },
+  { "bd_diamond",              "diamond (BD style)"            },
+  { "emerald",                 "yellow emerald"                },
+  { "wall",                    "wall with BD style diamond"    },
+  { "wall",                    "wall with yellow emerald"      },
+  { "dark_yamyam",             "dark yam yam"                  },     /* 60 */
+  { "bd_magic_wall",           "magic wall (BD style)"         },
+  { "wall",                    "invisible steel wall"          },
+  { NULL,                      "-"                             },
+  { "dynabomb_nr",             "increases number of bombs"     },
+  { "dynabomb_sz",             "increases explosion size"      },
+  { "dynabomb_xl",             "increases power of explosion"  },
+  { "sokoban_object",          "sokoban object"                },
+  { "sokoban_field",           "sokoban empty field"           },
+  { "sokoban_field",           "sokoban field with object"     },
+  { "bd_butterfly",            "butterfly (starts moving right)"},    /* 70 */
+  { "bd_butterfly",            "butterfly (starts moving up)"  },
+  { "bd_butterfly",            "butterfly (starts moving left)"},
+  { "bd_butterfly",            "butterfly (starts moving down)"},
+  { "bd_firefly",              "firefly (starts moving right)" },
+  { "bd_firefly",              "firefly (starts moving up)"    },
+  { "bd_firefly",              "firefly (starts moving left)"  },
+  { "bd_firefly",              "firefly (starts moving down)"  },
+  { "bd_butterfly",            "butterfly"                     },
+  { "bd_firefly",              "firefly"                       },
+  { "player",                  "yellow player"                 },     /* 80 */
+  { "player",                  "red player"                    },
+  { "player",                  "green player"                  },
+  { "player",                  "blue player"                   },
+  { "bug",                     "bug (starts moving right)"     },
+  { "bug",                     "bug (starts moving up)"        },
+  { "bug",                     "bug (starts moving left)"      },
+  { "bug",                     "bug (starts moving down)"      },
+  { "spaceship",               "spaceship (starts moving right)"},
+  { "spaceship",               "spaceship (starts moving up)"  },
+  { "spaceship",               "spaceship (starts moving left)"},     /* 90 */
+  { "spaceship",               "spaceship (starts moving down)"},
+  { "pacman",                  "pac man (starts moving right)" },
+  { "pacman",                  "pac man (starts moving up)"    },
+  { "pacman",                  "pac man (starts moving left)"  },
+  { "pacman",                  "pac man (starts moving down)"  },
+  { "emerald",                 "red emerald"                   },
+  { "emerald",                 "purple emerald"                },
+  { "wall",                    "wall with red emerald"         },
+  { "wall",                    "wall with purple emerald"      },
+  { NULL,                      "unknown"                       },    /* 100 */
+  { NULL,                      "unknown"                       },
+  { NULL,                      "unknown"                       },
+  { NULL,                      "unknown"                       },
+  { NULL,                      "unknown"                       },
+  { NULL,                      "normal wall (BD style)"        },
+  { "bd_rock",                 "rock (BD style)"               },
+  { "exit",                    "open exit"                     },
+  { NULL,                      "black orb bomb"                },
+  { "amoeba",                  "amoeba"                        },
+  { "mole",                    "mole"                          },    /* 110 */
+  { "penguin",                 "penguin"                       },
+  { "satellite",               "satellite"                     },
+  { NULL,                      "arrow left"                    },
+  { NULL,                      "arrow right"                   },
+  { NULL,                      "arrow up"                      },
+  { NULL,                      "arrow down"                    },
+  { "pig",                     "pig"                           },
+  { "dragon",                  "fire breathing dragon"         },
+  { "key",                     "red key (EM style)"            },
+  { NULL,                      "letter ' '"                    },    /* 120 */
+  { NULL,                      "letter '!'"                    },
+  { NULL,                      "letter '\"'"                   },
+  { NULL,                      "letter '#'"                    },
+  { NULL,                      "letter '$'"                    },
+  { NULL,                      "letter '%'"                    },
+  { NULL,                      "letter '&'"                    },
+  { NULL,                      "letter '''"                    },
+  { NULL,                      "letter '('"                    },
+  { NULL,                      "letter ')'"                    },
+  { NULL,                      "letter '*'"                    },    /* 130 */
+  { NULL,                      "letter '+'"                    },
+  { NULL,                      "letter ','"                    },
+  { NULL,                      "letter '-'"                    },
+  { NULL,                      "letter '.'"                    },
+  { NULL,                      "letter '/'"                    },
+  { NULL,                      "letter '0'"                    },
+  { NULL,                      "letter '1'"                    },
+  { NULL,                      "letter '2'"                    },
+  { NULL,                      "letter '3'"                    },
+  { NULL,                      "letter '4'"                    },    /* 140 */
+  { NULL,                      "letter '5'"                    },
+  { NULL,                      "letter '6'"                    },
+  { NULL,                      "letter '7'"                    },
+  { NULL,                      "letter '8'"                    },
+  { NULL,                      "letter '9'"                    },
+  { NULL,                      "letter ':'"                    },
+  { NULL,                      "letter ';'"                    },
+  { NULL,                      "letter '<'"                    },
+  { NULL,                      "letter '='"                    },
+  { NULL,                      "letter '>'"                    },    /* 150 */
+  { NULL,                      "letter '?'"                    },
+  { NULL,                      "letter '@'"                    },
+  { NULL,                      "letter 'A'"                    },
+  { NULL,                      "letter 'B'"                    },
+  { NULL,                      "letter 'C'"                    },
+  { NULL,                      "letter 'D'"                    },
+  { NULL,                      "letter 'E'"                    },
+  { NULL,                      "letter 'F'"                    },
+  { NULL,                      "letter 'G'"                    },
+  { NULL,                      "letter 'H'"                    },    /* 160 */
+  { NULL,                      "letter 'I'"                    },
+  { NULL,                      "letter 'J'"                    },
+  { NULL,                      "letter 'K'"                    },
+  { NULL,                      "letter 'L'"                    },
+  { NULL,                      "letter 'M'"                    },
+  { NULL,                      "letter 'N'"                    },
+  { NULL,                      "letter 'O'"                    },
+  { NULL,                      "letter 'P'"                    },
+  { NULL,                      "letter 'Q'"                    },
+  { NULL,                      "letter 'R'"                    },    /* 170 */
+  { NULL,                      "letter 'S'"                    },
+  { NULL,                      "letter 'T'"                    },
+  { NULL,                      "letter 'U'"                    },
+  { NULL,                      "letter 'V'"                    },
+  { NULL,                      "letter 'W'"                    },
+  { NULL,                      "letter 'X'"                    },
+  { NULL,                      "letter 'Y'"                    },
+  { NULL,                      "letter 'Z'"                    },
+  { NULL,                      "letter 'Ä'"                    },
+  { NULL,                      "letter 'Ö'"                    },    /* 180 */
+  { NULL,                      "letter 'Ü'"                    },
+  { NULL,                      "letter '^'"                    },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },    /* 190 */
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { NULL,                      "letter ''"                     },
+  { "wall",                    "growing wall (horizontal)"     },    /* 200 */
+  { "wall",                    "growing wall (vertical)"       },
+  { "wall",                    "growing wall (all directions)" },
+  { "gate",                    "red door (EM style)"           },
+  { "gate",                    "yellow door (EM style)"        },
+  { "gate",                    "green door (EM style)"         },
+  { "gate",                    "blue door (EM style)"          },
+  { "key",                     "yellow key (EM style)"         },
+  { "key",                     "green key (EM style)"          },
+  { "key",                     "blue key (EM style)"           },
+  { "empty_space",             "empty space"                   },    /* 210 */
+  { "sp_zonk",                 "zonk"                          },
+  { "sp_base",                 "base"                          },
+  { "player",                  "murphy"                        },
+  { "sp_infotron",             "infotron"                      },
+  { "wall",                    "chip (single)"                 },
+  { "wall",                    "hardware"                      },
+  { "sp_exit",                 "exit"                          },
+  { "sp_disk_orange",          "orange disk"                   },
+  { "sp_port",                 "port (leading right)"          },
+  { "sp_port",                 "port (leading down)"           },    /* 220 */
+  { "sp_port",                 "port (leading left)"           },
+  { "sp_port",                 "port (leading up)"             },
+  { "sp_port",                 "port (leading right)"          },
+  { "sp_port",                 "port (leading down)"           },
+  { "sp_port",                 "port (leading left)"           },
+  { "sp_port",                 "port (leading up)"             },
+  { "sp_sniksnak",             "snik snak"                     },
+  { "sp_disk_yellow",          "yellow disk"                   },
+  { "sp_terminal",             "terminal"                      },
+  { "sp_disk_red",             "red disk"                      },    /* 230 */
+  { "sp_port",                 "port (vertical)"               },
+  { "sp_port",                 "port (horizontal)"             },
+  { "sp_port",                 "port (all directions)"         },
+  { "sp_electron",             "electron"                      },
+  { "sp_buggy_base",           "buggy base"                    },
+  { "wall",                    "chip (left half)"              },
+  { "wall",                    "chip (right half)"             },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },    /* 240 */
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "hardware"                      },
+  { "wall",                    "chip (upper half)"             },
+  { "wall",                    "chip (lower half)"             },
+  { "gate",                    "gray door (EM style, red key)" },    /* 250 */
+  { "gate",                    "gray door (EM style, yellow key)"},
+  { "gate",                    "gray door (EM style, green key)"},
+  { "gate",                    "gray door (EM style, blue key)"},
+  { NULL,                      "unknown"                       },
+  { NULL,                      "unknown"                       },
 
   /* 256 */
 
-  "pearl",                                     /* (256) */
-  "crystal",
-  "wall with pearl",
-  "wall with crystal",
-  "white door",                                        /* 260 */
-  "gray door (opened by white key)",
-  "white key",
-  "shield (passive)",
-  "extra time",
-  "switch gate (open)",
-  "switch gate (closed)",
-  "switch for switch gate",
-  "switch for switch gate",
-  "-",
-  "-",                                         /* 270 */
-  "red conveyor belt (left)",
-  "red conveyor belt (middle)",
-  "red conveyor belt (right)",
-  "switch for red conveyor belt (left)",
-  "switch for red conveyor belt (middle)",
-  "switch for red conveyor belt (right)",
-  "yellow conveyor belt (left)",
-  "yellow conveyor belt (middle)",
-  "yellow conveyor belt (right)",
-  "switch for yellow conveyor belt (left)",    /* 280 */
-  "switch for yellow conveyor belt (middle)",
-  "switch for yellow conveyor belt (right)",
-  "green conveyor belt (left)",
-  "green conveyor belt (middle)",
-  "green conveyor belt (right)",
-  "switch for green conveyor belt (left)",
-  "switch for green conveyor belt (middle)",
-  "switch for green conveyor belt (right)",
-  "blue conveyor belt (left)",
-  "blue conveyor belt (middle)",               /* 290 */
-  "blue conveyor belt (right)",
-  "switch for blue conveyor belt (left)",
-  "switch for blue conveyor belt (middle)",
-  "switch for blue conveyor belt (right)",
-  "land mine",
-  "mail envelope",
-  "light switch (off)",
-  "light switch (on)",
-  "sign (exclamation)",
-  "sign (radio activity)",                     /* 300 */
-  "sign (stop)",
-  "sign (wheel chair)",
-  "sign (parking)",
-  "sign (one way)",
-  "sign (heart)",
-  "sign (triangle)",
-  "sign (round)",
-  "sign (exit)",
-  "sign (yin yang)",
-  "sign (other)",                              /* 310 */
-  "mole (starts moving left)",
-  "mole (starts moving right)",
-  "mole (starts moving up)",
-  "mole (starts moving down)",
-  "steel wall (slanted)",
-  "invisible sand",
-  "dx unknown 15",
-  "dx unknown 42",
-  "-",
-  "-",                                         /* 320 */
-  "shield (active, kills enemies)",
-  "time gate (open)",
-  "time gate (closed)",
-  "switch for time gate",
-  "switch for time gate",
-  "balloon",
-  "send balloon to the left",
-  "send balloon to the right",
-  "send balloon up",
-  "send balloon down",                         /* 330 */
-  "send balloon in any direction",
-  "steel wall",
-  "steel wall",
-  "steel wall",
-  "steel wall",
-  "normal wall",
-  "normal wall",
-  "normal wall",
-  "normal wall",
-  "normal wall",                               /* 340 */
-  "normal wall",
-  "normal wall",
-  "normal wall",
-  "tube (all directions)",
-  "tube (vertical)",
-  "tube (horizontal)",
-  "tube (vertical & left)",
-  "tube (vertical & right)",
-  "tube (horizontal & up)",
-  "tube (horizontal & down)",                  /* 350 */
-  "tube (left & up)",
-  "tube (left & down)",
-  "tube (right & up)",
-  "tube (right & down)",
-  "spring",
-  "trap",
-  "stable bomb (DX style)",
-  "-"
+  { "pearl",                   "pearl"                         },  /* (256) */
+  { "crystal",                 "crystal"                       },
+  { "wall",                    "wall with pearl"               },
+  { "wall",                    "wall with crystal"             },
+  { "gate",                    "white door"                    },    /* 260 */
+  { "gate",                    "gray door (opened by white key)"},
+  { "key",                     "white key"                     },
+  { "shield_passive",          "shield (passive)"              },
+  { "extra_time",              "extra time"                    },
+  { "switchgate",              "switch gate (open)"            },
+  { "switchgate",              "switch gate (closed)"          },
+  { "switchgate_switch",       "switch for switch gate"        },
+  { "switchgate_switch",       "switch for switch gate"        },
+  { NULL,                      "-"                             },
+  { NULL,                      "-"                             },    /* 270 */
+  { "conveyor_belt",           "red conveyor belt (left)"      },
+  { "conveyor_belt",           "red conveyor belt (middle)"    },
+  { "conveyor_belt",           "red conveyor belt (right)"     },
+  { "conveyor_belt_switch",    "switch for red conveyor belt (left)"},
+  { "conveyor_belt_switch",    "switch for red conveyor belt (middle)"},
+  { "conveyor_belt_switch",    "switch for red conveyor belt (right)"},
+  { "conveyor_belt",           "yellow conveyor belt (left)"   },
+  { "conveyor_belt",           "yellow conveyor belt (middle)" },
+  { "conveyor_belt",           "yellow conveyor belt (right)"  },
+  { "conveyor_belt_switch",    "switch for yellow conveyor belt (left)"},
+  { "conveyor_belt_switch",    "switch for yellow conveyor belt (middle)"},
+  { "conveyor_belt_switch",    "switch for yellow conveyor belt (right)"},
+  { "conveyor_belt",           "green conveyor belt (left)"    },
+  { "conveyor_belt",           "green conveyor belt (middle)"  },
+  { "conveyor_belt",           "green conveyor belt (right)"   },
+  { "conveyor_belt_switch",    "switch for green conveyor belt (left)"},
+  { "conveyor_belt_switch",    "switch for green conveyor belt (middle)"},
+  { "conveyor_belt_switch",    "switch for green conveyor belt (right)"},
+  { "conveyor_belt",           "blue conveyor belt (left)"     },
+  { "conveyor_belt",           "blue conveyor belt (middle)"   },
+  { "conveyor_belt",           "blue conveyor belt (right)"    },
+  { "conveyor_belt_switch",    "switch for blue conveyor belt (left)"},
+  { "conveyor_belt_switch",    "switch for blue conveyor belt (middle)"},
+  { "conveyor_belt_switch",    "switch for blue conveyor belt (right)"},
+  { "sand",                    "land mine"                     },
+  { "envelope",                        "mail envelope"                 },
+  { "light_switch",            "light switch (off)"            },
+  { "light_switch",            "light switch (on)"             },
+  { "wall",                    "sign (exclamation)"            },
+  { "wall",                    "sign (radio activity)"         },    /* 300 */
+  { "wall",                    "sign (stop)"                   },
+  { "wall",                    "sign (wheel chair)"            },
+  { "wall",                    "sign (parking)"                },
+  { "wall",                    "sign (one way)"                },
+  { "wall",                    "sign (heart)"                  },
+  { "wall",                    "sign (triangle)"               },
+  { "wall",                    "sign (round)"                  },
+  { "wall",                    "sign (exit)"                   },
+  { "wall",                    "sign (yin yang)"               },
+  { "wall",                    "sign (other)"                  },    /* 310 */
+  { "mole",                    "mole (starts moving left)"     },
+  { "mole",                    "mole (starts moving right)"    },
+  { "mole",                    "mole (starts moving up)"       },
+  { "mole",                    "mole (starts moving down)"     },
+  { "wall",                    "steel wall (slanted)"          },
+  { "sand",                    "invisible sand"                },
+  { NULL,                      "dx unknown 15"                 },
+  { NULL,                      "dx unknown 42"                 },
+  { NULL,                      "-"                             },
+  { NULL,                      "-"                             },    /* 320 */
+  { "shield_active",           "shield (active, kills enemies)"},
+  { "timegate",                        "time gate (open)"              },
+  { "timegate",                        "time gate (closed)"            },
+  { "timegate_wheel",          "switch for time gate"          },
+  { "timegate_wheel",          "switch for time gate"          },
+  { "balloon",                 "balloon"                       },
+  { "wall",                    "send balloon to the left"      },
+  { "wall",                    "send balloon to the right"     },
+  { "balloon_switch",          "send balloon up"               },
+  { "balloon_switch",          "send balloon down"             },    /* 330 */
+  { "balloon_switch",          "send balloon in any direction" },
+  { "wall",                    "steel wall"                    },
+  { "wall",                    "steel wall"                    },
+  { "wall",                    "steel wall"                    },
+  { "wall",                    "steel wall"                    },
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "normal wall"                   },    /* 340 */
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "normal wall"                   },
+  { "wall",                    "normal wall"                   },
+  { "tube",                    "tube (all directions)"         },
+  { "tube",                    "tube (vertical)"               },
+  { "tube",                    "tube (horizontal)"             },
+  { "tube",                    "tube (vertical & left)"        },
+  { "tube",                    "tube (vertical & right)"       },
+  { "tube",                    "tube (horizontal & up)"        },
+  { "tube",                    "tube (horizontal & down)"      },    /* 350 */
+  { "tube",                    "tube (left & up)"              },
+  { "tube",                    "tube (left & down)"            },
+  { "tube",                    "tube (right & up)"             },
+  { "tube",                    "tube (right & down)"           },
+  { "spring",                  "spring"                        },
+  { "trap",                    "trap"                          },
+  { "dx_bomb",                 "stable bomb (DX style)"        },
+  { NULL,                      "-"                             }
 
   /*
   "-------------------------------",
   */
 };
-int num_element_info = sizeof(element_info)/sizeof(char *);
 
 
 /* ========================================================================= */
@@ -551,5 +739,6 @@ int main(int argc, char *argv[])
 
   EventLoop();
   CloseAllAndExit(0);
-  exit(0);     /* to keep compilers happy */
+
+  return 0;    /* to keep compilers happy */
 }
index 27cc057bde42de7ad8d069aac1422924ab4a964d..17136765340ef86cc56b8c5368f4a9af4452b3cd 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -17,6 +17,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #define WIN_XSIZE      672
 #define WIN_YSIZE      560
 
-#if !defined(PLATFORM_MSDOS)
-#define WIN_XPOS       0
-#define WIN_YPOS       0
-#else
-#define WIN_XPOS       ((XRES - WIN_XSIZE) / 2)
-#define WIN_YPOS       ((YRES - WIN_YSIZE) / 2)
-#endif
-
 #define SCR_FIELDX     17
 #define SCR_FIELDY     17
 #define MAX_BUF_XSIZE  (SCR_FIELDX + 2)
@@ -48,8 +41,6 @@
 #define MAX_LEV_FIELDX 128
 #define MAX_LEV_FIELDY 128
 
-#define MAX_PLAYERS    4
-
 #define SCREENX(a)     ((a) - scroll_x)
 #define SCREENY(a)     ((a) - scroll_y)
 #define LEVELX(a)      ((a) + scroll_x)
@@ -96,7 +87,7 @@
 #define EP_BIT_BELT            (1 << 0)
 #define EP_BIT_BELT_SWITCH     (1 << 1)
 #define EP_BIT_TUBE            (1 << 2)
-#define EP_BIT_SLIPPERY_GEMS   (1 << 3)
+#define EP_BIT_EM_SLIPPERY_WALL        (1 << 3)
 
 #define IS_AMOEBALIVE(e)       (Elementeigenschaften1[e] & EP_BIT_AMOEBALIVE)
 #define IS_AMOEBOID(e)         (Elementeigenschaften1[e] & EP_BIT_AMOEBOID)
 #define IS_BELT(e)             (Elementeigenschaften2[e] & EP_BIT_BELT)
 #define IS_BELT_SWITCH(e)      (Elementeigenschaften2[e] & EP_BIT_BELT_SWITCH)
 #define IS_TUBE(e)             (Elementeigenschaften2[e] & EP_BIT_TUBE)
-#define IS_SLIPPERY_GEMS(e)    (Elementeigenschaften2[e] & EP_BIT_SLIPPERY_GEMS)
+#define IS_EM_SLIPPERY_WALL(e) (Elementeigenschaften2[e] & EP_BIT_EM_SLIPPERY_WALL)
 
 #define IS_PLAYER(x,y)         (ELEM_IS_PLAYER(StorePlayer[x][y]))
 
 #define IS_DRAWABLE(e)         ((e) < EL_BLOCKED)
 #define IS_NOT_DRAWABLE(e)     ((e) >= EL_BLOCKED)
 #define TAPE_IS_EMPTY(x)       ((x).length == 0)
-#define TAPE_IS_STOPPED(x)     (!(x).recording && !(x).playing &&!(x).pausing)
+#define TAPE_IS_STOPPED(x)     (!(x).recording && !(x).playing)
 
 #define PLAYERINFO(x,y)                (&stored_player[StorePlayer[x][y]-EL_SPIELER1])
 #define SHIELD_ON(p)           ((p)->shield_passive_time_left > 0)
 #define NUM_BITMAPS            12
 
 /* boundaries of arrays etc. */
-#define MAX_PLAYER_NAME_LEN    10
 #define MAX_LEVEL_NAME_LEN     32
 #define MAX_LEVEL_AUTHOR_LEN   32
 #define MAX_TAPELEN            (1000 * 50)     /* max. time * framerate */
@@ -212,56 +202,6 @@ struct HiScore
   int Score;
 };
 
-struct SetupJoystickInfo
-{
-  char *device_name;
-  int xleft, xmiddle, xright;
-  int yupper, ymiddle, ylower;
-  int snap;
-  int bomb;
-};
-
-struct SetupKeyboardInfo
-{
-  Key left;
-  Key right;
-  Key up;
-  Key down;
-  Key snap;
-  Key bomb;
-};
-
-struct SetupInputInfo
-{
-  boolean use_joystick;
-  struct SetupJoystickInfo joy;
-  struct SetupKeyboardInfo key;
-};
-
-struct SetupInfo
-{
-  char *player_name;
-
-  boolean sound;
-  boolean sound_loops;
-  boolean sound_music;
-  boolean sound_simple;
-  boolean toons;
-  boolean double_buffering;
-  boolean direct_draw;         /* !double_buffering (redundant!) */
-  boolean scroll_delay;
-  boolean soft_scrolling;
-  boolean fading;
-  boolean autorecord;
-  boolean quick_doors;
-  boolean team_mode;
-  boolean handicap;
-  boolean time_limit;
-  boolean fullscreen;
-
-  struct SetupInputInfo input[MAX_PLAYERS];
-};
-
 struct PlayerInfo
 {
   boolean present;             /* player present in level playfield */
@@ -277,8 +217,6 @@ struct PlayerInfo
   byte programmed_action;      /* action forced by game itself (like moving
                                   through doors); overrides other actions */
 
-  int joystick_fd;             /* file descriptor of player's joystick */
-
   int jx,jy, last_jx,last_jy;
   int MovDir, MovPos, GfxPos;
   int Frame;
@@ -288,12 +226,12 @@ struct PlayerInfo
   boolean LevelSolved, GameOver;
   boolean snapped;
 
-  unsigned long move_delay;
-  int move_delay_value;
-
   int last_move_dir;
   int is_moving;
 
+  unsigned long move_delay;
+  int move_delay_value;
+
   unsigned long push_delay;
   unsigned long push_delay_value;
 
@@ -315,9 +253,10 @@ struct PlayerInfo
 
 struct LevelInfo
 {
-  int file_version;            /* version of file the level was stored with */
-  int game_version;            /* version of game engine to play this level */
-  boolean encoding_16bit_field;                /* level contains 16-bit elements */
+  int file_version;    /* file format version the level is stored with    */
+  int game_version;    /* game release version the level was created with */
+
+  boolean encoding_16bit_field;                /* level contains 16-bit elements  */
   boolean encoding_16bit_yamyam;       /* yamyam contains 16-bit elements */
   boolean encoding_16bit_amoeba;       /* amoeba contains 16-bit elements */
 
@@ -338,13 +277,15 @@ struct LevelInfo
   int time_timegate;
   boolean double_speed;
   boolean gravity;
+  boolean em_slippery_gems;    /* EM style "gems slip from wall" behaviour */
 };
 
 struct TapeInfo
 {
-  int file_version;    /* version of file this level tape was stored with */
-  int game_version;    /* version of game engine to play this tape´s level */
-  int version;
+  int file_version;    /* file format version the tape is stored with    */
+  int game_version;    /* game release version the tape was created with */
+  int engine_version;  /* game engine version the tape was recorded with */
+
   int level_nr;
   unsigned long random_seed;
   unsigned long date;
@@ -355,9 +296,13 @@ struct TapeInfo
   boolean pause_before_death;
   boolean recording, playing, pausing;
   boolean fast_forward;
+  boolean index_search;
+  boolean quick_resume;
+  boolean single_step;
   boolean changed;
   boolean player_participates[MAX_PLAYERS];
   int num_participating_players;
+
   struct
   {
     byte action[MAX_PLAYERS];
@@ -367,8 +312,13 @@ struct TapeInfo
 
 struct GameInfo
 {
-  int version;
+  /* constant within running game */
+  int engine_version;
   int emulation;
+  int initial_move_delay;
+  int initial_move_delay_value;
+
+  /* variable within running game */
   int yam_content_nr;
   boolean magic_wall_active;
   int magic_wall_time_left;
@@ -388,21 +338,23 @@ struct GlobalInfo
   int fps_slowdown_factor;
 };
 
+struct ElementInfo
+{
+  char *sound_class_name;
+  char *editor_description;
+};
+
 extern GC              tile_clip_gc;
 extern Bitmap         *pix[];
 extern Pixmap          tile_clipmask[];
 extern DrawBuffer      *fieldbuffer;
 extern DrawBuffer      *drawto_field;
 
-extern int             joystick_device;
-extern char           *joystick_device_name[];
-
 extern int             game_status;
 extern boolean         level_editor_test_game;
 extern boolean         network_playing;
 
 extern int             key_joystick_mapping;
-extern int             global_joystick_status, joystick_status;
 
 extern boolean         redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
 extern int             redraw_x1, redraw_y1;
@@ -424,7 +376,6 @@ extern short                ExplodeField[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 extern unsigned long   Elementeigenschaften1[MAX_ELEMENTS];
 extern unsigned long   Elementeigenschaften2[MAX_ELEMENTS];
 
-extern int             level_nr;
 extern int             lev_fieldx,lev_fieldy, scroll_x,scroll_y;
 
 extern int             FX,FY, ScrollStepSize;
@@ -448,16 +399,10 @@ extern struct LevelInfo           level;
 extern struct PlayerInfo       stored_player[], *local_player;
 extern struct HiScore          highscore[];
 extern struct TapeInfo         tape;
-extern struct JoystickInfo     joystick[];
-extern struct SetupInfo                setup;
 extern struct GameInfo         game;
 extern struct GlobalInfo       global;
-
-extern char            *sound_name[];
-extern int             background_loop[];
-extern int             num_bg_loops;
-extern char            *element_info[];
-extern int             num_element_info;
+extern struct ElementInfo      element_info[];
+extern struct SoundEffectInfo  sound_effects[];
 
 /* often used screen positions */
 #define SX                     8
@@ -882,6 +827,9 @@ extern int          num_element_info;
 #define EL_TRAP_INACTIVE       356
 #define EL_DX_SUPABOMB         357
 
+#define NUM_LEVEL_ELEMENTS     358
+
+
 /* "real" (and therefore drawable) runtime elements */
 #define EL_FIRST_RUNTIME_EL    500
 
@@ -1496,22 +1444,180 @@ extern int             num_element_info;
 
 #define NUM_SOUNDS             55
 
-/* default input keys */
-#define DEFAULT_KEY_LEFT       KSYM_Left
-#define DEFAULT_KEY_RIGHT      KSYM_Right
-#define DEFAULT_KEY_UP         KSYM_Up
-#define DEFAULT_KEY_DOWN       KSYM_Down
-#define DEFAULT_KEY_SNAP       KSYM_Shift_L
-#define DEFAULT_KEY_BOMB       KSYM_Shift_R
-#define DEFAULT_KEY_OKAY       KSYM_Return
-#define DEFAULT_KEY_CANCEL     KSYM_Escape
-
-/* directions for moving */
-#define MV_NO_MOVING           0
-#define MV_LEFT                        (1 << 0)
-#define MV_RIGHT               (1 << 1)
-#define MV_UP                  (1 << 2)
-#define MV_DOWN                        (1 << 3)
+
+/* values for sound effects */
+#define SND_BD_EMPTY_SPACE_DIGGING             0
+#define SND_BD_SAND_DIGGING                    1
+#define SND_BD_DIAMOND_COLLECTING              2
+#define SND_BD_DIAMOND_IMPACT                  3
+#define SND_BD_ROCK_PUSHING                    4
+#define SND_BD_ROCK_IMPACT                     5
+#define SND_BD_MAGIC_WALL_ACTIVATING           6
+#define SND_BD_MAGIC_WALL_CHANGING             7
+#define SND_BD_MAGIC_WALL_RUNNING              8
+#define SND_BD_AMOEBA_WAITING                  9
+#define SND_BD_AMOEBA_CREATING                 10
+#define SND_BD_AMOEBA_TURNING_TO_GEM           11
+#define SND_BD_AMOEBA_TURNING_TO_ROCK          12
+#define SND_BD_BUTTERFLY_MOVING                        13
+#define SND_BD_BUTTERFLY_WAITING               14
+#define SND_BD_FIREFLY_MOVING                  15
+#define SND_BD_FIREFLY_WAITING                 16
+#define SND_BD_EXIT_ENTERING                   17
+#define SND_SP_EMPTY_SPACE_DIGGING             18
+#define SND_SP_BASE_DIGGING                    19
+#define SND_SP_BUGGY_BASE_DIGGING              20
+#define SND_SP_BUGGY_BASE_ACTIVATING           21
+#define SND_SP_INFOTRON_COLLECTING             22
+#define SND_SP_INFOTRON_IMPACT                 23
+#define SND_SP_ZONK_PUSHING                    24
+#define SND_SP_ZONK_IMPACT                     25
+#define SND_SP_DISK_RED_COLLECTING             26
+#define SND_SP_DISK_ORANGE_PUSHING             27
+#define SND_SP_DISK_YELLOW_PUSHING             28
+#define SND_SP_PORT_PASSING                    29
+#define SND_SP_EXIT_ENTERING                   30
+#define SND_SP_ELEMENT_EXPLODING               31
+#define SND_SP_SNIKSNAK_MOVING                 32
+#define SND_SP_SNIKSNAK_WAITING                        33
+#define SND_SP_ELECTRON_MOVING                 34
+#define SND_SP_ELECTRON_WAITING                        35
+#define SND_SP_TERMINAL_ACTIVATING             36
+#define SND_SOKOBAN_OBJECT_PUSHING             37
+#define SND_SOKOBAN_FIELD_FILLING              38
+#define SND_SOKOBAN_FIELD_CLEARING             39
+#define SND_SOKOBAN_GAME_SOLVING               40
+#define SND_EMPTY_SPACE_DIGGING                        41
+#define SND_SAND_DIGGING                       42
+#define SND_EMERALD_COLLECTING                 43
+#define SND_EMERALD_IMPACT                     44
+#define SND_DIAMOND_COLLECTING                 45
+#define SND_DIAMOND_IMPACT                     46
+#define SND_DIAMOND_BREAKING                   47
+#define SND_ROCK_PUSHING                       48
+#define SND_ROCK_IMPACT                                49
+#define SND_BOMB_PUSHING                       50
+#define SND_NUT_PUSHING                                51
+#define SND_NUT_CRACKING                       52
+#define SND_NUT_IMPACT                         53
+#define SND_DYNAMITE_COLLECTING                        54
+#define SND_DYNAMITE_PLACING                   55
+#define SND_DYNAMITE_BURNING                   56
+#define SND_KEY_COLLECTING                     57
+#define SND_GATE_PASSING                       58
+#define SND_BUG_MOVING                         59
+#define SND_BUG_WAITING                                60
+#define SND_SPACESHIP_MOVING                   61
+#define SND_SPACESHIP_WAITING                  62
+#define SND_YAMYAM_MOVING                      63
+#define SND_YAMYAM_WAITING                     64
+#define SND_YAMYAM_EATING_DIAMOND              65
+#define SND_ROBOT_STEPPING                     66
+#define SND_ROBOT_WAITING                      67
+#define SND_ROBOT_WHEEL_ACTIVATING             68
+#define SND_ROBOT_WHEEL_RUNNING                        69
+#define SND_MAGIC_WALL_ACTIVATING              70
+#define SND_MAGIC_WALL_CHANGING                        71
+#define SND_MAGIC_WALL_RUNNING                 72
+#define SND_AMOEBA_WAITING                     73
+#define SND_AMOEBA_CREATING                    74
+#define SND_AMOEBA_DROPPING                    75
+#define SND_ACID_SPLASHING                     76
+#define SND_QUICKSAND_FILLING                  77
+#define SND_QUICKSAND_SLIPPING_THROUGH         78
+#define SND_QUICKSAND_EMPTYING                 79
+#define SND_EXIT_OPENING                       80
+#define SND_EXIT_ENTERING                      81
+#define SND_BALLOON_MOVING                     82
+#define SND_BALLOON_WAITING                    83
+#define SND_BALLOON_PUSHING                    84
+#define SND_BALLOON_SWITCH_ACTIVATING          85
+#define SND_SPRING_MOVING                      86
+#define SND_SPRING_PUSHING                     87
+#define SND_SPRING_IMPACT                      88
+#define SND_WALL_GROWING                       89
+#define SND_PEARL_COLLECTING                   90
+#define SND_PEARL_BREAKING                     91
+#define SND_PEARL_IMPACT                       92
+#define SND_CRYSTAL_COLLECTING                 93
+#define SND_CRYSTAL_IMPACT                     94
+#define SND_ENVELOPE_COLLECTING                        95
+#define SND_SAND_INVISIBLE_DIGGING             96
+#define SND_SHIELD_PASSIVE_COLLECTING          97
+#define SND_SHIELD_PASSIVE_ACTIVATED           98
+#define SND_SHIELD_ACTIVE_COLLECTING           99
+#define SND_SHIELD_ACTIVE_ACTIVATED            100
+#define SND_EXTRA_TIME_COLLECTING              101
+#define SND_MOLE_MOVING                                102
+#define SND_MOLE_WAITING                       103
+#define SND_MOLE_EATING_AMOEBA                 104
+#define SND_SWITCHGATE_SWITCH_ACTIVATING       105
+#define SND_SWITCHGATE_OPENING                 106
+#define SND_SWITCHGATE_CLOSING                 107
+#define SND_SWITCHGATE_PASSING                 108
+#define SND_TIMEGATE_WHEEL_ACTIVATING          109
+#define SND_TIMEGATE_WHEEL_RUNNING             110
+#define SND_TIMEGATE_OPENING                   111
+#define SND_TIMEGATE_CLOSING                   112
+#define SND_TIMEGATE_PASSING                   113
+#define SND_CONVEYOR_BELT_SWITCH_ACTIVATING    114
+#define SND_CONVEYOR_BELT_RUNNING              115
+#define SND_LIGHT_SWITCH_ACTIVATING            116
+#define SND_LIGHT_SWITCH_DEACTIVATING          117
+#define SND_DX_BOMB_PUSHING                    118
+#define SND_TRAP_INACTIVE_DIGGING              119
+#define SND_TRAP_ACTIVATING                    120
+#define SND_TUBE_PASSING                       121
+#define SND_AMOEBA_TURNING_TO_GEM              122
+#define SND_AMOEBA_TURNING_TO_ROCK             123
+#define SND_SPEED_PILL_COLLECTING              124
+#define SND_DYNABOMB_NR_COLLECTING             125
+#define SND_DYNABOMB_SZ_COLLECTING             126
+#define SND_DYNABOMB_XL_COLLECTING             127
+#define SND_DYNABOMB_PLACING                   128
+#define SND_DYNABOMB_BURNING                   129
+#define SND_SATELLITE_MOVING                   130
+#define SND_SATELLITE_WAITING                  131
+#define SND_SATELLITE_PUSHING                  132
+#define SND_LAMP_ACTIVATING                    133
+#define SND_LAMP_DEACTIVATING                  134
+#define SND_TIME_ORB_FULL_COLLECTING           135
+#define SND_TIME_ORB_FULL_IMPACT               136
+#define SND_TIME_ORB_EMPTY_PUSHING             137
+#define SND_TIME_ORB_EMPTY_IMPACT              138
+#define SND_GAMEOFLIFE_WAITING                 139
+#define SND_GAMEOFLIFE_CREATING                        140
+#define SND_BIOMAZE_WAITING                    141
+#define SND_BIOMAZE_CREATING                   142
+#define SND_PACMAN_MOVING                      143
+#define SND_PACMAN_WAITING                     144
+#define SND_PACMAN_EATING_AMOEBA               145
+#define SND_DARK_YAMYAM_MOVING                 146
+#define SND_DARK_YAMYAM_WAITING                        147
+#define SND_DARK_YAMYAM_EATING_ANY             148
+#define SND_PENGUIN_MOVING                     149
+#define SND_PENGUIN_WAITING                    150
+#define SND_PENGUIN_ENTERING_EXIT              151
+#define SND_PIG_MOVING                         152
+#define SND_PIG_WAITING                                153
+#define SND_PIG_EATING_GEM                     154
+#define SND_DRAGON_MOVING                      155
+#define SND_DRAGON_WAITING                     156
+#define SND_DRAGON_ATTACKING                   157
+#define SND_PLAYER_DYING                       158
+#define SND_ELEMENT_EXPLODING                  159
+#define SND_GAME_STARTING                      160
+#define SND_GAME_RUNNING_OUT_OF_TIME           161
+#define SND_GAME_LEVELTIME_BONUS               162
+#define SND_GAME_LOSING                                163
+#define SND_GAME_WINNING                       164
+#define SND_MENU_DOOR_OPENING                  165
+#define SND_MENU_DOOR_CLOSING                  166
+#define SND_MENU_HALL_OF_FAME                  167
+#define SND_MENU_INFO_SCREEN                   168
+
+#define NUM_SOUND_EFFECTS                      169
+
 
 /* values for game_status */
 #define EXITGAME               0
@@ -1523,23 +1629,23 @@ extern int              num_element_info;
 #define TYPENAME               6
 #define HALLOFFAME             7
 #define SETUP                  8
-#define SETUPINPUT             9
-#define CALIBRATION            10
 
 #define PROGRAM_VERSION_MAJOR  2
-#define PROGRAM_VERSION_MINOR  0
-#define PROGRAM_VERSION_PATCH  1
-#define PROGRAM_VERSION_STRING "2.0.1"
+#define PROGRAM_VERSION_MINOR  1
+#define PROGRAM_VERSION_PATCH  0
+#define PROGRAM_VERSION_STRING "2.1.0"
 
 #define PROGRAM_TITLE_STRING   "Rocks'n'Diamonds"
 #define PROGRAM_AUTHOR_STRING  "Holger Schemel"
-#define PROGRAM_RIGHTS_STRING  "Copyright ^1995-2001 by"
+#define PROGRAM_RIGHTS_STRING  "Copyright ^1995-2002 by"
 #define PROGRAM_DOS_PORT_STRING        "DOS port done by Guido Schulz"
 #define PROGRAM_IDENT_STRING   PROGRAM_VERSION_STRING " " TARGET_STRING
 #define WINDOW_TITLE_STRING    PROGRAM_TITLE_STRING " " PROGRAM_IDENT_STRING
 #define WINDOW_SUBTITLE_STRING PROGRAM_RIGHTS_STRING " " PROGRAM_AUTHOR_STRING
 #define ICON_TITLE_STRING      PROGRAM_TITLE_STRING
 #define UNIX_USERDATA_DIRECTORY        ".rocksndiamonds"
+#define COOKIE_PREFIX          "ROCKSNDIAMONDS"
+#define FILENAME_PREFIX                "Rocks"
 
 #define X11_ICON_FILENAME      "rocks_icon.xbm"
 #define X11_ICONMASK_FILENAME  "rocks_iconmask.xbm"
index e6ba74ab06c1fe618da823fba9bce0017f3af2a9..21a48572d08af642fc09fbb42cd94630971fa0b6 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -11,7 +11,7 @@
 * network.c                                                *
 ***********************************************************/
 
-#include "libgame/libgame.h"
+#include "libgame/platform.h"
 
 #if defined(PLATFORM_UNIX)
 
@@ -26,9 +26,7 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 
-#if 0
 #include "libgame/libgame.h"
-#endif
 
 #include "netserv.h"
 
index 88bceaf84c284717ca5a6afdf55de7dd1309dfbf..e3c20b72d397edf74ad06f9b39db78f64a838c9d 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
index 159ef458d5e458639d5d9ed0445ca5072342f602..2b0ac6c205ecf7a9cafe378e1c95f733405b4ee0 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -11,7 +11,7 @@
 * network.c                                                *
 ***********************************************************/
 
-#include "libgame/libgame.h"
+#include "libgame/platform.h"
 
 #if defined(PLATFORM_UNIX)
 
@@ -24,9 +24,7 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 
-#if 0
 #include "libgame/libgame.h"
-#endif
 
 #include "network.h"
 #include "netserv.h"
@@ -415,7 +413,7 @@ static void Handle_OP_PLAYER_DISCONNECTED()
 
 static void Handle_OP_START_PLAYING()
 {
-  struct LevelDirInfo *new_leveldir;
+  LevelDirTree *new_leveldir;
   int new_level_nr;
   int dummy;                           /* !!! HAS NO MEANING ANYMORE !!! */
   unsigned long new_random_seed;
@@ -427,7 +425,7 @@ static void Handle_OP_START_PLAYING()
     (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
   new_leveldir_filename = (char *)&buffer[10];
 
-  new_leveldir = getLevelDirInfoFromFilename(new_leveldir_filename);
+  new_leveldir = getTreeInfoFromFilename(leveldir_first,new_leveldir_filename);
   if (new_leveldir == NULL)
   {
     Error(ERR_WARN, "no such level directory: '%s'", new_leveldir_filename);
index 9bd89c707a4d9f55b9e814e9b0e63d737489ce98..6819bf27279a6dd823a15d65bea124b9005c0d58 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
index 2984e5dad87e829b703817fee38e6e5b79dcd9f1..ac88cbbedf89372adb10fff53e13f7a9a158695c 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "editor.h"
 #include "files.h"
 #include "tape.h"
-#include "joystick.h"
 #include "cartoons.h"
 #include "network.h"
 #include "init.h"
 
-/* for DrawSetupScreen(), HandleSetupScreen() */
-#define SETUP_SCREEN_POS_START         2
-#define SETUP_SCREEN_POS_END           (SCR_FIELDY - 1)
-#define SETUP_SCREEN_POS_EMPTY1                (SETUP_SCREEN_POS_END - 2)
-#define SETUP_SCREEN_POS_EMPTY2                (SETUP_SCREEN_POS_END - 2)
-
-/* for HandleSetupInputScreen() */
-#define SETUPINPUT_SCREEN_POS_START    2
-#define SETUPINPUT_SCREEN_POS_END      (SCR_FIELDY - 2)
+/* screens in the setup menu */
+#define SETUP_MODE_MAIN                        0
+#define SETUP_MODE_GAME                        1
+#define SETUP_MODE_INPUT               2
+#define SETUP_MODE_SHORTCUT            3
+#define SETUP_MODE_GRAPHICS            4
+#define SETUP_MODE_SOUND               5
+#define SETUP_MODE_ARTWORK             6
+#define SETUP_MODE_CHOOSE_GRAPHICS     7
+#define SETUP_MODE_CHOOSE_SOUNDS       8
+#define SETUP_MODE_CHOOSE_MUSIC                9
+
+#define MAX_SETUP_MODES                        10
+
+/* for input setup functions */
+#define SETUPINPUT_SCREEN_POS_START    0
+#define SETUPINPUT_SCREEN_POS_END      (SCR_FIELDY - 4)
 #define SETUPINPUT_SCREEN_POS_EMPTY1   (SETUPINPUT_SCREEN_POS_START + 3)
 #define SETUPINPUT_SCREEN_POS_EMPTY2   (SETUPINPUT_SCREEN_POS_END - 1)
 
-/* for HandleChooseLevel() */
-#define MAX_LEVEL_SERIES_ON_SCREEN     (SCR_FIELDY - 2)
+/* for various menu stuff  */
+#define MAX_MENU_ENTRIES_ON_SCREEN     (SCR_FIELDY - 2)
+#define MENU_SCREEN_START_YPOS         2
+#define MENU_SCREEN_VALUE_XPOS         14
 
 /* buttons and scrollbars identifiers */
 #define SCREEN_CTRL_ID_SCROLL_UP       0
 #define NUM_SCREEN_SCROLLBARS          1
 #define NUM_SCREEN_GADGETS             3
 
-/* forward declaration for internal use */
+/* forward declarations of internal functions */
 static void HandleScreenGadgets(struct GadgetInfo *);
+static void HandleSetupScreen_Generic(int, int, int, int, int);
+static void HandleSetupScreen_Input(int, int, int, int, int);
+static void CustomizeKeyboard(int);
+static void CalibrateJoystick(int);
+static void execSetupArtwork(void);
+static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
 
 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
+static int setup_mode = SETUP_MODE_MAIN;
+
+static void drawCursorExt(int pos, int color, int graphic)
+{
+  static int cursor_array[SCR_FIELDY];
+
+  if (graphic)
+    cursor_array[pos] = graphic;
+
+  graphic = cursor_array[pos];
+
+  if (color == FC_RED)
+    graphic = (graphic == GFX_ARROW_BLUE_LEFT  ? GFX_ARROW_RED_LEFT  :
+              graphic == GFX_ARROW_BLUE_RIGHT ? GFX_ARROW_RED_RIGHT :
+              GFX_KUGEL_ROT);
+
+  DrawGraphic(0, MENU_SCREEN_START_YPOS + pos, graphic);
+}
+
+static void initCursor(int pos, int graphic)
+{
+  drawCursorExt(pos, FC_BLUE, graphic);
+}
+
+static void drawCursor(int pos, int color)
+{
+  drawCursorExt(pos, color, 0);
+}
 
 void DrawHeadline()
 {
@@ -87,13 +130,16 @@ static void ToggleFullscreenIfNeeded()
 
 void DrawMainMenu()
 {
-  static struct LevelDirInfo *leveldir_last_valid = NULL;
+  static LevelDirTree *leveldir_last_valid = NULL;
   int i;
   char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:");
 
   UnmapAllGadgets();
   FadeSounds();
   KeyboardAutoRepeatOn();
+  ActivateJoystick();
+  SetDrawDeactivationMask(REDRAW_NONE);
+  audio.sound_deactivated = FALSE;
 
   /* needed if last screen was the playing screen, invoked from level editor */
   if (level_editor_test_game)
@@ -109,6 +155,9 @@ void DrawMainMenu()
   /* needed if last screen was the setup screen and fullscreen state changed */
   ToggleFullscreenIfNeeded();
 
+  /* needed if last screen (level choice) changed graphics, sounds or music */
+  ReloadCustomArtwork();
+
 #ifdef TARGET_SDL
   SetDrawtoField(DRAW_BACKBUFFER);
 #endif
@@ -118,7 +167,7 @@ void DrawMainMenu()
 
   /* leveldir_current may be invalid (level group, parent link) */
   if (!validLevelSeries(leveldir_current))
-    leveldir_current = getFirstValidLevelSeries(leveldir_last_valid);
+    leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
 
   /* store valid level series information */
   leveldir_last_valid = leveldir_current;
@@ -156,8 +205,9 @@ void DrawMainMenu()
     DrawTextF(15*32 + 6, 3*32 + 9 + 7, FC_RED, "ONLY");
   }
 
-  for(i=2; i<10; i++)
-    DrawGraphic(0, i, GFX_KUGEL_BLAU);
+  for(i=0; i<8; i++)
+    initCursor(i, (i == 1 || i == 6 ? GFX_ARROW_BLUE_RIGHT : GFX_KUGEL_BLAU));
+
   DrawGraphic(10, 3, GFX_ARROW_BLUE_LEFT);
   DrawGraphic(14, 3, GFX_ARROW_BLUE_RIGHT);
 
@@ -187,7 +237,6 @@ void DrawMainMenu()
 #if 0
   ClearEventQueue();
 #endif
-
 }
 
 static void gotoTopLevelDir()
@@ -198,18 +247,18 @@ static void gotoTopLevelDir()
     /* write a "path" into level tree for easy navigation to last level */
     if (leveldir_current->node_parent->node_group->cl_first == -1)
     {
-      int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
-      int leveldir_pos = posLevelDirInfo(leveldir_current);
+      int num_leveldirs = numTreeInfoInGroup(leveldir_current);
+      int leveldir_pos = posTreeInfo(leveldir_current);
       int num_page_entries;
       int cl_first, cl_cursor;
 
-      if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
+      if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN)
        num_page_entries = num_leveldirs;
       else
-       num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+       num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
 
       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
-      cl_cursor = leveldir_pos - cl_first + 3;
+      cl_cursor = leveldir_pos - cl_first;
 
       leveldir_current->node_parent->node_group->cl_first = cl_first;
       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
@@ -221,48 +270,31 @@ static void gotoTopLevelDir()
 
 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
 {
-  static int choice = 3;
-  static int redraw = TRUE;
-  int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
-
-  if (redraw || button == MB_MENU_INITIALIZE)
-  {
-    DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
-    redraw = FALSE;
-  }
+  static int choice = 0;
+  int x = 0;
+  int y = choice;
 
   if (button == MB_MENU_INITIALIZE)
+  {
+    drawCursor(choice, FC_RED);
     return;
+  }
 
-  if (dx || dy)
+  if (mx || my)                /* mouse input */
   {
-    if (dx && choice == 4)
-    {
-      x = (dx < 0 ? 11 : 15);
-      y = 4;
-    }
-    else if (dy)
-    {
-      x = 1;
-      y = choice + dy;
-    }
-    else
-      x = y = 0;
-
-    if (y < 3)
-      y = 3;
-    else if (y > 10)
-      y = 10;
+    x = (mx - SX) / 32;
+    y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
   }
-
-  if (!mx && !my && !dx && !dy)
+  else if (dx || dy)   /* keyboard input */
   {
-    x = 1;
-    y = choice;
+    if (dx && choice == 1)
+      x = (dx < 0 ? 10 : 14);
+    else if (dy)
+      y = choice + dy;
   }
 
-  if (y == 4 && ((x == 11 && level_nr > leveldir_current->first_level) ||
-                (x == 15 && level_nr < leveldir_current->last_level)) &&
+  if (y == 1 && ((x == 10 && level_nr > leveldir_current->first_level) ||
+                (x == 14 && level_nr < leveldir_current->last_level)) &&
       button)
   {
     static unsigned long level_delay = 0;
@@ -270,7 +302,7 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
     int new_level_nr, old_level_nr = level_nr;
     int font_color = (leveldir_current->readonly ? FC_RED : FC_YELLOW);
 
-    new_level_nr = level_nr + (x == 11 ? -step : +step);
+    new_level_nr = level_nr + (x == 10 ? -step : +step);
     if (new_level_nr < leveldir_current->first_level)
       new_level_nr = leveldir_current->first_level;
     if (new_level_nr > leveldir_current->last_level)
@@ -302,25 +334,25 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
     SyncDisplay();
     DelayReached(&level_delay, 0);     /* reset delay counter */
   }
-  else if (x == 1 && y >= 3 && y <= 10)
+  else if (x == 0 && y >= 0 && y <= 7)
   {
     if (button)
     {
       if (y != choice)
       {
-       DrawGraphic(0, y - 1, GFX_KUGEL_ROT);
-       DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
+       drawCursor(y, FC_RED);
+       drawCursor(choice, FC_BLUE);
        choice = y;
       }
     }
     else
     {
-      if (y == 3)
+      if (y == 0)
       {
        game_status = TYPENAME;
        HandleTypeName(strlen(setup.player_name), 0);
       }
-      else if (y == 4)
+      else if (y == 1)
       {
        if (leveldir_first)
        {
@@ -333,12 +365,12 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
          DrawChooseLevel();
        }
       }
-      else if (y == 5)
+      else if (y == 2)
       {
        game_status = HALLOFFAME;
        DrawHallOfFame(-1);
       }
-      else if (y == 6)
+      else if (y == 3)
       {
        if (leveldir_current->readonly &&
            strcmp(setup.player_name, "Artsoft") != 0)
@@ -346,12 +378,12 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
        game_status = LEVELED;
        DrawLevelEd();
       }
-      else if (y == 7)
+      else if (y == 4)
       {
        game_status = HELPSCREEN;
        DrawHelpScreen();
       }
-      else if (y == 8)
+      else if (y == 5)
       {
        if (setup.autorecord)
          TapeStartRecording();
@@ -367,22 +399,22 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
          InitGame();
        }
       }
-      else if (y == 9)
+      else if (y == 6)
       {
        game_status = SETUP;
+       setup_mode = SETUP_MODE_MAIN;
        DrawSetupScreen();
       }
-      else if (y == 10)
+      else if (y == 7)
       {
        SaveLevelSetup_LastSeries();
        SaveLevelSetup_SeriesInfo();
         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
          game_status = EXITGAME;
       }
-
-      redraw = TRUE;
     }
   }
+
   BackToFront();
 
   out:
@@ -394,6 +426,7 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button)
   }
 }
 
+
 #define MAX_HELPSCREEN_ELS     10
 #define HA_NEXT                        -999
 #define HA_END                 -1000
@@ -786,7 +819,7 @@ void DrawHelpScreen()
 
   FadeToFront();
   InitAnimation();
-  PlaySoundLoop(SND_RHYTHMLOOP);
+  PlaySoundLoop(SND_MENU_INFO_SCREEN);
 }
 
 void HandleHelpScreen(int button)
@@ -804,8 +837,8 @@ void HandleHelpScreen(int button)
       for(i=0;i<MAX_HELPSCREEN_ELS;i++)
        helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
       helpscreen_state++;
-      DrawHelpScreenElText(helpscreen_state*MAX_HELPSCREEN_ELS);
-      DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
+      DrawHelpScreenElText(helpscreen_state * MAX_HELPSCREEN_ELS);
+      DrawHelpScreenElAction(helpscreen_state * MAX_HELPSCREEN_ELS);
     }
     else if (helpscreen_state <
             num_helpscreen_els_pages + num_helpscreen_music - 1)
@@ -834,11 +867,17 @@ void HandleHelpScreen(int button)
   }
   else
   {
-    if (DelayReached(&hs_delay,GAME_FRAME_DELAY * 2))
+    if (DelayReached(&hs_delay, GAME_FRAME_DELAY * 2))
     {
-      if (helpscreen_state<num_helpscreen_els_pages)
-       DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
+      if (helpscreen_state < num_helpscreen_els_pages)
+       DrawHelpScreenElAction(helpscreen_state * MAX_HELPSCREEN_ELS);
     }
+
+    /* !!! workaround for playing "music" that is really a sound loop (and
+       must therefore periodically be reactivated with the current sound
+       engine !!! */
+    PlaySoundLoop(SND_MENU_INFO_SCREEN);
+
     DoAnimation();
   }
 
@@ -896,53 +935,26 @@ void HandleTypeName(int newxpos, Key key)
   BackToFront();
 }
 
-static void drawCursorExt(int ypos, int color, int graphic)
-{
-  static int cursor_array[SCR_FIELDY];
-
-  if (graphic)
-    cursor_array[ypos] = graphic;
-
-  graphic = cursor_array[ypos];
-
-  if (color == FC_RED)
-    graphic = (graphic == GFX_ARROW_BLUE_LEFT  ? GFX_ARROW_RED_LEFT  :
-              graphic == GFX_ARROW_BLUE_RIGHT ? GFX_ARROW_RED_RIGHT :
-              GFX_KUGEL_ROT);
-
-  DrawGraphic(0, ypos, graphic);
-}
-
-static void initCursor(int ypos, int graphic)
-{
-  drawCursorExt(ypos, FC_BLUE, graphic);
-}
-
-static void drawCursor(int ypos, int color)
-{
-  drawCursorExt(ypos, color, 0);
-}
-
-void DrawChooseLevel()
+static void DrawChooseTree(TreeInfo **ti_ptr)
 {
   UnmapAllGadgets();
   CloseDoor(DOOR_CLOSE_2);
 
   ClearWindow();
-  HandleChooseLevel(0,0, 0,0, MB_MENU_INITIALIZE);
-  MapChooseLevelGadgets();
+  HandleChooseTree(0,0, 0,0, MB_MENU_INITIALIZE, ti_ptr);
+  MapChooseTreeGadgets(*ti_ptr);
 
   FadeToFront();
   InitAnimation();
 }
 
-static void AdjustChooseLevelScrollbar(int id, int first_entry)
+static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
 {
   struct GadgetInfo *gi = screen_gadget[id];
   int items_max, items_visible, item_position;
 
-  items_max = numLevelDirInfoInGroup(leveldir_current);
-  items_visible = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+  items_max = numTreeInfoInGroup(ti);
+  items_visible = MAX_MENU_ENTRIES_ON_SCREEN - 1;
   item_position = first_entry;
 
   if (item_position > items_max - items_visible)
@@ -952,26 +964,36 @@ static void AdjustChooseLevelScrollbar(int id, int first_entry)
               GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
 }
 
-static void drawChooseLevelList(int first_entry, int num_page_entries)
+static void drawChooseTreeList(int first_entry, int num_page_entries,
+                              TreeInfo *ti)
 {
   int i;
   char buffer[SCR_FIELDX * 2];
   int max_buffer_len = (SCR_FIELDX - 2) * 2;
-  int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
+  int num_entries = numTreeInfoInGroup(ti);
+  char *title_string = NULL;
+  int offset = (ti->type == TREE_TYPE_LEVEL_DIR ? 0 : 16);
 
   ClearRectangle(backbuffer, SX, SY, SXSIZE - 32, SYSIZE);
   redraw_mask |= REDRAW_FIELD;
 
-  DrawText(SX, SY, "Level Directories", FS_BIG, FC_GREEN);
+  title_string =
+    (ti->type == TREE_TYPE_LEVEL_DIR ? "Level Directories" :
+     ti->type == TREE_TYPE_GRAPHICS_DIR ? "Custom Graphics" :
+     ti->type == TREE_TYPE_SOUNDS_DIR ? "Custom Sounds" :
+     ti->type == TREE_TYPE_MUSIC_DIR ? "Custom Music" : "");
+
+  DrawText(SX + offset, SY + offset, title_string, FS_BIG,
+          (ti->type == TREE_TYPE_LEVEL_DIR ? FC_GREEN : FC_YELLOW));
 
   for(i=0; i<num_page_entries; i++)
   {
-    struct LevelDirInfo *node, *node_first;
-    int leveldir_pos = first_entry + i;
-    int ypos = i + 2;
+    TreeInfo *node, *node_first;
+    int entry_pos = first_entry + i;
+    int ypos = MENU_SCREEN_START_YPOS + i;
 
-    node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
-    node = getLevelDirInfoFromPos(node_first, leveldir_pos);
+    node_first = getTreeInfoFirstGroupEntry(ti);
+    node = getTreeInfoFromPos(node_first, entry_pos);
 
     strncpy(buffer, node->name , max_buffer_len);
     buffer[max_buffer_len] = '\0';
@@ -979,27 +1001,30 @@ static void drawChooseLevelList(int first_entry, int num_page_entries)
     DrawText(SX + 32, SY + ypos * 32, buffer, FS_MEDIUM, node->color);
 
     if (node->parent_link)
-      initCursor(ypos, GFX_ARROW_BLUE_LEFT);
+      initCursor(i, GFX_ARROW_BLUE_LEFT);
     else if (node->level_group)
-      initCursor(ypos, GFX_ARROW_BLUE_RIGHT);
+      initCursor(i, GFX_ARROW_BLUE_RIGHT);
     else
-      initCursor(ypos, GFX_KUGEL_BLAU);
+      initCursor(i, GFX_KUGEL_BLAU);
   }
 
   if (first_entry > 0)
     DrawGraphic(0, 1, GFX_ARROW_BLUE_UP);
 
-  if (first_entry + num_page_entries < num_leveldirs)
-    DrawGraphic(0, MAX_LEVEL_SERIES_ON_SCREEN + 1, GFX_ARROW_BLUE_DOWN);
+  if (first_entry + num_page_entries < num_entries)
+    DrawGraphic(0, MAX_MENU_ENTRIES_ON_SCREEN + 1, GFX_ARROW_BLUE_DOWN);
 }
 
-static void drawChooseLevelInfo(int leveldir_pos)
+static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
 {
-  struct LevelDirInfo *node, *node_first;
+  TreeInfo *node, *node_first;
   int x, last_redraw_mask = redraw_mask;
 
-  node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
-  node = getLevelDirInfoFromPos(node_first, leveldir_pos);
+  if (ti->type != TREE_TYPE_LEVEL_DIR)
+    return;
+
+  node_first = getTreeInfoFirstGroupEntry(ti);
+  node = getTreeInfoFromPos(node_first, entry_pos);
 
   ClearRectangle(drawto, SX + 32, SY + 32, SXSIZE - 64, 32);
 
@@ -1007,7 +1032,7 @@ static void drawChooseLevelInfo(int leveldir_pos)
     DrawTextFCentered(40, FC_RED, "leave group \"%s\"", node->class_desc);
   else if (node->level_group)
     DrawTextFCentered(40, FC_RED, "enter group \"%s\"", node->class_desc);
-  else
+  else if (ti->type == TREE_TYPE_LEVEL_DIR)
     DrawTextFCentered(40, FC_RED, "%3d levels (%s)",
                      node->levels, node->class_desc);
 
@@ -1017,194 +1042,217 @@ static void drawChooseLevelInfo(int leveldir_pos)
     MarkTileDirty(x, 1);
 }
 
-void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
+static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
+                            TreeInfo **ti_ptr)
 {
   static unsigned long choose_delay = 0;
-  static int redraw = TRUE;
-  int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
+  TreeInfo *ti = *ti_ptr;
+  int x = 0;
+  int y = ti->cl_cursor;
   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
-  int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
+  int num_entries = numTreeInfoInGroup(ti);
   int num_page_entries;
 
-  if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
-    num_page_entries = num_leveldirs;
+  if (num_entries <= MAX_MENU_ENTRIES_ON_SCREEN)
+    num_page_entries = num_entries;
   else
-    num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+    num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
 
   if (button == MB_MENU_INITIALIZE)
   {
-    int leveldir_pos = posLevelDirInfo(leveldir_current);
+    int entry_pos = posTreeInfo(ti);
 
-    if (leveldir_current->cl_first == -1)
+    if (ti->cl_first == -1)
     {
-      leveldir_current->cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
-      leveldir_current->cl_cursor =
-       leveldir_pos - leveldir_current->cl_first + 3;
+      ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
+      ti->cl_cursor =
+       entry_pos - ti->cl_first;
     }
 
     if (dx == 999)     /* first entry is set by scrollbar position */
-      leveldir_current->cl_first = dy;
+      ti->cl_first = dy;
     else
-      AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
-                                leveldir_current->cl_first);
+      AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
+                               ti->cl_first, ti);
 
-    drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
-    drawChooseLevelInfo(leveldir_pos);
-    redraw = TRUE;
+    drawChooseTreeList(ti->cl_first, num_page_entries, ti);
+    drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
+    drawCursor(ti->cl_cursor, FC_RED);
+    return;
   }
-
-  if (redraw)
+  else if (button == MB_MENU_LEAVE)
   {
-    drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
-    redraw = FALSE;
-  }
+    if (ti->node_parent)
+    {
+      *ti_ptr = ti->node_parent;
+      DrawChooseTree(ti_ptr);
+    }
+    else if (game_status == SETUP)
+    {
+      execSetupArtwork();
+    }
+    else
+    {
+      game_status = MAINMENU;
+      DrawMainMenu();
+    }
 
-  if (button == MB_MENU_INITIALIZE)
     return;
+  }
 
-  if (dx || dy)
+  if (mx || my)                /* mouse input */
+  {
+    x = (mx - SX) / 32;
+    y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
+  }
+  else if (dx || dy)   /* keyboard input */
   {
     if (dy)
-    {
-      x = 1;
-      y = leveldir_current->cl_cursor + dy;
-    }
-    else
-      x = y = 0;       /* no action */
+      y = ti->cl_cursor + dy;
 
     if (ABS(dy) == SCR_FIELDY) /* handle KSYM_Page_Up, KSYM_Page_Down */
     {
       dy = SIGN(dy);
       step = num_page_entries - 1;
-      x = 1;
-      y = (dy < 0 ? 2 : num_page_entries + 3);
+      y = (dy < 0 ? -1 : num_page_entries);
     }
   }
 
-  if (x == 1 && y == 2)
+  if (x == 0 && y == -1)
   {
-    if (leveldir_current->cl_first > 0 &&
+    if (ti->cl_first > 0 &&
        (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
     {
-      leveldir_current->cl_first -= step;
-      if (leveldir_current->cl_first < 0)
-       leveldir_current->cl_first = 0;
-
-      drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
-      drawChooseLevelInfo(leveldir_current->cl_first +
-                         leveldir_current->cl_cursor - 3);
-      drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
-      AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
-                                leveldir_current->cl_first);
+      ti->cl_first -= step;
+      if (ti->cl_first < 0)
+       ti->cl_first = 0;
+
+      drawChooseTreeList(ti->cl_first, num_page_entries, ti);
+      drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
+      drawCursor(ti->cl_cursor, FC_RED);
+      AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
+                               ti->cl_first, ti);
       return;
     }
   }
-  else if (x == 1 && y > num_page_entries + 2)
+  else if (x == 0 && y > num_page_entries - 1)
   {
-    if (leveldir_current->cl_first + num_page_entries < num_leveldirs &&
+    if (ti->cl_first + num_page_entries < num_entries &&
        (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
     {
-      leveldir_current->cl_first += step;
-      if (leveldir_current->cl_first + num_page_entries > num_leveldirs)
-       leveldir_current->cl_first = MAX(0, num_leveldirs - num_page_entries);
-
-      drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
-      drawChooseLevelInfo(leveldir_current->cl_first +
-                         leveldir_current->cl_cursor - 3);
-      drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
-      AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
-                                leveldir_current->cl_first);
+      ti->cl_first += step;
+      if (ti->cl_first + num_page_entries > num_entries)
+       ti->cl_first = MAX(0, num_entries - num_page_entries);
+
+      drawChooseTreeList(ti->cl_first, num_page_entries, ti);
+      drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
+      drawCursor(ti->cl_cursor, FC_RED);
+      AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
+                               ti->cl_first, ti);
       return;
     }
   }
 
-  if (!mx && !my && !dx && !dy)
-  {
-    x = 1;
-    y = leveldir_current->cl_cursor;
-  }
-
   if (dx == 1)
   {
-    struct LevelDirInfo *node_first, *node_cursor;
-    int leveldir_pos =
-      leveldir_current->cl_first + leveldir_current->cl_cursor - 3;
+    TreeInfo *node_first, *node_cursor;
+    int entry_pos = ti->cl_first + y;
 
-    node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
-    node_cursor = getLevelDirInfoFromPos(node_first, leveldir_pos);
+    node_first = getTreeInfoFirstGroupEntry(ti);
+    node_cursor = getTreeInfoFromPos(node_first, entry_pos);
 
     if (node_cursor->node_group)
     {
-      node_cursor->cl_first = leveldir_current->cl_first;
-      node_cursor->cl_cursor = leveldir_current->cl_cursor;
-      leveldir_current = node_cursor->node_group;
-      DrawChooseLevel();
+      node_cursor->cl_first = ti->cl_first;
+      node_cursor->cl_cursor = ti->cl_cursor;
+      *ti_ptr = node_cursor->node_group;
+      DrawChooseTree(ti_ptr);
+      return;
     }
   }
-  else if (dx == -1 && leveldir_current->node_parent)
+  else if (dx == -1 && ti->node_parent)
   {
-    leveldir_current = leveldir_current->node_parent;
-    DrawChooseLevel();
+    *ti_ptr = ti->node_parent;
+    DrawChooseTree(ti_ptr);
+    return;
   }
 
-  if (x == 1 && y >= 3 && y <= num_page_entries + 2)
+  if (x == 0 && y >= 0 && y < num_page_entries)
   {
     if (button)
     {
-      if (y != leveldir_current->cl_cursor)
+      if (y != ti->cl_cursor)
       {
-       drawCursor(y - 1, FC_RED);
-       drawCursor(leveldir_current->cl_cursor - 1, FC_BLUE);
-       drawChooseLevelInfo(leveldir_current->cl_first + y - 3);
-       leveldir_current->cl_cursor = y;
+       drawCursor(y, FC_RED);
+       drawCursor(ti->cl_cursor, FC_BLUE);
+       drawChooseTreeInfo(ti->cl_first + y, ti);
+       ti->cl_cursor = y;
       }
     }
     else
     {
-      struct LevelDirInfo *node_first, *node_cursor;
-      int leveldir_pos = leveldir_current->cl_first + y - 3;
+      TreeInfo *node_first, *node_cursor;
+      int entry_pos = ti->cl_first + y;
 
-      node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
-      node_cursor = getLevelDirInfoFromPos(node_first, leveldir_pos);
+      node_first = getTreeInfoFirstGroupEntry(ti);
+      node_cursor = getTreeInfoFromPos(node_first, entry_pos);
 
       if (node_cursor->node_group)
       {
-       node_cursor->cl_first = leveldir_current->cl_first;
-       node_cursor->cl_cursor = leveldir_current->cl_cursor;
-       leveldir_current = node_cursor->node_group;
-
-       DrawChooseLevel();
+       node_cursor->cl_first = ti->cl_first;
+       node_cursor->cl_cursor = ti->cl_cursor;
+       *ti_ptr = node_cursor->node_group;
+       DrawChooseTree(ti_ptr);
       }
       else if (node_cursor->parent_link)
       {
-       leveldir_current = node_cursor->node_parent;
-
-       DrawChooseLevel();
+       *ti_ptr = node_cursor->node_parent;
+       DrawChooseTree(ti_ptr);
       }
       else
       {
-       node_cursor->cl_first = leveldir_current->cl_first;
-       node_cursor->cl_cursor = leveldir_current->cl_cursor;
-       leveldir_current = node_cursor;
+       node_cursor->cl_first = ti->cl_first;
+       node_cursor->cl_cursor = ti->cl_cursor;
+       *ti_ptr = node_cursor;
 
-       LoadLevelSetup_SeriesInfo();
+       if (ti->type == TREE_TYPE_LEVEL_DIR)
+       {
+         LoadLevelSetup_SeriesInfo();
 
-       SaveLevelSetup_LastSeries();
-       SaveLevelSetup_SeriesInfo();
-       TapeErase();
+         SaveLevelSetup_LastSeries();
+         SaveLevelSetup_SeriesInfo();
+         TapeErase();
+       }
 
-       game_status = MAINMENU;
-       DrawMainMenu();
+       if (game_status == SETUP)
+       {
+         execSetupArtwork();
+       }
+       else
+       {
+         game_status = MAINMENU;
+         DrawMainMenu();
+       }
       }
     }
   }
 
   BackToFront();
 
-  if (game_status == CHOOSELEVEL)
+  if (game_status == CHOOSELEVEL || game_status == SETUP)
     DoAnimation();
 }
 
+void DrawChooseLevel()
+{
+  DrawChooseTree(&leveldir_current);
+}
+
+void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
+{
+  HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
+}
+
 void DrawHallOfFame(int highlight_position)
 {
   UnmapAllGadgets();
@@ -1217,7 +1265,7 @@ void DrawHallOfFame(int highlight_position)
   FadeToFront();
   InitAnimation();
   HandleHallOfFame(highlight_position,0, 0,0, MB_MENU_INITIALIZE);
-  PlaySound(SND_HALLOFFAME);
+  PlaySound(SND_MENU_HALL_OF_FAME);
 }
 
 static void drawHallOfFameList(int first_entry, int highlight_position)
@@ -1228,7 +1276,7 @@ static void drawHallOfFameList(int first_entry, int highlight_position)
   DrawText(SX + 80, SY + 8, "Hall Of Fame", FS_BIG, FC_YELLOW);
   DrawTextFCentered(46, FC_RED, "HighScores of Level %d", level_nr);
 
-  for(i=0; i<MAX_LEVEL_SERIES_ON_SCREEN; i++)
+  for(i=0; i<MAX_MENU_ENTRIES_ON_SCREEN; i++)
   {
     int entry = first_entry + i;
     int color = (entry == highlight_position ? FC_RED : FC_GREEN);
@@ -1266,7 +1314,7 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
   }
 
   if (ABS(dy) == SCR_FIELDY)   /* handle KSYM_Page_Up, KSYM_Page_Down */
-    step = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+    step = MAX_MENU_ENTRIES_ON_SCREEN - 1;
 
   if (dy < 0)
   {
@@ -1282,11 +1330,11 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
   }
   else if (dy > 0)
   {
-    if (first_entry + MAX_LEVEL_SERIES_ON_SCREEN < MAX_SCORE_ENTRIES)
+    if (first_entry + MAX_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
     {
       first_entry += step;
-      if (first_entry + MAX_LEVEL_SERIES_ON_SCREEN > MAX_SCORE_ENTRIES)
-       first_entry = MAX(0, MAX_SCORE_ENTRIES - MAX_LEVEL_SERIES_ON_SCREEN);
+      if (first_entry + MAX_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
+       first_entry = MAX(0, MAX_SCORE_ENTRIES - MAX_MENU_ENTRIES_ON_SCREEN);
 
       drawHallOfFameList(first_entry, highlight_position);
       return;
@@ -1295,7 +1343,7 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
 
   if (button_released)
   {
-    FadeSound(SND_HALLOFFAME);
+    FadeSound(SND_MENU_HALL_OF_FAME);
     game_status = MAINMENU;
     DrawMainMenu();
   }
@@ -1306,338 +1354,482 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
     DoAnimation();
 }
 
-void DrawSetupScreen()
+
+/* ========================================================================= */
+/* setup screen functions                                                    */
+/* ========================================================================= */
+
+static struct TokenInfo *setup_info;
+static int num_setup_info;
+
+static void execSetupMain()
 {
-  int i;
-  static struct setup
-  {
-    boolean *value;
-    char *text;
-  } setup_info[] =
-  {
-    { &setup.sound,            "Sound:",       },
-    { &setup.sound_loops,      " Sound Loops:" },
-    { &setup.sound_music,      " Game Music:"  },
-#if 0
-    { &setup.toons,            "Toons:"        },
-    { &setup.double_buffering, "Buffered gfx:" },
-#endif
-    { &setup.scroll_delay,     "Scroll Delay:" },
-    { &setup.soft_scrolling,   "Soft Scroll.:" },
+  setup_mode = SETUP_MODE_MAIN;
+  DrawSetupScreen();
+}
+
+static void execSetupGame()
+{
+  setup_mode = SETUP_MODE_GAME;
+  DrawSetupScreen();
+}
+
+static void execSetupGraphics()
+{
+  setup_mode = SETUP_MODE_GRAPHICS;
+  DrawSetupScreen();
+}
+
+static void execSetupSound()
+{
+  setup_mode = SETUP_MODE_SOUND;
+  DrawSetupScreen();
+}
+
+static void execSetupArtwork()
+{
+  /* needed if last screen (setup choice) changed graphics, sounds or music */
+  ReloadCustomArtwork();
+
+  setup.graphics_set = artwork.gfx_current->name;
+  setup.sounds_set = artwork.snd_current->name;
+  setup.music_set = artwork.mus_current->name;
+
+  setup_mode = SETUP_MODE_ARTWORK;
+  DrawSetupScreen();
+}
+
+static void execSetupChooseGraphics()
+{
+  setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
+  DrawSetupScreen();
+}
+
+static void execSetupChooseSounds()
+{
+  setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
+  DrawSetupScreen();
+}
+
+static void execSetupChooseMusic()
+{
+  setup_mode = SETUP_MODE_CHOOSE_MUSIC;
+  DrawSetupScreen();
+}
+
+static void execSetupInput()
+{
+  setup_mode = SETUP_MODE_INPUT;
+  DrawSetupScreen();
+}
+
+static void execSetupShortcut()
+{
+  setup_mode = SETUP_MODE_SHORTCUT;
+  DrawSetupScreen();
+}
+
+static void execExitSetup()
+{
+  game_status = MAINMENU;
+  DrawMainMenu();
+}
+
+static void execSaveAndExitSetup()
+{
+  SaveSetup();
+  execExitSetup();
+}
+
+static struct TokenInfo setup_info_main[] =
+{
+  { TYPE_ENTER_MENU,   execSetupGame,          "Game Settings"         },
+  { TYPE_ENTER_MENU,   execSetupGraphics,      "Graphics"              },
+  { TYPE_ENTER_MENU,   execSetupSound,         "Sound & Music"         },
+  { TYPE_ENTER_MENU,   execSetupArtwork,       "Custom Artwork"        },
+  { TYPE_ENTER_MENU,   execSetupInput,         "Input Devices"         },
+  { TYPE_ENTER_MENU,   execSetupShortcut,      "Key Shortcuts"         },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execExitSetup,          "Exit"                  },
+  { TYPE_LEAVE_MENU,   execSaveAndExitSetup,   "Save and exit"         },
+  { 0,                 NULL,                   NULL                    }
+};
+
+static struct TokenInfo setup_info_game[] =
+{
+  { TYPE_SWITCH,       &setup.team_mode,       "Team-Mode:"            },
+  { TYPE_SWITCH,       &setup.handicap,        "Handicap:"             },
+  { TYPE_SWITCH,       &setup.time_limit,      "Timelimit:"            },
+  { TYPE_SWITCH,       &setup.autorecord,      "Auto-Record:"          },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Exit"                  },
+  { 0,                 NULL,                   NULL                    }
+};
+
+static struct TokenInfo setup_info_graphics[] =
+{
+  { TYPE_SWITCH,       &setup.fullscreen,      "Fullscreen:"           },
+  { TYPE_SWITCH,       &setup.scroll_delay,    "Scroll Delay:"         },
+  { TYPE_SWITCH,       &setup.soft_scrolling,  "Soft Scroll.:"         },
 #if 0
-    { &setup.fading,           "Fading:"       },
+  { TYPE_SWITCH,       &setup.double_buffering,"Buffered gfx:"         },
+  { TYPE_SWITCH,       &setup.fading,          "Fading:"               },
 #endif
-    { &setup.fullscreen,       "Fullscreen:"   },
-    { &setup.quick_doors,      "Quick Doors:"  },
-    { &setup.autorecord,       "Auto-Record:"  },
-    { &setup.team_mode,                "Team-Mode:"    },
-    { &setup.handicap,         "Handicap:"     },
-    { &setup.time_limit,       "Timelimit:"    },
-    { NULL,                    "Input Devices" },
-    { NULL,                    ""              },
-    { NULL,                    "Exit"          },
-    { NULL,                    "Save and exit" }
-  };
+  { TYPE_SWITCH,       &setup.quick_doors,     "Quick Doors:"          },
+  { TYPE_SWITCH,       &setup.toons,           "Toons:"                },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Exit"                  },
+  { 0,                 NULL,                   NULL                    }
+};
 
-  UnmapAllGadgets();
-  CloseDoor(DOOR_CLOSE_2);
-  ClearWindow();
+static struct TokenInfo setup_info_sound[] =
+{
+  { TYPE_SWITCH,       &setup.sound,           "Sound:",               },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_SWITCH,       &setup.sound_simple,    "Simple Sound:"         },
+  { TYPE_SWITCH,       &setup.sound_loops,     "Sound Loops:"          },
+  { TYPE_SWITCH,       &setup.sound_music,     "Game Music:"           },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Exit"                  },
+  { 0,                 NULL,                   NULL                    }
+};
 
-  DrawText(SX + 16, SY + 16, "SETUP",FS_BIG,FC_YELLOW);
+static struct TokenInfo setup_info_artwork[] =
+{
+  { TYPE_ENTER_MENU,   execSetupChooseGraphics,"Custom Graphics"       },
+  { TYPE_STRING,       &setup.graphics_set,    ""                      },
+  { TYPE_ENTER_MENU,   execSetupChooseSounds,  "Custom Sounds"         },
+  { TYPE_STRING,       &setup.sounds_set,      ""                      },
+  { TYPE_ENTER_MENU,   execSetupChooseMusic,   "Custom Music"          },
+  { TYPE_STRING,       &setup.music_set,       ""                      },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_STRING,       NULL,                   "Override Level Artwork:"},
+  { TYPE_YES_NO,       &setup.override_level_graphics, "Graphics:"     },
+  { TYPE_YES_NO,       &setup.override_level_sounds,   "Sounds:"       },
+  { TYPE_YES_NO,       &setup.override_level_music,    "Music:"        },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Exit"                  },
+  { 0,                 NULL,                   NULL                    }
+};
 
-  for(i=SETUP_SCREEN_POS_START;i<=SETUP_SCREEN_POS_END;i++)
-  {
-    int base = i - SETUP_SCREEN_POS_START;
+static struct TokenInfo setup_info_shortcut[] =
+{
+  { TYPE_KEYTEXT,      NULL,                   "Quick Save Game:",     },
+  { TYPE_KEY,          &setup.shortcut.save_game,      ""              },
+  { TYPE_KEYTEXT,      NULL,                   "Quick Load Game:",     },
+  { TYPE_KEY,          &setup.shortcut.load_game,      ""              },
+  { TYPE_KEYTEXT,      NULL,                   "Toggle Pause:",        },
+  { TYPE_KEY,          &setup.shortcut.toggle_pause,   ""              },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_YES_NO,       &setup.ask_on_escape,   "Ask on Esc:"           },
+  { TYPE_EMPTY,                NULL,                   ""                      },
+  { TYPE_LEAVE_MENU,   execSetupMain,          "Exit"                  },
+  { 0,                 NULL,                   NULL                    }
+};
+
+static Key getSetupKey()
+{
+  Key key = KSYM_UNDEFINED;
+  boolean got_key_event = FALSE;
 
-    if (!(i >= SETUP_SCREEN_POS_EMPTY1 && i <= SETUP_SCREEN_POS_EMPTY2))
+  while (!got_key_event)
+  {
+    if (PendingEvent())                /* got event */
     {
-      DrawText(SX+32,SY+i*32, setup_info[base].text, FS_BIG,FC_GREEN);
+      Event event;
 
-      if (strcmp(setup_info[base].text, "Input Devices") == 0)
-       initCursor(i, GFX_ARROW_BLUE_RIGHT);
-      else
-       initCursor(i, GFX_KUGEL_BLAU);
-    }
+      NextEvent(&event);
 
-    if (setup_info[base].value)
-    {
-      int setting_value = *setup_info[base].value;
+      switch(event.type)
+      {
+        case EVENT_KEYPRESS:
+         {
+           key = GetEventKey((KeyEvent *)&event, TRUE);
 
-      DrawText(SX+14*32, SY+i*32, (setting_value ? "on" : "off"),
-              FS_BIG, (setting_value ? FC_YELLOW : FC_BLUE));
+           /* press 'Escape' or 'Enter' to keep the existing key binding */
+           if (key == KSYM_Escape || key == KSYM_Return)
+             key = KSYM_UNDEFINED;     /* keep old value */
+
+           got_key_event = TRUE;
+         }
+         break;
+
+        case EVENT_KEYRELEASE:
+         key_joystick_mapping = 0;
+         break;
+
+        default:
+         HandleOtherEvents(&event);
+         break;
+      }
     }
+
+    BackToFront();
+    DoAnimation();
+
+    /* don't eat all CPU time */
+    Delay(10);
   }
 
-  FadeToFront();
-  InitAnimation();
-  HandleSetupScreen(0,0,0,0,MB_MENU_INITIALIZE);
+  return key;
 }
 
-void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
+static void drawSetupValue(int pos)
 {
-  static int choice = 3;
-  static int redraw = TRUE;
-  int x = (mx+32-SX)/32, y = (my+32-SY)/32;
-  int pos_start  = SETUP_SCREEN_POS_START  + 1;
-  int pos_empty1 = SETUP_SCREEN_POS_EMPTY1 + 1;
-  int pos_empty2 = SETUP_SCREEN_POS_EMPTY2 + 1;
-  int pos_end    = SETUP_SCREEN_POS_END    + 1;
+  int xpos = MENU_SCREEN_VALUE_XPOS;
+  int ypos = MENU_SCREEN_START_YPOS + pos;
+  int font_size = FS_BIG;
+  int font_color = FC_YELLOW;
+  char *value_string = getSetupValue(setup_info[pos].type & ~TYPE_GHOSTED,
+                                    setup_info[pos].value);
+
+  if (value_string == NULL)
+    return;
 
-  if (button == MB_MENU_INITIALIZE)
-    redraw = TRUE;
+  if (setup_info[pos].type & TYPE_KEY)
+  {
+    xpos = 3;
 
-  if (redraw)
+    if (setup_info[pos].type & TYPE_QUERY)
+    {
+      value_string = "<press key>";
+      font_color = FC_RED;
+    }
+  }
+  else if (setup_info[pos].type & TYPE_STRING)
   {
-    drawCursor(choice - 1, FC_RED);
-    redraw = FALSE;
+    int max_value_len = (SCR_FIELDX - 2) * 2;
+
+    xpos = 1;
+    font_size = FS_MEDIUM;
+
+    if (strlen(value_string) > max_value_len)
+      value_string[max_value_len] = '\0';
   }
+  else if (setup_info[pos].type & TYPE_BOOLEAN_STYLE &&
+          !*(boolean *)(setup_info[pos].value))
+    font_color = FC_BLUE;
 
-  if (button == MB_MENU_INITIALIZE)
-    return;
+  DrawText(SX + xpos * 32, SY + ypos * 32,
+          (xpos == 3 ? "              " : "   "), FS_BIG, FC_YELLOW);
+  DrawText(SX + xpos * 32, SY + ypos * 32, value_string, font_size,font_color);
+}
 
-  if (dx || dy)
+static void changeSetupValue(int pos)
+{
+  if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
   {
-    if (dy)
-    {
-      x = 1;
-      y = choice+dy;
-    }
-    else
-      x = y = 0;
+    *(boolean *)setup_info[pos].value ^= TRUE;
+  }
+  else if (setup_info[pos].type & TYPE_KEY)
+  {
+    Key key;
 
-    if (y >= pos_empty1 && y <= pos_empty2)
-      y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
+    setup_info[pos].type |= TYPE_QUERY;
+    drawSetupValue(pos);
+    setup_info[pos].type &= ~TYPE_QUERY;
 
-    if (y < pos_start)
-      y = pos_start;
-    else if (y > pos_end)
-      y = pos_end;
+    key = getSetupKey();
+    if (key != KSYM_UNDEFINED)
+      *(Key *)setup_info[pos].value = key;
   }
 
-  if (!mx && !my && !dx && !dy)
+  drawSetupValue(pos);
+}
+
+static void DrawSetupScreen_Generic()
+{
+  char *title_string = NULL;
+  int i;
+
+  UnmapAllGadgets();
+  CloseDoor(DOOR_CLOSE_2);
+  ClearWindow();
+
+  if (setup_mode == SETUP_MODE_MAIN)
+  {
+    setup_info = setup_info_main;
+    title_string = "Setup";
+  }
+  else if (setup_mode == SETUP_MODE_GAME)
+  {
+    setup_info = setup_info_game;
+    title_string = "Setup Game";
+  }
+  else if (setup_mode == SETUP_MODE_GRAPHICS)
+  {
+    setup_info = setup_info_graphics;
+    title_string = "Setup Graphics";
+  }
+  else if (setup_mode == SETUP_MODE_SOUND)
+  {
+    setup_info = setup_info_sound;
+    title_string = "Setup Sound";
+  }
+  else if (setup_mode == SETUP_MODE_ARTWORK)
   {
-    x = 1;
-    y = choice;
+    setup_info = setup_info_artwork;
+    title_string = "Custom Artwork";
   }
+  else if (setup_mode == SETUP_MODE_SHORTCUT)
+  {
+    setup_info = setup_info_shortcut;
+    title_string = "Setup Shortcuts";
+  }
+
+  DrawText(SX + 16, SY + 16, title_string, FS_BIG, FC_YELLOW);
 
-  if (dx == 1 && choice == 14)
+  num_setup_info = 0;
+  for(i=0; setup_info[i].type != 0 && i < MAX_MENU_ENTRIES_ON_SCREEN; i++)
   {
-    game_status = SETUPINPUT;
-    DrawSetupInputScreen();
-    redraw = TRUE;
+    void *value_ptr = setup_info[i].value;
+    int ypos = MENU_SCREEN_START_YPOS + i;
+    int font_size = FS_BIG;
+
+    /* set some entries to "unchangeable" according to other variables */
+    if ((value_ptr == &setup.sound       && !audio.sound_available) ||
+       (value_ptr == &setup.sound_loops && !audio.loops_available) ||
+       (value_ptr == &setup.sound_music && !audio.music_available) ||
+       (value_ptr == &setup.fullscreen  && !video.fullscreen_available))
+      setup_info[i].type |= TYPE_GHOSTED;
+
+    if (setup_info[i].type & TYPE_STRING)
+      font_size = FS_MEDIUM;
+
+    DrawText(SX + 32, SY + ypos * 32, setup_info[i].text, font_size, FC_GREEN);
+
+    if (setup_info[i].type & TYPE_ENTER_MENU)
+      initCursor(i, GFX_ARROW_BLUE_RIGHT);
+    else if (setup_info[i].type & TYPE_LEAVE_MENU)
+      initCursor(i, GFX_ARROW_BLUE_LEFT);
+    else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
+      initCursor(i, GFX_KUGEL_BLAU);
+
+    if (setup_info[i].type & TYPE_VALUE)
+      drawSetupValue(i);
+
+    num_setup_info++;
   }
 
-  if (x==1 && y >= pos_start && y <= pos_end &&
-      !(y >= pos_empty1 && y <= pos_empty2))
+  FadeToFront();
+  InitAnimation();
+  HandleSetupScreen_Generic(0,0,0,0,MB_MENU_INITIALIZE);
+}
+
+void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
+{
+  static int choice_store[MAX_SETUP_MODES];
+  int choice = choice_store[setup_mode];
+  int x = 0;
+  int y = choice;
+
+  if (button == MB_MENU_INITIALIZE)
   {
-    if (button)
+    drawCursor(choice, FC_RED);
+    return;
+  }
+  else if (button == MB_MENU_LEAVE)
+  {
+    for (y=0; y<num_setup_info; y++)
     {
-      if (y!=choice)
+      if (setup_info[y].type & TYPE_LEAVE_MENU)
       {
-       drawCursor(y - 1, FC_RED);
-       drawCursor(choice - 1, FC_BLUE);
+       void (*menu_callback_function)(void) = setup_info[y].value;
+
+       menu_callback_function();
+       break;  /* absolutely needed because function changes 'setup_info'! */
       }
-      choice = y;
     }
-    else
+
+    return;
+  }
+
+  if (mx || my)                /* mouse input */
+  {
+    x = (mx - SX) / 32;
+    y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
+  }
+  else if (dx || dy)   /* keyboard input */
+  {
+    if (dx)
     {
-      int yy = y-1;
+      int menu_navigation_type = (dx < 0 ? TYPE_LEAVE_MENU : TYPE_ENTER_MENU);
 
-      if (y == 3 && audio.sound_available)
-      {
-       if (setup.sound)
-       {
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-         DrawText(SX+14*32, SY+(yy+1)*32,"off",FS_BIG,FC_BLUE);
-         DrawText(SX+14*32, SY+(yy+2)*32,"off",FS_BIG,FC_BLUE);
-         setup.sound_loops = FALSE;
-         setup.sound_music = FALSE;
-       }
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.sound = !setup.sound;
-      }
-      else if (y == 4 && audio.loops_available)
-      {
-       if (setup.sound_loops)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-       {
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-         DrawText(SX+14*32, SY+(yy-1)*32,"on ",FS_BIG,FC_YELLOW);
-         setup.sound = TRUE;
-       }
-       setup.sound_loops = !setup.sound_loops;
-      }
-      else if (y == 5 && audio.loops_available)
-      {
-       if (setup.sound_music)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-       {
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-         DrawText(SX+14*32, SY+(yy-2)*32,"on ",FS_BIG,FC_YELLOW);
-         setup.sound = TRUE;
-       }
-       setup.sound_music = !setup.sound_music;
-      }
+      if ((setup_info[choice].type & menu_navigation_type) ||
+         (setup_info[choice].type & TYPE_BOOLEAN_STYLE))
+       button = MB_MENU_CHOICE;
+    }
+    else if (dy)
+      y = choice + dy;
 
-#if 0
-      else if (y == 6)
-      {
-       if (setup.toons)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.toons = !setup.toons;
-      }
-      else if (y == 7)
-      {
-#if 0
-       if (setup.double_buffering)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.double_buffering = !setup.double_buffering;
-       setup.direct_draw = !setup.double_buffering;
-#else
-       DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.double_buffering = TRUE;
-       setup.direct_draw = !setup.double_buffering;
-#endif
-      }
-#endif
+    /* jump to next non-empty menu entry (up or down) */
+    while (y > 0 && y < num_setup_info - 1 &&
+          (setup_info[y].type & TYPE_SKIP_ENTRY))
+      y += dy;
+  }
 
-      else if (y == 6)
-      {
-       if (setup.scroll_delay)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.scroll_delay = !setup.scroll_delay;
-      }
-      else if (y == 7)
-      {
-       if (setup.soft_scrolling)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.soft_scrolling = !setup.soft_scrolling;
-      }
-#if 0
-      else if (y == 8)
-      {
-       if (setup.fading)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.fading = !setup.fading;
-      }
-#endif
-      else if (y == 8 && video.fullscreen_available)
-      {
-       if (setup.fullscreen)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.fullscreen = !setup.fullscreen;
-      }
-      else if (y == 9)
-      {
-       if (setup.quick_doors)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.quick_doors = !setup.quick_doors;
-      }
-      else if (y == 10)
-      {
-       if (setup.autorecord)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.autorecord = !setup.autorecord;
-      }
-      else if (y == 11)
-      {
-       if (setup.team_mode)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.team_mode = !setup.team_mode;
-      }
-      else if (y == 12)
-      {
-       if (setup.handicap)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.handicap = !setup.handicap;
-      }
-      else if (y == 13)
+  if (x == 0 && y >= 0 && y < num_setup_info &&
+      (setup_info[y].type & ~TYPE_SKIP_ENTRY))
+  {
+    if (button)
+    {
+      if (y != choice)
       {
-       if (setup.time_limit)
-         DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
-       else
-         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
-       setup.time_limit = !setup.time_limit;
+       drawCursor(y, FC_RED);
+       drawCursor(choice, FC_BLUE);
+       choice = choice_store[setup_mode] = y;
       }
-      else if (y == 14)
+    }
+    else if (!(setup_info[y].type & TYPE_GHOSTED))
+    {
+      if (setup_info[y].type & TYPE_ENTER_OR_LEAVE_MENU)
       {
-       game_status = SETUPINPUT;
-       DrawSetupInputScreen();
-       redraw = TRUE;
+       void (*menu_callback_function)(void) = setup_info[choice].value;
+
+       menu_callback_function();
       }
-      else if (y==pos_end-1 || y==pos_end)
+      else
       {
-        if (y==pos_end)
-       {
-         SaveSetup();
-
-         /*
-         SaveJoystickData();
-         */
+       if ((setup_info[y].type & TYPE_KEYTEXT) &&
+           (setup_info[y + 1].type & TYPE_KEY))
+         y++;
 
-#if defined(PLATFORM_MSDOS)
-         save_joystick_data(JOYSTICK_FILENAME);
-#endif
-
-
-       }
-
-       game_status = MAINMENU;
-       DrawMainMenu();
-       redraw = TRUE;
+       if (setup_info[y].type & TYPE_VALUE)
+         changeSetupValue(y);
       }
     }
   }
+
   BackToFront();
 
-  if (game_status==SETUP)
+  if (game_status == SETUP)
     DoAnimation();
 }
 
-void DrawSetupInputScreen()
+void DrawSetupScreen_Input()
 {
   ClearWindow();
-  DrawText(SX+16, SY+16, "SETUP INPUT", FS_BIG, FC_YELLOW);
+  DrawText(SX+16, SY+16, "Setup Input", FS_BIG, FC_YELLOW);
 
-  initCursor(2, GFX_KUGEL_BLAU);
-  initCursor(3, GFX_KUGEL_BLAU);
-  initCursor(4, GFX_ARROW_BLUE_RIGHT);
-  initCursor(15, GFX_KUGEL_BLAU);
+  initCursor(0, GFX_KUGEL_BLAU);
+  initCursor(1, GFX_KUGEL_BLAU);
+  initCursor(2, GFX_ARROW_BLUE_RIGHT);
+  initCursor(13, GFX_ARROW_BLUE_LEFT);
 
-  DrawGraphic(10, 2, GFX_ARROW_BLUE_LEFT);
-  DrawGraphic(12, 2, GFX_ARROW_BLUE_RIGHT);
+  DrawGraphic(10, MENU_SCREEN_START_YPOS, GFX_ARROW_BLUE_LEFT);
+  DrawGraphic(12, MENU_SCREEN_START_YPOS, GFX_ARROW_BLUE_RIGHT);
 
   DrawText(SX+32, SY+2*32, "Player:", FS_BIG, FC_GREEN);
   DrawText(SX+32, SY+3*32, "Device:", FS_BIG, FC_GREEN);
   DrawText(SX+32, SY+15*32, "Exit", FS_BIG, FC_GREEN);
 
+#if 0
+  DeactivateJoystickForCalibration();
   DrawTextFCentered(SYSIZE - 20, FC_BLUE,
                    "Joysticks deactivated on this screen");
+#endif
 
-  HandleSetupInputScreen(0,0, 0,0, MB_MENU_INITIALIZE);
+  HandleSetupScreen_Input(0,0, 0,0, MB_MENU_INITIALIZE);
   FadeToFront();
   InitAnimation();
 }
@@ -1659,7 +1851,8 @@ static void setJoystickDeviceToNr(char *device_name, int device_nr)
       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
   }
   else
-    strncpy(device_name, joystick_device_name[device_nr], strlen(device_name));
+    strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
+           strlen(device_name));
 }
 
 static void drawPlayerSetupInputInfo(int player_nr)
@@ -1733,94 +1926,76 @@ static void drawPlayerSetupInputInfo(int player_nr)
   }
 }
 
-void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
+void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
 {
-  static int choice = 3;
+  static int choice = 0;
   static int player_nr = 0;
-  static int redraw = TRUE;
-  int x = (mx+32-SX)/32, y = (my+32-SY)/32;
-  int pos_start  = SETUPINPUT_SCREEN_POS_START  + 1;
-  int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1 + 1;
-  int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2 + 1;
-  int pos_end    = SETUPINPUT_SCREEN_POS_END    + 1;
+  int x = 0;
+  int y = choice;
+  int pos_start  = SETUPINPUT_SCREEN_POS_START;
+  int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
+  int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
+  int pos_end    = SETUPINPUT_SCREEN_POS_END;
 
   if (button == MB_MENU_INITIALIZE)
   {
     drawPlayerSetupInputInfo(player_nr);
-    redraw = TRUE;
+    drawCursor(choice, FC_RED);
+    return;
   }
-
-  if (redraw)
+  else if (button == MB_MENU_LEAVE)
   {
-    drawCursor(choice - 1, FC_RED);
-    redraw = FALSE;
+    setup_mode = SETUP_MODE_MAIN;
+    DrawSetupScreen();
+    InitJoysticks();
   }
 
-  if (button == MB_MENU_INITIALIZE)
-    return;
-
-  if (dx || dy)
+  if (mx || my)                /* mouse input */
   {
-    if (dx && choice == 3)
-    {
-      x = (dx < 0 ? 11 : 13);
-      y = 3;
-    }
-    else if (dx && choice == 4)
-    {
+    x = (mx - SX) / 32;
+    y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
+  }
+  else if (dx || dy)   /* keyboard input */
+  {
+    if (dx && choice == 0)
+      x = (dx < 0 ? 10 : 12);
+    else if ((dx && choice == 1) ||
+            (dx == +1 && choice == 2) ||
+            (dx == -1 && choice == pos_end))
       button = MB_MENU_CHOICE;
-      x = 1;
-      y = 4;
-    }
     else if (dy)
-    {
-      x = 1;
       y = choice + dy;
-    }
-    else
-      x = y = 0;
 
     if (y >= pos_empty1 && y <= pos_empty2)
       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
-
-    if (y < pos_start)
-      y = pos_start;
-    else if (y > pos_end)
-      y = pos_end;
-  }
-
-  if (!mx && !my && !dx && !dy)
-  {
-    x = 1;
-    y = choice;
   }
 
-  if (y == 3 && ((x == 1 && !button) || ((x == 11 || x == 13) && button)))
+  if (y == 0 && ((x == 0 && !button) || ((x == 10 || x == 12) && button)))
   {
     static unsigned long delay = 0;
 
     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
       goto out;
 
-    player_nr = (player_nr + (x == 11 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
+    player_nr = (player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
 
     drawPlayerSetupInputInfo(player_nr);
   }
-  else if (x==1 && y >= pos_start && y <= pos_end &&
+  else if (x == 0 && y >= pos_start && y <= pos_end &&
           !(y >= pos_empty1 && y <= pos_empty2))
   {
     if (button)
     {
       if (y != choice)
       {
-       drawCursor(y - 1, FC_RED);
-       drawCursor(choice - 1, FC_BLUE);
+       drawCursor(y, FC_RED);
+       drawCursor(choice, FC_BLUE);
+       choice = y;
       }
-      choice = y;
     }
     else
     {
-      if (y == 4)
+      if (y == 1)
       {
        char *device_name = setup.input[player_nr].joy.device_name;
 
@@ -1842,61 +2017,33 @@ void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
            setJoystickDeviceToNr(device_name, new_device_nr);
        }
 
-
-       /*
-       InitJoysticks();
-       */
-
-
-#if 0
-       int one_joystick_nr       = (dx >= 0 ? 0 : 1);
-       int the_other_joystick_nr = (dx >= 0 ? 1 : 0);
-
-       if (setup.input[player_nr].use_joystick)
-       {
-         if (setup.input[player_nr].joystick_nr == one_joystick_nr)
-           setup.input[player_nr].joystick_nr = the_other_joystick_nr;
-         else
-           setup.input[player_nr].use_joystick = FALSE;
-       }
-       else
-       {
-         setup.input[player_nr].use_joystick = TRUE;
-         setup.input[player_nr].joystick_nr = one_joystick_nr;
-       }
-#endif
-
        drawPlayerSetupInputInfo(player_nr);
       }
-      else if (y == 5)
+      else if (y == 2)
       {
        if (setup.input[player_nr].use_joystick)
        {
          InitJoysticks();
-         game_status = CALIBRATION;
          CalibrateJoystick(player_nr);
-         game_status = SETUPINPUT;
        }
        else
          CustomizeKeyboard(player_nr);
-
-       redraw = TRUE;
       }
       else if (y == pos_end)
       {
        InitJoysticks();
 
-       game_status = SETUP;
+       setup_mode = SETUP_MODE_MAIN;
        DrawSetupScreen();
-       redraw = TRUE;
       }
     }
   }
+
   BackToFront();
 
   out:
 
-  if (game_status == SETUPINPUT)
+  if (game_status == SETUP)
     DoAnimation();
 }
 
@@ -1950,7 +2097,7 @@ void CustomizeKeyboard(int player_nr)
       {
         case EVENT_KEYPRESS:
          {
-           Key key = GetEventKey((KeyEvent *)&event, TRUE);
+           Key key = GetEventKey((KeyEvent *)&event, FALSE);
 
            if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
            {
@@ -2027,59 +2174,32 @@ void CustomizeKeyboard(int player_nr)
   setup.input[player_nr].key = custom_key;
 
   StopAnimation();
-  DrawSetupInputScreen();
+  DrawSetupScreen_Input();
 }
 
-void CalibrateJoystick(int player_nr)
+static boolean CalibrateJoystickMain(int player_nr)
 {
-#ifdef __FreeBSD__
-  struct joystick joy_ctrl;
-#else
-  struct joystick_control
-  {
-    int buttons;
-    int x;
-    int y;
-  } joy_ctrl;
-#endif
-
-#if !defined(PLATFORM_MSDOS)
-  int new_joystick_xleft = 128, new_joystick_xright = 128;
-  int new_joystick_yupper = 128, new_joystick_ylower = 128;
+  int new_joystick_xleft = JOYSTICK_XMIDDLE;
+  int new_joystick_xright = JOYSTICK_XMIDDLE;
+  int new_joystick_yupper = JOYSTICK_YMIDDLE;
+  int new_joystick_ylower = JOYSTICK_YMIDDLE;
   int new_joystick_xmiddle, new_joystick_ymiddle;
-#else
-  int calibration_step = 1;
-#endif
 
-  int joystick_fd = stored_player[player_nr].joystick_fd;
+  int joystick_fd = joystick.fd[player_nr];
   int x, y, last_x, last_y, xpos = 8, ypos = 3;
   boolean check[3][3];
-  int check_remaining;
+  int check_remaining = 3 * 3;
+  int joy_x, joy_y;
   int joy_value;
   int result = -1;
 
-  if (joystick_status == JOYSTICK_OFF ||
-      joystick_fd < 0 ||
-      !setup.input[player_nr].use_joystick)
-    goto error_out;
+  if (joystick.status == JOYSTICK_NOT_AVAILABLE)
+    return FALSE;
 
-  ClearWindow();
+  if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
+    return FALSE;
 
-#if !defined(PLATFORM_MSDOS)
-  DrawText(SX,      SY +  6*32, " ROTATE JOYSTICK ", FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY +  7*32, "IN ALL DIRECTIONS", FS_BIG, FC_YELLOW);
-  DrawText(SX + 16, SY +  9*32, "  IF ALL BALLS  ",  FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY + 10*32, "   ARE YELLOW,   ", FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
-  check_remaining = 3 * 3;
-#else
-  DrawText(SX,      SY +  7*32, "  MOVE JOYSTICK  ", FS_BIG, FC_YELLOW);
-  DrawText(SX + 16, SY +  8*32, "       TO       ",  FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY +  9*32, " CENTER POSITION ",  FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY + 10*32, "       AND       ", FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
-  check_remaining = 0;
-#endif
+  ClearWindow();
 
   for(y=0; y<3; y++)
   {
@@ -2090,21 +2210,29 @@ void CalibrateJoystick(int player_nr)
     }
   }
 
+  DrawText(SX,      SY +  6 * 32, " ROTATE JOYSTICK ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY +  7 * 32, "IN ALL DIRECTIONS", FS_BIG, FC_YELLOW);
+  DrawText(SX + 16, SY +  9 * 32, "  IF ALL BALLS  ",  FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 10 * 32, "   ARE YELLOW,   ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 11 * 32, " CENTER JOYSTICK ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 12 * 32, "       AND       ", FS_BIG, FC_YELLOW);
+  DrawText(SX,      SY + 13 * 32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
+
   joy_value = Joystick(player_nr);
   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
-  DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_ROT);
 
-  BackToFront();
+  /* eventually uncalibrated center position (joystick could be uncentered) */
+  if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
+    return FALSE;
 
-#ifdef __FreeBSD__
-  joy_ctrl.b1 = joy_ctrl.b2 = 0;
-#else
-  joy_ctrl.buttons = 0;
-#endif
+  new_joystick_xmiddle = joy_x;
+  new_joystick_ymiddle = joy_y;
 
-  while(Joystick(player_nr) & JOY_BUTTON);
+  DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_ROT);
+  BackToFront();
 
+  while(Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
   InitAnimation();
 
   while(result < 0)
@@ -2144,28 +2272,13 @@ void CalibrateJoystick(int player_nr)
       }
     }
 
-#if !defined(PLATFORM_MSDOS)
-
-#if defined(TARGET_SDL)
-    joy_ctrl.x = Get_SDL_Joystick_Axis(joystick_fd, 0);
-    joy_ctrl.y = Get_SDL_Joystick_Axis(joystick_fd, 1);
-#else
-    if (read(joystick_fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
-    {
-      joystick_status = JOYSTICK_OFF;
-      goto error_out;
-    }
-#endif
-
-    new_joystick_xleft  = MIN(new_joystick_xleft,  joy_ctrl.x);
-    new_joystick_xright = MAX(new_joystick_xright, joy_ctrl.x);
-    new_joystick_yupper = MIN(new_joystick_yupper, joy_ctrl.y);
-    new_joystick_ylower = MAX(new_joystick_ylower, joy_ctrl.y);
+    if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
+      return FALSE;
 
-    new_joystick_xmiddle =
-      new_joystick_xleft + (new_joystick_xright - new_joystick_xleft) / 2;
-    new_joystick_ymiddle =
-      new_joystick_yupper + (new_joystick_ylower - new_joystick_yupper) / 2;
+    new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
+    new_joystick_xright = MAX(new_joystick_xright, joy_x);
+    new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
+    new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
 
     setup.input[player_nr].joy.xleft = new_joystick_xleft;
     setup.input[player_nr].joy.yupper = new_joystick_yupper;
@@ -2175,60 +2288,18 @@ void CalibrateJoystick(int player_nr)
     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
 
     CheckJoystickData();
-#endif
 
     joy_value = Joystick(player_nr);
 
     if (joy_value & JOY_BUTTON && check_remaining == 0)
-    {
       result = 1;
 
-#if defined(PLATFORM_MSDOS)
-      if (calibration_step == 1)
-      {
-       remove_joystick();
-       InitJoysticks();
-      }
-      else if (calibrate_joystick(joystick_fd) != 0)
-      {
-       joystick_status = JOYSTICK_OFF;
-       goto error_out;
-      }
-
-      if (joy[joystick_fd].flags & JOYFLAG_CALIBRATE)
-      {
-       calibration_step++;
-       result = -1;
-
-       DrawText(SX,      SY +  7*32, "  MOVE JOYSTICK  ", FS_BIG, FC_YELLOW);
-       DrawText(SX + 16, SY +  8*32, "       TO       ",  FS_BIG, FC_YELLOW);
-
-       if (calibration_step == 2)
-         DrawText(SX + 16, SY + 9*32," THE UPPER LEFT ",  FS_BIG, FC_YELLOW);
-       else
-         DrawText(SX,      SY + 9*32," THE LOWER RIGHT ", FS_BIG, FC_YELLOW);
-
-       DrawText(SX,      SY + 10*32, "       AND       ", FS_BIG, FC_YELLOW);
-       DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
-
-       BackToFront();
-
-       while(Joystick(player_nr) & JOY_BUTTON)
-         DoAnimation();
-      }
-#endif
-    }
-
     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
 
     if (x != last_x || y != last_y)
     {
-#if !defined(PLATFORM_MSDOS)
       DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_GELB);
-#else
-      DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_BLAU);
-#endif
       DrawGraphic(xpos + x,      ypos + y,      GFX_KUGEL_ROT);
 
       last_x = x;
@@ -2241,6 +2312,7 @@ void CalibrateJoystick(int player_nr)
       }
 
 #if 0
+#ifdef DEBUG
       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
             setup.input[player_nr].joy.xleft,
             setup.input[player_nr].joy.xmiddle,
@@ -2249,6 +2321,7 @@ void CalibrateJoystick(int player_nr)
             setup.input[player_nr].joy.yupper,
             setup.input[player_nr].joy.ymiddle,
             setup.input[player_nr].joy.ylower);
+#endif
 #endif
 
     }
@@ -2260,12 +2333,19 @@ void CalibrateJoystick(int player_nr)
     Delay(10);
   }
 
+  /* calibrated center position (joystick should now be centered) */
+  if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
+    return FALSE;
+
+  new_joystick_xmiddle = joy_x;
+  new_joystick_ymiddle = joy_y;
+
   StopAnimation();
 
-  DrawSetupInputScreen();
+  DrawSetupScreen_Input();
 
   /* wait until the last pressed button was released */
-  while(Joystick(player_nr) & JOY_BUTTON)
+  while (Joystick(player_nr) & JOY_BUTTON)
   {
     if (PendingEvent())                /* got event */
     {
@@ -2277,16 +2357,51 @@ void CalibrateJoystick(int player_nr)
       Delay(10);
     }
   }
-  return;
 
-  error_out:
+  return TRUE;
+}
 
-  ClearWindow();
-  DrawText(SX + 16, SY + 6*32, "  JOYSTICK NOT  ", FS_BIG, FC_YELLOW);
-  DrawText(SX,      SY + 7*32, "    AVAILABLE    ", FS_BIG, FC_YELLOW);
-  BackToFront();
-  Delay(2000);
-  DrawSetupInputScreen();
+void CalibrateJoystick(int player_nr)
+{
+  if (!CalibrateJoystickMain(player_nr))
+  {
+    ClearWindow();
+
+    DrawText(SX + 16, SY + 6*32, "  JOYSTICK NOT  ",  FS_BIG, FC_YELLOW);
+    DrawText(SX,      SY + 7*32, "    AVAILABLE    ", FS_BIG, FC_YELLOW);
+    BackToFront();
+    Delay(2000);       /* show error message for two seconds */
+  }
+}
+
+void DrawSetupScreen()
+{
+  DeactivateJoystick();
+
+  if (setup_mode == SETUP_MODE_INPUT)
+    DrawSetupScreen_Input();
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
+    DrawChooseTree(&artwork.gfx_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
+    DrawChooseTree(&artwork.snd_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
+    DrawChooseTree(&artwork.mus_current);
+  else
+    DrawSetupScreen_Generic();
+}
+
+void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
+{
+  if (setup_mode == SETUP_MODE_INPUT)
+    HandleSetupScreen_Input(mx, my, dx, dy, button);
+  else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
+    HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
+    HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
+  else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
+    HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
+  else
+    HandleSetupScreen_Generic(mx, my, dx, dy, button);
 }
 
 void HandleGameActions()
@@ -2339,13 +2454,13 @@ static struct
     SC_SCROLLBUTTON_XPOS + 0 * SC_SCROLLBUTTON_XSIZE,   SC_SCROLLBUTTON_YPOS,
     SC_SCROLL_UP_XPOS,                                 SC_SCROLL_UP_YPOS,
     SCREEN_CTRL_ID_SCROLL_UP,
-    "scroll level series up"
+    "scroll up"
   },
   {
     SC_SCROLLBUTTON_XPOS + 1 * SC_SCROLLBUTTON_XSIZE,   SC_SCROLLBUTTON_YPOS,
     SC_SCROLL_DOWN_XPOS,                               SC_SCROLL_DOWN_YPOS,
     SCREEN_CTRL_ID_SCROLL_DOWN,
-    "scroll level series down"
+    "scroll down"
   }
 };
 
@@ -2430,13 +2545,13 @@ static void CreateScreenScrollbars()
     struct GadgetInfo *gi;
     int items_max, items_visible, item_position;
     unsigned long event_mask;
-    int num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+    int num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
 
 #if 0
-    if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
+    if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN)
       num_page_entries = num_leveldirs;
     else
-      num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
+      num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
 
     items_max = MAX(num_leveldirs, num_page_entries);
     items_visible = num_page_entries;
@@ -2486,19 +2601,19 @@ void CreateScreenGadgets()
   CreateScreenScrollbars();
 }
 
-void MapChooseLevelGadgets()
+void MapChooseTreeGadgets(TreeInfo *ti)
 {
-  int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
+  int num_entries = numTreeInfoInGroup(ti);
   int i;
 
-  if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
+  if (num_entries <= MAX_MENU_ENTRIES_ON_SCREEN)
     return;
 
   for (i=0; i<NUM_SCREEN_GADGETS; i++)
     MapGadget(screen_gadget[i]);
 }
 
-void UnmapChooseLevelGadgets()
+void UnmapChooseTreeGadgets()
 {
   int i;
 
@@ -2510,21 +2625,30 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
 {
   int id = gi->custom_id;
 
-  if (game_status != CHOOSELEVEL)
+  if (game_status != CHOOSELEVEL && game_status != SETUP)
     return;
 
   switch (id)
   {
     case SCREEN_CTRL_ID_SCROLL_UP:
-      HandleChooseLevel(SX,SY + 32, 0,0, MB_MENU_MARK);
+      if (game_status == CHOOSELEVEL)
+       HandleChooseLevel(SX,SY + 32, 0,0, MB_MENU_MARK);
+      else if (game_status == SETUP)
+       HandleSetupScreen(SX,SY + 32, 0,0, MB_MENU_MARK);
       break;
 
     case SCREEN_CTRL_ID_SCROLL_DOWN:
-      HandleChooseLevel(SX,SY + SYSIZE - 32, 0,0, MB_MENU_MARK);
+      if (game_status == CHOOSELEVEL)
+       HandleChooseLevel(SX,SY + SYSIZE - 32, 0,0, MB_MENU_MARK);
+      else if (game_status == SETUP)
+       HandleSetupScreen(SX,SY + SYSIZE - 32, 0,0, MB_MENU_MARK);
       break;
 
     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
-      HandleChooseLevel(0,0, 999,gi->event.item_position, MB_MENU_INITIALIZE);
+      if (game_status == CHOOSELEVEL)
+       HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
+      else if (game_status == SETUP)
+       HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
       break;
 
     default:
index 68567082cb1676f50c079cb4b2a18be55ac737c0..60f310165c0a8d2c5882ca97d96ca9a137631c4b 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "main.h"
 
 void DrawHeadline(void);
+
 void DrawMainMenu(void);
 void HandleMainMenu(int, int, int, int, int);
+
 void DrawHelpScreenElAction(int);
 void DrawHelpScreenElText(int);
 void DrawHelpScreenMusicText(int);
 void DrawHelpScreenCreditsText(void);
 void DrawHelpScreen(void);
 void HandleHelpScreen(int);
+
 void HandleTypeName(int, Key);
+
 void DrawChooseLevel(void);
 void HandleChooseLevel(int, int, int, int, int);
+
 void DrawHallOfFame(int);
 void HandleHallOfFame(int, int, int, int, int);
+
 void DrawSetupScreen(void);
 void HandleSetupScreen(int, int, int, int, int);
-void DrawSetupInputScreen(void);
-void HandleSetupInputScreen(int, int, int, int, int);
-void CustomizeKeyboard(int);
-void CalibrateJoystick(int);
+
 void HandleGameActions(void);
 
 void CreateScreenGadgets();
-void MapChooseLevelGadgets();
-void UnmapChooseLevelGadgets();
+void MapChooseTreeGadgets(TreeInfo *);
+void UnmapChooseTreeGadgets();
 
 #endif /* SCREENS_H */
index 44bbc57bcbe440274f30575c61e5f462207c19ce..b219109fb3528b85246a1059312da05891ea742f 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 
 /* tape button identifiers */
 #define TAPE_CTRL_ID_EJECT             0
-#define TAPE_CTRL_ID_STOP              1
-#define TAPE_CTRL_ID_PAUSE             2
-#define TAPE_CTRL_ID_RECORD            3
-#define TAPE_CTRL_ID_PLAY              4
+#define TAPE_CTRL_ID_INDEX             1
+#define TAPE_CTRL_ID_STOP              2
+#define TAPE_CTRL_ID_PAUSE             3
+#define TAPE_CTRL_ID_RECORD            4
+#define TAPE_CTRL_ID_PLAY              5
 
-#define NUM_TAPE_BUTTONS               5
+#define NUM_TAPE_BUTTONS               6
+
+/* values for tape handling */
+#define TAPE_PAUSE_SECONDS_BEFORE_DEATH                5
 
 /* forward declaration for internal use */
 static void HandleTapeButtons(struct GadgetInfo *);
+static void TapeStopIndexSearch();
 
 static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
 
@@ -286,79 +291,119 @@ void DrawCompleteVideoDisplay()
 /* tape control functions                                                    */
 /* ========================================================================= */
 
-void TapeStartRecording()
+void TapeErase()
 {
-  time_t zeit1 = time(NULL);
-  struct tm *zeit2 = localtime(&zeit1);
+  time_t epoch_seconds = time(NULL);
+  struct tm *time = localtime(&epoch_seconds);
   int i;
 
-  if (!TAPE_IS_STOPPED(tape))
-    TapeStop();
-
-  tape.level_nr = level_nr;
   tape.length = 0;
   tape.counter = 0;
+
+  tape.level_nr = level_nr;
   tape.pos[tape.counter].delay = 0;
-  tape.recording = TRUE;
-  tape.playing = FALSE;
-  tape.pausing = FALSE;
   tape.changed = TRUE;
-  tape.date = 10000*(zeit2->tm_year%100) + 100*zeit2->tm_mon + zeit2->tm_mday;
+
+  tape.date = 10000*(time->tm_year % 100) + 100*time->tm_mon + time->tm_mday;
   tape.random_seed = InitRND(NEW_RANDOMIZE);
 
+  tape.file_version = FILE_VERSION_ACTUAL;
+  tape.game_version = GAME_VERSION_ACTUAL;
+  tape.engine_version = level.game_version;
+
   for(i=0; i<MAX_PLAYERS; i++)
     tape.player_participates[i] = FALSE;
+}
+
+static void TapeRewind()
+{
+  tape.counter = 0;
+  tape.delay_played = 0;
+  tape.pause_before_death = FALSE;
+  tape.recording = FALSE;
+  tape.playing = FALSE;
+  tape.fast_forward = FALSE;
+  tape.index_search = FALSE;
+  tape.quick_resume = FALSE;
+  tape.single_step = FALSE;
+
+  InitRND(tape.random_seed);
+}
+
+void TapeStartRecording()
+{
+  if (!TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  TapeErase();
+  TapeRewind();
+
+  tape.recording = TRUE;
 
   DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
+  MapTapeIndexButton();
+
+  SetDrawDeactivationMask(REDRAW_NONE);
+  audio.sound_deactivated = FALSE;
 }
 
-void TapeStopRecording()
+static void TapeStartGameRecording()
 {
-#if 0
-  int i;
+  TapeStartRecording();
+
+#if defined(PLATFORM_UNIX)
+  if (options.network)
+    SendToServer_StartPlaying();
+  else
 #endif
+  {
+    game_status = PLAYING;
+    StopAnimation();
+    InitGame();
+  }
+}
 
-  if (!tape.recording)
+static void TapeAppendRecording()
+{
+  if (!tape.playing || !tape.pausing)
     return;
 
-#if 0
-  for(i=0; i<MAX_PLAYERS; i++)
-    tape.pos[tape.counter].action[i] = 0;
-#endif
+  tape.pos[tape.counter].delay = tape.delay_played;
+  tape.playing = FALSE;
+  tape.recording = TRUE;
+  tape.changed = TRUE;
+
+  DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON,0);
+}
+
+void TapeHaltRecording()
+{
+  if (!tape.recording)
+    return;
 
   tape.counter++;
+  tape.pos[tape.counter].delay = 0;
+
   tape.length = tape.counter;
   tape.length_seconds = GetTapeLength();
-  tape.recording = FALSE;
-  tape.pausing = FALSE;
-  DrawVideoDisplay(VIDEO_STATE_REC_OFF, 0);
 }
 
-#if 0
-void TapeRecordAction(byte joy[MAX_PLAYERS])
+void TapeStopRecording()
 {
-  int i;
-
-  if (!tape.recording || tape.pausing)
+  if (!tape.recording)
     return;
 
-  if (tape.counter >= MAX_TAPELEN-1)
-  {
-    TapeStopRecording();
-    return;
-  }
+  TapeHaltRecording();
 
-  for(i=0; i<MAX_PLAYERS; i++)
-    tape.pos[tape.counter].action[i] = joy[i];
+  tape.recording = FALSE;
+  tape.pausing = FALSE;
 
-  tape.counter++;
-  tape.pos[tape.counter].delay = 0;
+  DrawVideoDisplay(VIDEO_STATE_REC_OFF, 0);
+  MapTapeEjectButton();
 }
 
-#else
-
 void TapeRecordAction(byte action[MAX_PLAYERS])
 {
   int i;
@@ -366,7 +411,7 @@ void TapeRecordAction(byte action[MAX_PLAYERS])
   if (!tape.recording || tape.pausing)
     return;
 
-  if (tape.counter >= MAX_TAPELEN-1)
+  if (tape.counter >= MAX_TAPELEN - 1)
   {
     TapeStopRecording();
     return;
@@ -397,57 +442,41 @@ void TapeRecordAction(byte action[MAX_PLAYERS])
     tape.pos[tape.counter].delay++;
   }
 }
-#endif
 
-#if 0
-void TapeRecordDelay()
-{
-  int i;
-
-  if (!tape.recording || tape.pausing)
-    return;
-
-  if (tape.counter >= MAX_TAPELEN)
-  {
-    TapeStopRecording();
-    return;
-  }
-
-  tape.pos[tape.counter].delay++;
-
-  if (tape.pos[tape.counter].delay >= 255)
-  {
-    for(i=0; i<MAX_PLAYERS; i++)
-      tape.pos[tape.counter].action[i] = 0;
-
-    tape.counter++;
-    tape.pos[tape.counter].delay = 0;
-  }
-}
-
-#else
-
-void TapeRecordDelay()
-{
-}
-#endif
-
-void TapeTogglePause()
+void TapeTogglePause(boolean toggle_manual)
 {
   unsigned long state;
 
+#if 0
   if (!tape.recording && !tape.playing)
     return;
+#endif
 
   tape.pausing = !tape.pausing;
   tape.fast_forward = FALSE;
   tape.pause_before_death = FALSE;
 
+  if (tape.single_step && toggle_manual)
+    tape.single_step = FALSE;
+
   state = (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
   if (tape.playing)
     state |= VIDEO_STATE_PBEND_OFF;
 
   DrawVideoDisplay(state, 0);
+
+  if (tape.index_search)
+  {
+    TapeStopIndexSearch();
+
+    if (tape.quick_resume)
+    {
+      tape.quick_resume = FALSE;
+
+      TapeAppendRecording();
+      TapeTogglePause(toggle_manual);
+    }
+  }
 }
 
 void TapeStartPlaying()
@@ -458,18 +487,26 @@ void TapeStartPlaying()
   if (!TAPE_IS_STOPPED(tape))
     TapeStop();
 
-  tape.counter = 0;
-  tape.delay_played = 0;
-  tape.pause_before_death = FALSE;
-  tape.recording = FALSE;
+  TapeRewind();
+
   tape.playing = TRUE;
-  tape.pausing = FALSE;
-  tape.fast_forward = FALSE;
-  InitRND(tape.random_seed);
 
   DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
+  MapTapeIndexButton();
+
+  SetDrawDeactivationMask(REDRAW_NONE);
+  audio.sound_deactivated = FALSE;
+}
+
+static void TapeStartGamePlaying()
+{
+  TapeStartPlaying();
+
+  game_status = PLAYING;
+  StopAnimation();
+  InitGame();
 }
 
 void TapeStopPlaying()
@@ -479,43 +516,14 @@ void TapeStopPlaying()
 
   tape.playing = FALSE;
   tape.pausing = FALSE;
-  DrawVideoDisplay(VIDEO_STATE_PLAY_OFF, 0);
-}
 
-#if 0
-byte *TapePlayAction()
-{
-  static byte joy[MAX_PLAYERS];
-  int i;
+  if (tape.index_search)
+    TapeStopIndexSearch();
 
-  if (!tape.playing || tape.pausing)
-    return(NULL);
-
-  if (tape.counter >= tape.length)
-  {
-    TapeStop();
-    return(NULL);
-  }
-
-  if (tape.delay_played == tape.pos[tape.counter].delay)
-  {
-    tape.delay_played = 0;
-    tape.counter++;
-
-    for(i=0; i<MAX_PLAYERS; i++)
-      joy[i] = tape.pos[tape.counter-1].action[i];
-  }
-  else
-  {
-    for(i=0; i<MAX_PLAYERS; i++)
-      joy[i] = 0;
-  }
-
-  return(joy);
+  DrawVideoDisplay(VIDEO_STATE_PLAY_OFF, 0);
+  MapTapeEjectButton();
 }
 
-#else
-
 byte *TapePlayAction()
 {
   static byte action[MAX_PLAYERS];
@@ -536,14 +544,18 @@ byte *TapePlayAction()
 
     if (TimePlayed > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
     {
-      TapeTogglePause();
+      TapeTogglePause(TAPE_TOGGLE_MANUAL);
       return NULL;
     }
   }
 
-  if (tape.counter >= tape.length)
+  if (tape.counter >= tape.length)     /* end of tape reached */
   {
-    TapeStop();
+    if (tape.index_search)
+      TapeTogglePause(TAPE_TOGGLE_MANUAL);
+    else
+      TapeStop();
+
     return NULL;
   }
 
@@ -559,53 +571,6 @@ byte *TapePlayAction()
 
   return action;
 }
-#endif
-
-#if 0
-boolean TapePlayDelay()
-{
-  if (!tape.playing || tape.pausing)
-    return(FALSE);
-
-  if (tape.pause_before_death) /* STOP 10s BEFORE PLAYER GETS KILLED... */
-  {
-    if (!(FrameCounter % 20))
-    {
-      if ((FrameCounter / 20) % 2)
-       DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
-      else
-       DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, VIDEO_DISPLAY_LABEL_ONLY);
-    }
-
-    if (TimePlayed > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
-    {
-      TapeTogglePause();
-      return(FALSE);
-    }
-  }
-
-  if (tape.counter >= tape.length)
-  {
-    TapeStop();
-    return(TRUE);
-  }
-
-  if (tape.delay_played < tape.pos[tape.counter].delay)
-  {
-    tape.delay_played++;
-    return(TRUE);
-  }
-  else
-    return(FALSE);
-}
-
-#else
-
-boolean TapePlayDelay()
-{
-  return TRUE|FALSE;   /* ...it doesn't matter at all */
-}
-#endif
 
 void TapeStop()
 {
@@ -620,11 +585,6 @@ void TapeStop()
   }
 }
 
-void TapeErase()
-{
-  tape.length = 0;
-}
-
 unsigned int GetTapeLength()
 {
   unsigned int tape_length = 0;
@@ -639,6 +599,78 @@ unsigned int GetTapeLength()
   return(tape_length * GAME_FRAME_DELAY / 1000);
 }
 
+static void TapeStartIndexSearch()
+{
+  tape.index_search = TRUE;
+
+  if (!tape.fast_forward || tape.pause_before_death)
+  {
+    tape.pausing = FALSE;
+
+    SetDrawDeactivationMask(REDRAW_FIELD | REDRAW_DOOR_1);
+    audio.sound_deactivated = TRUE;
+  }
+}
+
+static void TapeStopIndexSearch()
+{
+  tape.index_search = FALSE;
+
+  SetDrawDeactivationMask(REDRAW_NONE);
+  audio.sound_deactivated = FALSE;
+
+  RedrawPlayfield(TRUE, 0,0,0,0);
+  DrawGameDoorValues();
+}
+
+static void TapeSingleStep()
+{
+  if (options.network)
+    return;
+
+  if (!tape.pausing)
+    TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+  tape.single_step = !tape.single_step;
+}
+
+void TapeQuickSave()
+{
+  if (game_status == PLAYING)
+  {
+    if (tape.recording)
+      TapeHaltRecording();     /* prepare tape for saving on-the-fly */
+
+    if (TAPE_IS_EMPTY(tape))
+      Request("No tape that can be saved !", REQ_CONFIRM);
+    else
+      SaveTape(tape.level_nr);
+  }
+  else if (game_status == MAINMENU)
+    Request("No game that can be saved !", REQ_CONFIRM);
+}
+
+void TapeQuickLoad()
+{
+  if (game_status == PLAYING || game_status == MAINMENU)
+  {
+    TapeStop();
+    TapeErase();
+
+    LoadTape(level_nr);
+    if (!TAPE_IS_EMPTY(tape))
+    {
+      TapeStartGamePlaying();
+      TapeStartIndexSearch();
+
+      tape.quick_resume = TRUE;
+    }
+    else
+      Request("No tape for this level !", REQ_CONFIRM);
+  }
+}
+
+
 /* ---------- new tape button stuff ---------------------------------------- */
 
 /* graphic position values for tape buttons */
@@ -648,6 +680,7 @@ unsigned int GetTapeLength()
 #define TAPE_BUTTON_YPOS       77
 
 #define TAPE_BUTTON_EJECT_XPOS (TAPE_BUTTON_XPOS + 0 * TAPE_BUTTON_XSIZE)
+#define TAPE_BUTTON_INDEX_XPOS (TAPE_BUTTON_XPOS + 0 * TAPE_BUTTON_XSIZE)
 #define TAPE_BUTTON_STOP_XPOS  (TAPE_BUTTON_XPOS + 1 * TAPE_BUTTON_XSIZE)
 #define TAPE_BUTTON_PAUSE_XPOS (TAPE_BUTTON_XPOS + 2 * TAPE_BUTTON_XSIZE)
 #define TAPE_BUTTON_RECORD_XPOS        (TAPE_BUTTON_XPOS + 3 * TAPE_BUTTON_XSIZE)
@@ -665,6 +698,11 @@ static struct
     TAPE_CTRL_ID_EJECT,
     "eject tape"
   },
+  {
+    TAPE_BUTTON_INDEX_XPOS,    TAPE_BUTTON_YPOS,
+    TAPE_CTRL_ID_INDEX,
+    "index mark"
+  },
   {
     TAPE_BUTTON_STOP_XPOS,     TAPE_BUTTON_YPOS,
     TAPE_CTRL_ID_STOP,
@@ -705,6 +743,12 @@ void CreateTapeButtons()
     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
     gd_y  = DOOR_GFX_PAGEY2 + gd_yoffset;
 
+    if (i == TAPE_CTRL_ID_INDEX)
+    {
+      gd_x1 = DOOR_GFX_PAGEX6 + gd_xoffset;
+      gd_x2 = DOOR_GFX_PAGEX5 + gd_xoffset;
+    }
+
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, tapebutton_info[i].infotext,
                      GDI_X, VX + gd_xoffset,
@@ -726,12 +770,28 @@ void CreateTapeButtons()
   }
 }
 
+void MapTapeEjectButton()
+{
+  UnmapGadget(tape_gadget[TAPE_CTRL_ID_INDEX]);
+  MapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
+}
+
+void MapTapeIndexButton()
+{
+  UnmapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
+  MapGadget(tape_gadget[TAPE_CTRL_ID_INDEX]);
+}
+
 void MapTapeButtons()
 {
   int i;
 
   for (i=0; i<NUM_TAPE_BUTTONS; i++)
-    MapGadget(tape_gadget[i]);
+    if (i != TAPE_CTRL_ID_INDEX)
+      MapGadget(tape_gadget[i]);
+
+  if (tape.recording || tape.playing)
+    MapTapeIndexButton();
 }
 
 void UnmapTapeButtons()
@@ -768,43 +828,30 @@ static void HandleTapeButtons(struct GadgetInfo *gi)
       DrawCompleteVideoDisplay();
       break;
 
+    case TAPE_CTRL_ID_INDEX:
+      if (tape.playing)
+       TapeStartIndexSearch();
+      else if (tape.recording)
+       TapeSingleStep();
+      break;
+
     case TAPE_CTRL_ID_STOP:
       TapeStop();
       break;
 
     case TAPE_CTRL_ID_PAUSE:
-      TapeTogglePause();
+      TapeTogglePause(TAPE_TOGGLE_MANUAL);
       break;
 
     case TAPE_CTRL_ID_RECORD:
       if (TAPE_IS_STOPPED(tape))
-      {
-       TapeStartRecording();
-
-#if defined(PLATFORM_UNIX)
-       if (options.network)
-         SendToServer_StartPlaying();
-       else
-#endif
-       {
-         game_status = PLAYING;
-         StopAnimation();
-         InitGame();
-       }
-      }
+       TapeStartGameRecording();
       else if (tape.pausing)
       {
        if (tape.playing)       /* PLAYING -> PAUSING -> RECORDING */
-       {
-         tape.pos[tape.counter].delay = tape.delay_played;
-         tape.playing = FALSE;
-         tape.recording = TRUE;
-         tape.changed = TRUE;
-
-         DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON,0);
-       }
+         TapeAppendRecording();
        else
-         TapeTogglePause();
+         TapeTogglePause(TAPE_TOGGLE_MANUAL);
       }
       break;
 
@@ -814,27 +861,25 @@ static void HandleTapeButtons(struct GadgetInfo *gi)
 
       if (TAPE_IS_STOPPED(tape))
       {
-       TapeStartPlaying();
-
-       game_status = PLAYING;
-       StopAnimation();
-       InitGame();
+       TapeStartGamePlaying();
       }
       else if (tape.playing)
       {
        if (tape.pausing)                       /* PAUSE -> PLAY */
-         TapeTogglePause();
+       {
+         TapeTogglePause(TAPE_TOGGLE_MANUAL);
+       }
        else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
        {
          tape.fast_forward = TRUE;
          DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
        }
-       else if (!tape.pause_before_death)      /* FFWD PLAY -> AUTO PAUSE */
+       else if (!tape.pause_before_death)      /* FFWD PLAY -> AUTO PAUSE */
        {
          tape.pause_before_death = TRUE;
          DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
        }
-       else                                    /* -> NORMAL PLAY */
+       else                                    /* AUTO PAUSE -> NORMAL PLAY */
        {
          tape.fast_forward = FALSE;
          tape.pause_before_death = FALSE;
index 0756dac7f48a2533662b1b7b4958c3803350ba3a..6a41a94d8bdf96ae53763d725bc4550aa3d8fbb6 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 
 #include "main.h"
 
-#define TAPE_PAUSE_SECONDS_BEFORE_DEATH                3
+
+/* values for TapeTogglePause() */
+#define        TAPE_TOGGLE_MANUAL      TRUE
+#define        TAPE_TOGGLE_AUTOMATIC   FALSE
 
 /* some positions in the video tape control window */
 #define VIDEO_DISPLAY1_XPOS    5
@@ -82,19 +85,22 @@ void DrawVideoDisplay(unsigned long, unsigned long);
 void DrawCompleteVideoDisplay(void);
 
 void TapeStartRecording(void);
+void TapeHaltRecording(void);
 void TapeStopRecording(void);
 void TapeRecordAction(byte *);
-void TapeRecordDelay(void);
-void TapeTogglePause(void);
+void TapeTogglePause(boolean);
 void TapeStartPlaying(void);
 void TapeStopPlaying(void);
 byte *TapePlayAction(void);
-boolean TapePlayDelay(void);
 void TapeStop(void);
 void TapeErase(void);
 unsigned int GetTapeLength(void);
+void TapeQuickSave(void);
+void TapeQuickLoad(void);
 
 void CreateTapeButtons();
+void MapTapeEjectButton();
+void MapTapeIndexButton();
 void MapTapeButtons();
 void UnmapTapeButtons();
 
index d467fcdab02194e28c0b98fe18be6567f47da3ba..e5055eda6513d061b8f6eaab76c1922a3f4f6158 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 * tools.c                                                  *
 ***********************************************************/
 
-#include <stdarg.h>
-
-#if defined(PLATFORM_FREEBSD)
-#include <machine/joystick.h>
-#endif
-
 #include "libgame/libgame.h"
 
 #include "tools.h"
 #include "game.h"
 #include "events.h"
-#include "joystick.h"
 #include "cartoons.h"
 #include "network.h"
 #include "tape.h"
 
-#if defined(PLATFORM_MSDOS)
-extern boolean wait_for_vsync;
-#endif
-
 /* tool button identifiers */
 #define TOOL_CTRL_ID_YES       0
 #define TOOL_CTRL_ID_NO                1
@@ -82,6 +71,51 @@ void SetDrawtoField(int mode)
   }
 }
 
+void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
+{
+  if (game_status == PLAYING)
+  {
+    if (force_redraw)
+    {
+      x = gfx.sx - TILEX;
+      y = gfx.sy - TILEY;
+      width = gfx.sxsize + 2 * TILEX;
+      height = gfx.sysize + 2 * TILEY;
+    }
+
+    if (force_redraw || setup.direct_draw)
+    {
+      int xx, yy;
+      int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
+      int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
+
+      if (setup.direct_draw)
+       SetDrawtoField(DRAW_BACKBUFFER);
+
+      for(xx=BX1; xx<=BX2; xx++)
+       for(yy=BY1; yy<=BY2; yy++)
+         if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
+           DrawScreenField(xx, yy);
+      DrawAllPlayers();
+
+      if (setup.direct_draw)
+       SetDrawtoField(DRAW_DIRECT);
+    }
+
+    if (setup.soft_scrolling)
+    {
+      int fx = FX, fy = FY;
+
+      fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
+      fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
+
+      BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
+    }
+  }
+
+  BlitBitmap(drawto, window, x, y, width, height, x, y);
+}
+
 void BackToFront()
 {
   int x,y;
@@ -96,7 +130,7 @@ void BackToFront()
   if (redraw_mask & REDRAW_FIELD)
     redraw_mask &= ~REDRAW_TILES;
 
-  if (!redraw_mask)
+  if (redraw_mask == REDRAW_NONE)
     return;
 
   if (global.fps_slowdown && game_status == PLAYING)
@@ -257,7 +291,7 @@ void BackToFront()
     for(y=0; y<MAX_BUF_YSIZE; y++)
       redraw[x][y] = 0;
   redraw_tiles = 0;
-  redraw_mask = 0;
+  redraw_mask = REDRAW_NONE;
 }
 
 void FadeToFront()
@@ -1737,6 +1771,9 @@ int REQ_in_range(int x, int y)
   return 0;
 }
 
+#define MAX_REQUEST_LINES              13
+#define MAX_REQUEST_LINE_LEN           7
+
 boolean Request(char *text, unsigned int req_state)
 {
   int mx, my, ty, result = -1;
@@ -1765,31 +1802,35 @@ boolean Request(char *text, unsigned int req_state)
   ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
 
   /* write text for request */
-  for(ty=0; ty<13; ty++)
+  for(ty=0; ty < MAX_REQUEST_LINES; ty++)
   {
+    char text_line[MAX_REQUEST_LINE_LEN + 1];
     int tx, tl, tc;
-    char txt[256];
 
     if (!*text)
       break;
 
-    for(tl=0,tx=0; tx<7; tl++,tx++)
+    for(tl=0,tx=0; tx < MAX_REQUEST_LINE_LEN; tl++,tx++)
     {
       tc = *(text + tx);
-      if (!tc || tc == 32)
+      if (!tc || tc == ' ')
        break;
     }
+
     if (!tl)
     { 
       text++; 
       ty--; 
       continue; 
     }
-    sprintf(txt, text); 
-    txt[tl] = 0;
-    DrawTextExt(drawto, DX + 51 - (tl * 14)/2, DY + 8 + ty * 16,
-               txt, FS_SMALL, FC_YELLOW);
-    text += tl + (tc == 32 ? 1 : 0);
+
+    strncpy(text_line, text, tl);
+    text_line[tl] = 0;
+
+    DrawTextExt(drawto, DX + 50 - (tl * 14)/2, DY + 8 + ty * 16,
+               text_line, FS_SMALL, FC_YELLOW);
+
+    text += tl + (tc == ' ' ? 1 : 0);
   }
 
   if (req_state & REQ_ASK)
@@ -1921,7 +1962,7 @@ boolean Request(char *text, unsigned int req_state)
          break;
 
        case EVENT_KEYRELEASE:
-         key_joystick_mapping = 0;
+         ClearPlayerAction();
          break;
 
        default:
@@ -2009,7 +2050,12 @@ unsigned int CloseDoor(unsigned int door_state)
 
 unsigned int GetDoorState()
 {
-  return(MoveDoor(DOOR_GET_STATE));
+  return MoveDoor(DOOR_GET_STATE);
+}
+
+unsigned int SetDoorState(unsigned int door_state)
+{
+  return MoveDoor(door_state | DOOR_SET_STATE);
 }
 
 unsigned int MoveDoor(unsigned int door_state)
@@ -2023,6 +2069,16 @@ unsigned int MoveDoor(unsigned int door_state)
   if (door_state == DOOR_GET_STATE)
     return(door1 | door2);
 
+  if (door_state & DOOR_SET_STATE)
+  {
+    if (door_state & DOOR_ACTION_1)
+      door1 = door_state & DOOR_ACTION_1;
+    if (door_state & DOOR_ACTION_2)
+      door2 = door_state & DOOR_ACTION_2;
+
+    return(door1 | door2);
+  }
+
   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
     door_state &= ~DOOR_OPEN_1;
   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
@@ -2036,13 +2092,20 @@ unsigned int MoveDoor(unsigned int door_state)
   {
     stepsize = 20;
     door_delay_value = 0;
-    StopSound(SND_OEFFNEN);
+    StopSound(SND_MENU_DOOR_OPENING);
+    StopSound(SND_MENU_DOOR_CLOSING);
   }
 
   if (door_state & DOOR_ACTION)
   {
     if (!(door_state & DOOR_NO_DELAY))
-      PlaySoundStereo(SND_OEFFNEN, PSND_MAX_RIGHT);
+    {
+      /* opening door sound has priority over simultaneously closing door */
+      if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
+       PlaySoundStereo(SND_MENU_DOOR_OPENING, SOUND_MAX_RIGHT);
+      else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
+       PlaySoundStereo(SND_MENU_DOOR_CLOSING, SOUND_MAX_RIGHT);
+    }
 
     start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
 
@@ -2137,7 +2200,10 @@ unsigned int MoveDoor(unsigned int door_state)
   }
 
   if (setup.quick_doors)
-    StopSound(SND_OEFFNEN);
+  {
+    StopSound(SND_MENU_DOOR_OPENING);
+    StopSound(SND_MENU_DOOR_CLOSING);
+  }
 
   if (door_state & DOOR_ACTION_1)
     door1 = door_state & DOOR_ACTION_1;
@@ -2282,13 +2348,6 @@ static struct
   }
 };
 
-#if 0
-static void DoNotDisplayInfoText(void *ptr)
-{
-  return;
-}
-#endif
-
 void CreateToolButtons()
 {
   int i;
@@ -2335,11 +2394,6 @@ void CreateToolButtons()
                      GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
                      GDI_DECORATION_SHIFTING, 1, 1,
                      GDI_EVENT_MASK, event_mask,
-
-#if 0
-                     GDI_CALLBACK_INFO, DoNotDisplayInfoText,
-#endif
-
                      GDI_CALLBACK_ACTION, HandleToolButtons,
                      GDI_END);
 
index cb159f174a0aa03e2ba9b9dbf122fa263a89e32c..1ed8d163f08be0095d64c67702b77a515caa2237 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2001 Artsoft Entertainment                      *
+* (c) 1995-2002 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -14,7 +14,6 @@
 #ifndef TOOLS_H
 #define TOOLS_H
 
-#include <sys/time.h>
 #include "main.h"
 
 /* for SetDrawtoField */
@@ -46,6 +45,7 @@
 #define DOOR_COPY_BACK         (1 << 4)
 #define DOOR_NO_DELAY          (1 << 5)
 #define DOOR_GET_STATE         (1 << 6)
+#define DOOR_SET_STATE         (1 << 7)
 
 /* for Request */
 #define REQ_ASK                        (1 << 0)
@@ -59,6 +59,7 @@
 #define REQUEST_WAIT_FOR       (REQ_ASK | REQ_CONFIRM | REQ_PLAYER)
 
 void SetDrawtoField(int);
+void RedrawPlayfield(boolean, int, int, int, int);
 void BackToFront();
 void FadeToFront();
 void ClearWindow();
@@ -104,6 +105,7 @@ boolean Request(char *, unsigned int);
 unsigned int OpenDoor(unsigned int);
 unsigned int CloseDoor(unsigned int);
 unsigned int GetDoorState(void);
+unsigned int SetDoorState(unsigned int);
 unsigned int MoveDoor(unsigned int);
 void DrawSpecialEditorDoor();
 void UndrawSpecialEditorDoor();