added event actions (by simulating keyboard input) for global animations
[rocksndiamonds.git] / src / libgame / misc.c
index 470fc057c171ed428d309b38b30ab0d80f6ff67a..6432523e41ce5c5755a6e385c28abd30995b5aee 100644 (file)
@@ -571,7 +571,7 @@ char *getLoginName()
     if (GetUserName(login_name, &buffer_size) == 0)
       strcpy(login_name, ANONYMOUS_NAME);
   }
-#else
+#elif defined(PLATFORM_UNIX) && !defined(PLATFORM_ANDROID)
   if (login_name == NULL)
   {
     struct passwd *pwd;
@@ -581,6 +581,8 @@ char *getLoginName()
     else
       login_name = getStringCopy(pwd->pw_name);
   }
+#else
+  login_name = ANONYMOUS_NAME;
 #endif
 
   return login_name;
@@ -680,66 +682,18 @@ char *getBasePath(char *filename)
   char *basepath = getStringCopy(filename);
   char *last_separator = getLastPathSeparatorPtr(basepath);
 
-  if (last_separator != NULL)
-    *last_separator = '\0';    /* separator found: strip basename */
-  else
-    basepath = ".";            /* no separator found: use current path */
-
-  return basepath;
-}
-
-static char *getProgramMainDataPath()
-{
-  char *main_data_path = getStringCopy(program.command_basepath);
-
-#if defined(PLATFORM_MACOSX)
-  static char *main_data_binary_subdir = NULL;
-  static char *main_data_files_subdir = NULL;
-
-  if (main_data_binary_subdir == NULL)
+  /* if no separator was found, use current directory */
+  if (last_separator == NULL)
   {
-    main_data_binary_subdir = checked_malloc(strlen(program.program_title) + 1 +
-                                            strlen("app") + 1 +
-                                            strlen(MAC_APP_BINARY_SUBDIR) + 1);
+    free(basepath);
 
-    sprintf(main_data_binary_subdir, "%s.app/%s",
-           program.program_title, MAC_APP_BINARY_SUBDIR);
+    return getStringCopy(".");
   }
 
-  if (main_data_files_subdir == NULL)
-  {
-    main_data_files_subdir = checked_malloc(strlen(program.program_title) + 1 +
-                                           strlen("app") + 1 +
-                                           strlen(MAC_APP_FILES_SUBDIR) + 1);
+  /* separator found: strip basename */
+  *last_separator = '\0';
 
-    sprintf(main_data_files_subdir, "%s.app/%s",
-           program.program_title, MAC_APP_FILES_SUBDIR);
-  }
-
-  if (strSuffix(main_data_path, main_data_binary_subdir))
-  {
-    char *main_data_path_old = main_data_path;
-
-    // cut relative path to Mac OS X application binary directory from path
-    main_data_path[strlen(main_data_path) -
-                  strlen(main_data_binary_subdir)] = '\0';
-
-    // cut trailing path separator from path (but not if path is root directory)
-    if (strSuffix(main_data_path, "/") && !strEqual(main_data_path, "/"))
-      main_data_path[strlen(main_data_path) - 1] = '\0';
-
-    // replace empty path with current directory
-    if (strEqual(main_data_path, ""))
-      main_data_path = ".";
-
-    // add relative path to Mac OS X application resources directory to path
-    main_data_path = getPath2(main_data_path, main_data_files_subdir);
-
-    free(main_data_path_old);
-  }
-#endif
-
-  return main_data_path;
+  return basepath;
 }
 
 
