rnd-20020402-1-src
[rocksndiamonds.git] / src / libgame / misc.c
index 572ff3601a8efce0d9eb8d119000025e9dc61724..6fe6949b45e3adc687938977ee4248b491449d5e 100644 (file)
@@ -1,38 +1,38 @@
 /***********************************************************
-*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+* Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-*  (c) 1995-98 Artsoft Entertainment                       *
-*              Holger Schemel                              *
-*              Oststrasse 11a                              *
-*              33604 Bielefeld                             *
-*              phone: ++49 +521 290471                     *
-*              email: aeglos@valinor.owl.de                *
+* (c) 1994-2001 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
 *----------------------------------------------------------*
-*  misc.c                                                  *
+* misc.c                                                   *
 ***********************************************************/
 
 #include <time.h>
 #include <sys/time.h>
 #include <sys/types.h>
+/*
+#include <sys/stat.h>
+*/
 #include <stdarg.h>
 #include <ctype.h>
+#include <string.h>
+#include <unistd.h>
 
-#include "libgame.h"
+#include "platform.h"
 
 #if !defined(PLATFORM_WIN32)
 #include <pwd.h>
 #include <sys/param.h>
 #endif
 
-#include "libgame.h"
-
-#include "main_TMP.h"
-
 #include "misc.h"
+#include "setup.h"
+#include "random.h"
 
-#if 0
-#include "joystick_TMP.h"
-#endif
 
 #if defined(PLATFORM_MSDOS)
 volatile unsigned long counter = 0;
@@ -417,37 +417,6 @@ char *getStringToLower(char *s)
   return s_copy;
 }
 
-void MarkTileDirty(int x, int y)
-{
-  int xx = redraw_x1 + x;
-  int yy = redraw_y1 + y;
-
-  if (!redraw[xx][yy])
-    redraw_tiles++;
-
-  redraw[xx][yy] = TRUE;
-  redraw_mask |= REDRAW_TILES;
-}
-
-void SetBorderElement()
-{
-  int x, y;
-
-  BorderElement = EL_LEERRAUM;
-
-  for(y=0; y<lev_fieldy && BorderElement == EL_LEERRAUM; y++)
-  {
-    for(x=0; x<lev_fieldx; x++)
-    {
-      if (!IS_MASSIVE(Feld[x][y]))
-       BorderElement = EL_BETON;
-
-      if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
-       x = lev_fieldx - 2;
-    }
-  }
-}
-
 void GetOptions(char *argv[])
 {
   char **options_left = &argv[1];
@@ -459,16 +428,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;
 
-  /* initialize some more global variables */
-  global.frames_per_second = 0;
-  global.fps_slowdown = FALSE;
-  global.fps_slowdown_factor = 1;
-
   while (*options_left)
   {
     char option_str[MAX_OPTION_LEN];
@@ -507,13 +474,17 @@ void GetOptions(char *argv[])
     {
       printf("Usage: %s [options] [server.name [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 graphics directory\n"
+            "  -m, --music <directory>       alternative graphics directory\n"
             "  -n, --network                 network multiplayer game\n"
-            "  -v, --verbose                 verbose mode\n",
-            program_name);
+            "      --serveronly              only start network server\n"
+            "  -v, --verbose                 verbose mode\n"
+            "      --debug                   display debugging information\n",
+            program.command_basename);
       exit(0);
     }
     else if (strncmp(option, "-display", option_len) == 0)
@@ -549,6 +520,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;
@@ -586,20 +584,40 @@ 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 = "";
   FILE *error = stderr;
+  char *newline = "\n";
 
   /* display warnings only when running in verbose mode */
   if (mode & ERR_WARN && !options.verbose)
     return;
 
 #if !defined(PLATFORM_UNIX)
+  newline = "\r\n";
+
   if ((error = openErrorFile()) == NULL)
   {
-    printf("Cannot write to error output file!\n");
-    CloseAllAndExit(1);
+    printf("Cannot write to error output file!%s", newline);
+    program.exit_function(1);
   }
 #endif
 
@@ -614,7 +632,7 @@ void Error(int mode, char *format, ...)
   {
     va_list ap;
 
-    fprintf(error, "%s%s: ", program_name, process_name);
+    fprintf(error, "%s%s: ", program.command_basename, process_name);
 
     if (mode & ERR_WARN)
       fprintf(error, "warning: ");
@@ -623,15 +641,16 @@ void Error(int mode, char *format, ...)
     vfprintf(error, format, ap);
     va_end(ap);
   
-    fprintf(error, "\n");
+    fprintf(error, "%s", newline);
   }
   
   if (mode & ERR_HELP)
-    fprintf(error, "%s: Try option '--help' for more information.\n",
-           program_name);
+    fprintf(error, "%s: Try option '--help' for more information.%s",
+           program.command_basename, newline);
 
   if (mode & ERR_EXIT)
-    fprintf(error, "%s%s: aborting\n", program_name, process_name);
+    fprintf(error, "%s%s: aborting%s",
+           program.command_basename, process_name, newline);
 
   if (error != stderr)
     fclose(error);
@@ -641,7 +660,7 @@ void Error(int mode, char *format, ...)
     if (mode & ERR_FROM_SERVER)
       exit(1);                         /* child process: normal exit */
     else
-      CloseAllAndExit(1);              /* main process: clean up stuff */
+      program.exit_function(1);                /* main process: clean up stuff */
   }
 }
 
