Unify the way functions are called from menus.
[Rockbox.git] / apps / filetypes.c
blob49ce1c72aaf1ed20dd3ea80c12b4153061422c65
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * $Id$
10 * Copyright (C) 2004 Henrik Backe
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
25 #include "sprintf.h"
26 #include "settings.h"
27 #include "debug.h"
28 #include "lang.h"
29 #include "language.h"
30 #include "kernel.h"
31 #include "plugin.h"
32 #include "filetypes.h"
33 #include "screens.h"
34 #include "icons.h"
35 #include "dir.h"
36 #include "file.h"
37 #include "icons.h"
38 #include "splash.h"
40 /* max plugin name size without extensions and path */
41 #define MAX_PLUGIN_LENGTH 32
43 /* max filetypes (plugins & icons stored here) */
44 #if CONFIG_CODEC == SWCODEC
45 #define MAX_FILETYPES 64
46 #else
47 #define MAX_FILETYPES 48
48 #endif
50 /* max exttypes (extensions stored here) */
51 #if CONFIG_CODEC == SWCODEC
52 /* Software codecs require more file extensions */
53 #define MAX_EXTTYPES 64
54 #else
55 #define MAX_EXTTYPES 32
56 #endif
58 /* string buffer length */
59 #define STRING_BUFFER_SIZE 548
61 /* number of bytes for the binary icon */
62 #define ICON_LENGTH 6
64 /* mask for dynamic filetype info in attribute */
65 #define FILETYPES_MASK 0xFF00
67 /* filenames */
68 #define ROCK_EXTENSION ".rock"
69 #define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config"
70 #define VIEWERS_DIR ROCKBOX_DIR "/viewers"
72 /* global variables */
73 static int cnt_filetypes;
74 static int cnt_exttypes;
75 static struct ext_type exttypes [MAX_EXTTYPES];
76 static struct file_type filetypes[MAX_FILETYPES];
77 static int first_soft_exttype;
78 static int first_soft_filetype;
79 static char* next_free_string;
80 static char plugin_name[sizeof(VIEWERS_DIR) + 7 + MAX_PLUGIN_LENGTH];
81 static char string_buffer[STRING_BUFFER_SIZE];
83 /* prototypes */
84 #ifdef HAVE_LCD_BITMAP
85 static char* string2icon(const char*);
86 static int add_plugin(char*,char*);
87 #else
88 static int add_plugin(char*);
89 #endif
90 static char* get_string(const char*);
91 static int find_attr_index(int);
92 static bool read_config(const char*);
93 static void rm_whitespaces(char*);
94 static void scan_plugins(void);
96 /* initialize dynamic filetypes (called at boot from tree.c) */
97 void filetype_init(void)
99 int cnt,i,ix;
100 const struct filetype* ftypes;
102 memset(exttypes,0,sizeof(exttypes));
103 memset(filetypes,0,sizeof(filetypes));
104 next_free_string=string_buffer;
106 /* The special filetype folder must always be stored at index 0 */
107 #ifdef HAVE_LCD_BITMAP
108 if (!filetypes[0].icon)
109 filetypes[0].icon = bitmap_icons_6x8[Icon_Folder];
110 #else
111 if (!filetypes[0].icon)
112 filetypes[0].icon = Icon_Folder;
113 for (i=1; i < MAX_FILETYPES; i++)
114 filetypes[i].icon = -1;
115 #endif
117 /* register hardcoded filetypes */
118 tree_get_filetypes(&ftypes, &cnt);
119 cnt_exttypes=0;
120 cnt_filetypes=0;
122 for (i = 0; i < cnt ; i++)
124 ix = ((ftypes[i].tree_attr & FILETYPES_MASK) >> 8);
125 if (ix < MAX_FILETYPES && i < MAX_EXTTYPES)
127 #ifdef HAVE_LCD_BITMAP
128 if (filetypes[ix].icon == NULL)
129 filetypes[ix].icon=bitmap_icons_6x8[ftypes[i].icon];
130 #else
131 if (filetypes[ix].icon == -1)
132 filetypes[ix].icon=ftypes[i].icon;
133 #endif
134 if (ix > cnt_filetypes)
135 cnt_filetypes=ix;
136 exttypes[cnt_exttypes].type=&filetypes[ix];
137 exttypes[cnt_exttypes].extension=ftypes[i].extension;
138 cnt_exttypes++;
141 first_soft_exttype=cnt_exttypes;
142 cnt_filetypes++;
143 first_soft_filetype=cnt_filetypes;
145 /* register dynamic filetypes */
146 read_config(VIEWERS_CONFIG);
147 scan_plugins();
150 /* get icon */
151 #ifdef HAVE_LCD_BITMAP
152 const unsigned char* filetype_get_icon(int attr)
153 #else
154 int filetype_get_icon(int attr)
155 #endif
157 int ix;
159 ix = find_attr_index(attr);
161 if (ix < 0)
163 #ifdef HAVE_LCD_BITMAP
164 return NULL;
165 #else
166 return Icon_Unknown;
167 #endif
169 else
171 return filetypes[ix].icon;
175 /* get plugin */
176 char* filetype_get_plugin(const struct entry* file)
178 int ix;
180 ix=find_attr_index(file->attr);
182 if (ix < 0)
184 return NULL;
187 if ((filetypes[ix].plugin == NULL) ||
188 (strlen(filetypes[ix].plugin) > MAX_PLUGIN_LENGTH))
189 return NULL;
191 snprintf(plugin_name, sizeof(plugin_name),
192 "%s/%s.rock", ROCKBOX_DIR, filetypes[ix].plugin);
194 return plugin_name;
197 /* check if filetype is supported */
198 bool filetype_supported(int attr)
200 int ix;
202 ix=find_attr_index(attr);
204 /* hard filetypes and soft filetypes with plugins is supported */
205 if (ix > 0)
206 if (filetypes[ix].plugin || ix < first_soft_filetype)
207 return true;
209 return false;
212 /* get the "dynamic" attribute for an extension */
213 int filetype_get_attr(const char* name)
215 int i;
216 const char *cp = strrchr(name,'.');
218 if (!cp) /* no extension? -> can't be a supported type */
219 return 0;
220 cp++;
222 for (i=0; i < cnt_exttypes; i++)
224 if (exttypes[i].extension)
226 if (!strcasecmp(cp,exttypes[i].extension))
228 return ((((unsigned long)exttypes[i].type -
229 (unsigned long)&filetypes[0]) /
230 sizeof(struct file_type)) << 8);
235 return 0;
238 /* fill a menu list with viewers (used in onplay.c) */
239 int filetype_load_menu(struct menu_item* menu,int max_items)
241 int i;
242 char *cp;
243 int cnt=0;
245 for (i=0; i < cnt_filetypes; i++)
247 if (filetypes[i].plugin)
249 int j;
250 for (j=0;j<cnt;j++) /* check if the plugin is in the list yet */
252 if (!strcmp(menu[j].desc,filetypes[i].plugin))
253 break;
255 if (j<cnt) continue; /* it is so grab the next plugin */
256 cp=strrchr(filetypes[i].plugin,'/');
257 if (cp) cp++;
258 else cp=filetypes[i].plugin;
259 menu[cnt].desc = (unsigned char *)cp;
260 cnt++;
261 if (cnt == max_items)
262 break;
265 return cnt;
268 /* start a plugin with an argument (called from onplay.c) */
269 int filetype_load_plugin(const char* plugin, char* file)
271 int fd;
272 snprintf(plugin_name,sizeof(plugin_name),"%s/%s.rock",
273 VIEWERS_DIR,plugin);
274 if ((fd = open(plugin_name,O_RDONLY))>=0)
276 close(fd);
277 return plugin_load(plugin_name,file);
279 else
281 snprintf(plugin_name,sizeof(plugin_name),"%s/%s.rock",
282 PLUGIN_DIR,plugin);
283 if ((fd = open(plugin_name,O_RDONLY))>=0)
285 close(fd);
286 return plugin_load(plugin_name,file);
289 return PLUGIN_ERROR;
292 /* get index to filetypes[] from the file attribute */
293 static int find_attr_index(int attr)
295 int ix;
296 ix = ((attr & FILETYPES_MASK) >> 8);
298 if ((attr & ATTR_DIRECTORY)==ATTR_DIRECTORY)
300 ix=0;
302 else
304 if (ix==0)
305 ix=-1;
306 if (ix > cnt_filetypes)
307 ix=-1;
308 else
309 if ((filetypes[ix].plugin == NULL) &&
310 #ifdef HAVE_LCD_BITMAP
311 (filetypes[ix].icon == NULL)
312 #else
313 (filetypes[ix].icon == -1)
314 #endif
316 ix=-1;
319 return ix;
322 /* scan the plugin directory and register filetypes */
323 static void scan_plugins(void)
325 DIR *dir;
326 struct dirent *entry;
327 char* cp;
328 char* dot;
329 char* dash;
330 int ix;
331 int i;
332 bool found;
334 dir = opendir(VIEWERS_DIR);
335 if(!dir)
336 return;
338 while (true)
340 /* exttypes[] full, bail out */
341 if (cnt_exttypes >= MAX_EXTTYPES)
343 gui_syncsplash(HZ, str(LANG_FILETYPES_EXTENSION_FULL));
344 break;
347 /* filetypes[] full, bail out */
348 if (cnt_filetypes >= MAX_FILETYPES)
350 gui_syncsplash(HZ, str(LANG_FILETYPES_FULL));
351 break;
354 entry = readdir(dir);
356 if (!entry)
357 break;
359 /* skip directories */
360 if ((entry->attribute & ATTR_DIRECTORY))
361 continue;
363 /* Skip FAT volume ID */
364 if (entry->attribute & ATTR_VOLUME_ID)
365 continue;
367 /* filter out dotfiles and hidden files */
368 if ((entry->d_name[0]=='.') ||
369 (entry->attribute & ATTR_HIDDEN)) {
370 continue;
373 /* filter out non rock files */
374 if (strcasecmp((char *)&entry->d_name[strlen((char *)entry->d_name) -
375 sizeof(ROCK_EXTENSION) + 1],
376 ROCK_EXTENSION)) {
377 continue;
380 /* filter out to long filenames */
381 if (strlen((char *)entry->d_name) > MAX_PLUGIN_LENGTH + 5)
383 gui_syncsplash(HZ, str(LANG_FILETYPES_PLUGIN_NAME_LONG));
384 continue;
387 dot=strrchr((char *)entry->d_name,'.');
388 *dot='\0';
389 dash=strchr((char *)entry->d_name,'-');
391 /* add plugin and extension */
392 if (dash)
394 *dash='\0';
395 ix=(filetype_get_attr((char *)entry->d_name) >> 8);
396 if (!ix)
398 cp=get_string((char *)entry->d_name);
399 if (cp)
401 exttypes[cnt_exttypes].extension=cp;
402 exttypes[cnt_exttypes].type=&filetypes[cnt_filetypes];
403 #ifdef HAVE_LCD_BITMAP
404 exttypes[cnt_exttypes].type->icon = bitmap_icons_6x8[Icon_Plugin];
405 #else
406 exttypes[cnt_exttypes].type->icon = Icon_Plugin;
407 #endif
408 cnt_exttypes++;
410 *dash='-';
411 cp=get_string((char *)entry->d_name);
412 if (cp)
414 filetypes[cnt_filetypes].plugin=cp;
415 cnt_filetypes++;
417 else
418 break;
420 else
421 break;
423 else
425 *dash='-';
426 if (!filetypes[ix].plugin)
428 cp=get_string((char *)entry->d_name);
429 if (cp)
431 filetypes[cnt_filetypes].plugin=cp;
432 cnt_filetypes++;
434 else
435 break;
438 *dash='-';
440 /* add plugin only */
441 else
443 found=false;
444 for (i = first_soft_filetype; i < cnt_filetypes; i++)
446 if (filetypes[i].plugin)
447 if (!strcasecmp(filetypes[i].plugin, (char *)entry->d_name))
449 found=true;
450 break;
454 if (!found)
456 cp=get_string((char *)entry->d_name);
457 if (cp)
459 filetypes[cnt_filetypes].plugin=cp;
460 filetypes[cnt_filetypes].no_extension=true;
461 cnt_filetypes++;
463 else
464 break;
467 *dot='.';
469 closedir(dir);
472 #ifdef HAVE_LCD_BITMAP
473 static int add_plugin(char *plugin, char *icon)
474 #else
475 static int add_plugin(char *plugin)
476 #endif
478 char *cp;
479 int i;
481 if (!plugin)
482 return 0;
484 #if 0
485 /* starting now, Oct 2005, the plugins are given without extension in the
486 viewers.config file */
487 cp=strrchr(plugin, '.');
488 if (cp)
489 *cp='\0';
490 #endif
492 for (i=first_soft_filetype; i < cnt_filetypes; i++)
494 if (filetypes[i].plugin)
496 if (!strcasecmp(plugin, filetypes[i].plugin))
498 #ifdef HAVE_LCD_BITMAP
499 if (filetypes[i].icon == NULL && icon)
501 cp = string2icon(icon);
502 if (cp)
503 filetypes[cnt_filetypes].icon = (unsigned char *)cp;
504 else
505 return 0;
507 #endif
508 return i;
513 /* new plugin */
514 cp = get_string(plugin);
515 if (cp)
517 filetypes[cnt_filetypes].plugin = cp;
518 #ifdef HAVE_LCD_BITMAP
519 /* add icon */
520 if (icon)
522 cp = string2icon(icon);
523 if (cp)
524 filetypes[cnt_filetypes].icon = (unsigned char *)cp;
525 else
526 return 0;
528 #endif
530 else
532 return 0;
535 cnt_filetypes++;
536 return cnt_filetypes - 1;
539 /* read config file (or cahe file) */
540 static bool read_config(const char* file)
542 enum {extension,
543 plugin,
544 #ifdef HAVE_LCD_BITMAP
545 icon,
546 #endif
547 last};
549 int i,ix;
550 int fd;
551 char* end;
552 char* cp;
553 char* str[last];
554 char buf[80];
556 fd = open(file, O_RDONLY);
557 if (fd < 0)
558 return false;
560 while (read_line(fd, buf, sizeof(buf)))
562 if (cnt_exttypes >= MAX_EXTTYPES)
564 gui_syncsplash(HZ, str(LANG_FILETYPES_EXTENSION_FULL));
565 break;
568 if (cnt_filetypes >= MAX_FILETYPES)
570 gui_syncsplash(HZ, str(LANG_FILETYPES_FULL));
571 break;
574 /* parse buffer */
575 rm_whitespaces(buf);
577 if (strlen(buf) == 0)
578 continue;
580 if (buf[0] == '#')
581 continue;
583 memset(str,0,sizeof(str));
584 i=0;
585 cp=buf;
586 while (*cp==',') {
587 cp++;
588 i++;
590 str[i] = strtok_r(cp, ",", &end);
591 i++;
593 while (end && i < last)
595 if (end)
597 cp=end;
598 while (*cp==',') {
599 cp++;
600 i++;
603 str[i] = strtok_r(NULL, ",", &end);
604 if (str[i])
605 if (!strlen(str[i]))
606 str[i]=NULL;
607 i++;
610 /* bail out if no icon and no plugin */
611 if (!str[plugin]
612 #ifdef HAVE_LCD_BITMAP
613 && !str[icon]
614 #endif
616 continue;
618 /* bail out if no plugin and icon is incorrect*/
619 if (!str[plugin]
620 #ifdef HAVE_LCD_BITMAP
621 && strlen(str[icon]) != ICON_LENGTH*2
622 #endif
624 continue;
626 /* bail out if no icon and no plugin and no extension*/
627 if (!str[plugin] &&
628 #ifdef HAVE_LCD_BITMAP
629 !str[icon] &&
630 #endif
631 !str[extension])
632 continue;
634 /* bail out if we are not able to start plugin from onplay.c ?*/
635 if (str[plugin])
637 if (strlen(str[plugin]) > MAX_PLUGIN_LENGTH)
639 gui_syncsplash(HZ, str(LANG_FILETYPES_PLUGIN_NAME_LONG));
640 str[plugin] = NULL;
641 continue;
645 ix=0;
646 /* if extension already exist don't add a new one */
647 for (i=0; i < cnt_exttypes; i++)
649 if (!strcasecmp(str[extension],exttypes[i].extension))
651 #ifdef HAVE_LCD_BITMAP
652 ix=add_plugin(str[plugin],NULL);
653 if (ix)
655 if (str[icon] && filetypes[ix].icon == NULL)
657 if (exttypes[i].type->icon == NULL)
659 cp = string2icon(str[icon]);
660 if (cp)
661 exttypes[i].type->icon = (unsigned char *)cp;
665 #else
666 ix=add_plugin(str[plugin]);
667 #endif
668 if (exttypes[i].type == NULL)
670 exttypes[i].type = &filetypes[ix];
672 break;
675 if (ix)
676 continue;
678 /* add extension */
679 if (str[extension])
681 #ifdef HAVE_LCD_BITMAP
682 ix=add_plugin(str[plugin],str[icon]);
683 #else
684 ix=add_plugin(str[plugin]);
685 #endif
686 if (ix)
688 cp=get_string(str[extension]);
689 if (cp)
691 exttypes[cnt_exttypes].extension = cp;
693 exttypes[cnt_exttypes].type = &filetypes[ix];
694 cnt_exttypes++;
695 filetypes[i].no_extension=false;
697 else
699 break;
702 else
704 break;
707 else
709 #ifdef HAVE_LCD_BITMAP
710 ix=add_plugin(str[plugin],str[icon]);
711 #else
712 ix=add_plugin(str[plugin]);
713 #endif
714 filetypes[ix].no_extension=true;
715 if (!i)
716 break;
719 close(fd);
721 return true;
724 #ifdef HAVE_LCD_BITMAP
725 /* convert an ascii hexadecimal icon to a binary icon */
726 static char* string2icon(const char* str)
728 char tmp[ICON_LENGTH*2];
729 char *cp;
730 int i;
732 if (strlen(str)!=ICON_LENGTH*2)
733 return NULL;
735 if ((sizeof(string_buffer) +
736 (unsigned long) string_buffer -
737 (unsigned long) next_free_string) < ICON_LENGTH)
739 gui_syncsplash(HZ, str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
740 return NULL;
743 for (i=0; i<12; i++)
745 if (str[i] >= '0' && str[i] <= '9')
747 tmp[i]=str[i]-'0';
748 continue;
751 if (str[i] >= 'a' && str[i] <= 'f')
753 tmp[i]=str[i]-'a'+10;
754 continue;
757 if (str[i] >= 'A' && str[i] <= 'F')
759 tmp[i]=str[i]-'A'+10;
760 continue;
763 return NULL;
766 cp=next_free_string;
767 for (i = 0; i < ICON_LENGTH; i++)
768 cp[i]=((tmp[i*2]<<4) | tmp[i*2+1]);
770 next_free_string=&next_free_string[ICON_LENGTH];
771 return cp;
773 #endif
775 /* get string from buffer */
776 static char* get_string(const char* str)
778 unsigned int l=strlen(str)+1;
779 char* cp;
781 if (!str)
782 return NULL;
784 if (l <= (sizeof(string_buffer) +
785 (unsigned long) string_buffer -
786 (unsigned long) next_free_string))
788 strcpy(next_free_string, str);
789 cp=next_free_string;
790 next_free_string=&next_free_string[l];
791 return cp;
793 else
795 gui_syncsplash(HZ, str(LANG_FILETYPES_STRING_BUFFER_EMPTY));
796 return NULL;
800 /* remove all white spaces from string */
801 static void rm_whitespaces(char* str)
803 char *cp, *free;
805 cp=str;
806 free=cp;
808 while (cp < &str[strlen(str)])
810 switch (*cp)
812 case ' ' :
813 case '\t' :
814 case '\r' :
815 break;
817 default:
818 *free=*cp;
819 free++;
820 break;
822 cp++;
825 *free='\0';