@@ -955,8 +909,8 @@ void GetOptions(int argc, char *argv[],
                void (*print_usage_function)(void),
                void (*print_version_function)(void))
 {
-  char *ro_base_path = RO_BASE_PATH;
-  char *rw_base_path = RW_BASE_PATH;
+  char *ro_base_path = getProgramMainDataPath(argv[0], RO_BASE_PATH);
+  char *rw_base_path = getProgramMainDataPath(argv[0], RW_BASE_PATH);
   char **argvplus = checked_calloc((argc + 1) * sizeof(char **));
   char **options_left = &argvplus[1];
 
@@ -964,16 +918,6 @@ void GetOptions(int argc, char *argv[],
   while (argc--)
     argvplus[argc] = argv[argc];
 
-  /* if the program is configured to start from current directory (default),
-     determine program package directory from program binary (some versions
-     of KDE/Konqueror and Mac OS X (especially "Mavericks") apparently do not
-     set the current working directory to the program package directory) */
-
-  if (strEqual(ro_base_path, "."))
-    ro_base_path = getProgramMainDataPath();
-  if (strEqual(rw_base_path, "."))
-    rw_base_path = getProgramMainDataPath();
-
   /* initialize global program options */
   options.server_host = NULL;
   options.server_port = 0;
@@ -985,6 +929,7 @@ void GetOptions(int argc, char *argv[],
   options.sounds_directory   = getPath2(ro_base_path, SOUNDS_DIRECTORY);
   options.music_directory    = getPath2(ro_base_path, MUSIC_DIRECTORY);
   options.docs_directory     = getPath2(ro_base_path, DOCS_DIRECTORY);
+  options.conf_directory     = getPath2(ro_base_path, CONF_DIRECTORY);
 
   options.execute_command = NULL;
   options.special_flags = NULL;
@@ -1002,6 +947,12 @@ void GetOptions(int argc, char *argv[],
   if (*options_left == NULL)   /* no options given -- enable verbose mode */
     options.verbose = TRUE;
 #endif
+#endif
+
+#if DEBUG
+#if defined(PLATFORM_ANDROID)
+  options.debug = TRUE;
+#endif
 #endif
 
   while (*options_left)
@@ -1063,6 +1014,7 @@ void GetOptions(int argc, char *argv[],
       options.sounds_directory   = getPath2(ro_base_path, SOUNDS_DIRECTORY);
       options.music_directory    = getPath2(ro_base_path, MUSIC_DIRECTORY);
       options.docs_directory     = getPath2(ro_base_path, DOCS_DIRECTORY);
+      options.conf_directory     = getPath2(ro_base_path, CONF_DIRECTORY);
     }
     else if (strncmp(option, "-levels", option_len) == 0)
     {
@@ -1210,6 +1162,10 @@ void Error(int mode, char *format, ...)
                      ANDROID_LOG_UNKNOWN);
 #endif
 
+  /* display debug messages only when running in debug mode */
+  if (mode & ERR_DEBUG && !options.debug)
+    return;
+
   /* display warnings only when running in verbose mode */
   if (mode & ERR_WARN && !options.verbose)
     return;
@@ -1486,28 +1442,28 @@ int putFileChunk(FILE *file, char *chunk_name, int chunk_size,
 
 int getFileVersion(File *file)
 {
+  int version_super = getByteFromFile(file);
   int version_major = getByteFromFile(file);
   int version_minor = getByteFromFile(file);
   int version_patch = getByteFromFile(file);
-  int version_build = getByteFromFile(file);
 
-  return VERSION_IDENT(version_major, version_minor, version_patch,
-                      version_build);
+  return VERSION_IDENT(version_super, version_major, version_minor,
+                      version_patch);
 }
 
 int putFileVersion(FILE *file, int version)
 {
   if (file != NULL)
   {
+    int version_super = VERSION_SUPER(version);
     int version_major = VERSION_MAJOR(version);
     int version_minor = VERSION_MINOR(version);
     int version_patch = VERSION_PATCH(version);
-    int version_build = VERSION_BUILD(version);
 
+    fputc(version_super, file);
     fputc(version_major, file);
     fputc(version_minor, file);
     fputc(version_patch, file);
-    fputc(version_build, file);
   }
 
   return 4;
@@ -1611,8 +1567,14 @@ void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
     { KSYM_Page_Down,  "XK_Page_Down",         "page down" },
 
 #if defined(TARGET_SDL2)
+    { KSYM_Select,     "XK_Select",            "select" },
     { KSYM_Menu,       "XK_Menu",              "menu" },        /* menu key */
     { KSYM_Back,       "XK_Back",              "back" },        /* back key */
+    { KSYM_PlayPause,  "XK_PlayPause",         "play/pause" },
+#if defined(PLATFORM_ANDROID)
+    { KSYM_Rewind,     "XK_Rewind",            "rewind" },
+    { KSYM_FastForward,        "XK_FastForward",       "fast forward" },
+#endif
 #endif
 
     /* ASCII 0x20 to 0x40 keys (except numbers) */
@@ -2173,6 +2135,8 @@ void dumpList(ListNode *node_first)
 /* functions for file handling                                               */
 /* ------------------------------------------------------------------------- */
 
+#define MAX_BUFFER_SIZE                        4096
+
 File *openFile(char *filename, char *mode)
 {
   File *file = checked_calloc(sizeof(File));
@@ -2255,6 +2219,11 @@ size_t readFile(File *file, void *buffer, size_t item_size, size_t num_items)
   return fread(buffer, item_size, num_items, file->file);
 }
 
+size_t writeFile(File *file, void *buffer, size_t item_size, size_t num_items)
+{
+  return fwrite(buffer, item_size, num_items, file->file);
+}
+
 int seekFile(File *file, long offset, int whence)
 {
 #if defined(PLATFORM_ANDROID)
@@ -2324,6 +2293,36 @@ char *getStringFromFile(File *file, char *line, int size)
   return fgets(line, size, file->file);
 }
 
+int copyFile(char *filename_from, char *filename_to)
+{
+  File *file_from, *file_to;
+
+  if ((file_from = openFile(filename_from, MODE_READ)) == NULL)
+  {
+    return -1;
+  }
+
+  if ((file_to = openFile(filename_to, MODE_WRITE)) == NULL)
+  {
+    closeFile(file_from);
+
+    return -1;
+  }
+
+  while (!checkEndOfFile(file_from))
+  {
+    byte buffer[MAX_BUFFER_SIZE];
+    size_t bytes_read = readFile(file_from, buffer, 1, MAX_BUFFER_SIZE);
+
+    writeFile(file_to, buffer, 1, bytes_read);
+  }
+
+  closeFile(file_from);
+  closeFile(file_to);
+
+  return 0;
+}
+
 
 /* ------------------------------------------------------------------------- */
 /* functions for directory handling                                          */
@@ -2655,6 +2654,30 @@ char *get_special_base_token(struct ArtworkListInfo *artwork_info, char *token)
     { "global.anim_6"  },
     { "global.anim_7"  },
     { "global.anim_8"  },
+    { "global.anim_9"  },
+    { "global.anim_10" },
+    { "global.anim_11" },
+    { "global.anim_12" },
+    { "global.anim_13" },
+    { "global.anim_14" },
+    { "global.anim_15" },
+    { "global.anim_16" },
+    { "global.anim_17" },
+    { "global.anim_18" },
+    { "global.anim_19" },
+    { "global.anim_20" },
+    { "global.anim_21" },
+    { "global.anim_22" },
+    { "global.anim_23" },
+    { "global.anim_24" },
+    { "global.anim_25" },
+    { "global.anim_26" },
+    { "global.anim_27" },
+    { "global.anim_28" },
+    { "global.anim_29" },
+    { "global.anim_30" },
+    { "global.anim_31" },
+    { "global.anim_32" },
 
     { NULL             }
   };
@@ -2716,25 +2739,6 @@ static boolean string_has_parameter(char *s, char *s_contained)
   return string_has_parameter(substring, s_contained);
 }
 
-static boolean string_has_anim_parameter(char *s, char *s_contained)
-{
-  char *s_copy = getStringCopy(s);
-  boolean has_parameter = FALSE;
-  int len_s_copy = strlen(s_copy);
-  int i;
-
-  // replace all "anim" and "part" numbers with 'X'
-  for (i = 0; i < len_s_copy; i++)
-    if (s_copy[i] >= '1' && s_copy[i] <= '8')
-      s_copy[i] = 'X';
-
-  has_parameter = string_has_parameter(s_copy, s_contained);
-
-  checked_free(s_copy);
-
-  return has_parameter;
-}
-
 int get_anim_parameter_value(char *s)
 {
   char *pattern_1 = "click:anim_";
@@ -2745,47 +2749,88 @@ int get_anim_parameter_value(char *s)
 
   matching_char = strstr(s_ptr, pattern_1);
   if (matching_char == NULL)
-    return result;
+    return ANIM_EVENT_NONE;
 
   s_ptr = matching_char + strlen(pattern_1);
-  if (*s_ptr == '\0')
-    return result;
 
-  // check for "click:anim_X"
-  if (*s_ptr >= '1' && *s_ptr <= '8')
+  // check for main animation number ("anim_X" or "anim_XX")
+  if (*s_ptr >= '0' && *s_ptr <= '9')
   {
-    result |= ANIM_EVENT_CLICK_ANIM_1 << (*s_ptr - '1');
-    s_ptr++;
+    int gic_anim_nr = (*s_ptr++ - '0');
 
-    // check for "click:anim_X.part_X"
-    if (strPrefix(s_ptr, pattern_2))
+    if (*s_ptr >= '0' && *s_ptr <= '9')
+      gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
+
+    if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
+      return ANIM_EVENT_NONE;
+
+    result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
+  }
+  else
+  {
+    // invalid main animation number specified
+
+    return ANIM_EVENT_NONE;
+  }
+
+  // check for animation part number ("part_X" or "part_XX") (optional)
+  if (strPrefix(s_ptr, pattern_2))
+  {
+    s_ptr += strlen(pattern_2);
+
+    if (*s_ptr >= '0' && *s_ptr <= '9')
     {
-      s_ptr += strlen(pattern_2);
+      int gic_part_nr = (*s_ptr++ - '0');
 
-      if (*s_ptr >= '1' && *s_ptr <= '8')
-      {
-       result |= ANIM_EVENT_CLICK_PART_1 << (*s_ptr - '1');
-       s_ptr++;
-      }
+      if (*s_ptr >= '0' && *s_ptr <= '9')
+       gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
+
+      if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
+       return ANIM_EVENT_NONE;
+
+      result |= gic_part_nr << ANIM_EVENT_PART_BIT;
     }
     else
     {
-      // no "part_X" specified -- trigger by click on any part
-      result |= ANIM_EVENT_CLICK_PART_ALL;
+      // invalid animation part number specified
+
+      return ANIM_EVENT_NONE;
     }
   }
 
   /* discard result if next character is neither delimiter nor whitespace */
   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
        *s_ptr == ' ' || *s_ptr == '\t'))
-    return get_anim_parameter_value(s_ptr);
+    return ANIM_EVENT_NONE;
 
-  /* check if string contains another parameter string after a comma */
-  s_ptr = strchr(s_ptr, ',');
-  if (s_ptr == NULL)   /* string does not contain a comma */
-    return result;
+  return result;
+}
+
+int get_anim_action_parameter_value(char *token)
+{
+  int result = getImageIDFromToken(token);
 
-  return result | get_anim_parameter_value(s_ptr);
+  if (result == -1)
+  {
+    char *gfx_token = getStringCat2("gfx.", token);
+
+    result = getImageIDFromToken(gfx_token);
+
+    checked_free(gfx_token);
+  }
+
+  if (result == -1)
+  {
+    Key key = getKeyFromX11KeyName(token);
+
+    if (key != KSYM_UNDEFINED)
+      result = -(int)key;
+  }
+
+  if (result == -1)
+    result = ANIM_EVENT_ACTION_NONE;
+
+  return result;
 }
 
 int get_parameter_value(char *value_raw, char *suffix, int type)
@@ -2809,7 +2854,8 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              strEqual(value, "middle") ? POS_MIDDLE :
              strEqual(value, "lower")  ? POS_LOWER :
              strEqual(value, "bottom") ? POS_BOTTOM :
-             strEqual(value, "any")    ? POS_ANY : POS_UNDEFINED);
+             strEqual(value, "any")    ? POS_ANY :
+             strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
   }
   else if (strEqual(suffix, ".align"))
   {
@@ -2860,14 +2906,18 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
     result = ANIM_EVENT_DEFAULT;
 
     if (string_has_parameter(value, "any"))
-      result |= ANIM_EVENT_CLICK_ANY;
+      result |= ANIM_EVENT_ANY;
 
     if (string_has_parameter(value, "click"))
-      result |= ANIM_EVENT_CLICK_SELF;
+      result |= ANIM_EVENT_SELF;
 
-    if (string_has_anim_parameter(value, "click:anim_X") ||
-       string_has_anim_parameter(value, "click:anim_X.part_X"))
-      result |= get_anim_parameter_value(value);
+    // add optional "click:anim_X" or "click:anim_X.part_X" parameter
+    result |= get_anim_parameter_value(value);
+  }
+  else if (strEqual(suffix, ".init_event_action") ||
+          strEqual(suffix, ".anim_event_action"))
+  {
+    result = get_anim_action_parameter_value(value_raw);
   }
   else if (strEqual(suffix, ".class"))
   {
@@ -2883,6 +2933,15 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
 
     if (string_has_parameter(value, "inner_corners"))
       result |= STYLE_INNER_CORNERS;
+
+    if (string_has_parameter(value, "reverse"))
+      result |= STYLE_REVERSE;
+
+    if (string_has_parameter(value, "passthrough_clicks"))
+      result |= STYLE_PASSTHROUGH;
+
+    if (string_has_parameter(value, "multiple_actions"))
+      result |= STYLE_MULTIPLE_ACTIONS;
   }
   else if (strEqual(suffix, ".fade_mode"))
   {