rnd-20100327-1-src
[rocksndiamonds.git] / src / files.c
index 01c1b1ac9a4bdb1c21d808d36e054efbe3331f21..91e597b1fb680e5367cb6ce4700311321b2bf6b0 100644 (file)
@@ -1346,6 +1346,8 @@ static struct DateInfo getCurrentDate()
   date.month = now->tm_mon  + 1;
   date.day   = now->tm_mday;
 
+  date.src   = DATE_SRC_CLOCK;
+
   return date;
 }
 
@@ -1877,18 +1879,23 @@ static boolean checkForPackageFromBasename(char *basename)
   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
 }
 
-static char *getSingleLevelBasename(int nr)
+static char *getSingleLevelBasenameExt(int nr, char *extension)
 {
   static char basename[MAX_FILENAME_LEN];
 
   if (nr < 0)
-    sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
+    sprintf(basename, "template.%s", extension);
   else
-    sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
+    sprintf(basename, "%03d.%s", nr, extension);
 
   return basename;
 }
 
+static char *getSingleLevelBasename(int nr)
+{
+  return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
+}
+
 static char *getPackedLevelBasename(int type)
 {
   static char basename[MAX_FILENAME_LEN];
@@ -2220,6 +2227,8 @@ static int LoadLevel_DATE(FILE *file, int chunk_size, struct LevelInfo *level)
   level->creation_date.month = getFile8Bit(file);
   level->creation_date.day   = getFile8Bit(file);
 
+  level->creation_date.src   = DATE_SRC_LEVELFILE;
+
   return chunk_size;
 }
 
@@ -4273,25 +4282,8 @@ void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
   level_sp->height = level->fieldy;
 
   for (x = 0; x < level->fieldx; x++)
-  {
     for (y = 0; y < level->fieldy; y++)
-    {
-      int element_old = level->field[x][y];
-      int element_new;
-
-      if (element_old >= EL_SP_START &&
-         element_old <= EL_SP_END)
-       element_new = element_old - EL_SP_START;
-      else if (element_old == EL_EMPTY_SPACE)
-       element_new = 0x00;
-      else if (element_old == EL_INVISIBLE_WALL)
-       element_new = 0x28;
-      else
-       element_new = 0x20;     /* map unknown elements to yellow "hardware" */
-
-      level_sp->playfield[x][y] = element_new;
-    }
-  }
+      level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
 
   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
 
@@ -4378,20 +4370,12 @@ void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
     for (y = 0; y < level->fieldy; y++)
     {
       int element_old = level_sp->playfield[x][y];
-      int element_new;
+      int element_new = getMappedElement(map_element_SP_to_RND(element_old));
 
-      if (element_old <= 0x27)
-       element_new = getMappedElement(EL_SP_START + element_old);
-      else if (element_old == 0x28)
-       element_new = EL_INVISIBLE_WALL;
-      else
-      {
+      if (element_new == EL_UNKNOWN)
        Error(ERR_WARN, "invalid element %d at position %d, %d",
              element_old, x, y);
 
-       element_new = EL_UNKNOWN;
-      }
-
       level->field[x][y] = element_new;
     }
   }
@@ -4469,22 +4453,61 @@ void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
        level->yamyam_content[i].e[x][y] = EL_EMPTY;
 }
 
+static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
+{
+  struct LevelInfo_SP *level_sp = level->native_sp_level;
+  struct DemoInfo_SP *demo = &level_sp->demo;
+  int i, j;
+
+  /* always start with reliable default values */
+  demo->is_available = FALSE;
+  demo->length = 0;
+
+  if (TAPE_IS_EMPTY(tape))
+    return;
+
+  demo->level_nr = tape.level_nr;      /* (currently not used) */
+
+  level_sp->header.DemoRandomSeed = tape.random_seed;
+
+  demo->length = 0;
+  for (i = 0; i < tape.length; i++)
+  {
+    int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
+    int demo_repeat = tape.pos[i].delay;
+
+    for (j = 0; j < demo_repeat / 16; j++)
+      demo->data[demo->length++] = 0xf0 | demo_action;
+
+    if (demo_repeat % 16)
+      demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
+  }
+
+  demo->data[demo->length++] = 0xff;
+
+  demo->is_available = TRUE;
+}
+
 static void setTapeInfoToDefaults();
 
 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
 {
   struct LevelInfo_SP *level_sp = level->native_sp_level;
   struct DemoInfo_SP *demo = &level_sp->demo;
+  char *filename = level->file_info.filename;
   int i;
 
   /* always start with reliable default values */
   setTapeInfoToDefaults();
 
+  if (!demo->is_available)
+    return;
+
   tape.level_nr = demo->level_nr;      /* (currently not used) */
   tape.length = demo->length - 1;      /* without "end of demo" byte */
   tape.random_seed = level_sp->header.DemoRandomSeed;
 
-  // tape.date = <SET FROM FILE DATE OF *.SP FILE>
+  TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
 
   for (i = 0; i < demo->length - 1; i++)
   {
@@ -6854,6 +6877,20 @@ void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
     CopyNativeLevel_SP_to_RND(level);
 }
 
+void SaveNativeLevel(struct LevelInfo *level)
+{
+  if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
+    char *filename = getLevelFilenameFromBasename(basename);
+
+    CopyNativeLevel_RND_to_SP(level);
+    CopyNativeTape_RND_to_SP(level);
+
+    SaveNativeLevel_SP(filename);
+  }
+}
+
 
 /* ------------------------------------------------------------------------- */
 /* functions for loading generic level                                       */
