/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * * $Id$ * * Copyright (C) 2007 Jonathan Gordon * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include #include "string.h" #include #include "settings.h" #include "debug.h" #include "lang.h" #include "kernel.h" #include "plugin.h" #include "filetypes.h" #include "screens.h" #include "dir.h" #include "file.h" #include "splash.h" #include "core_alloc.h" #include "icons.h" #include "logf.h" /* max filetypes (plugins & icons stored here) */ #define MAX_FILETYPES 192 /* max viewer plugins */ #define MAX_VIEWERS 56 /* a table for the known file types */ static const struct filetype inbuilt_filetypes[] = { { "mp3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mp2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mpa", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mp1", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "ogg", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "oga", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "wma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "wmv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "asf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "wav", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "flac",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "ac3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "a52", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mpc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "wv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mod", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mpga", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "spx" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "opus",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "sid", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "adx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "nsf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "nsfe",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "spc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "ape", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "sap" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "rm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "ra", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "rmvb",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "cmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "cm3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "cmr", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "cms", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "dmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "dlt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mpt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mpd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "rmt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "tmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "tm8", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "tm2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "oma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "aa3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "at3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "mmf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "au", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "snd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "ay", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vtx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "gbs", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "hes", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "sgc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "kss", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "aac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "cfg", FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG }, { "wps", FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS }, #ifdef HAVE_REMOTE_LCD { "rwps",FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS }, #endif #if CONFIG_TUNER { "fmr", FILE_ATTR_FMR, Icon_Preset, LANG_FMR }, { "fms", FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS }, #endif { "lng", FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE }, { "rock",FILE_ATTR_ROCK,Icon_Plugin, VOICE_EXT_ROCK }, { "lua", FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK }, { "opx", FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK }, { "fnt", FILE_ATTR_FONT,Icon_Font, VOICE_EXT_FONT }, { "kbd", FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD }, { "bmark",FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK }, { "cue", FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET }, { "sbs", FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS }, #ifdef HAVE_REMOTE_LCD { "rsbs", FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS }, #if CONFIG_TUNER { "rfms", FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS }, #endif #endif #ifdef BOOTFILE_EXT { BOOTFILE_EXT, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, #endif #ifdef BOOTFILE_EXT2 { BOOTFILE_EXT2, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ }, #endif }; void tree_get_filetypes(const struct filetype** types, int* count) { *types = inbuilt_filetypes; *count = sizeof(inbuilt_filetypes) / sizeof(*inbuilt_filetypes); } #define ROCK_EXTENSION "rock" struct file_type { enum themable_icons icon; /* the icon which shall be used for it, NOICON if unknown */ unsigned char attr; /* FILE_ATTR_MASK >> 8 */ char* plugin; /* Which plugin to use, NULL if unknown, or builtin */ char* extension; /* NULL for none */ }; static struct file_type filetypes[MAX_FILETYPES]; static int custom_filetype_icons[MAX_FILETYPES]; static bool custom_icons_loaded = false; #ifdef HAVE_LCD_COLOR static int custom_colors[MAX_FILETYPES]; #endif struct filetype_unknown { int icon; #ifdef HAVE_LCD_COLOR int color; #endif }; static struct filetype_unknown unknown_file = { .icon = Icon_NOICON, #ifdef HAVE_LCD_COLOR .color = -1, #endif }; /* index array to filetypes used in open with list. */ static int viewers[MAX_VIEWERS]; static int filetype_count = 0; static unsigned char highest_attr = 0; static int viewer_count = 0; static int strdup_handle, strdup_cur_idx; static size_t strdup_bufsize; static int move_callback(int handle, void* current, void* new) { /*could compare to strdup_handle, but ops is only used once */ (void)handle; size_t diff = new - current; #define FIX_PTR(x) \ { if ((void*)x >= current && (void*)x < (current+strdup_bufsize)) x+= diff; } for(int i = 0; i < filetype_count; i++) { FIX_PTR(filetypes[i].extension); FIX_PTR(filetypes[i].plugin); } return BUFLIB_CB_OK; } static struct buflib_callbacks ops = { .move_callback = move_callback, .shrink_callback = NULL, }; static char *filetypes_strdup(char* string) { char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; strdup_cur_idx += strlcpy(buffer, string, strdup_bufsize-strdup_cur_idx)+1; return buffer; } static char *filetypes_store_plugin(char *plugin, int n) { int i; /* if the plugin is in the list already, use it. */ for (i=0; i 0) { if (!settings_parseline(buffer, &ext, &color)) continue; if (!strcasecmp(ext, "folder")) { hex_to_rgb(color, &custom_colors[0]); continue; } if (!strcasecmp(ext, "???")) { hex_to_rgb(color, &unknown_file.color); continue; } i = find_extension(ext); if (i >= 0) hex_to_rgb(color, &custom_colors[i]); } close(fd); } #endif void read_viewer_theme_file(void) { char buffer[MAX_PATH]; int fd; char *ext, *icon; int i; int *icon_dest; global_status.viewer_icon_count = 0; custom_icons_loaded = false; custom_filetype_icons[0] = Icon_Folder; for (i=1; i 0) { if (!settings_parseline(buffer, &ext, &icon)) continue; i = find_extension(ext); if (i >= 0) icon_dest = &custom_filetype_icons[i]; else if (!strcmp(ext, "???")) icon_dest = &unknown_file.icon; else icon_dest = NULL; if (icon_dest) { if (*icon == '*') *icon_dest = atoi(icon+1); else if (*icon == '-') *icon_dest = Icon_NOICON; else if (*icon >= '0' && *icon <= '9') { int number = atoi(icon); if (number > global_status.viewer_icon_count) global_status.viewer_icon_count++; *icon_dest = Icon_Last_Themeable + number; } } } close(fd); custom_icons_loaded = true; } static void read_viewers_config(void) { int fd = open(VIEWERS_CONFIG, O_RDONLY); if(fd < 0) return; off_t filesz = filesize(fd); if(filesz <= 0) goto out; /* estimate bufsize with the filesize, will not be larger */ strdup_bufsize = (size_t)filesz; strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops); if(strdup_handle <= 0) goto out; read_config(fd); core_shrink(strdup_handle, core_get_data(strdup_handle), strdup_cur_idx); out: close(fd); } void filetype_init(void) { /* set the directory item first */ filetypes[0].extension = NULL; filetypes[0].plugin = NULL; filetypes[0].attr = 0; filetypes[0].icon = Icon_Folder; viewer_count = 0; filetype_count = 1; read_builtin_types(); read_viewers_config(); read_viewer_theme_file(); #ifdef HAVE_LCD_COLOR read_color_theme_file(); #endif } /* remove all white spaces from string */ static void rm_whitespaces(char* str) { char *s = str; while (*str) { if (!isspace(*str)) { *s = *str; s++; } str++; } *s = '\0'; } static void read_builtin_types(void) { int count = sizeof(inbuilt_filetypes)/sizeof(*inbuilt_filetypes), i; for(i=0; i>8; if (filetypes[filetype_count].attr > highest_attr) highest_attr = filetypes[filetype_count].attr; filetypes[filetype_count].icon = inbuilt_filetypes[i].icon; filetype_count++; } } static void read_config(int fd) { char line[64], *s, *e; char *extension, *plugin; /* config file is in the format ,, ignore line if either of the first two are missing */ while (read_line(fd, line, sizeof line) > 0) { if (filetype_count >= MAX_FILETYPES) { splash(HZ, ID2P(LANG_FILETYPES_FULL)); break; } rm_whitespaces(line); /* get the extension */ s = line; e = strchr(s, ','); if (!e) continue; *e = '\0'; extension = s; /* get the plugin */ s = e+1; e = strchr(s, ','); if (!e) continue; *e = '\0'; plugin = s; if (!strcmp("???", extension)) { /* get the icon */ s = e+1; if (*s == '*') unknown_file.icon = atoi(s+1); else if (*s == '-') unknown_file.icon = Icon_NOICON; else if (*s >= '0' && *s <= '9') unknown_file.icon = Icon_Last_Themeable + atoi(s); continue; } /* ok, store this plugin/extension, check icon after */ struct file_type *file_type = &filetypes[filetype_count]; file_type->extension = filetypes_strdup(extension); file_type->plugin = filetypes_store_plugin(plugin, filetype_count); file_type->attr = highest_attr +1; file_type->icon = Icon_Questionmark; highest_attr++; /* get the icon */ s = e+1; if (*s == '*') file_type->icon = atoi(s+1); else if (*s == '-') file_type->icon = Icon_NOICON; else if (*s >= '0' && *s <= '9') file_type->icon = Icon_Last_Themeable + atoi(s); filetype_count++; } } int filetype_get_attr(const char* file) { char *extension = strrchr(file, '.'); int i; if (!extension) return 0; extension++; i = find_extension(extension); if (i >= 0) return (filetypes[i].attr<<8)&FILE_ATTR_MASK; return 0; } static int find_attr(int attr) { int i; /* skip the directory item */ if ((attr & ATTR_DIRECTORY)==ATTR_DIRECTORY) return 0; for (i=1; i>8) == filetypes[i].attr) return i; } return -1; } #ifdef HAVE_LCD_COLOR int filetype_get_color(const char * name, int attr) { char *extension; int i; if ((attr & ATTR_DIRECTORY)==ATTR_DIRECTORY) return custom_colors[0]; extension = strrchr(name, '.'); if (!extension) return unknown_file.color; extension++; i = find_extension(extension); if (i >= 0) return custom_colors[i]; return unknown_file.color; } #endif int filetype_get_icon(int attr) { int index = find_attr(attr); if (index < 0) return unknown_file.icon; if (custom_icons_loaded) return custom_filetype_icons[index]; return filetypes[index].icon; } char* filetype_get_plugin(const struct entry* file, char *buffer, size_t buffer_len) { int index = find_attr(file->attr); if (index < 0 || !buffer) return NULL; struct file_type *ft_indexed = &filetypes[index]; /* attempt to find a suitable viewer by file extension */ if(ft_indexed->plugin == NULL && ft_indexed->extension != NULL) { struct file_type *ft; int i = filetype_count; while (--i > index) { ft = &filetypes[i]; if (ft->plugin == NULL || ft->extension == NULL) continue; else if (strcmp(ft->extension, ft_indexed->extension) == 0) { /*splashf(HZ*3, "Found %d %s %s", i, ft->extension, ft->plugin);*/ ft_indexed = ft; break; } } } if (ft_indexed->plugin == NULL) return NULL; snprintf(buffer, buffer_len, "%s/%s." ROCK_EXTENSION, PLUGIN_DIR, ft_indexed->plugin); return buffer; } bool filetype_supported(int attr) { return find_attr(attr) >= 0; } /**** Open With Screen ****/ struct cb_data { const char *current_file; }; static enum themable_icons openwith_get_icon(int selected_item, void * data) { (void)data; return filetypes[viewers[selected_item]].icon; } static const char* openwith_get_name(int selected_item, void * data, char * buffer, size_t buffer_len) { (void)data; (void)buffer; (void)buffer_len; const char *s = strrchr(filetypes[viewers[selected_item]].plugin, '/'); if (s) return s+1; else return filetypes[viewers[selected_item]].plugin; } static int openwith_get_talk(int selected_item, void * data) { (void)data; char viewer_filename[MAX_FILENAME]; snprintf(viewer_filename, MAX_FILENAME, "%s." ROCK_EXTENSION, filetypes[viewers[selected_item]].plugin); talk_file_or_spell(PLUGIN_DIR, viewer_filename, NULL, false); return 0; } int filetype_list_viewers(const char* current_file) { struct simplelist_info info; simplelist_info_init(&info, str(LANG_ONPLAY_OPEN_WITH), viewer_count, NULL); info.get_name = openwith_get_name; info.get_icon = global_settings.show_icons?openwith_get_icon:NULL; info.get_talk = openwith_get_talk; int ret = simplelist_show_list(&info); if (info.selection >= 0) /* run user selected viewer */ { char plugin[MAX_PATH]; int i = viewers[info.selection]; snprintf(plugin, MAX_PATH, "%s/%s." ROCK_EXTENSION, PLUGIN_DIR, filetypes[i].plugin); ret = plugin_load(plugin, current_file); } return ret; } int filetype_load_plugin(const char* plugin, const char* file) { int i; char plugin_name[MAX_PATH]; char *s; for (i=0;i= filetype_count) return PLUGIN_ERROR; snprintf(plugin_name, MAX_PATH, "%s/%s." ROCK_EXTENSION, PLUGIN_DIR, filetypes[i].plugin); return plugin_load(plugin_name, file); }