Patch #1352575 - Shorten codec from the ffmpeg project. Rockbox implementation by...
[Rockbox.git] / apps / bookmark.c
blob593bfe98cbd2ece79348369b5dbc41c55609bb17
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 "audio.h"
30 #include "wps.h"
31 #include "settings.h"
32 #include "tree.h"
33 #include "bookmark.h"
34 #include "dir.h"
35 #include "status.h"
36 #include "system.h"
37 #include "errno.h"
38 #include "icons.h"
39 #include "atoi.h"
40 #include "string.h"
41 #include "menu.h"
42 #include "lang.h"
43 #include "screens.h"
44 #include "status.h"
45 #include "debug.h"
46 #include "kernel.h"
47 #include "sprintf.h"
48 #include "talk.h"
49 #include "misc.h"
50 #include "abrepeat.h"
52 #define MAX_BOOKMARKS 10
53 #define MAX_BOOKMARK_SIZE 350
54 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
56 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark);
57 static bool check_bookmark(const char* bookmark);
58 static char* create_bookmark(void);
59 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
60 static void display_bookmark(const char* bookmark,
61 int bookmark_id,
62 int bookmark_count);
63 static void say_bookmark(const char* bookmark,
64 int bookmark_id);
65 static bool generate_bookmark_file_name(const char *in);
66 static char* get_bookmark(const char* bookmark_file, int bookmark_count);
67 static bool parse_bookmark(const char *bookmark,
68 int *resume_index,
69 int *resume_offset,
70 int *resume_seed,
71 int *resume_first_index,
72 char* resume_file,
73 unsigned int resume_file_size,
74 long* ms,
75 int * repeat_mode,
76 bool *shuffle,
77 char* file_name);
78 static char* select_bookmark(const char* bookmark_file_name);
79 static bool system_check(void);
80 static bool write_bookmark(bool create_bookmark_file);
81 static int get_bookmark_count(const char* bookmark_file_name);
83 static char global_temp_buffer[MAX_PATH+1];
84 static char global_bookmark_file_name[MAX_PATH];
85 static char global_read_buffer[MAX_BOOKMARK_SIZE];
86 static char global_bookmark[MAX_BOOKMARK_SIZE];
87 static char global_filename[MAX_PATH];
89 /* ----------------------------------------------------------------------- */
90 /* This is the interface function from the main menu. */
91 /* ----------------------------------------------------------------------- */
92 bool bookmark_create_menu(void)
94 write_bookmark(true);
95 return false;
98 /* ----------------------------------------------------------------------- */
99 /* This function acts as the load interface from the main menu */
100 /* This function determines the bookmark file name and then loads that file*/
101 /* for the user. The user can then select a bookmark to load. */
102 /* If no file/directory is currently playing, the menu item does not work. */
103 /* ----------------------------------------------------------------------- */
104 bool bookmark_load_menu(void)
106 bool success = true;
107 int offset;
108 int seed;
109 int index;
110 char* bookmark;
112 if(!system_check())
113 return false;
114 else
116 char* name = playlist_get_name(NULL, global_temp_buffer,
117 sizeof(global_temp_buffer));
118 if (generate_bookmark_file_name(name))
120 bookmark = select_bookmark(global_bookmark_file_name);
121 if (!bookmark)
122 return false; /* User exited without selecting a bookmark */
124 success = parse_bookmark(bookmark,
125 &index,
126 &offset,
127 &seed,
128 NULL,
129 global_temp_buffer,
130 sizeof(global_temp_buffer),
131 NULL,
132 &global_settings.repeat_mode,
133 &global_settings.playlist_shuffle,
134 global_filename);
136 else
138 /* something bad happened while creating bookmark name*/
139 success = false;
142 if (success)
143 bookmark_play(global_temp_buffer, index, offset, seed,
144 global_filename);
147 return success;
150 /* ----------------------------------------------------------------------- */
151 /* Gives the user a list of the Most Recent Bookmarks. This is an */
152 /* interface function */
153 /* ----------------------------------------------------------------------- */
154 bool bookmark_mrb_load()
156 bool success = true;
157 int offset;
158 int seed;
159 int index;
160 char* bookmark;
162 bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
163 if (!bookmark)
164 return false; /* User exited without selecting a bookmark */
166 success = parse_bookmark(bookmark,
167 &index,
168 &offset,
169 &seed,
170 NULL,
171 global_temp_buffer,
172 sizeof(global_temp_buffer),
173 NULL,
174 &global_settings.repeat_mode,
175 &global_settings.playlist_shuffle,
176 global_filename);
178 if (success)
179 bookmark_play(global_temp_buffer, index, offset, seed,
180 global_filename);
182 return success;
186 /* ----------------------------------------------------------------------- */
187 /* This function handles an autobookmark creation. This is an interface */
188 /* function. */
189 /* ----------------------------------------------------------------------- */
190 bool bookmark_autobookmark(void)
192 /* prompts the user as to create a bookmark */
193 bool done = false;
194 int key = 0;
196 if (!system_check())
197 return false;
199 audio_pause(); /* first pause playback */
200 switch (global_settings.autocreatebookmark)
202 case BOOKMARK_YES:
203 return write_bookmark(true);
205 case BOOKMARK_NO:
206 return false;
208 case BOOKMARK_RECENT_ONLY_YES:
209 return write_bookmark(false);
212 /* Prompting user to confirm bookmark creation */
213 lcd_clear_display();
214 #ifdef HAVE_LCD_BITMAP
215 lcd_setmargins(0, STATUSBAR_HEIGHT);
216 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
217 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
218 lcd_puts(0,2, str(LANG_CANCEL_WITH_ANY_RECORDER));
219 #else
220 status_draw(false);
221 lcd_puts(0,0, str(LANG_AUTO_BOOKMARK_QUERY));
222 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
223 #endif
224 lcd_update();
226 while (!done)
228 /* Wait for a key to be pushed */
229 key = button_get(true);
230 switch (key)
232 case SETTINGS_OK:
233 if (global_settings.autocreatebookmark ==
234 BOOKMARK_RECENT_ONLY_ASK)
235 return write_bookmark(false);
236 else
237 return write_bookmark(true);
238 break;
240 default:
241 /* Handle sys events, ignore button releases & repeats */
242 if(default_event_handler(key) ||
243 !(key & (BUTTON_REL|BUTTON_REPEAT)))
244 done = true;
245 break;
248 return false;
251 /* ----------------------------------------------------------------------- */
252 /* This function takes the current current resume information and writes */
253 /* that to the beginning of the bookmark file. */
254 /* This file will contain N number of bookmarks in the following format: */
255 /* resume_index*resume_offset*resume_seed*resume_first_index* */
256 /* resume_file*milliseconds*MP3 Title* */
257 /* ------------------------------------------------------------------------*/
258 static bool write_bookmark(bool create_bookmark_file)
260 bool success=false;
261 char* bookmark;
263 if (!system_check())
264 return false; /* something didn't happen correctly, do nothing */
266 bookmark = create_bookmark();
267 if (!bookmark)
268 return false; /* something didn't happen correctly, do nothing */
270 if (global_settings.usemrb)
271 success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark);
274 /* writing the bookmark */
275 if (create_bookmark_file)
277 char* name = playlist_get_name(NULL, global_temp_buffer,
278 sizeof(global_temp_buffer));
279 if (generate_bookmark_file_name(name))
281 success = add_bookmark(global_bookmark_file_name, bookmark);
285 if (success)
286 splash(HZ, true, str(LANG_BOOKMARK_CREATE_SUCCESS));
287 else
288 splash(HZ, true, str(LANG_BOOKMARK_CREATE_FAILURE));
290 return true;
293 /* ----------------------------------------------------------------------- */
294 /* This function adds a bookmark to a file. */
295 /* ------------------------------------------------------------------------*/
296 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark)
298 int temp_bookmark_file = 0;
299 int bookmark_file = 0;
300 int bookmark_count = 0;
301 char* playlist = NULL;
302 char* cp;
303 char* tmp;
304 int len = 0;
305 bool unique = false;
307 /* Opening up a temp bookmark file */
308 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
309 "%s.tmp", bookmark_file_name);
310 temp_bookmark_file = open(global_temp_buffer,
311 O_WRONLY | O_CREAT | O_TRUNC);
312 if (temp_bookmark_file < 0)
313 return false; /* can't open the temp file */
315 if (!strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE) &&
316 (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
318 playlist = strchr(bookmark,'/');
319 cp = strrchr(bookmark,';');
320 len = cp - playlist;
321 unique = true;
324 /* Writing the new bookmark to the begining of the temp file */
325 write(temp_bookmark_file, bookmark, strlen(bookmark));
326 write(temp_bookmark_file, "\n", 1);
327 bookmark_count++;
329 /* Reading in the previous bookmarks and writing them to the temp file */
330 bookmark_file = open(bookmark_file_name, O_RDONLY);
331 if (bookmark_file >= 0)
333 while (read_line(bookmark_file, global_read_buffer,
334 sizeof(global_read_buffer)))
336 /* The MRB has a max of MAX_BOOKMARKS in it */
337 /* This keeps it from getting too large */
338 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
340 if(bookmark_count >= MAX_BOOKMARKS)
341 break;
344 cp = strchr(global_read_buffer,'/');
345 tmp = strrchr(global_read_buffer,';');
346 if (check_bookmark(global_read_buffer) &&
347 (!unique || len != tmp -cp || strncmp(playlist,cp,len)))
349 bookmark_count++;
350 write(temp_bookmark_file, global_read_buffer,
351 strlen(global_read_buffer));
352 write(temp_bookmark_file, "\n", 1);
355 close(bookmark_file);
357 close(temp_bookmark_file);
359 remove(bookmark_file_name);
360 rename(global_temp_buffer, bookmark_file_name);
362 return true;
366 /* ----------------------------------------------------------------------- */
367 /* This function takes the system resume data and formats it into a valid */
368 /* bookmark. */
369 /* ----------------------------------------------------------------------- */
370 static char* create_bookmark()
372 int resume_index = 0;
373 char *file;
375 /* grab the currently playing track */
376 struct mp3entry *id3 = audio_current_track();
377 if(!id3)
378 return NULL;
380 /* Get some basic resume information */
381 /* queue_resume and queue_resume_index are not used and can be ignored.*/
382 playlist_get_resume_info(&resume_index);
384 /* Get the currently playing file minus the path */
385 /* This is used when displaying the available bookmarks */
386 file = strrchr(id3->path,'/');
387 if(NULL == file)
388 return NULL;
390 /* create the bookmark */
391 snprintf(global_bookmark, sizeof(global_bookmark),
392 "%d;%ld;%d;%d;%ld;%d;%d;%s;%s",
393 resume_index,
394 id3->offset,
395 playlist_get_seed(NULL),
397 id3->elapsed,
398 global_settings.repeat_mode,
399 global_settings.playlist_shuffle,
400 playlist_get_name(NULL, global_temp_buffer,
401 sizeof(global_temp_buffer)),
402 file+1);
404 /* checking to see if the bookmark is valid */
405 if (check_bookmark(global_bookmark))
406 return global_bookmark;
407 else
408 return NULL;
411 static bool check_bookmark(const char* bookmark)
413 return parse_bookmark(bookmark,
414 NULL,NULL,NULL, NULL,
415 NULL,0,NULL,NULL,
416 NULL, NULL);
419 /* ----------------------------------------------------------------------- */
420 /* This function will determine if an autoload is necessary. This is an */
421 /* interface function. */
422 /* ------------------------------------------------------------------------*/
423 bool bookmark_autoload(const char* file)
425 int key;
426 int fd;
427 bool done = false;
429 if(global_settings.autoloadbookmark == BOOKMARK_NO)
430 return false;
432 /*Checking to see if a bookmark file exists.*/
433 if(!generate_bookmark_file_name(file))
435 return false;
438 fd = open(global_bookmark_file_name, O_RDONLY);
439 if(fd<0)
440 return false;
441 if(-1 == lseek(fd, 0, SEEK_END))
443 close(fd);
444 return false;
446 close(fd);
448 if(global_settings.autoloadbookmark == BOOKMARK_YES)
450 return bookmark_load(global_bookmark_file_name, true);
452 else
454 /* Prompting user to confirm bookmark load */
455 lcd_clear_display();
456 #ifdef HAVE_LCD_BITMAP
457 lcd_setmargins(0, STATUSBAR_HEIGHT);
458 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
459 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
460 lcd_puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
461 lcd_puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
462 #else
463 status_draw(false);
464 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
465 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
466 #endif
467 lcd_update();
469 while(!done)
471 /* Wait for a key to be pushed */
472 key = button_get(true);
473 switch(key)
475 #ifdef HAVE_LCD_BITMAP
476 case BUTTON_DOWN:
477 return bookmark_load(global_bookmark_file_name, false);
478 #endif
479 case SETTINGS_OK:
480 return bookmark_load(global_bookmark_file_name, true);
482 default:
483 /* Handle sys events, ignore button releases & repeats */
484 if (default_event_handler(key) ||
485 !(key & (BUTTON_REPEAT|BUTTON_REL)))
486 done = true;
487 break;
490 return false;
494 /* ----------------------------------------------------------------------- */
495 /* This function loads the bookmark information into the resume memory. */
496 /* This is an interface function. */
497 /* ------------------------------------------------------------------------*/
498 bool bookmark_load(const char* file, bool autoload)
500 int fd;
501 bool success = true;
502 int offset;
503 int seed;
504 int index;
505 char* bookmark = NULL;;
507 if(autoload)
509 fd = open(file, O_RDONLY);
510 if(fd >= 0)
512 if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)))
513 bookmark=global_read_buffer;
514 close(fd);
517 else
519 /* This is not an auto-load, so list the bookmarks */
520 bookmark=select_bookmark(file);
521 if(!bookmark)
522 return true; /* User exited without selecting a bookmark */
525 if(bookmark)
527 success = parse_bookmark(bookmark,
528 &index,
529 &offset,
530 &seed,
531 NULL,
532 global_temp_buffer,
533 sizeof(global_temp_buffer),
534 NULL,
535 &global_settings.repeat_mode,
536 &global_settings.playlist_shuffle,
537 global_filename);
541 if(success)
542 bookmark_play(global_temp_buffer, index, offset, seed,
543 global_filename);
545 return success;
549 static int get_bookmark_count(const char* bookmark_file_name)
551 int read_count = 0;
552 int file = open(bookmark_file_name, O_RDONLY);
554 if(file < 0)
555 return -1;
557 /* Get the requested bookmark */
558 while(read_line(file, global_read_buffer, sizeof(global_read_buffer)))
560 if(check_bookmark(global_read_buffer))
561 read_count++;
564 close(file);
565 return read_count;
570 #if CONFIG_KEYPAD == ONDIO_PAD
571 #define BOOKMARK_SELECT_PRE BUTTON_RIGHT
572 #define BOOKMARK_SELECT (BUTTON_RIGHT | BUTTON_REL)
573 #define BOOKMARK_DELETE (BUTTON_RIGHT | BUTTON_REPEAT)
575 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
576 (CONFIG_KEYPAD == IRIVER_H300_PAD)
577 #define BOOKMARK_SELECT BUTTON_SELECT
578 #define BOOKMARK_DELETE (BUTTON_ON | BUTTON_SELECT)
580 #else /* player, recorder, gmini */
581 #define BOOKMARK_SELECT BUTTON_PLAY
582 #define BOOKMARK_DELETE (BUTTON_ON | BUTTON_PLAY)
583 #endif
585 /* ----------------------------------------------------------------------- */
586 /* This displays a the bookmarks in a file and allows the user to */
587 /* select one to play. */
588 /* ------------------------------------------------------------------------*/
589 static char* select_bookmark(const char* bookmark_file_name)
591 int bookmark_id = 0;
592 int bookmark_id_prev = -1;
593 int key;
594 int lastkey = BUTTON_NONE;
595 char* bookmark = NULL;
596 int bookmark_count = 0;
598 #ifdef HAVE_LCD_BITMAP
599 int x = lcd_getxmargin();
600 int y = lcd_getymargin();
601 lcd_setmargins(0, 0);
602 #endif
604 bookmark_count = get_bookmark_count(bookmark_file_name);
606 while(true)
608 if(bookmark_id < 0)
609 bookmark_id = bookmark_count -1;
610 if(bookmark_id >= bookmark_count)
611 bookmark_id = 0;
613 if (bookmark_id != bookmark_id_prev)
615 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
616 bookmark_id_prev = bookmark_id;
619 if (!bookmark)
621 /* if there were no bookmarks in the file, delete the file and exit. */
622 if(bookmark_id <= 0)
624 splash(HZ, true, str(LANG_BOOKMARK_LOAD_EMPTY));
625 remove(bookmark_file_name);
626 return NULL;
628 else
630 bookmark_id_prev = bookmark_id;
631 bookmark_id--;
634 else
636 display_bookmark(bookmark, bookmark_id, bookmark_count);
637 if (global_settings.talk_menu) /* for voice UI */
638 say_bookmark(bookmark, bookmark_id);
641 /* waiting for the user to click a button */
642 key = button_get(true);
643 switch(key)
645 case BOOKMARK_SELECT:
646 #ifdef BOOKMARK_SELECT_PRE
647 if (lastkey != BOOKMARK_SELECT_PRE)
648 break;
649 #endif
650 /* User wants to use this bookmark */
651 #ifdef HAVE_LCD_BITMAP
652 if (global_settings.statusbar)
653 lcd_setmargins(0, STATUSBAR_HEIGHT);
654 else
655 lcd_setmargins(0, 0);
656 #endif
657 return bookmark;
659 case BOOKMARK_DELETE:
660 /* User wants to delete this bookmark */
661 delete_bookmark(bookmark_file_name, bookmark_id);
662 bookmark_id_prev=-2;
663 bookmark_count--;
664 if(bookmark_id >= bookmark_count)
665 bookmark_id = bookmark_count -1;
666 break;
668 case SETTINGS_DEC:
669 case SETTINGS_DEC | BUTTON_REPEAT:
670 bookmark_id--;
671 break;
673 case SETTINGS_INC:
674 case SETTINGS_INC | BUTTON_REPEAT:
675 bookmark_id++;
676 break;
678 case SETTINGS_CANCEL:
679 #ifdef SETTINGS_CANCEL2
680 case SETTINGS_CANCEL2:
681 #endif
682 #ifdef SETTINGS_OK2
683 case SETTINGS_OK2:
684 #endif
685 #ifdef HAVE_LCD_BITMAP
686 lcd_setmargins(x, y);
687 #endif
688 return NULL;
690 default:
691 if(default_event_handler(key) == SYS_USB_CONNECTED)
692 return NULL;
693 break;
695 lastkey = key;
698 return NULL;
702 /* ----------------------------------------------------------------------- */
703 /* This function takes a location in a bookmark file and deletes that */
704 /* bookmark. */
705 /* ------------------------------------------------------------------------*/
706 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
708 int temp_bookmark_file = 0;
709 int bookmark_file = 0;
710 int bookmark_count = 0;
712 /* Opening up a temp bookmark file */
713 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
714 "%s.tmp", bookmark_file_name);
715 temp_bookmark_file = open(global_temp_buffer,
716 O_WRONLY | O_CREAT | O_TRUNC);
717 bookmark_file = open(bookmark_file_name, O_RDONLY);
719 if (temp_bookmark_file < 0 || bookmark_file < 0)
720 return false; /* can't open one of the files */
722 /* Reading in the previous bookmarks and writing them to the temp file */
723 while (read_line(bookmark_file, global_read_buffer,
724 sizeof(global_read_buffer)))
726 /* The MRB has a max of MAX_BOOKMARKS in it */
727 /* This keeps it from getting too large */
728 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
730 if(bookmark_count >= MAX_BOOKMARKS)
731 break;
734 if (check_bookmark(global_read_buffer))
736 if (bookmark_id != bookmark_count)
738 write(temp_bookmark_file, global_read_buffer,
739 strlen(global_read_buffer));
740 write(temp_bookmark_file, "\n", 1);
742 bookmark_count++;
746 close(bookmark_file);
747 close(temp_bookmark_file);
749 remove(bookmark_file_name);
750 rename(global_temp_buffer, bookmark_file_name);
752 return true;
755 /* ----------------------------------------------------------------------- */
756 /* This function parses a bookmark and displays it for the user. */
757 /* ------------------------------------------------------------------------*/
758 static void display_bookmark(const char* bookmark,
759 int bookmark_id,
760 int bookmark_count)
762 int resume_index = 0;
763 long ms = 0;
764 int repeat_mode = 0;
765 bool playlist_shuffle = false;
766 int len;
767 char *dot;
769 /* getting the index and the time into the file */
770 parse_bookmark(bookmark,
771 &resume_index, NULL, NULL, NULL, NULL, 0,
772 &ms, &repeat_mode, &playlist_shuffle,
773 global_filename);
775 lcd_clear_display();
776 lcd_stop_scroll();
778 #ifdef HAVE_LCD_BITMAP
779 /* bookmark shuffle and repeat states*/
780 switch (repeat_mode)
782 #ifdef AB_REPEAT_ENABLE
783 case REPEAT_AB:
784 statusbar_icon_play_mode(Icon_RepeatAB);
785 break;
786 #endif
788 case REPEAT_ONE:
789 statusbar_icon_play_mode(Icon_RepeatOne);
790 break;
792 case REPEAT_ALL:
793 statusbar_icon_play_mode(Icon_Repeat);
794 break;
796 if(playlist_shuffle)
797 statusbar_icon_shuffle();
799 /* File Name */
800 len=strlen(global_filename);
801 if (len>3)
802 dot=strrchr(global_filename + len - 4, '.');
803 else
804 dot=NULL;
805 if (dot)
806 *dot='\0';
807 lcd_puts_scroll(0, 0, global_filename);
808 if (dot)
809 *dot='.';
811 /* bookmark number */
812 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d/%2d",
813 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
814 bookmark_id + 1, bookmark_count);
815 lcd_puts_scroll(0, 1, global_temp_buffer);
817 /* bookmark resume index */
818 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d",
819 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
820 lcd_puts_scroll(0, 2, global_temp_buffer);
822 /* elapsed time*/
823 if ( ms < 3600000 )
825 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %ld:%02d",
826 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
827 ms / 60000,
828 (unsigned int)(ms % 60000) / 1000);
829 /* unsigned int: hinting for 16bits archs */
831 else
833 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
834 "%s: %ld:%02ld:%02d",
835 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
836 ms / 3600000,
837 ms % 3600000 / 60000,
838 (unsigned int)(ms % 60000) / 1000);
840 lcd_puts_scroll(0, 3, global_temp_buffer);
842 /* commands */
843 lcd_puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
844 lcd_puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
845 lcd_puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
846 #else
847 (void)bookmark_id;
848 len=strlen(global_filename);
849 if (len>3)
850 dot=strrchr(global_filename+len-4,'.');
851 else
852 dot=NULL;
853 if (dot)
854 *dot='\0';
855 if ( ms < 3600000 )
857 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
858 "%2d, %ld:%02ld, %s,",
859 (bookmark_count+1),
860 ms / 60000,
861 ms % 60000 / 1000,
862 global_filename);
864 else
866 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
867 "%2d, %ld:%02ld:%02ld, %s,",
868 (bookmark_count+1),
869 ms / 60000,
870 ms % 3600000 / 60000,
871 ms % 60000 / 1000,
872 global_filename);
875 status_draw(false);
876 lcd_puts_scroll(0,0,global_temp_buffer);
877 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
878 if (dot)
879 *dot='.';
880 #endif
881 lcd_update();
885 /* ----------------------------------------------------------------------- */
886 /* This function parses a bookmark, says the voice UI part of it. */
887 /* ------------------------------------------------------------------------*/
888 static void say_bookmark(const char* bookmark,
889 int bookmark_id)
891 int resume_index;
892 long ms;
893 char dir[MAX_PATH];
894 bool enqueue = false; /* only the first voice is not queued */
896 parse_bookmark(bookmark,
897 &resume_index,
898 NULL, NULL, NULL,
899 dir, sizeof(dir),
900 &ms, NULL, NULL,
901 NULL);
902 /* disabled, because transition between talkbox and voice UI clip is not nice */
903 #if 0
904 if (global_settings.talk_dir >= 3)
905 { /* "talkbox" enabled */
906 char* last = strrchr(dir, '/');
907 if (last)
908 { /* compose filename for talkbox */
909 strncpy(last + 1, dir_thumbnail_name, sizeof(dir)-(last-dir)-1);
910 talk_file(dir, enqueue);
911 enqueue = true;
914 #endif
915 talk_id(VOICE_EXT_BMARK, enqueue);
916 talk_number(bookmark_id + 1, true);
917 talk_id(LANG_BOOKMARK_SELECT_INDEX_TEXT, true);
918 talk_number(resume_index + 1, true);
919 talk_id(LANG_BOOKMARK_SELECT_TIME_TEXT, true);
920 if (ms / 60000)
921 talk_value(ms / 60000, UNIT_MIN, true);
922 talk_value((ms % 60000) / 1000, UNIT_SEC, true);
926 /* ----------------------------------------------------------------------- */
927 /* This function retrieves a given bookmark from a file. */
928 /* If the bookmark requested is beyond the number of bookmarks available */
929 /* in the file, it will return the last one. */
930 /* It also returns the index number of the bookmark in the file */
931 /* ------------------------------------------------------------------------*/
932 static char* get_bookmark(const char* bookmark_file, int bookmark_count)
934 int read_count = -1;
935 int result = 0;
936 int file = open(bookmark_file, O_RDONLY);
938 if (file < 0)
939 return NULL;
941 if (bookmark_count < 0)
942 return NULL;
944 /* Get the requested bookmark */
945 while (read_count < bookmark_count)
947 /*Reading in a single bookmark */
948 result = read_line(file,
949 global_read_buffer,
950 sizeof(global_read_buffer));
952 /* Reading past the last bookmark in the file
953 causes the loop to stop */
954 if (result <= 0)
955 break;
957 read_count++;
960 close(file);
961 if (read_count == bookmark_count)
962 return global_read_buffer;
963 else
964 return NULL;
967 /* ----------------------------------------------------------------------- */
968 /* This function takes a bookmark and parses it. This function also */
969 /* validates the bookmark. Passing in NULL for an output variable */
970 /* indicates that value is not requested. */
971 /* ----------------------------------------------------------------------- */
972 static bool parse_bookmark(const char *bookmark,
973 int *resume_index,
974 int *resume_offset,
975 int *resume_seed,
976 int *resume_first_index,
977 char* resume_file,
978 unsigned int resume_file_size,
979 long* ms,
980 int * repeat_mode, bool *shuffle,
981 char* file_name)
983 /* First check to see if a valid line was passed in. */
984 int bookmark_len = strlen(bookmark);
985 int local_resume_index = 0;
986 int local_resume_offset = 0;
987 int local_resume_seed = 0;
988 int local_resume_first_index = 0;
989 int local_mS = 0;
990 int local_shuffle = 0;
991 int local_repeat_mode = 0;
992 char* local_resume_file = NULL;
993 char* local_file_name = NULL;
994 char* field;
995 char* end;
996 static char bookmarkcopy[MAX_BOOKMARK_SIZE];
998 /* Don't do anything if the bookmark length is 0 */
999 if (bookmark_len <= 0)
1000 return false;
1002 /* Making a dup of the bookmark to use with strtok_r */
1003 strncpy(bookmarkcopy, bookmark, sizeof(bookmarkcopy));
1004 bookmarkcopy[sizeof(bookmarkcopy) - 1] = 0;
1006 /* resume_index */
1007 if ((field = strtok_r(bookmarkcopy, ";", &end)))
1008 local_resume_index = atoi(field);
1009 else
1010 return false;
1012 /* resume_offset */
1013 if ((field = strtok_r(NULL, ";", &end)))
1014 local_resume_offset = atoi(field);
1015 else
1016 return false;
1018 /* resume_seed */
1019 if ((field = strtok_r(NULL, ";", &end)))
1020 local_resume_seed = atoi(field);
1021 else
1022 return false;
1024 /* resume_first_index */
1025 if ((field = strtok_r(NULL, ";", &end)))
1026 local_resume_first_index = atoi(field);
1027 else
1028 return false;
1030 /* Milliseconds into MP3. Used for the bookmark select menu */
1031 if ((field = strtok_r(NULL, ";", &end)))
1032 local_mS = atoi(field);
1033 else
1034 return false;
1036 /* repeat_mode */
1037 if ((field = strtok_r(NULL, ";", &end)))
1038 local_repeat_mode = atoi(field);
1039 else
1040 return false;
1042 /* shuffle mode */
1043 if ((field = strtok_r(NULL, ";", &end)))
1044 local_shuffle = atoi(field);
1045 else
1046 return false;
1048 /* resume_file & file_name (for the bookmark select menu)*/
1049 if (end)
1051 local_resume_file = strtok_r(NULL, ";", &end);
1053 if (end)
1054 local_file_name = strtok_r(NULL, ";", &end);
1056 else
1057 return false;
1059 /* Only return the values the calling function wants */
1060 if (resume_index)
1061 *resume_index = local_resume_index;
1063 if (resume_offset)
1064 *resume_offset = local_resume_offset;
1066 if (resume_seed)
1067 *resume_seed = local_resume_seed;
1069 if (resume_first_index)
1070 *resume_first_index = local_resume_first_index;
1072 if (resume_file && local_resume_file)
1074 strncpy(resume_file, local_resume_file,
1075 MIN(strlen(local_resume_file), resume_file_size-1));
1076 resume_file[MIN(strlen(local_resume_file), resume_file_size-1)]=0;
1079 if (ms)
1080 *ms = local_mS;
1082 if (shuffle)
1083 *shuffle = local_shuffle;
1085 if (repeat_mode)
1086 *repeat_mode = local_repeat_mode;
1088 if (file_name && local_file_name)
1090 strncpy(file_name, local_file_name,MAX_PATH-1);
1091 file_name[MAX_PATH-1] = 0;
1094 return true;
1097 /* ----------------------------------------------------------------------- */
1098 /* This function is used by multiple functions and is used to generate a */
1099 /* bookmark named based off of the input. */
1100 /* Changing this function could result in how the bookmarks are stored. */
1101 /* it would be here that the centralized/decentralized bookmark code */
1102 /* could be placed. */
1103 /* ----------------------------------------------------------------------- */
1104 static bool generate_bookmark_file_name(const char *in)
1106 int len = strlen(in);
1108 /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
1109 /* otherwise, name it based on the in variable */
1110 if (!strcmp("/", in))
1111 strcpy(global_bookmark_file_name, "/root_dir.bmark");
1112 else
1114 strcpy(global_bookmark_file_name, in);
1115 if(global_bookmark_file_name[len-1] == '/')
1116 len--;
1117 strcpy(&global_bookmark_file_name[len], ".bmark");
1120 return true;
1123 /* ----------------------------------------------------------------------- */
1124 /* Returns the bookmark name for the current playlist */
1125 /* ----------------------------------------------------------------------- */
1126 bool bookmark_exist(void)
1128 bool exist=false;
1130 if(system_check())
1132 char* name = playlist_get_name(NULL, global_temp_buffer,
1133 sizeof(global_temp_buffer));
1134 if (generate_bookmark_file_name(name))
1136 int fd=open(global_bookmark_file_name, O_RDONLY);
1137 if (fd >=0)
1139 close(fd);
1140 exist=true;
1145 return exist;
1148 /* ----------------------------------------------------------------------- */
1149 /* Checks the current state of the system and returns if it is in a */
1150 /* bookmarkable state. */
1151 /* ----------------------------------------------------------------------- */
1152 /* Inputs: */
1153 /* ----------------------------------------------------------------------- */
1154 /* Outputs: */
1155 /* return bool: Indicates if the system was in a bookmarkable state */
1156 /* ----------------------------------------------------------------------- */
1157 static bool system_check(void)
1159 int resume_index = 0;
1160 struct mp3entry *id3 = audio_current_track();
1162 if (!id3)
1164 /* no track playing */
1165 return false;
1168 /* Checking to see if playing a queued track */
1169 if (playlist_get_resume_info(&resume_index) == -1)
1171 /* something bad happened while getting the queue information */
1172 return false;
1174 else if (playlist_modified(NULL))
1176 /* can't bookmark while in the queue */
1177 return false;
1180 return true;