rockbox/apps/filetypes.c

826 lines
20 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
*
* $Id$
*
* Copyright (C) 2004 Henrik Backe
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "sprintf.h"
#include "settings.h"
#include "debug.h"
#include "lang.h"
#include "language.h"
#include "kernel.h"
#include "plugin.h"
#include "filetypes.h"
#include "screens.h"
#include "icons.h"
#include "dir.h"
#include "file.h"
#include "icons.h"
#include "splash.h"
/* max plugin name size without extensions and path */
#define MAX_PLUGIN_LENGTH 32
/* max filetypes (plugins & icons stored here) */
#if CONFIG_CODEC == SWCODEC
#define MAX_FILETYPES 64
#else
#define MAX_FILETYPES 48
#endif
/* max exttypes (extensions stored here) */
#if CONFIG_CODEC == SWCODEC
/* Software codecs require more file extensions */
#define MAX_EXTTYPES 64
#else
#define MAX_EXTTYPES 32
#endif
/* string buffer length */
#define STRING_BUFFER_SIZE 548
/* number of bytes for the binary icon */
#define ICON_LENGTH 6
/* mask for dynamic filetype info in attribute */
#define FILETYPES_MASK 0xFF00
/* filenames */
#define ROCK_EXTENSION ".rock"
#define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config"
#define VIEWERS_DIR ROCKBOX_DIR "/viewers"
/* global variables */
static int cnt_filetypes;
static int cnt_exttypes;
static struct ext_type exttypes [MAX_EXTTYPES];
static struct file_type filetypes[MAX_FILETYPES];
static int first_soft_exttype;
static int first_soft_filetype;
static char* next_free_string;
static char plugin_name[sizeof(VIEWERS_DIR) + 7 + MAX_PLUGIN_LENGTH];
static char string_buffer[STRING_BUFFER_SIZE];
/* prototypes */
#ifdef HAVE_LCD_BITMAP
static char* string2icon(const char*);
static int add_plugin(char*,char*);
#else
static int add_plugin(char*);
#endif
static char* get_string(const char*);
static int find_attr_index(int);
static bool read_config(const char*);
static void rm_whitespaces(char*);
static void scan_plugins(void);
/* initialize dynamic filetypes (called at boot from tree.c) */
void filetype_init(void)
{
int cnt,i,ix;
const struct filetype* ftypes;
memset(exttypes,0,sizeof(exttypes));
memset(filetypes,0,sizeof(filetypes));
next_free_string=string_buffer;
/* The special filetype folder must always be stored at index 0 */
#ifdef HAVE_LCD_BITMAP
if (!filetypes[0].icon)
filetypes[0].icon = bitmap_icons_6x8[Icon_Folder];
#else
if (!filetypes[0].icon)
filetypes[0].icon = Icon_Folder;
for (i=1; i < MAX_FILETYPES; i++)
filetypes[i].icon = -1;
#endif
/* register hardcoded filetypes */
tree_get_filetypes(&ftypes, &cnt);
cnt_exttypes=0;
cnt_filetypes=0;
for (i = 0; i < cnt ; i++)
{
ix = ((ftypes[i].tree_attr & FILETYPES_MASK) >> 8);
if (ix < MAX_FILETYPES && i < MAX_EXTTYPES)
{
#ifdef HAVE_LCD_BITMAP
if (filetypes[ix].icon == NULL)
filetypes[ix].icon=bitmap_icons_6x8[ftypes[i].icon];
#else
if (filetypes[ix].icon == -1)
filetypes[ix].icon=ftypes[i].icon;
#endif
if (ix > cnt_filetypes)
cnt_filetypes=ix;
exttypes[cnt_exttypes].type=&filetypes[ix];
exttypes[cnt_exttypes].extension=ftypes[i].extension;
cnt_exttypes++;
}
}
first_soft_exttype=cnt_exttypes;
cnt_filetypes++;
first_soft_filetype=cnt_filetypes;
/* register dynamic filetypes */
read_config(VIEWERS_CONFIG);
scan_plugins();
}
/* get icon */
#ifdef HAVE_LCD_BITMAP
const unsigned char* filetype_get_icon(int attr)
#else
int filetype_get_icon(int attr)
#endif
{
int ix;
ix = find_attr_index(attr);
if (ix < 0)
{
#ifdef HAVE_LCD_BITMAP
return NULL;
#else
return Icon_Unknown;
#endif
}
else
{
return filetypes[ix].icon;
}
}
/* get plugin */
char* filetype_get_plugin(const struct entry* file)
{
int ix;
ix=find_attr_index(file->attr);
if (ix < 0)
{
return NULL;
}
if ((filetypes[ix].plugin == NULL) ||
(strlen(filetypes[ix].plugin) > MAX_PLUGIN_LENGTH))
return NULL;
snprintf(plugin_name, sizeof(plugin_name),
"%s/%s.rock", ROCKBOX_DIR, filetypes[ix].plugin);
return plugin_name;
}
/* check if filetype is supported */
bool filetype_supported(int attr)
{
int ix;
ix=find_attr_index(attr);
/* hard filetypes and soft filetypes with plugins is supported */
if (ix > 0)
if (filetypes[ix].plugin || ix < first_soft_filetype)
return true;
return false;
}
/* get the "dynamic" attribute for an extension */
int filetype_get_attr(const char* name)
{
int i;
const char *cp = strrchr(name,'.');
if (!cp) /* no extension? -> can't be a supported type */
return 0;
cp++;
for (i=0; i < cnt_exttypes; i++)
{
if (exttypes[i].extension)
{
if (!strcasecmp(cp,exttypes[i].extension))
{
return ((((unsigned long)exttypes[i].type -
(unsigned long)&filetypes[0]) /
sizeof(struct file_type)) << 8);
}
}
}
return 0;
}
/* fill a menu list with viewers (used in onplay.c) */
int filetype_load_menu(struct menu_item* menu,int max_items)
{
int i;
char *cp;
int cnt=0;
for (i=0; i < cnt_filetypes; i++)
{
if (filetypes[i].plugin)
{
int j;
for (j=0;j<cnt;j++) /* check if the plugin is in the list yet */
{
if (!strcmp(menu[j].desc,filetypes[i].plugin))
break;
}
if (j<cnt) continue; /* it is so grab the next plugin */
cp=strrchr(filetypes[i].plugin,'/');
if (cp) cp++;
else cp=filetypes[i].plugin;
menu[cnt].desc = (unsigned char *)cp;
cnt++;
if (cnt == max_items)
break;
}
}
return cnt;
}
/* start a plugin with an argument (called from onplay.c) */
int filetype_load_plugin(const char* plugin, char* file)
{
int fd;
snprintf(plugin_name,sizeof(plugin_name),"%s/%s.rock",
VIEWERS_DIR,plugin);
if ((fd = open(plugin_name,O_RDONLY))>=0)
{
close(fd);
return plugin_load(plugin_name,file);
}
else
{
snprintf(plugin_name,sizeof(plugin_name),"%s/%s.rock",
PLUGIN_DIR,plugin);
if ((fd = open(plugin_name,O_RDONLY))>=0)
{
close(fd);
return plugin_load(plugin_name,file);
}
}
return PLUGIN_ERROR;
}
/* get index to filetypes[] from the file attribute */
static int find_attr_index(int attr)
{
int ix;
ix = ((attr & FILETYPES_MASK) >> 8);
if ((attr & ATTR_DIRECTORY)==ATTR_DIRECTORY)
{
ix=0;
}
else
{
if (ix==0)
ix=-1;
if (ix > cnt_filetypes)
ix=-1;
else
if ((filetypes[ix].plugin == NULL) &&
#ifdef HAVE_LCD_BITMAP
(filetypes[ix].icon == NULL)
#else
(filetypes[ix].icon == -1)
#endif
)
ix=-1;
}
return ix;
}
/* scan the plugin directory and register filetypes */
static void scan_plugins(void)
{
DIR *dir;
struct dirent *entry;
char* cp;
char* dot;
char* dash;
int ix;
int i;
bool found;
dir = opendir(VIEWERS_DIR);
if(!dir)
return;
while (true)
{
/* exttypes[] full, bail out */
if (cnt_exttypes >= MAX_EXTTYPES)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_EXTENSION_FULL));
break;
}
/* filetypes[] full, bail out */
if (cnt_filetypes >= MAX_FILETYPES)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_FULL));
break;
}
entry = readdir(dir);
if (!entry)
break;
/* skip directories */
if ((entry->attribute & ATTR_DIRECTORY))
continue;
/* Skip FAT volume ID */
if (entry->attribute & ATTR_VOLUME_ID)
continue;
/* filter out dotfiles and hidden files */
if ((entry->d_name[0]=='.') ||
(entry->attribute & ATTR_HIDDEN)) {
continue;
}
/* filter out non rock files */
if (strcasecmp((char *)&entry->d_name[strlen((char *)entry->d_name) -
sizeof(ROCK_EXTENSION) + 1],
ROCK_EXTENSION)) {
continue;
}
/* filter out to long filenames */
if (strlen((char *)entry->d_name) > MAX_PLUGIN_LENGTH + 5)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_PLUGIN_NAME_LONG));
continue;
}
dot=strrchr((char *)entry->d_name,'.');
*dot='\0';
dash=strchr((char *)entry->d_name,'-');
/* add plugin and extension */
if (dash)
{
*dash='\0';
ix=(filetype_get_attr((char *)entry->d_name) >> 8);
if (!ix)
{
cp=get_string((char *)entry->d_name);
if (cp)
{
exttypes[cnt_exttypes].extension=cp;
exttypes[cnt_exttypes].type=&filetypes[cnt_filetypes];
#ifdef HAVE_LCD_BITMAP
exttypes[cnt_exttypes].type->icon = bitmap_icons_6x8[Icon_Plugin];
#else
exttypes[cnt_exttypes].type->icon = Icon_Plugin;
#endif
cnt_exttypes++;
*dash='-';
cp=get_string((char *)entry->d_name);
if (cp)
{
filetypes[cnt_filetypes].plugin=cp;
cnt_filetypes++;
}
else
break;
}
else
break;
}
else
{
*dash='-';
if (!filetypes[ix].plugin)
{
cp=get_string((char *)entry->d_name);
if (cp)
{
filetypes[cnt_filetypes].plugin=cp;
cnt_filetypes++;
}
else
break;
}
}
*dash='-';
}
/* add plugin only */
else
{
found=false;
for (i = first_soft_filetype; i < cnt_filetypes; i++)
{
if (filetypes[i].plugin)
if (!strcasecmp(filetypes[i].plugin, (char *)entry->d_name))
{
found=true;
break;
}
}
if (!found)
{
cp=get_string((char *)entry->d_name);
if (cp)
{
filetypes[cnt_filetypes].plugin=cp;
filetypes[cnt_filetypes].no_extension=true;
cnt_filetypes++;
}
else
break;
}
}
*dot='.';
}
closedir(dir);
}
#ifdef HAVE_LCD_BITMAP
static int add_plugin(char *plugin, char *icon)
#else
static int add_plugin(char *plugin)
#endif
{
char *cp;
int i;
if (!plugin)
return 0;
#if 0
/* starting now, Oct 2005, the plugins are given without extension in the
viewers.config file */
cp=strrchr(plugin, '.');
if (cp)
*cp='\0';
#endif
for (i=first_soft_filetype; i < cnt_filetypes; i++)
{
if (filetypes[i].plugin)
{
if (!strcasecmp(plugin, filetypes[i].plugin))
{
#ifdef HAVE_LCD_BITMAP
if (filetypes[i].icon == NULL && icon)
{
cp = string2icon(icon);
if (cp)
filetypes[cnt_filetypes].icon = (unsigned char *)cp;
else
return 0;
}
#endif
return i;
}
}
}
/* new plugin */
cp = get_string(plugin);
if (cp)
{
filetypes[cnt_filetypes].plugin = cp;
#ifdef HAVE_LCD_BITMAP
/* add icon */
if (icon)
{
cp = string2icon(icon);
if (cp)
filetypes[cnt_filetypes].icon = (unsigned char *)cp;
else
return 0;
}
#endif
}
else
{
return 0;
}
cnt_filetypes++;
return cnt_filetypes - 1;
}
/* read config file (or cahe file) */
bool read_config(const char* file)
{
enum {extension,
plugin,
#ifdef HAVE_LCD_BITMAP
icon,
#endif
last};
int i,ix;
int fd;
char* end;
char* cp;
char* str[last];
char buf[80];
fd = open(file, O_RDONLY);
if (fd < 0)
return false;
while (read_line(fd, buf, sizeof(buf)))
{
if (cnt_exttypes >= MAX_EXTTYPES)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_EXTENSION_FULL));
break;
}
if (cnt_filetypes >= MAX_FILETYPES)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_FULL));
break;
}
/* parse buffer */
rm_whitespaces(buf);
if (strlen(buf) == 0)
continue;
if (buf[0] == '#')
continue;
memset(str,0,sizeof(str));
i=0;
cp=buf;
while (*cp==',') {
cp++;
i++;
}
str[i] = strtok_r(cp, ",", &end);
i++;
while (end && i < last)
{
if (end)
{
cp=end;
while (*cp==',') {
cp++;
i++;
}
}
str[i] = strtok_r(NULL, ",", &end);
if (str[i])
if (!strlen(str[i]))
str[i]=NULL;
i++;
}
/* bail out if no icon and no plugin */
if (!str[plugin]
#ifdef HAVE_LCD_BITMAP
&& !str[icon]
#endif
)
continue;
/* bail out if no plugin and icon is incorrect*/
if (!str[plugin]
#ifdef HAVE_LCD_BITMAP
&& strlen(str[icon]) != ICON_LENGTH*2
#endif
)
continue;
/* bail out if no icon and no plugin and no extension*/
if (!str[plugin] &&
#ifdef HAVE_LCD_BITMAP
!str[icon] &&
#endif
!str[extension])
continue;
/* bail out if we are not able to start plugin from onplay.c ?*/
if (str[plugin])
{
if (strlen(str[plugin]) > MAX_PLUGIN_LENGTH)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_PLUGIN_NAME_LONG));
str[plugin] = NULL;
continue;
}
}
ix=0;
/* if extension already exist don't add a new one */
for (i=0; i < cnt_exttypes; i++)
{
if (!strcasecmp(str[extension],exttypes[i].extension))
{
#ifdef HAVE_LCD_BITMAP
ix=add_plugin(str[plugin],NULL);
if (ix)
{
if (str[icon] && filetypes[ix].icon == NULL)
{
if (exttypes[i].type->icon == NULL)
{
cp = string2icon(str[icon]);
if (cp)
exttypes[i].type->icon = (unsigned char *)cp;
}
}
}
#else
ix=add_plugin(str[plugin]);
#endif
if (exttypes[i].type == NULL)
{
exttypes[i].type = &filetypes[ix];
}
break;
}
}
if (ix)
continue;
/* add extension */
if (str[extension])
{
#ifdef HAVE_LCD_BITMAP
ix=add_plugin(str[plugin],str[icon]);
#else
ix=add_plugin(str[plugin]);
#endif
if (ix)
{
cp=get_string(str[extension]);
if (cp)
{
exttypes[cnt_exttypes].extension = cp;
exttypes[cnt_exttypes].type = &filetypes[ix];
cnt_exttypes++;
filetypes[i].no_extension=false;
}
else
{
break;
}
}
else
{
break;
}
}
else
{
#ifdef HAVE_LCD_BITMAP
ix=add_plugin(str[plugin],str[icon]);
#else
ix=add_plugin(str[plugin]);
#endif
filetypes[ix].no_extension=true;
if (!i)
break;
}
}
close(fd);
return true;
}
#ifdef HAVE_LCD_BITMAP
/* convert an ascii hexadecimal icon to a binary icon */
static char* string2icon(const char* str)
{
char tmp[ICON_LENGTH*2];
char *cp;
int i;
if (strlen(str)!=ICON_LENGTH*2)
return NULL;
if ((sizeof(string_buffer) +
(unsigned long) string_buffer -
(unsigned long) next_free_string) < ICON_LENGTH)
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
return NULL;
}
for (i=0; i<12; i++)
{
if (str[i] >= '0' && str[i] <= '9')
{
tmp[i]=str[i]-'0';
continue;
}
if (str[i] >= 'a' && str[i] <= 'f')
{
tmp[i]=str[i]-'a'+10;
continue;
}
if (str[i] >= 'A' && str[i] <= 'F')
{
tmp[i]=str[i]-'A'+10;
continue;
}
return NULL;
}
cp=next_free_string;
for (i = 0; i < ICON_LENGTH; i++)
cp[i]=((tmp[i*2]<<4) | tmp[i*2+1]);
next_free_string=&next_free_string[ICON_LENGTH];
return cp;
}
#endif
/* get string from buffer */
static char* get_string(const char* str)
{
unsigned int l=strlen(str)+1;
char* cp;
if (!str)
return NULL;
if (l <= (sizeof(string_buffer) +
(unsigned long) string_buffer -
(unsigned long) next_free_string))
{
strcpy(next_free_string, str);
cp=next_free_string;
next_free_string=&next_free_string[l];
return cp;
}
else
{
gui_syncsplash(HZ, true, str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
return NULL;
}
}
/* remove all white spaces from string */
static void rm_whitespaces(char* str)
{
char *cp, *free;
cp=str;
free=cp;
while (cp < &str[strlen(str)])
{
switch (*cp)
{
case ' ' :
case '\t' :
case '\r' :
break;
default:
*free=*cp;
free++;
break;
}
cp++;
}
*free='\0';
}