@@ -669,6 +688,36 @@ void *checked_calloc(unsigned long size)
   return ptr;
 }
 
+void *checked_realloc(void *ptr, unsigned long size)
+{
+  ptr = realloc(ptr, size);
+
+  if (ptr == NULL)
+    Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", 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)
@@ -725,31 +774,52 @@ void putFile32BitInteger(FILE *file, int value, int byte_order)
   }
 }
 
-void getFileChunk(FILE *file, char *chunk_buffer, int *chunk_length,
-                 int byte_order)
+boolean getFileChunk(FILE *file, char *chunk_name, int *chunk_size,
+                    int byte_order)
 {
-  const int chunk_identifier_length = 4;
+  const int chunk_name_length = 4;
+
+  /* read chunk name */
+  fgets(chunk_name, chunk_name_length + 1, file);
 
-  /* read chunk identifier */
-  fgets(chunk_buffer, chunk_identifier_length + 1, file);
+  if (chunk_size != NULL)
+  {
+    /* read chunk size */
+    *chunk_size = getFile32BitInteger(file, byte_order);
+  }
 
-  /* read chunk length */
-  *chunk_length = getFile32BitInteger(file, byte_order);
+  return (feof(file) || ferror(file) ? FALSE : TRUE);
 }
 
-void putFileChunk(FILE *file, char *chunk_name, int chunk_length,
+void putFileChunk(FILE *file, char *chunk_name, int chunk_size,
                  int byte_order)
 {
-  /* write chunk identifier */
+  /* write chunk name */
   fputs(chunk_name, file);
 
-  /* write chunk length */
-  putFile32BitInteger(file, chunk_length, byte_order);
+  if (chunk_size >= 0)
+  {
+    /* write chunk size */
+    putFile32BitInteger(file, chunk_size, byte_order);
+  }
+}
+
+void ReadUnusedBytesFromFile(FILE *file, unsigned long bytes)
+{
+  while (bytes--)
+    fgetc(file);
+}
+
+void WriteUnusedBytesToFile(FILE *file, unsigned long bytes)
+{
+  while (bytes--)
+    fputc(0, file);
 }
 
 #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)
 {
@@ -936,6 +1006,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;
@@ -1037,6 +1127,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;
@@ -1060,236 +1158,95 @@ 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;
-}
+/* ========================================================================= */
+/* functions for checking filenames                                          */
+/* ========================================================================= */
 
-boolean validLevelSeries(struct LevelDirInfo *node)
+boolean FileIsGraphic(char *filename)
 {
-  return (node != NULL && !node->node_group && !node->parent_link);
-}
+  if (strlen(filename) > 4 &&
+      strcmp(&filename[strlen(filename) - 4], ".pcx") == 0)
+    return TRUE;
 
-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;
+  return FALSE;
 }
 
-struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *node)
+boolean FileIsSound(char *filename)
 {
-  if (node == NULL)
-    return NULL;
+  if (strlen(filename) > 4 &&
+      strcmp(&filename[strlen(filename) - 4], ".wav") == 0)
+    return TRUE;
 
-  if (node->node_parent == NULL)               /* top level group */
-    return leveldir_first;
-  else                                         /* sub level group */
-    return node->node_parent->node_group;
+  return FALSE;
 }
 
-int numLevelDirInfoInGroup(struct LevelDirInfo *node)
+boolean FileIsMusic(char *filename)
 {
-  return numLevelDirInfo(getLevelDirInfoFirstGroupEntry(node));
+  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))
+    return TRUE;
+
+  return FALSE;
 }
 
-int posLevelDirInfo(struct LevelDirInfo *node)
-{
-  struct LevelDirInfo *node_cmp = getLevelDirInfoFirstGroupEntry(node);
-  int pos = 0;
-
-  while (node_cmp)
-  {
-    if (node_cmp == node)
-      return pos;
+/* ========================================================================= */
+/* functions only needed for non-Unix (non-command-line) systems */
+/* ========================================================================= */
 
-    pos++;
-    node_cmp = node_cmp->next;
-  }
+#if !defined(PLATFORM_UNIX)
 
-  return 0;
-}
+#define ERROR_FILENAME         "error.out"
 
-struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *node, int pos)
+void initErrorFile()
 {
-  struct LevelDirInfo *node_default = node;
-  int pos_cmp = 0;
-
-  while (node)
-  {
-    if (pos_cmp == pos)
-      return node;
+  char *filename;
 
-    pos_cmp++;
-    node = node->next;
-  }
+  InitUserDataDirectory();
 
-  return node_default;
+  filename = getPath2(getUserDataDir(), ERROR_FILENAME);
+  unlink(filename);
+  free(filename);
 }
 
-struct LevelDirInfo *getLevelDirInfoFromFilenameExt(struct LevelDirInfo *node,
-                                                   char *filename)
+FILE *openErrorFile()
 {
-  if (filename == NULL)
-    return NULL;
+  char *filename;
+  FILE *error_file;
 
-  while (node)
-  {
-    if (node->node_group)
-    {
-      struct LevelDirInfo *node_group;
-
-      node_group = getLevelDirInfoFromFilenameExt(node->node_group, filename);
+  filename = getPath2(getUserDataDir(), ERROR_FILENAME);
+  error_file = fopen(filename, MODE_APPEND);
+  free(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;
+  return error_file;
 }
 
-struct LevelDirInfo *getLevelDirInfoFromFilename(char *filename)
+void dumpErrorFile()
 {
-  return getLevelDirInfoFromFilenameExt(leveldir_first, filename);
-}
+  char *filename;
+  FILE *error_file;
 
-void dumpLevelDirInfo(struct LevelDirInfo *node, int depth)
-{
-  int i;
+  filename = getPath2(getUserDataDir(), ERROR_FILENAME);
+  error_file = fopen(filename, MODE_READ);
+  free(filename);
 
-  while (node)
+  if (error_file != NULL)
   {
-    for (i=0; i<depth * 3; i++)
-      printf(" ");
+    while (!feof(error_file))
+      fputc(fgetc(error_file), stderr);
 
-    printf("filename == '%s'\n", node->filename);
-
-    if (node->node_group != NULL)
-      dumpLevelDirInfo(node->node_group, depth + 1);
-
-    node = node->next;
+    fclose(error_file);
   }
 }
-
-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;
-}
+#endif
 
 
-/* ------------------------------------------------------------------------- */
+/* ========================================================================= */
 /* the following is only for debugging purpose and normally not used         */
-/* ------------------------------------------------------------------------- */
+/* ========================================================================= */
 
 #define DEBUG_NUM_TIMESTAMPS   3