@@ -8415,6 +8452,79 @@ static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
   return chunk_size;
 }
 
+void LoadTape_SokobanSolution(char *filename)
+{
+  FILE *file;
+  int move_delay = TILESIZE / level.initial_player_stepsize[0];
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    tape.no_valid_file = TRUE;
+
+    return;
+  }
+
+  while (!feof(file))
+  {
+    unsigned char c = fgetc(file);
+
+    if (feof(file))
+      break;
+
+    switch (c)
+    {
+      case 'u':
+      case 'U':
+       tape.pos[tape.length].action[0] = MV_UP;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case 'd':
+      case 'D':
+       tape.pos[tape.length].action[0] = MV_DOWN;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case 'l':
+      case 'L':
+       tape.pos[tape.length].action[0] = MV_LEFT;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case 'r':
+      case 'R':
+       tape.pos[tape.length].action[0] = MV_RIGHT;
+       tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
+       tape.length++;
+       break;
+
+      case '\n':
+      case '\r':
+      case '\t':
+      case ' ':
+       /* ignore white-space characters */
+       break;
+
+      default:
+       tape.no_valid_file = TRUE;
+
+       Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
+
+       break;
+    }
+  }
+
+  fclose(file);
+
+  if (tape.no_valid_file)
+    return;
+
+  tape.length_seconds = GetTapeLength();
+}
+
 void LoadTapeFromFilename(char *filename)
 {
   char cookie[MAX_LINE_LEN];
@@ -8425,6 +8535,13 @@ void LoadTapeFromFilename(char *filename)
   /* always start with reliable default values */
   setTapeInfoToDefaults();
 
+  if (strSuffix(filename, ".sln"))
+  {
+    LoadTape_SokobanSolution(filename);
+
+    return;
+  }
+
   if (!(file = fopen(filename, MODE_READ)))
   {
     tape.no_valid_file = TRUE;
@@ -8469,6 +8586,7 @@ void LoadTapeFromFilename(char *filename)
 
       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
       fclose(file);
+
       return;
     }
 
@@ -8941,8 +9059,16 @@ void SaveScore(int nr)
 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3    5
 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4    6
 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL  7
+#define SETUP_TOKEN_SHORTCUT_TAPE_EJECT                8
+#define SETUP_TOKEN_SHORTCUT_TAPE_STOP         9
+#define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE                10
+#define SETUP_TOKEN_SHORTCUT_TAPE_RECORD       11
+#define SETUP_TOKEN_SHORTCUT_TAPE_PLAY         12
+#define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE      13
+#define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS       14
+#define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC       15
 
-#define NUM_SHORTCUT_SETUP_TOKENS              8
+#define NUM_SHORTCUT_SETUP_TOKENS              16
 
 /* player setup */
 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK                0
@@ -9087,6 +9213,14 @@ static struct TokenInfo shortcut_setup_tokens[] =
   { TYPE_KEY_X11, &ssi.focus_player[2],        "shortcut.focus_player_3"       },
   { TYPE_KEY_X11, &ssi.focus_player[3],        "shortcut.focus_player_4"       },
   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"    },
+  { TYPE_KEY_X11, &ssi.tape_eject,     "shortcut.tape_eject"           },
+  { TYPE_KEY_X11, &ssi.tape_stop,      "shortcut.tape_stop"            },
+  { TYPE_KEY_X11, &ssi.tape_pause,     "shortcut.tape_pause"           },
+  { TYPE_KEY_X11, &ssi.tape_record,    "shortcut.tape_record"          },
+  { TYPE_KEY_X11, &ssi.tape_play,      "shortcut.tape_play"            },
+  { TYPE_KEY_X11, &ssi.sound_simple,   "shortcut.sound_simple"         },
+  { TYPE_KEY_X11, &ssi.sound_loops,    "shortcut.sound_loops"          },
+  { TYPE_KEY_X11, &ssi.sound_music,    "shortcut.sound_music"          },
 };
 
 static struct TokenInfo player_setup_tokens[] =
@@ -9203,6 +9337,16 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
   si->shortcut.focus_player_all        = DEFAULT_KEY_FOCUS_PLAYER_ALL;
 
+  si->shortcut.tape_eject      = DEFAULT_KEY_TAPE_EJECT;
+  si->shortcut.tape_stop       = DEFAULT_KEY_TAPE_STOP;
+  si->shortcut.tape_pause      = DEFAULT_KEY_TAPE_PAUSE;
+  si->shortcut.tape_record     = DEFAULT_KEY_TAPE_RECORD;
+  si->shortcut.tape_play       = DEFAULT_KEY_TAPE_PLAY;
+
+  si->shortcut.sound_simple    = DEFAULT_KEY_SOUND_SIMPLE;
+  si->shortcut.sound_loops     = DEFAULT_KEY_SOUND_LOOPS;
+  si->shortcut.sound_music     = DEFAULT_KEY_SOUND_MUSIC;
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     si->input[i].use_joystick = FALSE;