rnd-20020318-3-src
[rocksndiamonds.git] / src / libgame / misc.c
index b72fa146cd684385f84190b654cfd66b8a784233..b3edef75f1bbf367d8a8d244ad6ef3b67a3ac8c8 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Artsoft Retro-Game Library                               *
 *----------------------------------------------------------*
-* (c) 1994-2000 Artsoft Entertainment                      *
+* (c) 1994-2001 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -473,7 +473,8 @@ void GetOptions(char *argv[])
             "  -l, --level directory         alternative level directory\n"
             "  -s, --serveronly              only start network server\n"
             "  -n, --network                 network multiplayer game\n"
-            "  -v, --verbose                 verbose mode\n",
+            "  -v, --verbose                 verbose mode\n"
+            "      --debug                   display debugging information\n",
             program.command_basename);
       exit(0);
     }
@@ -551,15 +552,18 @@ 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");
+    printf("Cannot write to error output file!%s", newline);
     program.exit_function(1);
   }
 #endif
@@ -584,15 +588,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.command_basename);
+    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.command_basename, process_name);
+    fprintf(error, "%s%s: aborting%s",
+           program.command_basename, process_name, newline);
 
   if (error != stderr)
     fclose(error);
@@ -696,26 +701,46 @@ 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
@@ -1262,10 +1287,41 @@ inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
 /* 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
+#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 USERDATA_DIR_MODE      (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
+
+#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)
 {
@@ -1282,20 +1338,350 @@ char *getUserDataDir(void)
   return userdata_dir;
 }
 
-void createDirectory(char *dir, char *text)
+char *getSetupDir()
+{
+  return getUserDataDir();
+}
+
+void createDirectory(char *dir, char *text, int permission_class)
 {
+#if defined(PLATFORM_UNIX)
+  /* 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 = umask(0);
+  mode_t group_umask = ~(dir_mode & S_IRWXG);
+  umask(normal_umask & group_umask);
+#endif
+
   if (access(dir, F_OK) != 0)
 #if defined(PLATFORM_WIN32)
     if (mkdir(dir) != 0)
 #else
-    if (mkdir(dir, USERDATA_DIR_MODE) != 0)
+    if (mkdir(dir, dir_mode) != 0)
 #endif
       Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
+
+#if defined(PLATFORM_UNIX)
+  umask(normal_umask);         /* reset normal umask */
+#endif
 }
 
 void InitUserDataDirectory()
 {
-  createDirectory(getUserDataDir(), "user data");
+  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)
+    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);
+}
+
+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 (strcmp(setup_file_list->value, identifier) != 0)
+    {
+      Error(ERR_WARN, "configuration file has wrong version");
+      return;
+    }
+    else
+      return;
+  }
+
+  if (setup_file_list->next)
+    checkSetupFileListIdentifier(setup_file_list->next, identifier);
+  else
+  {
+    Error(ERR_WARN, "configuration file has no version information");
+    return;
+  }
 }