Don't die on opendir() failure. Index .mp2 files too.
[kugel-rb.git] / apps / bookmark.c
blob969371eeeb06ce8ac6065def0b515273e57c95b8
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
9 * Copyright (C) 2003 by Benjamin Metzler
11 * All files in this archive are subject to the GNU General Public License.
12 * See the file COPYING in the source tree root for full license agreement.
14 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
15 * KIND, either express or implied.
17 ****************************************************************************/
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdbool.h>
25 #include "applimits.h"
26 #include "lcd.h"
27 #include "button.h"
28 #include "usb.h"
29 #include "mpeg.h"
30 #include "wps.h"
31 #include "settings.h"
32 #include "bookmark.h"
33 #include "dir.h"
34 #include "status.h"
35 #include "system.h"
36 #include "errno.h"
37 #include "icons.h"
38 #include "atoi.h"
39 #include "string.h"
40 #include "menu.h"
41 #include "lang.h"
42 #include "screens.h"
43 #include "status.h"
44 #include "debug.h"
45 #include "kernel.h"
46 #include "sprintf.h"
47 #include "talk.h"
48 #include "misc.h"
50 #define MAX_BOOKMARKS 10
51 #define MAX_BOOKMARK_SIZE 350
52 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
54 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark);
55 static bool bookmark_load_menu(void);
56 static bool check_bookmark(const char* bookmark);
57 static char* create_bookmark(void);
58 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
59 static void display_bookmark(const char* bookmark,
60 int bookmark_id,
61 int bookmark_count);
62 static void say_bookmark(const char* bookmark,
63 int bookmark_id);
64 static bool generate_bookmark_file_name(const char *in);
65 static char* get_bookmark(const char* bookmark_file, int bookmark_count);
66 static bool parse_bookmark(const char *bookmark,
67 int *resume_index,
68 int *resume_offset,
69 int *resume_seed,
70 int *resume_first_index,
71 char* resume_file,
72 unsigned int resume_file_size,
73 int* ms,
74 int * repeat_mode,
75 bool *shuffle,
76 char* file_name);
77 static char* select_bookmark(const char* bookmark_file_name);
78 static bool system_check(void);
79 static bool write_bookmark(bool create_bookmark_file);
80 static int get_bookmark_count(const char* bookmark_file_name);
82 static char global_temp_buffer[MAX_PATH+1];
83 static char global_bookmark_file_name[MAX_PATH];
84 static char global_read_buffer[MAX_BOOKMARK_SIZE];
85 static char global_bookmark[MAX_BOOKMARK_SIZE];
86 static char global_filename[MAX_PATH];
88 /* ----------------------------------------------------------------------- */
89 /* Displays the bookmark menu options for the user to decide. This is an */
90 /* interface function. */
91 /* ----------------------------------------------------------------------- */
92 bool bookmark_menu(void)
94 int m;
95 bool result;
97 static const struct menu_item items[] = {
98 { ID2P(LANG_BOOKMARK_MENU_CREATE), bookmark_create_menu},
99 { ID2P(LANG_BOOKMARK_MENU_LIST), bookmark_load_menu},
100 { ID2P(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS), bookmark_mrb_load},
103 m=menu_init( items, sizeof items / sizeof(struct menu_item), NULL,
104 NULL, NULL, NULL);
106 #ifdef HAVE_LCD_CHARCELLS
107 status_set_param(true);
108 #endif
109 result = menu_run(m);
110 #ifdef HAVE_LCD_CHARCELLS
111 status_set_param(false);
112 #endif
113 menu_exit(m);
115 settings_save();
117 return result;
120 /* ----------------------------------------------------------------------- */
121 /* This is the interface function from the main menu. */
122 /* ----------------------------------------------------------------------- */
123 bool bookmark_create_menu(void)
125 write_bookmark(true);
126 return false;
129 /* ----------------------------------------------------------------------- */
130 /* This function acts as the load interface from the main menu */
131 /* This function determines the bookmark file name and then loads that file*/
132 /* for the user. The user can then select a bookmark to load. */
133 /* If no file/directory is currently playing, the menu item does not work. */
134 /* ----------------------------------------------------------------------- */
135 static bool bookmark_load_menu(void)
137 bool success = true;
138 int offset;
139 int seed;
140 int index;
141 char* bookmark;
143 if(!system_check())
144 return false;
145 else
147 char* name = playlist_get_name(NULL, global_temp_buffer,
148 sizeof(global_temp_buffer));
149 if (generate_bookmark_file_name(name))
151 bookmark = select_bookmark(global_bookmark_file_name);
152 if (!bookmark)
153 return false; /* User exited without selecting a bookmark */
155 success = parse_bookmark(bookmark,
156 &index,
157 &offset,
158 &seed,
159 NULL,
160 global_temp_buffer,
161 sizeof(global_temp_buffer),
162 NULL,
163 &global_settings.repeat_mode,
164 &global_settings.playlist_shuffle,
165 global_filename);
167 else
169 /* something bad happened while creating bookmark name*/
170 success = false;
173 if (success)
174 bookmark_play(global_temp_buffer, index, offset, seed,
175 global_filename);
178 return success;
181 /* ----------------------------------------------------------------------- */
182 /* Gives the user a list of the Most Recent Bookmarks. This is an */
183 /* interface function */
184 /* ----------------------------------------------------------------------- */
185 bool bookmark_mrb_load()
187 bool success = true;
188 int offset;
189 int seed;
190 int index;
191 char* bookmark;
193 bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
194 if (!bookmark)
195 return false; /* User exited without selecting a bookmark */
197 success = parse_bookmark(bookmark,
198 &index,
199 &offset,
200 &seed,
201 NULL,
202 global_temp_buffer,
203 sizeof(global_temp_buffer),
204 NULL,
205 &global_settings.repeat_mode,
206 &global_settings.playlist_shuffle,
207 global_filename);
209 if (success)
210 bookmark_play(global_temp_buffer, index, offset, seed,
211 global_filename);
213 return success;
217 /* ----------------------------------------------------------------------- */
218 /* This function handles an autobookmark creation. This is an interface */
219 /* function. */
220 /* ----------------------------------------------------------------------- */
221 bool bookmark_autobookmark(void)
223 /* prompts the user as to create a bookmark */
224 bool done = false;
225 int key = 0;
227 if (!system_check())
228 return false;
230 mpeg_pause(); /* first pause playback */
231 switch (global_settings.autocreatebookmark)
233 case BOOKMARK_YES:
234 return write_bookmark(true);
236 case BOOKMARK_NO:
237 return false;
239 case BOOKMARK_RECENT_ONLY_YES:
240 return write_bookmark(false);
243 /* Prompting user to confirm bookmark creation */
244 lcd_clear_display();
245 #ifdef HAVE_LCD_BITMAP
246 lcd_setmargins(0, STATUSBAR_HEIGHT);
247 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
248 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
249 lcd_puts(0,2, str(LANG_CANCEL_WITH_ANY_RECORDER));
250 #else
251 status_draw(false);
252 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
253 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
254 #endif
255 lcd_update();
257 while (!done)
259 /* Wait for a key to be pushed */
260 key = button_get(true);
261 switch (key)
263 case SETTINGS_OK:
264 if (global_settings.autocreatebookmark ==
265 BOOKMARK_RECENT_ONLY_ASK)
266 return write_bookmark(false);
267 else
268 return write_bookmark(true);
269 break;
271 default:
272 /* Handle sys events, ignore button releases & repeats */
273 if(default_event_handler(key) ||
274 !(key & (BUTTON_REL|BUTTON_REPEAT)))
275 done = true;
276 break;
279 return false;
282 /* ----------------------------------------------------------------------- */
283 /* This function takes the current current resume information and writes */
284 /* that to the beginning of the bookmark file. */
285 /* This file will contain N number of bookmarks in the following format: */
286 /* resume_index*resume_offset*resume_seed*resume_first_index* */
287 /* resume_file*milliseconds*MP3 Title* */
288 /* ------------------------------------------------------------------------*/
289 static bool write_bookmark(bool create_bookmark_file)
291 bool success=false;
292 char* bookmark;
294 if (!system_check())
295 return false; /* something didn't happen correctly, do nothing */
297 bookmark = create_bookmark();
298 if (!bookmark)
299 return false; /* something didn't happen correctly, do nothing */
301 if (global_settings.usemrb)
302 success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark);
305 /* writing the bookmark */
306 if (create_bookmark_file)
308 char* name = playlist_get_name(NULL, global_temp_buffer,
309 sizeof(global_temp_buffer));
310 if (generate_bookmark_file_name(name))
312 success = add_bookmark(global_bookmark_file_name, bookmark);
316 if (success)
317 splash(HZ, true, str(LANG_BOOKMARK_CREATE_SUCCESS));
318 else
319 splash(HZ, true, str(LANG_BOOKMARK_CREATE_FAILURE));
321 return true;
324 /* ----------------------------------------------------------------------- */
325 /* This function adds a bookmark to a file. */
326 /* ------------------------------------------------------------------------*/
327 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark)
329 int temp_bookmark_file = 0;
330 int bookmark_file = 0;
331 int bookmark_count = 0;
332 char* playlist = NULL;
333 char* cp;
334 char* tmp;
335 int len = 0;
336 bool unique = false;
338 /* Opening up a temp bookmark file */
339 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
340 "%s.tmp", bookmark_file_name);
341 temp_bookmark_file = open(global_temp_buffer,
342 O_WRONLY | O_CREAT | O_TRUNC);
343 if (temp_bookmark_file < 0)
344 return false; /* can't open the temp file */
346 if (!strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE) &&
347 (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
349 playlist = strchr(bookmark,'/');
350 cp = strrchr(bookmark,';');
351 len = cp - playlist;
352 unique = true;
355 /* Writing the new bookmark to the begining of the temp file */
356 write(temp_bookmark_file, bookmark, strlen(bookmark));
357 write(temp_bookmark_file, "\n", 1);
358 bookmark_count++;
360 /* Reading in the previous bookmarks and writing them to the temp file */
361 bookmark_file = open(bookmark_file_name, O_RDONLY);
362 if (bookmark_file >= 0)
364 while (read_line(bookmark_file, global_read_buffer,
365 sizeof(global_read_buffer)))
367 /* The MRB has a max of MAX_BOOKMARKS in it */
368 /* This keeps it from getting too large */
369 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
371 if(bookmark_count >= MAX_BOOKMARKS)
372 break;
375 cp = strchr(global_read_buffer,'/');
376 tmp = strrchr(global_read_buffer,';');
377 if (check_bookmark(global_read_buffer) &&
378 (!unique || len != tmp -cp || strncmp(playlist,cp,len)))
380 bookmark_count++;
381 write(temp_bookmark_file, global_read_buffer,
382 strlen(global_read_buffer));
383 write(temp_bookmark_file, "\n", 1);
386 close(bookmark_file);
388 close(temp_bookmark_file);
390 remove(bookmark_file_name);
391 rename(global_temp_buffer, bookmark_file_name);
393 return true;
397 /* ----------------------------------------------------------------------- */
398 /* This function takes the system resume data and formats it into a valid */
399 /* bookmark. */
400 /* ----------------------------------------------------------------------- */
401 static char* create_bookmark()
403 int resume_index = 0;
404 char *file;
406 /* grab the currently playing track */
407 struct mp3entry *id3 = mpeg_current_track();
408 if(!id3)
409 return NULL;
411 /* Get some basic resume information */
412 /* queue_resume and queue_resume_index are not used and can be ignored.*/
413 playlist_get_resume_info(&resume_index);
415 /* Get the currently playing file minus the path */
416 /* This is used when displaying the available bookmarks */
417 file = strrchr(id3->path,'/');
418 if(NULL == file)
419 return NULL;
421 /* create the bookmark */
422 snprintf(global_bookmark, sizeof(global_bookmark),
423 "%d;%d;%d;%d;%d;%d;%d;%s;%s",
424 resume_index,
425 id3->offset,
426 playlist_get_seed(NULL),
428 id3->elapsed,
429 global_settings.repeat_mode,
430 global_settings.playlist_shuffle,
431 playlist_get_name(NULL, global_temp_buffer,
432 sizeof(global_temp_buffer)),
433 file+1);
435 /* checking to see if the bookmark is valid */
436 if (check_bookmark(global_bookmark))
437 return global_bookmark;
438 else
439 return NULL;
442 static bool check_bookmark(const char* bookmark)
444 return parse_bookmark(bookmark,
445 NULL,NULL,NULL, NULL,
446 NULL,0,NULL,NULL,
447 NULL, NULL);
450 /* ----------------------------------------------------------------------- */
451 /* This function will determine if an autoload is necessary. This is an */
452 /* interface function. */
453 /* ------------------------------------------------------------------------*/
454 bool bookmark_autoload(const char* file)
456 int key;
457 int fd;
458 bool done = false;
460 if(global_settings.autoloadbookmark == BOOKMARK_NO)
461 return false;
463 /*Checking to see if a bookmark file exists.*/
464 if(!generate_bookmark_file_name(file))
466 return false;
469 fd = open(global_bookmark_file_name, O_RDONLY);
470 if(fd<0)
471 return false;
472 if(-1 == lseek(fd, 0, SEEK_END))
474 close(fd);
475 return false;
477 close(fd);
479 if(global_settings.autoloadbookmark == BOOKMARK_YES)
481 return bookmark_load(global_bookmark_file_name, true);
483 else
485 button_clear_queue(); /* clear button queue */
486 /* Prompting user to confirm bookmark load */
487 lcd_clear_display();
488 #ifdef HAVE_LCD_BITMAP
489 lcd_setmargins(0, STATUSBAR_HEIGHT);
490 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
491 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
492 lcd_puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
493 lcd_puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
494 #else
495 status_draw(false);
496 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
497 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
498 #endif
499 lcd_update();
501 sleep(100);
503 while(!done)
505 button_clear_queue();
507 /* Wait for a key to be pushed */
508 key = button_get(true);
509 switch(key)
511 #ifdef HAVE_LCD_BITMAP
512 case BUTTON_DOWN:
513 return bookmark_load(global_bookmark_file_name, false);
514 #endif
515 case SETTINGS_OK:
516 return bookmark_load(global_bookmark_file_name, true);
518 default:
519 if(default_event_handler(key) == SYS_USB_CONNECTED)
520 return true;
521 return false;
524 return true;
528 /* ----------------------------------------------------------------------- */
529 /* This function loads the bookmark information into the resume memory. */
530 /* This is an interface function. */
531 /* ------------------------------------------------------------------------*/
532 bool bookmark_load(const char* file, bool autoload)
534 int fd;
535 bool success = true;
536 int offset;
537 int seed;
538 int index;
539 char* bookmark = NULL;;
541 if(autoload)
543 fd = open(file, O_RDONLY);
544 if(fd >= 0)
546 if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)))
547 bookmark=global_read_buffer;
548 close(fd);
551 else
553 /* This is not an auto-load, so list the bookmarks */
554 bookmark=select_bookmark(file);
555 if(!bookmark)
556 return true; /* User exited without selecting a bookmark */
559 if(bookmark)
561 success = parse_bookmark(bookmark,
562 &index,
563 &offset,
564 &seed,
565 NULL,
566 global_temp_buffer,
567 sizeof(global_temp_buffer),
568 NULL,
569 &global_settings.repeat_mode,
570 &global_settings.playlist_shuffle,
571 global_filename);
575 if(success)
576 bookmark_play(global_temp_buffer, index, offset, seed,
577 global_filename);
579 return success;
583 static int get_bookmark_count(const char* bookmark_file_name)
585 int read_count = 0;
586 int file = open(bookmark_file_name, O_RDONLY);
588 if(file < 0)
589 return -1;
591 /* Get the requested bookmark */
592 while(read_line(file, global_read_buffer, sizeof(global_read_buffer)))
594 if(check_bookmark(global_read_buffer))
595 read_count++;
598 close(file);
599 return read_count;
605 /* ----------------------------------------------------------------------- */
606 /* This displays a the bookmarks in a file and allows the user to */
607 /* select one to play. */
608 /* ------------------------------------------------------------------------*/
609 static char* select_bookmark(const char* bookmark_file_name)
611 int bookmark_id = 0;
612 int bookmark_id_prev = -1;
613 int key = 0;
614 char* bookmark = NULL;
615 int bookmark_count = 0;
617 #ifdef HAVE_LCD_BITMAP
618 lcd_setmargins(0, 0);
619 #endif
621 button_clear_queue(); /* clear button queue */
622 bookmark_count = get_bookmark_count(bookmark_file_name);
624 while(true)
626 if(bookmark_id < 0)
627 bookmark_id = bookmark_count -1;
628 if(bookmark_id >= bookmark_count)
629 bookmark_id = 0;
631 if (bookmark_id != bookmark_id_prev)
633 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
634 bookmark_id_prev = bookmark_id;
637 if (!bookmark)
639 /* if there were no bookmarks in the file, delete the file and exit. */
640 if(bookmark_id <= 0)
642 splash(HZ, true, str(LANG_BOOKMARK_LOAD_EMPTY));
643 remove(bookmark_file_name);
644 button_clear_queue(); /* clear button queue */
645 return NULL;
647 else
649 bookmark_id_prev = bookmark_id;
650 bookmark_id--;
653 else
655 display_bookmark(bookmark, bookmark_id, bookmark_count);
656 if (global_settings.talk_menu) /* for voice UI */
657 say_bookmark(bookmark, bookmark_id);
660 /* waiting for the user to click a button */
661 key = button_get(true);
662 switch(key)
664 case SETTINGS_OK:
665 /* User wants to use this bookmark */
666 #ifdef HAVE_LCD_BITMAP
667 if (global_settings.statusbar)
668 lcd_setmargins(0, STATUSBAR_HEIGHT);
669 else
670 lcd_setmargins(0, 0);
671 #endif
672 return bookmark;
674 #if CONFIG_KEYPAD == ONDIO_PAD
675 case BUTTON_MENU | BUTTON_RIGHT:
676 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
677 case BUTTON_ON | BUTTON_SELECT:
678 #else
679 case BUTTON_ON | BUTTON_PLAY:
680 #endif
681 /* User wants to delete this bookmark */
682 delete_bookmark(bookmark_file_name, bookmark_id);
683 bookmark_id_prev=-2;
684 bookmark_count--;
685 if(bookmark_id >= bookmark_count)
686 bookmark_id = bookmark_count -1;
687 button_clear_queue(); /* clear button queue */
688 break;
690 case SETTINGS_INC:
691 bookmark_id--;
692 break;
694 case SETTINGS_DEC:
695 bookmark_id++;
696 break;
698 #if CONFIG_KEYPAD != ONDIO_PAD
699 case SETTINGS_CANCEL:
700 #endif
701 #ifdef SETTINGS_CANCEL2
702 case SETTINGS_CANCEL2:
703 #endif
704 #ifdef SETTINGS_OK2
705 case SETTINGS_OK2:
706 #endif
707 return NULL;
709 default:
710 if(default_event_handler(key) == SYS_USB_CONNECTED)
711 return NULL;
712 break;
716 return NULL;
720 /* ----------------------------------------------------------------------- */
721 /* This function takes a location in a bookmark file and deletes that */
722 /* bookmark. */
723 /* ------------------------------------------------------------------------*/
724 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
726 int temp_bookmark_file = 0;
727 int bookmark_file = 0;
728 int bookmark_count = 0;
730 /* Opening up a temp bookmark file */
731 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
732 "%s.tmp", bookmark_file_name);
733 temp_bookmark_file = open(global_temp_buffer,
734 O_WRONLY | O_CREAT | O_TRUNC);
735 bookmark_file = open(bookmark_file_name, O_RDONLY);
737 if (temp_bookmark_file < 0 || bookmark_file < 0)
738 return false; /* can't open one of the files */
740 /* Reading in the previous bookmarks and writing them to the temp file */
741 while (read_line(bookmark_file, global_read_buffer,
742 sizeof(global_read_buffer)))
744 /* The MRB has a max of MAX_BOOKMARKS in it */
745 /* This keeps it from getting too large */
746 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
748 if(bookmark_count >= MAX_BOOKMARKS)
749 break;
752 if (check_bookmark(global_read_buffer))
754 if (bookmark_id != bookmark_count)
756 write(temp_bookmark_file, global_read_buffer,
757 strlen(global_read_buffer));
758 write(temp_bookmark_file, "\n", 1);
760 bookmark_count++;
764 close(bookmark_file);
765 close(temp_bookmark_file);
767 remove(bookmark_file_name);
768 rename(global_temp_buffer, bookmark_file_name);
770 return true;
773 /* ----------------------------------------------------------------------- */
774 /* This function parses a bookmark and displays it for the user. */
775 /* ------------------------------------------------------------------------*/
776 static void display_bookmark(const char* bookmark,
777 int bookmark_id,
778 int bookmark_count)
780 int resume_index = 0;
781 int ms = 0;
782 int repeat_mode = 0;
783 bool playlist_shuffle = false;
784 int len;
785 char *dot;
787 /* getting the index and the time into the file */
788 parse_bookmark(bookmark,
789 &resume_index, NULL, NULL, NULL, NULL, 0,
790 &ms, &repeat_mode, &playlist_shuffle,
791 global_filename);
793 lcd_clear_display();
794 lcd_stop_scroll();
796 #ifdef HAVE_LCD_BITMAP
797 /* bookmark shuffle and repeat states*/
798 switch (repeat_mode)
800 case REPEAT_ONE:
801 statusbar_icon_play_mode(Icon_RepeatOne);
802 break;
804 case REPEAT_ALL:
805 statusbar_icon_play_mode(Icon_Repeat);
806 break;
808 if(playlist_shuffle)
809 statusbar_icon_shuffle();
811 /* File Name */
812 len=strlen(global_filename);
813 if (len>3)
814 dot=strrchr(global_filename + len - 4, '.');
815 else
816 dot=NULL;
817 if (dot)
818 *dot='\0';
819 lcd_puts_scroll(0, 0, global_filename);
820 if (dot)
821 *dot='.';
823 /* bookmark number */
824 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d/%2d",
825 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
826 bookmark_id + 1, bookmark_count);
827 lcd_puts_scroll(0, 1, global_temp_buffer);
829 /* bookmark resume index */
830 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d",
831 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
832 lcd_puts_scroll(0, 2, global_temp_buffer);
834 /* elapsed time*/
835 if ( ms < 3600000 )
837 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d:%02d",
838 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
839 ms / 60000,
840 ms % 60000 / 1000);
842 else
844 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
845 "%s: %d:%02d:%02d",
846 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
847 ms / 3600000,
848 ms % 3600000 / 60000,
849 ms % 60000 / 1000);
851 lcd_puts_scroll(0, 3, global_temp_buffer);
853 /* commands */
854 lcd_puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
855 lcd_puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
856 lcd_puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
857 #else
858 (void)bookmark_id;
859 len=strlen(global_filename);
860 if (len>3)
861 dot=strrchr(global_filename+len-4,'.');
862 else
863 dot=NULL;
864 if (dot)
865 *dot='\0';
866 if ( ms < 3600000 )
868 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
869 "%2d, %d:%02d, %s,",
870 (bookmark_count+1),
871 ms / 60000,
872 ms % 60000 / 1000,
873 global_filename);
875 else
877 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
878 "%2d, %d:%02d:%02d, %s,",
879 (bookmark_count+1),
880 ms / 60000,
881 ms % 3600000 / 60000,
882 ms % 60000 / 1000,
883 global_filename);
886 status_draw(false);
887 lcd_puts_scroll(0,0,global_temp_buffer);
888 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
889 if (dot)
890 *dot='.';
891 #endif
892 lcd_update();
896 /* ----------------------------------------------------------------------- */
897 /* This function parses a bookmark, says the voice UI part of it. */
898 /* ------------------------------------------------------------------------*/
899 static void say_bookmark(const char* bookmark,
900 int bookmark_id)
902 int resume_index;
903 int ms;
904 char dir[MAX_PATH];
905 bool enqueue = false; /* only the first voice is not queued */
907 parse_bookmark(bookmark,
908 &resume_index,
909 NULL, NULL, NULL,
910 dir, sizeof(dir),
911 &ms, NULL, NULL,
912 NULL);
913 /* disabled, because transition between talkbox and voice UI clip is not nice */
914 #if 0
915 if (global_settings.talk_dir >= 3)
916 { /* "talkbox" enabled */
917 char* last = strrchr(dir, '/');
918 if (last)
919 { /* compose filename for talkbox */
920 strncpy(last + 1, dir_thumbnail_name, sizeof(dir)-(last-dir)-1);
921 talk_file(dir, enqueue);
922 enqueue = true;
925 #endif
926 talk_id(VOICE_EXT_BMARK, enqueue);
927 talk_number(bookmark_id + 1, true);
928 talk_id(LANG_BOOKMARK_SELECT_INDEX_TEXT, true);
929 talk_number(resume_index + 1, true);
930 talk_id(LANG_BOOKMARK_SELECT_TIME_TEXT, true);
931 if (ms / 60000)
932 talk_value(ms / 60000, UNIT_MIN, true);
933 talk_value((ms % 60000) / 1000, UNIT_SEC, true);
937 /* ----------------------------------------------------------------------- */
938 /* This function retrieves a given bookmark from a file. */
939 /* If the bookmark requested is beyond the number of bookmarks available */
940 /* in the file, it will return the last one. */
941 /* It also returns the index number of the bookmark in the file */
942 /* ------------------------------------------------------------------------*/
943 static char* get_bookmark(const char* bookmark_file, int bookmark_count)
945 int read_count = -1;
946 int result = 0;
947 int file = open(bookmark_file, O_RDONLY);
949 if (file < 0)
950 return NULL;
952 if (bookmark_count < 0)
953 return NULL;
955 /* Get the requested bookmark */
956 while (read_count < bookmark_count)
958 /*Reading in a single bookmark */
959 result = read_line(file,
960 global_read_buffer,
961 sizeof(global_read_buffer));
963 /* Reading past the last bookmark in the file
964 causes the loop to stop */
965 if (result <= 0)
966 break;
968 read_count++;
971 close(file);
972 if (read_count == bookmark_count)
973 return global_read_buffer;
974 else
975 return NULL;
978 /* ----------------------------------------------------------------------- */
979 /* This function takes a bookmark and parses it. This function also */
980 /* validates the bookmark. Passing in NULL for an output variable */
981 /* indicates that value is not requested. */
982 /* ----------------------------------------------------------------------- */
983 static bool parse_bookmark(const char *bookmark,
984 int *resume_index,
985 int *resume_offset,
986 int *resume_seed,
987 int *resume_first_index,
988 char* resume_file,
989 unsigned int resume_file_size,
990 int* ms,
991 int * repeat_mode, bool *shuffle,
992 char* file_name)
994 /* First check to see if a valid line was passed in. */
995 int bookmark_len = strlen(bookmark);
996 int local_resume_index = 0;
997 int local_resume_offset = 0;
998 int local_resume_seed = 0;
999 int local_resume_first_index = 0;
1000 int local_mS = 0;
1001 int local_shuffle = 0;
1002 int local_repeat_mode = 0;
1003 char* local_resume_file = NULL;
1004 char* local_file_name = NULL;
1005 char* field;
1006 char* end;
1007 static char bookmarkcopy[MAX_BOOKMARK_SIZE];
1009 /* Don't do anything if the bookmark length is 0 */
1010 if (bookmark_len <= 0)
1011 return false;
1013 /* Making a dup of the bookmark to use with strtok_r */
1014 strncpy(bookmarkcopy, bookmark, sizeof(bookmarkcopy));
1015 bookmarkcopy[sizeof(bookmarkcopy) - 1] = 0;
1017 /* resume_index */
1018 if ((field = strtok_r(bookmarkcopy, ";", &end)))
1019 local_resume_index = atoi(field);
1020 else
1021 return false;
1023 /* resume_offset */
1024 if ((field = strtok_r(NULL, ";", &end)))
1025 local_resume_offset = atoi(field);
1026 else
1027 return false;
1029 /* resume_seed */
1030 if ((field = strtok_r(NULL, ";", &end)))
1031 local_resume_seed = atoi(field);
1032 else
1033 return false;
1035 /* resume_first_index */
1036 if ((field = strtok_r(NULL, ";", &end)))
1037 local_resume_first_index = atoi(field);
1038 else
1039 return false;
1041 /* Milliseconds into MP3. Used for the bookmark select menu */
1042 if ((field = strtok_r(NULL, ";", &end)))
1043 local_mS = atoi(field);
1044 else
1045 return false;
1047 /* repeat_mode */
1048 if ((field = strtok_r(NULL, ";", &end)))
1049 local_repeat_mode = atoi(field);
1050 else
1051 return false;
1053 /* shuffle mode */
1054 if ((field = strtok_r(NULL, ";", &end)))
1055 local_shuffle = atoi(field);
1056 else
1057 return false;
1059 /* resume_file & file_name (for the bookmark select menu)*/
1060 if (end)
1062 local_resume_file = strtok_r(NULL, ";", &end);
1064 if (end)
1065 local_file_name = strtok_r(NULL, ";", &end);
1067 else
1068 return false;
1070 /* Only return the values the calling function wants */
1071 if (resume_index)
1072 *resume_index = local_resume_index;
1074 if (resume_offset)
1075 *resume_offset = local_resume_offset;
1077 if (resume_seed)
1078 *resume_seed = local_resume_seed;
1080 if (resume_first_index)
1081 *resume_first_index = local_resume_first_index;
1083 if (resume_file && local_resume_file)
1085 strncpy(resume_file, local_resume_file,
1086 MIN(strlen(local_resume_file), resume_file_size-1));
1087 resume_file[MIN(strlen(local_resume_file), resume_file_size-1)]=0;
1090 if (ms)
1091 *ms = local_mS;
1093 if (shuffle)
1094 *shuffle = local_shuffle;
1096 if (repeat_mode)
1097 *repeat_mode = local_repeat_mode;
1099 if (file_name && local_file_name)
1101 strncpy(file_name, local_file_name,MAX_PATH-1);
1102 file_name[MAX_PATH-1] = 0;
1105 return true;
1108 /* ----------------------------------------------------------------------- */
1109 /* This function is used by multiple functions and is used to generate a */
1110 /* bookmark named based off of the input. */
1111 /* Changing this function could result in how the bookmarks are stored. */
1112 /* it would be here that the centralized/decentralized bookmark code */
1113 /* could be placed. */
1114 /* ----------------------------------------------------------------------- */
1115 static bool generate_bookmark_file_name(const char *in)
1117 int len = strlen(in);
1119 /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
1120 /* otherwise, name it based on the in variable */
1121 if (!strcmp("/", in))
1122 strcpy(global_bookmark_file_name, "/root_dir.bmark");
1123 else
1125 strcpy(global_bookmark_file_name, in);
1126 if(global_bookmark_file_name[len-1] == '/')
1127 len--;
1128 strcpy(&global_bookmark_file_name[len], ".bmark");
1131 return true;
1134 /* ----------------------------------------------------------------------- */
1135 /* Checks the current state of the system and returns if it is in a */
1136 /* bookmarkable state. */
1137 /* ----------------------------------------------------------------------- */
1138 /* Inputs: */
1139 /* ----------------------------------------------------------------------- */
1140 /* Outputs: */
1141 /* return bool: Indicates if the system was in a bookmarkable state */
1142 /* ----------------------------------------------------------------------- */
1143 static bool system_check(void)
1145 int resume_index = 0;
1146 struct mp3entry *id3 = mpeg_current_track();
1148 if (!id3)
1150 /* no track playing */
1151 return false;
1154 /* Checking to see if playing a queued track */
1155 if (playlist_get_resume_info(&resume_index) == -1)
1157 /* something bad happened while getting the queue information */
1158 return false;
1160 else if (playlist_modified(NULL))
1162 /* can't bookmark while in the queue */
1163 return false;
1166 return true;