The Poweroff menu entry is only for Players
[kugel-rb.git] / apps / tree.c
blob1a92dfd3a992edd757ccf25cf3b4fe55d1087115
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Daniel Stenberg
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 ****************************************************************************/
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdbool.h>
24 #include "applimits.h"
25 #include "dir.h"
26 #include "file.h"
27 #include "lcd.h"
28 #include "font.h"
29 #include "backlight.h"
30 #include "button.h"
31 #include "kernel.h"
32 #include "usb.h"
33 #include "tree.h"
34 #include "main_menu.h"
35 #include "sprintf.h"
36 #include "mpeg.h"
37 #include "playlist.h"
38 #include "menu.h"
39 #include "wps.h"
40 #include "wps-display.h"
41 #include "settings.h"
42 #include "status.h"
43 #include "debug.h"
44 #include "ata.h"
45 #include "rolo.h"
46 #include "icons.h"
47 #include "lang.h"
48 #include "language.h"
49 #include "screens.h"
50 #include "keyboard.h"
51 #include "bookmark.h"
52 #include "onplay.h"
53 #include "buffer.h"
54 #include "plugin.h"
55 #include "power.h"
56 #include "action.h"
57 #include "talk.h"
58 #include "filetypes.h"
59 #include "misc.h"
61 #ifdef HAVE_LCD_BITMAP
62 #include "widgets.h"
63 #define BOOTFILE "ajbrec.ajz"
64 #else
65 #define BOOTFILE "archos.mod"
66 #endif
68 /* This flag is set by dirbrowse() if a new language is loaded.
69 It is defined in settings_menu.c */
70 extern bool language_changed;
72 /* a table for the know file types */
73 const struct filetype filetypes[] = {
74 { ".mp3", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
75 { ".mp2", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
76 { ".mpa", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
77 { ".m3u", TREE_ATTR_M3U, Playlist, LANG_PLAYINDICES_PLAYLIST },
78 { ".cfg", TREE_ATTR_CFG, Config, VOICE_EXT_CFG },
79 { ".wps", TREE_ATTR_WPS, Wps, VOICE_EXT_WPS },
80 { ".lng", TREE_ATTR_LNG, Language, LANG_LANGUAGE },
81 { ".rock",TREE_ATTR_ROCK,Plugin, VOICE_EXT_ROCK },
82 #ifdef HAVE_LCD_BITMAP
83 { ".fnt", TREE_ATTR_FONT,Font, VOICE_EXT_FONT },
84 #endif
85 { ".bmark",TREE_ATTR_BMARK, Bookmark, VOICE_EXT_BMARK },
86 #ifndef SIMULATOR
87 #ifdef HAVE_LCD_BITMAP
88 { ".ajz", TREE_ATTR_MOD, Mod_Ajz, VOICE_EXT_AJZ },
89 #else
90 { ".mod", TREE_ATTR_MOD, Mod_Ajz, VOICE_EXT_AJZ },
91 #endif
92 #endif /* #ifndef SIMULATOR */
95 /* Boot value of global_settings.max_files_in_dir */
96 static int max_files_in_dir;
98 static char *name_buffer;
99 static int name_buffer_size; /* Size of allocated buffer */
100 static int name_buffer_length; /* Currently used amount */
102 static struct entry *dircache;
104 static int dircursor;
105 static int dirstart;
106 static int dirlevel;
107 static int filesindir;
108 static int dirsindir; /* how many of the dircache entries are directories */
109 static int dirpos[MAX_DIR_LEVELS];
110 static int cursorpos[MAX_DIR_LEVELS];
111 static char lastdir[MAX_PATH];
112 static char lastfile[MAX_PATH];
113 static char currdir[MAX_PATH];
114 static char currdir_save[MAX_PATH];
115 static bool reload_dir = false;
116 static int boot_size = 0;
117 static int boot_cluster;
118 static bool boot_changed = false;
120 static bool start_wps = false;
121 static bool dirbrowse(const char *root, const int *dirfilter);
123 bool check_rockboxdir(void)
125 DIR *dir = opendir(ROCKBOX_DIR);
126 if(!dir)
128 lcd_clear_display();
129 splash(HZ*2, true, str(LANG_NO_ROCKBOX_DIR));
130 lcd_clear_display();
131 splash(HZ*2, true, str(LANG_INSTALLATION_INCOMPLETE));
132 return false;
134 closedir(dir);
135 return true;
138 void browse_root(void)
140 filetype_init();
141 check_rockboxdir();
143 #ifndef SIMULATOR
144 dirbrowse("/", &global_settings.dirfilter);
146 #else
147 if (!dirbrowse("/", &global_settings.dirfilter)) {
148 DEBUGF("No filesystem found. Have you forgotten to create it?\n");
150 #endif
153 void tree_get_filetypes(const struct filetype** types, int* count)
155 *types = filetypes;
156 *count = sizeof(filetypes) / sizeof(*filetypes);
159 #ifdef HAVE_LCD_BITMAP
161 /* pixel margins */
162 #define MARGIN_X (global_settings.scrollbar && \
163 filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \
164 CURSOR_WIDTH + (global_settings.show_icons && ICON_WIDTH > 0 ? ICON_WIDTH :0)
165 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
167 /* position the entry-list starts at */
168 #define LINE_X 0
169 #define LINE_Y (global_settings.statusbar ? 1 : 0)
171 #define CURSOR_X (global_settings.scrollbar && \
172 filesindir > tree_max_on_screen ? 1 : 0)
173 #define CURSOR_Y 0 /* the cursor is not positioned in regard to
174 the margins, so this is the amount of lines
175 we add to the cursor Y position to position
176 it on a line */
177 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
179 #define ICON_WIDTH 6
181 #define SCROLLBAR_X 0
182 #define SCROLLBAR_Y lcd_getymargin()
183 #define SCROLLBAR_WIDTH 6
185 #else /* HAVE_LCD_BITMAP */
187 #define TREE_MAX_ON_SCREEN 2
188 #define TREE_MAX_LEN_DISPLAY 11 /* max length that fits on screen */
189 #define LINE_X 2 /* X position the entry-list starts at */
190 #define LINE_Y 0 /* Y position the entry-list starts at */
192 #define CURSOR_X 0
193 #define CURSOR_Y 0 /* not really used for players */
195 #endif /* HAVE_LCD_BITMAP */
197 /* talkbox hovering delay, to avoid immediate disk activity */
198 #define HOVER_DELAY (HZ/2)
200 static int build_playlist(int start_index)
202 int i;
203 int start=start_index;
205 for(i = 0;i < filesindir;i++)
207 if((dircache[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
209 DEBUGF("Adding %s\n", dircache[i].name);
210 if (playlist_add(dircache[i].name) < 0)
211 break;
213 else
215 /* Adjust the start index when se skip non-MP3 entries */
216 if(i < start)
217 start_index--;
221 return start_index;
224 static int play_filenumber(int pos, int attr)
226 /* try to find a voice ID for the extension, if known */
227 unsigned int j;
228 int ext_id = -1; /* default to none */
229 for (j=0; j<sizeof(filetypes)/sizeof(*filetypes); j++)
231 if (attr == filetypes[j].tree_attr)
233 ext_id = filetypes[j].voiceclip;
234 break;
238 talk_id(VOICE_FILE, false);
239 talk_number(pos, true);
240 talk_id(ext_id, true);
241 return 1;
244 static int play_dirname(int start_index)
246 int fd;
247 char dirname_mp3_filename[MAX_PATH+1];
249 if (mpeg_status() & MPEG_STATUS_PLAY)
250 return 0;
252 snprintf(dirname_mp3_filename, sizeof(dirname_mp3_filename), "%s/%s/%s",
253 currdir, dircache[start_index].name, dir_thumbnail_name);
255 DEBUGF("Checking for %s\n", dirname_mp3_filename);
257 fd = open(dirname_mp3_filename, O_RDONLY);
258 if (fd < 0)
260 DEBUGF("Failed to find: %s\n", dirname_mp3_filename);
261 return -1;
264 close(fd);
266 DEBUGF("Found: %s\n", dirname_mp3_filename);
268 talk_file(dirname_mp3_filename, false);
269 return 1;
272 static int play_filename(char *dir, char *file)
274 int fd;
275 char name_mp3_filename[MAX_PATH+1];
277 if (mpeg_status() & MPEG_STATUS_PLAY)
278 return 0;
280 if (strcasecmp(&file[strlen(file) - strlen(TALK_EXT)], TALK_EXT))
281 { /* file has no .talk extension */
282 snprintf(name_mp3_filename, sizeof(name_mp3_filename),
283 "%s/%s" TALK_EXT, dir, file);
285 /* check if a corresponding .talk file exists */
286 DEBUGF("Checking for Filename Thumb %s\n", name_mp3_filename);
287 fd = open(name_mp3_filename, O_RDONLY);
288 if (fd < 0)
290 DEBUGF("Failed to find: %s\n", name_mp3_filename);
291 return -1;
293 DEBUGF("Found: %s\n", name_mp3_filename);
294 close(fd);
295 talk_file(name_mp3_filename, false);
297 else
298 { /* it already is a .talk file, play this directly */
299 snprintf(name_mp3_filename, sizeof(name_mp3_filename),
300 "%s/%s", dir, file);
301 talk_id(LANG_VOICE_DIR_HOVER, false); /* prefix it */
302 talk_file(name_mp3_filename, true);
305 return 1;
308 static int compare(const void* p1, const void* p2)
310 struct entry* e1 = (struct entry*)p1;
311 struct entry* e2 = (struct entry*)p2;
312 int criteria;
314 if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY)
315 { /* two directories */
316 criteria = global_settings.sort_dir;
318 else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY))
319 { /* two files */
320 criteria = global_settings.sort_file;
322 else /* dir and file, dir goes first */
323 return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY );
325 switch(criteria)
327 case 3: /* sort type */
329 int t1 = e1->attr & TREE_ATTR_MASK;
330 int t2 = e2->attr & TREE_ATTR_MASK;
332 if (!t1) /* unknown type */
333 t1 = 0x7FFFFFFF; /* gets a high number, to sort after known */
334 if (!t2) /* unknown type */
335 t2 = 0x7FFFFFFF; /* gets a high number, to sort after known */
337 if (t1 - t2) /* if different */
338 return t1 - t2;
339 /* else fall through to alphabetical sorting */
341 case 0: /* sort alphabetically */
342 if (global_settings.sort_case)
343 return strncmp(e1->name, e2->name, MAX_PATH);
344 else
345 return strncasecmp(e1->name, e2->name, MAX_PATH);
346 case 1: /* sort date */
347 return e1->time_write - e2->time_write;
348 case 2: /* sort date, newest first */
349 return e2->time_write - e1->time_write;
351 return 0; /* never reached */
354 static void showfileline(int line, int direntry, bool scroll, const int *dirfilter)
356 char* name = dircache[direntry].name;
357 int xpos = LINE_X;
358 char* dotpos = NULL;
360 #ifdef HAVE_LCD_CHARCELLS
361 if (!global_settings.show_icons)
362 xpos--;
363 #endif
365 /* if any file filter is on, strip the extension */
366 if (*dirfilter != SHOW_ALL &&
367 !(dircache[direntry].attr & ATTR_DIRECTORY))
369 dotpos = strrchr(name, '.');
370 if (dotpos) {
371 *dotpos = 0;
375 if(scroll) {
376 #ifdef HAVE_LCD_BITMAP
377 lcd_setfont(FONT_UI);
378 if (global_settings.invert_cursor)
379 lcd_puts_scroll_style(xpos, line, name, STYLE_INVERT);
380 else
381 #endif
382 lcd_puts_scroll(xpos, line, name);
383 } else
384 lcd_puts(xpos, line, name);
386 /* Restore the dot before the extension if it was removed */
387 if (dotpos)
388 *dotpos = '.';
391 /* load sorted directory into dircache. returns NULL on failure. */
392 struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter,
393 int *num_files, bool *buffer_full)
395 int i;
397 DIR *dir = opendir(dirname);
398 if(!dir)
399 return NULL; /* not a directory */
401 name_buffer_length = 0;
402 dirsindir = 0;
403 *buffer_full = false;
405 for ( i=0; i < max_files_in_dir; i++ ) {
406 int len;
407 struct dirent *entry = readdir(dir);
408 struct entry* dptr = &dircache[i];
409 if (!entry)
410 break;
412 len = strlen(entry->d_name);
414 /* skip directories . and .. */
415 if ((entry->attribute & ATTR_DIRECTORY) &&
416 (((len == 1) &&
417 (!strncmp(entry->d_name, ".", 1))) ||
418 ((len == 2) &&
419 (!strncmp(entry->d_name, "..", 2))))) {
420 i--;
421 continue;
424 /* Skip FAT volume ID */
425 if (entry->attribute & ATTR_VOLUME_ID) {
426 i--;
427 continue;
430 /* filter out dotfiles and hidden files */
431 if (*dirfilter != SHOW_ALL &&
432 ((entry->d_name[0]=='.') ||
433 (entry->attribute & ATTR_HIDDEN))) {
434 i--;
435 continue;
438 dptr->attr = entry->attribute;
440 /* check for known file types */
441 if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) )
442 dptr->attr |= filetype_get_attr(entry->d_name);
444 /* memorize/compare details about the boot file */
445 if ((currdir[1] == 0) && !strcasecmp(entry->d_name, BOOTFILE)) {
446 if (boot_size) {
447 if ((entry->size != boot_size) ||
448 (entry->startcluster != boot_cluster))
449 boot_changed = true;
451 boot_size = entry->size;
452 boot_cluster = entry->startcluster;
455 /* filter out non-visible files */
456 if (!(dptr->attr & ATTR_DIRECTORY) && (
457 (*dirfilter == SHOW_PLAYLIST &&
458 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
459 ((*dirfilter == SHOW_MUSIC &&
460 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) &&
461 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
462 (*dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)) ||
463 (*dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) ||
464 (*dirfilter == SHOW_CFG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_CFG) ||
465 (*dirfilter == SHOW_LNG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_LNG) ||
466 (*dirfilter == SHOW_MOD && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MOD) ||
467 (*dirfilter == SHOW_FONT && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_FONT) ||
468 (*dirfilter == SHOW_PLUGINS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_ROCK)))
470 i--;
471 continue;
474 if (len > name_buffer_size - name_buffer_length - 1) {
475 /* Tell the world that we ran out of buffer space */
476 *buffer_full = true;
477 break;
479 dptr->name = &name_buffer[name_buffer_length];
480 dptr->time_write = entry->wrtdate<<16 | entry->wrttime; /* in one # */
481 strcpy(dptr->name,entry->d_name);
482 name_buffer_length += len + 1;
484 if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */
485 dirsindir++;
487 *num_files = i;
488 closedir(dir);
489 strncpy(lastdir,dirname,sizeof(lastdir));
490 lastdir[sizeof(lastdir)-1] = 0;
491 qsort(dircache,i,sizeof(struct entry),compare);
493 return dircache;
496 #ifdef HAVE_LCD_BITMAP
497 static int recalc_screen_height(void)
499 int fw, fh;
500 int height = LCD_HEIGHT;
502 lcd_setfont(FONT_UI);
503 lcd_getstringsize("A", &fw, &fh);
504 if(global_settings.statusbar)
505 height -= STATUSBAR_HEIGHT;
507 if(global_settings.buttonbar)
508 height -= BUTTONBAR_HEIGHT;
510 return height / fh;
512 #endif
514 static int showdir(const char *path, int start, const int *dirfilter)
516 int i;
517 int tree_max_on_screen;
518 bool dir_buffer_full;
520 #ifdef HAVE_LCD_BITMAP
521 const char* icon;
522 int line_height;
523 int fw, fh;
524 lcd_setfont(FONT_UI);
525 lcd_getstringsize("A", &fw, &fh);
526 tree_max_on_screen = recalc_screen_height();
527 line_height = fh;
528 #else
529 int icon;
530 tree_max_on_screen = TREE_MAX_ON_SCREEN;
531 #endif
533 /* new dir? cache it */
534 if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) {
535 if (!load_and_sort_directory(path, dirfilter,
536 &filesindir, &dir_buffer_full))
537 return -1;
539 if ( dir_buffer_full || filesindir == max_files_in_dir ) {
540 #ifdef HAVE_LCD_CHARCELLS
541 lcd_double_height(false);
542 #endif
543 lcd_clear_display();
544 lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER));
545 lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL));
546 lcd_update();
547 sleep(HZ*2);
548 lcd_clear_display();
552 if (start == -1)
554 int diff_files;
556 /* use lastfile to determine start (default=0) */
557 start = 0;
559 for (i=0; i<filesindir; i++)
561 if (!strcasecmp(dircache[i].name, lastfile))
563 start = i;
564 break;
568 diff_files = filesindir - start;
569 if (diff_files < tree_max_on_screen)
571 int oldstart = start;
573 start -= (tree_max_on_screen - diff_files);
574 if (start < 0)
575 start = 0;
577 dircursor = oldstart - start;
580 dirstart = start;
583 /* The cursor might point to an invalid line, for example if someone
584 deleted the last file in the dir */
585 if(filesindir)
587 while(start + dircursor >= filesindir)
589 if(start)
590 start--;
591 else
592 if(dircursor)
593 dircursor--;
595 dirstart = start;
598 #ifdef HAVE_LCD_CHARCELLS
599 lcd_stop_scroll();
600 lcd_double_height(false);
601 #endif
602 lcd_clear_display();
603 #ifdef HAVE_LCD_BITMAP
604 lcd_setmargins(MARGIN_X,MARGIN_Y); /* leave room for cursor and icon */
605 lcd_setfont(FONT_UI);
606 #endif
608 for ( i=start; i < start+tree_max_on_screen; i++ ) {
609 if ( i >= filesindir )
610 break;
612 icon = filetype_get_icon(dircache[i].attr);
614 if (icon && global_settings.show_icons) {
615 #ifdef HAVE_LCD_BITMAP
616 int offset=0;
617 if ( line_height > 8 )
618 offset = (line_height - 8) / 2;
619 lcd_bitmap(icon,
620 CURSOR_X * 6 + CURSOR_WIDTH,
621 MARGIN_Y+(i-start)*line_height + offset,
622 6, 8, true);
623 #else
624 if (icon < 0 )
625 icon = Unknown;
626 lcd_putc(LINE_X-1, i-start, icon);
627 #endif
630 showfileline(i-start, i, false, dirfilter); /* no scroll */
633 #ifdef HAVE_LCD_BITMAP
634 if (global_settings.scrollbar && (filesindir > tree_max_on_screen))
635 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
636 tree_max_on_screen * line_height, filesindir, start,
637 start + tree_max_on_screen, VERTICAL);
639 #if CONFIG_KEYPAD == RECORDER_PAD
640 if(global_settings.buttonbar) {
641 buttonbar_set(*dirfilter < NUM_FILTER_MODES ?
642 str(LANG_DIRBROWSE_F1) : (unsigned char *) "",
643 str(LANG_DIRBROWSE_F2),
644 str(LANG_DIRBROWSE_F3));
645 buttonbar_draw();
647 #endif
648 #endif
649 status_draw(true);
651 return filesindir;
654 static bool ask_resume(bool ask_once)
656 int button;
657 bool stop = false;
659 #ifdef HAVE_LCD_CHARCELLS
660 lcd_double_height(false);
661 #endif
663 if (usb_detect()) {
664 default_event_handler(SYS_USB_CONNECTED);
665 return false;
668 /* always resume? */
669 if ( global_settings.resume == RESUME_ON )
670 return true;
672 lcd_clear_display();
673 lcd_puts(0,0,str(LANG_RESUME_ASK));
674 #ifdef HAVE_LCD_CHARCELLS
675 status_draw(false);
676 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
677 #else
678 lcd_puts(0,1,str(LANG_CONFIRM_WITH_PLAY_RECORDER));
679 lcd_puts(0,2,str(LANG_CANCEL_WITH_ANY_RECORDER));
680 #endif
681 lcd_update();
683 while (!stop) {
684 button = button_get(true);
685 switch (button) {
686 case TREE_RUN:
687 #ifdef TREE_RC_RUN
688 case TREE_RC_RUN:
689 #endif
690 return true;
692 #ifdef BUTTON_ON
693 /* ignore the ON button, since it might
694 still be pressed since booting */
695 case BUTTON_ON:
696 case BUTTON_ON | BUTTON_REL:
697 case BUTTON_ON | BUTTON_REPEAT:
698 break;
699 #endif
700 default:
701 if(default_event_handler(button) || (button & BUTTON_REL))
702 stop = true;
703 break;
707 if ( global_settings.resume == RESUME_ASK_ONCE && ask_once) {
708 global_settings.resume_index = -1;
709 settings_save();
712 return false;
715 /* load tracks from specified directory to resume play */
716 void resume_directory(const char *dir)
718 bool buffer_full;
720 if (!load_and_sort_directory(dir, &global_settings.dirfilter, &filesindir,
721 &buffer_full))
722 return;
723 lastdir[0] = 0;
725 build_playlist(0);
728 /* Returns the current working directory and also writes cwd to buf if
729 non-NULL. In case of error, returns NULL. */
730 char *getcwd(char *buf, int size)
732 if (!buf)
733 return currdir;
734 else if (size > 0)
736 strncpy(buf, currdir, size);
737 return buf;
739 else
740 return NULL;
743 /* Force a reload of the directory next time directory browser is called */
744 void reload_directory(void)
746 reload_dir = true;
749 static void start_resume(bool ask_once)
751 if ( global_settings.resume &&
752 global_settings.resume_index != -1 ) {
753 DEBUGF("Resume index %X offset %X\n",
754 global_settings.resume_index,
755 global_settings.resume_offset);
757 if (!ask_resume(ask_once))
758 return;
760 if (playlist_resume() != -1)
762 playlist_start(global_settings.resume_index,
763 global_settings.resume_offset);
765 start_wps = true;
767 else
768 return;
772 void set_current_file(char *path)
774 char *name;
775 unsigned int i;
777 /* separate directory from filename */
778 name = strrchr(path+1,'/');
779 if (name)
781 *name = 0;
782 strcpy(currdir, path);
783 *name = '/';
784 name++;
786 else
788 strcpy(currdir, "/");
789 name = path+1;
792 strcpy(lastfile, name);
794 dircursor = 0;
795 dirstart = -1;
797 if (strncmp(currdir,lastdir,sizeof(lastdir)))
799 dirlevel = 0;
800 dirpos[dirlevel] = -1;
801 cursorpos[dirlevel] = 0;
803 /* use '/' to calculate dirlevel */
804 for (i=1; i<strlen(path)+1; i++)
806 if (path[i] == '/')
808 dirlevel++;
809 dirpos[dirlevel] = -1;
810 cursorpos[dirlevel] = 0;
816 static bool dirbrowse(const char *root, const int *dirfilter)
818 int numentries=0;
819 char buf[MAX_PATH];
820 int i;
821 int lasti=-1;
822 int button;
823 int tree_max_on_screen;
824 bool reload_root = false;
825 int lastfilter = *dirfilter;
826 bool lastsortcase = global_settings.sort_case;
827 int lastdircursor=-1;
828 bool need_update = true;
829 bool exit_func = false;
830 long thumbnail_time = -1; /* for delaying a thumbnail */
831 bool update_all = false; /* set this to true when the whole file list
832 has been refreshed on screen */
833 int lastbutton = 0;
835 #ifdef HAVE_LCD_BITMAP
836 tree_max_on_screen = recalc_screen_height();
837 #else
838 tree_max_on_screen = TREE_MAX_ON_SCREEN;
839 #endif
841 dircursor=0;
842 dirstart=0;
843 dirlevel=0;
845 memcpy(currdir,root,sizeof(currdir));
847 if (*dirfilter < NUM_FILTER_MODES)
848 start_resume(true);
850 numentries = showdir(currdir, dirstart, dirfilter);
851 if (numentries == -1)
852 return false; /* currdir is not a directory */
854 if (*dirfilter > NUM_FILTER_MODES && numentries==0)
856 splash(HZ*2, true, str(LANG_NO_FILES));
857 return false; /* No files found for rockbox_browser() */
860 update_all = true;
862 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
864 while(1) {
865 struct entry* file = &dircache[dircursor+dirstart];
867 bool restore = false;
869 button = button_get_w_tmo(HZ/5);
871 /* ignore leftover release event */
872 if (!lastbutton && (button & BUTTON_REL))
873 continue;
875 #ifndef SIMULATOR
876 if (boot_changed) {
877 bool stop = false;
878 int button;
880 lcd_clear_display();
881 lcd_puts(0,0,str(LANG_BOOT_CHANGED));
882 lcd_puts(0,1,str(LANG_REBOOT_NOW));
883 #ifdef HAVE_LCD_BITMAP
884 lcd_puts(0,3,str(LANG_CONFIRM_WITH_PLAY_RECORDER));
885 lcd_puts(0,4,str(LANG_CANCEL_WITH_ANY_RECORDER));
886 lcd_update();
887 #endif
888 while (!stop) {
889 button = button_get(true);
890 switch (button) {
891 case TREE_RUN:
892 rolo_load("/" BOOTFILE);
893 stop = true;
894 break;
896 default:
897 if(default_event_handler(button) ||
898 (button & BUTTON_REL))
899 stop = true;
900 break;
904 restore = true;
905 boot_changed = false;
907 #endif
909 switch ( button ) {
910 case TREE_EXIT:
911 case TREE_EXIT | BUTTON_REPEAT:
912 #ifdef TREE_RC_EXIT
913 case TREE_RC_EXIT:
914 #endif
915 i=strlen(currdir);
916 if (i>1) {
917 while (currdir[i-1]!='/')
918 i--;
919 strcpy(buf,&currdir[i]);
920 if (i==1)
921 currdir[i]=0;
922 else
923 currdir[i-1]=0;
925 if (*dirfilter > NUM_FILTER_MODES && dirlevel < 1)
926 exit_func = true;
928 dirlevel--;
929 if ( dirlevel < MAX_DIR_LEVELS ) {
930 dirstart = dirpos[dirlevel];
931 dircursor = cursorpos[dirlevel];
933 else
934 dirstart = dircursor = 0;
936 if (dirstart == -1)
937 strcpy(lastfile, buf);
939 restore = true;
941 else
943 if (*dirfilter > NUM_FILTER_MODES && dirlevel < 1)
944 exit_func = true;
946 break;
948 #ifdef TREE_OFF
949 case TREE_OFF:
950 /* Stop the music if it is playing, else show the shutdown
951 screen */
952 if(mpeg_status())
953 mpeg_stop();
954 else {
955 if (!charger_inserted()) {
956 shutdown_screen();
957 } else {
958 charging_splash();
960 restore = true;
962 break;
964 case TREE_OFF | BUTTON_REPEAT:
965 if (charger_inserted()) {
966 charging_splash();
967 restore = true;
969 break;
970 #endif
972 #ifdef TREE_ENTER
973 case TREE_ENTER:
974 case TREE_ENTER | BUTTON_REPEAT:
975 #endif
976 #ifdef TREE_RC_ENTER
977 case TREE_RC_ENTER:
978 #endif
979 case TREE_RUN:
980 if ( !numentries )
981 break;
982 if (currdir[1])
983 snprintf(buf,sizeof(buf),"%s/%s",currdir, file->name);
984 else
985 snprintf(buf,sizeof(buf),"/%s",file->name);
987 if (file->attr & ATTR_DIRECTORY) {
988 memcpy(currdir,buf,sizeof(currdir));
989 if ( dirlevel < MAX_DIR_LEVELS ) {
990 dirpos[dirlevel] = dirstart;
991 cursorpos[dirlevel] = dircursor;
993 dirlevel++;
994 dircursor=0;
995 dirstart=0;
997 else {
998 int seed = current_tick;
999 bool play = false;
1000 int start_index=0;
1002 lcd_stop_scroll();
1003 switch ( file->attr & TREE_ATTR_MASK ) {
1004 case TREE_ATTR_M3U:
1005 if (bookmark_autoload(buf))
1007 restore = true;
1008 break;
1011 if (playlist_create(currdir, file->name) != -1)
1013 if (global_settings.playlist_shuffle)
1014 playlist_shuffle(seed, -1);
1015 start_index = 0;
1016 playlist_start(start_index,0);
1017 play = true;
1019 break;
1021 case TREE_ATTR_MPA:
1022 if (bookmark_autoload(currdir))
1024 restore = true;
1025 break;
1028 if (playlist_create(currdir, NULL) != -1)
1030 start_index =
1031 build_playlist(dircursor+dirstart);
1032 if (global_settings.playlist_shuffle)
1034 start_index =
1035 playlist_shuffle(seed,start_index);
1037 /* when shuffling dir.: play all files
1038 even if the file selected by user is
1039 not the first one */
1040 if (!global_settings.play_selected)
1041 start_index = 0;
1044 playlist_start(start_index, 0);
1045 play = true;
1047 break;
1049 /* wps config file */
1050 case TREE_ATTR_WPS:
1051 wps_load(buf,true);
1052 set_file(buf, global_settings.wps_file,
1053 MAX_FILENAME);
1054 restore = true;
1055 break;
1057 case TREE_ATTR_CFG:
1058 if (!settings_load_config(buf))
1059 break;
1060 lcd_clear_display();
1061 lcd_puts(0,0,str(LANG_SETTINGS_LOADED1));
1062 lcd_puts(0,1,str(LANG_SETTINGS_LOADED2));
1063 #ifdef HAVE_LCD_BITMAP
1064 lcd_update();
1066 /* maybe we have a new font */
1067 tree_max_on_screen = recalc_screen_height();
1068 /* make sure cursor is on screen */
1069 while ( dircursor > tree_max_on_screen )
1071 dircursor--;
1072 dirstart++;
1074 #endif
1075 sleep(HZ/2);
1076 restore = true;
1077 break;
1079 case TREE_ATTR_BMARK:
1080 bookmark_load(buf, false);
1081 restore = true;
1082 reload_dir = true;
1083 break;
1085 case TREE_ATTR_LNG:
1086 if(!lang_load(buf)) {
1087 set_file(buf, global_settings.lang_file,
1088 MAX_FILENAME);
1089 talk_init(); /* use voice of same language */
1090 splash(HZ, true, str(LANG_LANGUAGE_LOADED));
1091 restore = true;
1093 language_changed = true;
1095 break;
1097 #ifdef HAVE_LCD_BITMAP
1098 case TREE_ATTR_FONT:
1099 font_load(buf);
1100 set_file(buf, global_settings.font_file,
1101 MAX_FILENAME);
1103 tree_max_on_screen = recalc_screen_height();
1104 /* make sure cursor is on screen */
1105 while ( dircursor > tree_max_on_screen ) {
1106 dircursor--;
1107 dirstart++;
1109 restore = true;
1110 break;
1111 #endif
1113 #ifndef SIMULATOR
1114 /* firmware file */
1115 case TREE_ATTR_MOD:
1116 rolo_load(buf);
1117 break;
1118 #endif
1120 /* plugin file */
1121 case TREE_ATTR_ROCK:
1122 if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED)
1123 reload_root = true;
1124 else
1125 restore = true;
1126 break;
1128 default:
1130 char* plugin = filetype_get_plugin(file);
1131 if (plugin)
1133 if (plugin_load(plugin,buf) == PLUGIN_USB_CONNECTED)
1134 reload_root = true;
1135 else
1136 restore = true;
1138 break;
1142 if ( play ) {
1143 if ( global_settings.resume ) {
1144 /* the resume_index must always be the index in the
1145 shuffled list in case shuffle is enabled */
1146 global_settings.resume_index = start_index;
1147 global_settings.resume_offset = 0;
1148 settings_save();
1151 start_wps = true;
1153 else if (*dirfilter > NUM_FILTER_MODES &&
1154 *dirfilter != SHOW_FONT &&
1155 *dirfilter != SHOW_PLUGINS)
1156 exit_func = true;
1158 restore = true;
1159 break;
1161 case TREE_PREV:
1162 case TREE_PREV | BUTTON_REPEAT:
1163 #ifdef TREE_RC_PREV
1164 case TREE_RC_PREV:
1165 case TREE_RC_PREV | BUTTON_REPEAT:
1166 #endif
1167 if(filesindir) {
1168 if(dircursor) {
1169 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false);
1170 dircursor--;
1171 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1173 else {
1174 if (dirstart) {
1175 dirstart--;
1176 numentries = showdir(currdir, dirstart, dirfilter);
1177 update_all=true;
1178 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1180 else {
1181 if (numentries < tree_max_on_screen) {
1182 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor,
1183 false);
1184 dircursor = numentries - 1;
1185 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor,
1186 true);
1188 else {
1189 dirstart = numentries - tree_max_on_screen;
1190 dircursor = tree_max_on_screen - 1;
1191 numentries = showdir(currdir, dirstart, dirfilter);
1192 update_all = true;
1193 put_cursorxy(CURSOR_X, CURSOR_Y +
1194 tree_max_on_screen - 1, true);
1198 need_update = true;
1200 break;
1202 case TREE_NEXT:
1203 case TREE_NEXT | BUTTON_REPEAT:
1204 #ifdef TREE_RC_NEXT
1205 case TREE_RC_NEXT:
1206 case TREE_RC_NEXT | BUTTON_REPEAT:
1207 #endif
1208 if(filesindir)
1210 if (dircursor + dirstart + 1 < numentries ) {
1211 if(dircursor+1 < tree_max_on_screen) {
1212 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false);
1213 dircursor++;
1214 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1216 else {
1217 dirstart++;
1218 numentries = showdir(currdir, dirstart, dirfilter);
1219 update_all = true;
1220 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1223 else {
1224 if(numentries < tree_max_on_screen) {
1225 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false);
1226 dirstart = dircursor = 0;
1227 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1229 else {
1230 dirstart = dircursor = 0;
1231 numentries = showdir(currdir, dirstart, dirfilter);
1232 update_all=true;
1233 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1236 need_update = true;
1238 break;
1240 #ifdef TREE_PGUP
1241 case TREE_PGUP:
1242 case TREE_PGUP | BUTTON_REPEAT:
1243 if ( dirstart ) {
1244 dirstart -= tree_max_on_screen;
1245 if ( dirstart < 0 )
1246 dirstart = 0;
1248 else
1249 dircursor = 0;
1250 restore = true;
1251 break;
1253 case TREE_PGDN:
1254 case TREE_PGDN | BUTTON_REPEAT:
1255 if ( dirstart < numentries - tree_max_on_screen ) {
1256 dirstart += tree_max_on_screen;
1257 if ( dirstart >
1258 numentries - tree_max_on_screen )
1259 dirstart = numentries - tree_max_on_screen;
1261 else
1262 dircursor = numentries - dirstart - 1;
1263 restore = true;
1264 break;
1265 #endif
1267 case TREE_MENU:
1268 if (*dirfilter < NUM_FILTER_MODES)
1270 lcd_stop_scroll();
1271 if (main_menu())
1272 reload_root = true;
1273 restore = true;
1275 break;
1277 case TREE_WPS:
1278 #ifdef TREE_WPS_PRE
1279 if (lastbutton != TREE_WPS_PRE)
1280 break;
1281 #endif
1282 /* don't enter wps from plugin browser etc */
1283 if (*dirfilter < NUM_FILTER_MODES)
1285 if (mpeg_status() & MPEG_STATUS_PLAY)
1287 start_wps=true;
1289 else
1291 start_resume(false);
1292 restore = true;
1295 break;
1297 #ifdef BUTTON_F2
1298 case BUTTON_F2:
1299 if (*dirfilter < NUM_FILTER_MODES)
1301 if (quick_screen(CONTEXT_TREE, BUTTON_F2))
1302 reload_root = true;
1303 restore = true;
1304 break;
1307 case BUTTON_F3:
1308 if (*dirfilter < NUM_FILTER_MODES)
1310 if (quick_screen(CONTEXT_TREE, BUTTON_F3))
1311 reload_root = true;
1312 tree_max_on_screen = recalc_screen_height();
1313 restore = true;
1315 break;
1316 #endif
1318 case TREE_CONTEXT:
1319 #ifdef TREE_CONTEXT2
1320 case TREE_CONTEXT2:
1321 #endif
1323 int onplay_result;
1325 if(!numentries)
1326 onplay_result = onplay(NULL, 0);
1327 else {
1328 if (currdir[1])
1329 snprintf(buf, sizeof buf, "%s/%s",
1330 currdir, dircache[dircursor+dirstart].name);
1331 else
1332 snprintf(buf, sizeof buf, "/%s",
1333 dircache[dircursor+dirstart].name);
1334 onplay_result = onplay(buf,
1335 dircache[dircursor+dirstart].attr);
1338 switch (onplay_result)
1340 case ONPLAY_OK:
1341 restore = true;
1342 break;
1344 case ONPLAY_RELOAD_DIR:
1345 reload_dir = true;
1346 break;
1348 case ONPLAY_START_PLAY:
1349 start_wps = true;
1350 break;
1352 break;
1355 case BUTTON_NONE:
1356 if (thumbnail_time != -1 &&
1357 TIME_AFTER(current_tick, thumbnail_time))
1358 { /* a delayed hovering thumbnail is due now */
1359 int res;
1360 if (dircache[lasti].attr & ATTR_DIRECTORY)
1362 DEBUGF("Playing directory thumbnail: %s", currdir);
1363 res = play_dirname(lasti);
1364 if (res < 0) /* failed, not existing */
1365 { /* say the number instead, as a fallback */
1366 talk_id(VOICE_DIR, false);
1367 talk_number(lasti+1, true);
1370 else
1372 DEBUGF("Playing file thumbnail: %s/%s%s\n",
1373 currdir, dircache[lasti].name, TALK_EXT);
1374 res = play_filename(currdir, dircache[lasti].name);
1375 if (res < 0) /* failed, not existing */
1376 { /* say the number instead, as a fallback */
1377 play_filenumber(lasti-dirsindir+1,
1378 dircache[lasti].attr);
1381 thumbnail_time = -1; /* job done */
1383 status_draw(false);
1384 break;
1386 default:
1387 if(default_event_handler(button) == SYS_USB_CONNECTED)
1388 reload_root = true;
1389 break;
1392 if ( button )
1394 ata_spin();
1395 lastbutton = button;
1398 if (start_wps)
1400 lcd_stop_scroll();
1401 if (wps_show() == SYS_USB_CONNECTED)
1402 reload_root = true;
1403 #ifdef HAVE_LCD_BITMAP
1404 tree_max_on_screen = recalc_screen_height();
1405 #endif
1406 restore = true;
1407 start_wps=false;
1410 /* do we need to rescan dir? */
1411 if (reload_dir || reload_root ||
1412 lastfilter != *dirfilter ||
1413 lastsortcase != global_settings.sort_case)
1415 if ( reload_root ) {
1416 strcpy(currdir, "/");
1417 dirlevel = 0;
1418 reload_root = false;
1420 if (! reload_dir )
1422 dircursor = 0;
1423 dirstart = 0;
1424 lastdir[0] = 0;
1427 lastfilter = *dirfilter;
1428 lastsortcase = global_settings.sort_case;
1429 restore = true;
1430 while (button_get(false)); /* clear button queue */
1433 if (exit_func)
1434 break;
1436 if (restore || reload_dir) {
1437 /* restore display */
1439 #ifdef HAVE_LCD_BITMAP
1440 tree_max_on_screen = recalc_screen_height();
1441 #endif
1443 /* We need to adjust if the number of lines on screen have
1444 changed because of a status bar change */
1445 if(CURSOR_Y+LINE_Y+dircursor>tree_max_on_screen) {
1446 dirstart++;
1447 dircursor--;
1449 #ifdef HAVE_LCD_BITMAP
1450 /* the sub-screen might've ruined the margins */
1451 lcd_setmargins(MARGIN_X,MARGIN_Y); /* leave room for cursor and
1452 icon */
1453 lcd_setfont(FONT_UI);
1454 #endif
1455 numentries = showdir(currdir, dirstart, dirfilter);
1456 update_all = true;
1457 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true);
1459 need_update = true;
1460 reload_dir = false;
1463 if ( numentries && need_update) {
1464 i = dirstart+dircursor;
1466 /* if MP3 filter is on, cut off the extension */
1467 if(lasti!=i || restore) {
1468 lcd_stop_scroll();
1470 /* So if lastdircursor and dircursor differ, and then full
1471 screen was not refreshed, restore the previous line */
1472 if ((lastdircursor != dircursor) && !update_all ) {
1473 showfileline(lastdircursor, lasti, false, dirfilter); /* no scroll */
1475 lasti=i;
1476 lastdircursor=dircursor;
1477 thumbnail_time = -1; /* cancel whatever we were about to say */
1479 showfileline(dircursor, i, true, dirfilter); /* scroll please */
1480 need_update = true;
1482 if (dircache[i].attr & ATTR_DIRECTORY) /* directory? */
1484 /* play directory thumbnail */
1485 if (global_settings.talk_dir == 3) /* hover */
1486 { /* "schedule" a thumbnail, to have a little dalay */
1487 thumbnail_time = current_tick + HOVER_DELAY;
1489 else if (global_settings.talk_dir == 1) /* dirs as numbers */
1491 talk_id(VOICE_DIR, false);
1492 talk_number(i+1, true);
1494 else if (global_settings.talk_dir == 2) /* dirs spelled */
1496 talk_spell(dircache[i].name, false);
1499 else if (global_settings.talk_file == 1) /* files as numbers */
1501 play_filenumber(i-dirsindir+1,
1502 dircache[i].attr & TREE_ATTR_MASK);
1504 else if (global_settings.talk_file == 2) /* files spelled */
1506 talk_spell(dircache[i].name, false);
1508 else if (global_settings.talk_file == 3) /* hover */
1509 { /* "schedule" a thumbnail, to have a little dalay */
1510 thumbnail_time = current_tick + HOVER_DELAY;
1516 if(need_update) {
1517 lcd_update();
1519 need_update = false;
1520 update_all = false;
1524 return true;
1527 static int plsize = 0;
1528 static bool add_dir(char* dirname, int len, int fd)
1530 bool abort = false;
1531 DIR* dir;
1533 /* check for user abort */
1534 #ifdef BUTTON_STOP
1535 if (button_get(false) == BUTTON_STOP)
1536 #else
1537 if (button_get(false) == BUTTON_OFF)
1538 #endif
1539 return true;
1541 dir = opendir(dirname);
1542 if(!dir)
1543 return true;
1545 while (true) {
1546 struct dirent *entry;
1548 entry = readdir(dir);
1549 if (!entry)
1550 break;
1551 if (entry->attribute & ATTR_DIRECTORY) {
1552 int dirlen = strlen(dirname);
1553 bool result;
1555 if (!strcmp(entry->d_name, ".") ||
1556 !strcmp(entry->d_name, ".."))
1557 continue;
1559 if (dirname[1])
1560 snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name);
1561 else
1562 snprintf(dirname, len, "/%s", entry->d_name);
1564 result = add_dir(dirname, len, fd);
1565 dirname[dirlen] = '\0';
1566 if (result) {
1567 abort = true;
1568 break;
1571 else {
1572 int x = strlen(entry->d_name);
1573 if ((!strcasecmp(&entry->d_name[x-4], ".mp3")) ||
1574 (!strcasecmp(&entry->d_name[x-4], ".mp2")) ||
1575 (!strcasecmp(&entry->d_name[x-4], ".mpa")))
1577 char buf[8];
1578 write(fd, dirname, strlen(dirname));
1579 write(fd, "/", 1);
1580 write(fd, entry->d_name, x);
1581 write(fd, "\n", 1);
1583 plsize++;
1584 snprintf(buf, sizeof buf, "%d", plsize);
1585 #ifdef HAVE_LCD_BITMAP
1586 lcd_puts(0,4,buf);
1587 lcd_update();
1588 #else
1589 x = 10;
1590 if (plsize > 999)
1591 x=7;
1592 else {
1593 if (plsize > 99)
1594 x=8;
1595 else {
1596 if (plsize > 9)
1597 x=9;
1600 lcd_puts(x,0,buf);
1601 #endif
1605 closedir(dir);
1607 return abort;
1610 bool create_playlist(void)
1612 int fd;
1613 char filename[MAX_PATH];
1615 snprintf(filename, sizeof filename, "%s.m3u",
1616 currdir[1] ? currdir : "/root");
1618 lcd_clear_display();
1619 lcd_puts(0,0,str(LANG_CREATING));
1620 lcd_puts_scroll(0,1,filename);
1621 lcd_update();
1623 fd = creat(filename,0);
1624 if (fd < 0)
1625 return false;
1627 snprintf(filename, sizeof(filename), "%s", currdir[1] ? currdir : "/");
1628 plsize = 0;
1629 add_dir(filename, sizeof(filename), fd);
1630 close(fd);
1631 sleep(HZ);
1633 return true;
1636 bool rockbox_browse(const char *root, int dirfilter)
1638 bool rc;
1639 int dircursor_save = dircursor;
1640 int dirstart_save = dirstart;
1641 int dirlevel_save = dirlevel;
1642 int dirpos_save = dirpos[0];
1643 int cursorpos_save = cursorpos[0];
1645 memcpy(currdir_save, currdir, sizeof(currdir));
1646 reload_dir = true;
1647 start_wps = false;
1648 rc = dirbrowse(root, &dirfilter);
1649 memcpy(currdir, currdir_save, sizeof(currdir));
1651 reload_dir = true;
1652 dirstart = dirstart_save;
1653 cursorpos[0] = cursorpos_save;
1654 dirlevel = dirlevel_save;
1655 dircursor = dircursor_save;
1656 dirpos[0] = dirpos_save;
1658 return false;
1661 void tree_init(void)
1663 /* We copy the settings value in case it is changed by the user. We can't
1664 use it until the next reboot. */
1665 max_files_in_dir = global_settings.max_files_in_dir;
1666 name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files_in_dir;
1668 name_buffer = buffer_alloc(name_buffer_size);
1669 dircache = buffer_alloc(max_files_in_dir * sizeof(struct entry));
1672 void bookmark_play(char *resume_file, int index, int offset, int seed)
1674 int len=strlen(resume_file);
1676 if (!strcasecmp(&resume_file[len-4], ".m3u"))
1678 char* slash;
1679 // check that the file exists
1680 int fd = open(resume_file, O_RDONLY);
1681 if(fd<0)
1682 return;
1683 close(fd);
1685 slash = strrchr(resume_file,'/');
1686 if (slash)
1688 char* cp;
1689 *slash=0;
1691 cp=resume_file;
1692 if (!cp[0])
1693 cp="/";
1695 if (playlist_create(cp, slash+1) != -1)
1697 if (global_settings.playlist_shuffle)
1698 playlist_shuffle(seed, -1);
1699 playlist_start(index,offset);
1701 *slash='/';
1704 else
1706 lastdir[0]='\0';
1707 if (playlist_create(resume_file, NULL) != -1)
1709 resume_directory(resume_file);
1710 if (global_settings.playlist_shuffle)
1711 playlist_shuffle(seed, -1);
1712 playlist_start(index,offset);
1716 start_wps=true;