HAVE_ADJUSTABLE_CPU_FREQ isn't defined for simulators, so we don't have to check...
[Rockbox.git] / apps / bookmark.c
blobc8c6f941f2130d015f4f75e7aea90ab47ca593ee
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 "playlist.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"
51 #include "splash.h"
52 #include "yesno.h"
54 #define MAX_BOOKMARKS 10
55 #define MAX_BOOKMARK_SIZE 350
56 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
58 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark);
59 static bool check_bookmark(const char* bookmark);
60 static char* create_bookmark(void);
61 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
62 static void display_bookmark(const char* bookmark,
63 int bookmark_id,
64 int bookmark_count);
65 static void say_bookmark(const char* bookmark,
66 int bookmark_id);
67 static bool generate_bookmark_file_name(const char *in);
68 static char* get_bookmark(const char* bookmark_file, int bookmark_count);
69 static bool parse_bookmark(const char *bookmark,
70 int *resume_index,
71 int *resume_offset,
72 int *resume_seed,
73 int *resume_first_index,
74 char* resume_file,
75 unsigned int resume_file_size,
76 long* ms,
77 int * repeat_mode,
78 bool *shuffle,
79 char* file_name);
80 static char* select_bookmark(const char* bookmark_file_name);
81 static bool system_check(void);
82 static bool write_bookmark(bool create_bookmark_file);
83 static int get_bookmark_count(const char* bookmark_file_name);
85 static char global_temp_buffer[MAX_PATH+1];
86 static char global_bookmark_file_name[MAX_PATH];
87 static char global_read_buffer[MAX_BOOKMARK_SIZE];
88 static char global_bookmark[MAX_BOOKMARK_SIZE];
89 static char global_filename[MAX_PATH];
91 /* ----------------------------------------------------------------------- */
92 /* This is the interface function from the main menu. */
93 /* ----------------------------------------------------------------------- */
94 bool bookmark_create_menu(void)
96 write_bookmark(true);
97 return false;
100 /* ----------------------------------------------------------------------- */
101 /* This function acts as the load interface from the main menu */
102 /* This function determines the bookmark file name and then loads that file*/
103 /* for the user. The user can then select a bookmark to load. */
104 /* If no file/directory is currently playing, the menu item does not work. */
105 /* ----------------------------------------------------------------------- */
106 bool bookmark_load_menu(void)
108 bool success = true;
109 int offset;
110 int seed;
111 int index;
112 char* bookmark;
114 if(!system_check())
115 return false;
116 else
118 char* name = playlist_get_name(NULL, global_temp_buffer,
119 sizeof(global_temp_buffer));
120 if (generate_bookmark_file_name(name))
122 bookmark = select_bookmark(global_bookmark_file_name);
123 if (!bookmark)
124 return false; /* User exited without selecting a bookmark */
126 success = parse_bookmark(bookmark,
127 &index,
128 &offset,
129 &seed,
130 NULL,
131 global_temp_buffer,
132 sizeof(global_temp_buffer),
133 NULL,
134 &global_settings.repeat_mode,
135 &global_settings.playlist_shuffle,
136 global_filename);
138 else
140 /* something bad happened while creating bookmark name*/
141 success = false;
144 if (success)
145 bookmark_play(global_temp_buffer, index, offset, seed,
146 global_filename);
149 return success;
152 /* ----------------------------------------------------------------------- */
153 /* Gives the user a list of the Most Recent Bookmarks. This is an */
154 /* interface function */
155 /* ----------------------------------------------------------------------- */
156 bool bookmark_mrb_load()
158 bool success = true;
159 int offset;
160 int seed;
161 int index;
162 char* bookmark;
164 bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
165 if (!bookmark)
166 return false; /* User exited without selecting a bookmark */
168 success = parse_bookmark(bookmark,
169 &index,
170 &offset,
171 &seed,
172 NULL,
173 global_temp_buffer,
174 sizeof(global_temp_buffer),
175 NULL,
176 &global_settings.repeat_mode,
177 &global_settings.playlist_shuffle,
178 global_filename);
180 if (success)
181 bookmark_play(global_temp_buffer, index, offset, seed,
182 global_filename);
184 return success;
188 /* ----------------------------------------------------------------------- */
189 /* This function handles an autobookmark creation. This is an interface */
190 /* function. */
191 /* ----------------------------------------------------------------------- */
192 bool bookmark_autobookmark(void)
194 if (!system_check())
195 return false;
197 audio_pause(); /* first pause playback */
198 switch (global_settings.autocreatebookmark)
200 case BOOKMARK_YES:
201 return write_bookmark(true);
203 case BOOKMARK_NO:
204 return false;
206 case BOOKMARK_RECENT_ONLY_YES:
207 return write_bookmark(false);
209 #ifdef HAVE_LCD_BITMAP
210 unsigned char *lines[]={str(LANG_AUTO_BOOKMARK_QUERY)};
211 struct text_message message={(char **)lines, 1};
212 #else
213 unsigned char *lines[]={str(LANG_AUTO_BOOKMARK_QUERY),
214 str(LANG_RESUME_CONFIRM_PLAYER)};
215 struct text_message message={(char **)lines, 2};
216 #endif
217 gui_syncstatusbar_draw(&statusbars, false);
218 if(gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
220 if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK)
221 return write_bookmark(false);
222 else
223 return write_bookmark(true);
225 return false;
228 /* ----------------------------------------------------------------------- */
229 /* This function takes the current current resume information and writes */
230 /* that to the beginning of the bookmark file. */
231 /* This file will contain N number of bookmarks in the following format: */
232 /* resume_index*resume_offset*resume_seed*resume_first_index* */
233 /* resume_file*milliseconds*MP3 Title* */
234 /* ------------------------------------------------------------------------*/
235 static bool write_bookmark(bool create_bookmark_file)
237 bool success=false;
238 char* bookmark;
240 if (!system_check())
241 return false; /* something didn't happen correctly, do nothing */
243 bookmark = create_bookmark();
244 if (!bookmark)
245 return false; /* something didn't happen correctly, do nothing */
247 if (global_settings.usemrb)
248 success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark);
251 /* writing the bookmark */
252 if (create_bookmark_file)
254 char* name = playlist_get_name(NULL, global_temp_buffer,
255 sizeof(global_temp_buffer));
256 if (generate_bookmark_file_name(name))
258 success = add_bookmark(global_bookmark_file_name, bookmark);
262 if (success)
263 gui_syncsplash(HZ, true, str(LANG_BOOKMARK_CREATE_SUCCESS));
264 else
265 gui_syncsplash(HZ, true, str(LANG_BOOKMARK_CREATE_FAILURE));
267 return true;
270 /* ----------------------------------------------------------------------- */
271 /* This function adds a bookmark to a file. */
272 /* ------------------------------------------------------------------------*/
273 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark)
275 int temp_bookmark_file = 0;
276 int bookmark_file = 0;
277 int bookmark_count = 0;
278 char* playlist = NULL;
279 char* cp;
280 char* tmp;
281 int len = 0;
282 bool unique = false;
284 /* Opening up a temp bookmark file */
285 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
286 "%s.tmp", bookmark_file_name);
287 temp_bookmark_file = open(global_temp_buffer,
288 O_WRONLY | O_CREAT | O_TRUNC);
289 if (temp_bookmark_file < 0)
290 return false; /* can't open the temp file */
292 if (!strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE) &&
293 (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
295 playlist = strchr(bookmark,'/');
296 cp = strrchr(bookmark,';');
297 len = cp - playlist;
298 unique = true;
301 /* Writing the new bookmark to the begining of the temp file */
302 write(temp_bookmark_file, bookmark, strlen(bookmark));
303 write(temp_bookmark_file, "\n", 1);
304 bookmark_count++;
306 /* Reading in the previous bookmarks and writing them to the temp file */
307 bookmark_file = open(bookmark_file_name, O_RDONLY);
308 if (bookmark_file >= 0)
310 while (read_line(bookmark_file, global_read_buffer,
311 sizeof(global_read_buffer)))
313 /* The MRB has a max of MAX_BOOKMARKS in it */
314 /* This keeps it from getting too large */
315 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
317 if(bookmark_count >= MAX_BOOKMARKS)
318 break;
321 cp = strchr(global_read_buffer,'/');
322 tmp = strrchr(global_read_buffer,';');
323 if (check_bookmark(global_read_buffer) &&
324 (!unique || len != tmp -cp || strncmp(playlist,cp,len)))
326 bookmark_count++;
327 write(temp_bookmark_file, global_read_buffer,
328 strlen(global_read_buffer));
329 write(temp_bookmark_file, "\n", 1);
332 close(bookmark_file);
334 close(temp_bookmark_file);
336 remove(bookmark_file_name);
337 rename(global_temp_buffer, bookmark_file_name);
339 return true;
343 /* ----------------------------------------------------------------------- */
344 /* This function takes the system resume data and formats it into a valid */
345 /* bookmark. */
346 /* ----------------------------------------------------------------------- */
347 static char* create_bookmark()
349 int resume_index = 0;
350 char *file;
352 /* grab the currently playing track */
353 struct mp3entry *id3 = audio_current_track();
354 if(!id3)
355 return NULL;
357 /* Get some basic resume information */
358 /* queue_resume and queue_resume_index are not used and can be ignored.*/
359 playlist_get_resume_info(&resume_index);
361 /* Get the currently playing file minus the path */
362 /* This is used when displaying the available bookmarks */
363 file = strrchr(id3->path,'/');
364 if(NULL == file)
365 return NULL;
367 /* create the bookmark */
368 snprintf(global_bookmark, sizeof(global_bookmark),
369 "%d;%ld;%d;%d;%ld;%d;%d;%s;%s",
370 resume_index,
371 id3->offset,
372 playlist_get_seed(NULL),
374 id3->elapsed,
375 global_settings.repeat_mode,
376 global_settings.playlist_shuffle,
377 playlist_get_name(NULL, global_temp_buffer,
378 sizeof(global_temp_buffer)),
379 file+1);
381 /* checking to see if the bookmark is valid */
382 if (check_bookmark(global_bookmark))
383 return global_bookmark;
384 else
385 return NULL;
388 static bool check_bookmark(const char* bookmark)
390 return parse_bookmark(bookmark,
391 NULL,NULL,NULL, NULL,
392 NULL,0,NULL,NULL,
393 NULL, NULL);
396 /* ----------------------------------------------------------------------- */
397 /* This function will determine if an autoload is necessary. This is an */
398 /* interface function. */
399 /* ------------------------------------------------------------------------*/
400 bool bookmark_autoload(const char* file)
402 int key;
403 int fd;
404 bool done = false;
406 if(global_settings.autoloadbookmark == BOOKMARK_NO)
407 return false;
409 /*Checking to see if a bookmark file exists.*/
410 if(!generate_bookmark_file_name(file))
412 return false;
414 fd = open(global_bookmark_file_name, O_RDONLY);
415 if(fd<0)
416 return false;
417 if(-1 == lseek(fd, 0, SEEK_END))
419 close(fd);
420 return false;
422 close(fd);
423 if(global_settings.autoloadbookmark == BOOKMARK_YES)
425 return bookmark_load(global_bookmark_file_name, true);
427 else
429 /* Prompting user to confirm bookmark load */
430 lcd_clear_display();
431 gui_syncstatusbar_draw(&statusbars, false);
432 #ifdef HAVE_LCD_BITMAP
433 lcd_setmargins(0, STATUSBAR_HEIGHT);
434 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
435 lcd_puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
436 lcd_puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
437 lcd_puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
438 #else
439 lcd_puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
440 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
441 #endif
442 lcd_update();
444 while(!done)
446 /* Wait for a key to be pushed */
447 key = button_get(true);
448 switch(key)
450 #ifdef HAVE_LCD_BITMAP
451 case BOOKMARK_DOWN:
452 return bookmark_load(global_bookmark_file_name, false);
453 #endif
454 case SETTINGS_OK:
455 return bookmark_load(global_bookmark_file_name, true);
457 default:
458 /* Handle sys events, ignore button releases & repeats */
459 if (default_event_handler(key) ||
460 !(key & (BUTTON_REPEAT|BUTTON_REL)))
461 done = true;
462 break;
465 return false;
469 /* ----------------------------------------------------------------------- */
470 /* This function loads the bookmark information into the resume memory. */
471 /* This is an interface function. */
472 /* ------------------------------------------------------------------------*/
473 bool bookmark_load(const char* file, bool autoload)
475 int fd;
476 bool success = true;
477 int offset;
478 int seed;
479 int index;
480 char* bookmark = NULL;;
482 if(autoload)
484 fd = open(file, O_RDONLY);
485 if(fd >= 0)
487 if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)))
488 bookmark=global_read_buffer;
489 close(fd);
492 else
494 /* This is not an auto-load, so list the bookmarks */
495 bookmark=select_bookmark(file);
496 if(!bookmark)
497 return true; /* User exited without selecting a bookmark */
500 if(bookmark)
502 success = parse_bookmark(bookmark,
503 &index,
504 &offset,
505 &seed,
506 NULL,
507 global_temp_buffer,
508 sizeof(global_temp_buffer),
509 NULL,
510 &global_settings.repeat_mode,
511 &global_settings.playlist_shuffle,
512 global_filename);
516 if(success)
517 bookmark_play(global_temp_buffer, index, offset, seed,
518 global_filename);
520 return success;
524 static int get_bookmark_count(const char* bookmark_file_name)
526 int read_count = 0;
527 int file = open(bookmark_file_name, O_RDONLY);
529 if(file < 0)
530 return -1;
532 /* Get the requested bookmark */
533 while(read_line(file, global_read_buffer, sizeof(global_read_buffer)))
535 if(check_bookmark(global_read_buffer))
536 read_count++;
539 close(file);
540 return read_count;
545 /* ----------------------------------------------------------------------- */
546 /* This displays a the bookmarks in a file and allows the user to */
547 /* select one to play. */
548 /* ------------------------------------------------------------------------*/
549 static char* select_bookmark(const char* bookmark_file_name)
551 int bookmark_id = 0;
552 int bookmark_id_prev = -1;
553 int key;
554 int lastkey = BUTTON_NONE;
555 char* bookmark = NULL;
556 int bookmark_count = 0;
558 #ifdef HAVE_LCD_BITMAP
559 int x = lcd_getxmargin();
560 int y = lcd_getymargin();
561 lcd_setmargins(0, 0);
562 #endif
564 bookmark_count = get_bookmark_count(bookmark_file_name);
566 while(true)
568 if(bookmark_id < 0)
569 bookmark_id = bookmark_count -1;
570 if(bookmark_id >= bookmark_count)
571 bookmark_id = 0;
573 if (bookmark_id != bookmark_id_prev)
575 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
576 bookmark_id_prev = bookmark_id;
579 if (!bookmark)
581 /* if there were no bookmarks in the file, delete the file and exit. */
582 if(bookmark_id <= 0)
584 gui_syncsplash(HZ, true, str(LANG_BOOKMARK_LOAD_EMPTY));
585 remove(bookmark_file_name);
586 return NULL;
588 else
590 bookmark_id_prev = bookmark_id;
591 bookmark_id--;
594 else
596 display_bookmark(bookmark, bookmark_id, bookmark_count);
597 if (global_settings.talk_menu) /* for voice UI */
598 say_bookmark(bookmark, bookmark_id);
601 /* waiting for the user to click a button */
602 key = button_get(true);
603 switch(key)
605 case BOOKMARK_SELECT:
606 #ifdef BOOKMARK_SELECT_PRE
607 if (lastkey != BOOKMARK_SELECT_PRE)
608 break;
609 #endif
610 /* User wants to use this bookmark */
611 #ifdef HAVE_LCD_BITMAP
612 if (global_settings.statusbar)
613 lcd_setmargins(0, STATUSBAR_HEIGHT);
614 else
615 lcd_setmargins(0, 0);
616 #endif
617 return bookmark;
619 case BOOKMARK_DELETE:
620 /* User wants to delete this bookmark */
621 delete_bookmark(bookmark_file_name, bookmark_id);
622 bookmark_id_prev=-2;
623 bookmark_count--;
624 if(bookmark_id >= bookmark_count)
625 bookmark_id = bookmark_count -1;
626 break;
628 case SETTINGS_DEC:
629 case SETTINGS_DEC | BUTTON_REPEAT:
630 bookmark_id--;
631 break;
633 case SETTINGS_INC:
634 case SETTINGS_INC | BUTTON_REPEAT:
635 bookmark_id++;
636 break;
638 case SETTINGS_CANCEL:
639 #ifdef SETTINGS_CANCEL2
640 case SETTINGS_CANCEL2:
641 #endif
642 #ifdef SETTINGS_OK2
643 case SETTINGS_OK2:
644 #endif
645 #ifdef HAVE_LCD_BITMAP
646 lcd_setmargins(x, y);
647 #endif
648 return NULL;
650 default:
651 if(default_event_handler(key) == SYS_USB_CONNECTED)
652 return NULL;
653 break;
655 lastkey = key;
658 return NULL;
662 /* ----------------------------------------------------------------------- */
663 /* This function takes a location in a bookmark file and deletes that */
664 /* bookmark. */
665 /* ------------------------------------------------------------------------*/
666 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
668 int temp_bookmark_file = 0;
669 int bookmark_file = 0;
670 int bookmark_count = 0;
672 /* Opening up a temp bookmark file */
673 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
674 "%s.tmp", bookmark_file_name);
675 temp_bookmark_file = open(global_temp_buffer,
676 O_WRONLY | O_CREAT | O_TRUNC);
677 bookmark_file = open(bookmark_file_name, O_RDONLY);
679 if (temp_bookmark_file < 0 || bookmark_file < 0)
680 return false; /* can't open one of the files */
682 /* Reading in the previous bookmarks and writing them to the temp file */
683 while (read_line(bookmark_file, global_read_buffer,
684 sizeof(global_read_buffer)))
686 /* The MRB has a max of MAX_BOOKMARKS in it */
687 /* This keeps it from getting too large */
688 if ((strcmp(bookmark_file_name,RECENT_BOOKMARK_FILE)==0))
690 if(bookmark_count >= MAX_BOOKMARKS)
691 break;
694 if (check_bookmark(global_read_buffer))
696 if (bookmark_id != bookmark_count)
698 write(temp_bookmark_file, global_read_buffer,
699 strlen(global_read_buffer));
700 write(temp_bookmark_file, "\n", 1);
702 bookmark_count++;
706 close(bookmark_file);
707 close(temp_bookmark_file);
709 remove(bookmark_file_name);
710 rename(global_temp_buffer, bookmark_file_name);
712 return true;
715 /* ----------------------------------------------------------------------- */
716 /* This function parses a bookmark and displays it for the user. */
717 /* ------------------------------------------------------------------------*/
718 static void display_bookmark(const char* bookmark,
719 int bookmark_id,
720 int bookmark_count)
722 int resume_index = 0;
723 long ms = 0;
724 int repeat_mode = 0;
725 bool playlist_shuffle = false;
726 int len;
727 char *dot;
729 /* getting the index and the time into the file */
730 parse_bookmark(bookmark,
731 &resume_index, NULL, NULL, NULL, NULL, 0,
732 &ms, &repeat_mode, &playlist_shuffle,
733 global_filename);
735 lcd_clear_display();
736 lcd_stop_scroll();
738 #ifdef HAVE_LCD_BITMAP
739 /* bookmark shuffle and repeat states*/
740 switch (repeat_mode)
742 #if (AB_REPEAT_ENABLE == 1)
743 case REPEAT_AB:
744 statusbar_icon_play_mode(Icon_RepeatAB);
745 break;
746 #endif
748 case REPEAT_ONE:
749 statusbar_icon_play_mode(Icon_RepeatOne);
750 break;
752 case REPEAT_ALL:
753 statusbar_icon_play_mode(Icon_Repeat);
754 break;
756 if(playlist_shuffle)
757 statusbar_icon_shuffle();
759 /* File Name */
760 len=strlen(global_filename);
761 if (len>3)
762 dot=strrchr(global_filename + len - 4, '.');
763 else
764 dot=NULL;
765 if (dot)
766 *dot='\0';
767 lcd_puts_scroll(0, 0, (unsigned char *)global_filename);
768 if (dot)
769 *dot='.';
771 /* bookmark number */
772 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d/%2d",
773 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
774 bookmark_id + 1, bookmark_count);
775 lcd_puts_scroll(0, 1, (unsigned char *)global_temp_buffer);
777 /* bookmark resume index */
778 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %2d",
779 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
780 lcd_puts_scroll(0, 2, (unsigned char *)global_temp_buffer);
782 /* elapsed time*/
783 if ( ms < 3600000 )
785 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %ld:%02d",
786 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
787 ms / 60000,
788 (unsigned int)(ms % 60000) / 1000);
789 /* unsigned int: hinting for 16bits archs */
791 else
793 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
794 "%s: %ld:%02ld:%02d",
795 str(LANG_BOOKMARK_SELECT_TIME_TEXT),
796 ms / 3600000,
797 ms % 3600000 / 60000,
798 (unsigned int)(ms % 60000) / 1000);
800 lcd_puts_scroll(0, 3, (unsigned char *)global_temp_buffer);
802 /* commands */
803 lcd_puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
804 lcd_puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
805 lcd_puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
806 #else
807 (void)bookmark_id;
808 len=strlen(global_filename);
809 if (len>3)
810 dot=strrchr(global_filename+len-4,'.');
811 else
812 dot=NULL;
813 if (dot)
814 *dot='\0';
815 if ( ms < 3600000 )
817 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
818 "%2d, %ld:%02ld, %s,",
819 (bookmark_count+1),
820 ms / 60000,
821 ms % 60000 / 1000,
822 global_filename);
824 else
826 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
827 "%2d, %ld:%02ld:%02ld, %s,",
828 (bookmark_count+1),
829 ms / 60000,
830 ms % 3600000 / 60000,
831 ms % 60000 / 1000,
832 global_filename);
835 gui_syncstatusbar_draw(&statusbars, false);
836 lcd_puts_scroll(0,0,global_temp_buffer);
837 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
838 if (dot)
839 *dot='.';
840 #endif
841 lcd_update();
845 /* ----------------------------------------------------------------------- */
846 /* This function parses a bookmark, says the voice UI part of it. */
847 /* ------------------------------------------------------------------------*/
848 static void say_bookmark(const char* bookmark,
849 int bookmark_id)
851 int resume_index;
852 long ms;
853 char dir[MAX_PATH];
854 bool enqueue = false; /* only the first voice is not queued */
856 parse_bookmark(bookmark,
857 &resume_index,
858 NULL, NULL, NULL,
859 dir, sizeof(dir),
860 &ms, NULL, NULL,
861 NULL);
862 /* disabled, because transition between talkbox and voice UI clip is not nice */
863 #if 0
864 if (global_settings.talk_dir >= 3)
865 { /* "talkbox" enabled */
866 char* last = strrchr(dir, '/');
867 if (last)
868 { /* compose filename for talkbox */
869 strncpy(last + 1, dir_thumbnail_name, sizeof(dir)-(last-dir)-1);
870 talk_file(dir, enqueue);
871 enqueue = true;
874 #endif
875 talk_id(VOICE_EXT_BMARK, enqueue);
876 talk_number(bookmark_id + 1, true);
877 talk_id(LANG_BOOKMARK_SELECT_INDEX_TEXT, true);
878 talk_number(resume_index + 1, true);
879 talk_id(LANG_BOOKMARK_SELECT_TIME_TEXT, true);
880 if (ms / 60000)
881 talk_value(ms / 60000, UNIT_MIN, true);
882 talk_value((ms % 60000) / 1000, UNIT_SEC, true);
886 /* ----------------------------------------------------------------------- */
887 /* This function retrieves a given bookmark from a file. */
888 /* If the bookmark requested is beyond the number of bookmarks available */
889 /* in the file, it will return the last one. */
890 /* It also returns the index number of the bookmark in the file */
891 /* ------------------------------------------------------------------------*/
892 static char* get_bookmark(const char* bookmark_file, int bookmark_count)
894 int read_count = -1;
895 int result = 0;
896 int file = open(bookmark_file, O_RDONLY);
898 if (file < 0)
899 return NULL;
901 if (bookmark_count < 0)
902 return NULL;
904 /* Get the requested bookmark */
905 while (read_count < bookmark_count)
907 /*Reading in a single bookmark */
908 result = read_line(file,
909 global_read_buffer,
910 sizeof(global_read_buffer));
912 /* Reading past the last bookmark in the file
913 causes the loop to stop */
914 if (result <= 0)
915 break;
917 read_count++;
920 close(file);
921 if (read_count == bookmark_count)
922 return global_read_buffer;
923 else
924 return NULL;
927 /* ----------------------------------------------------------------------- */
928 /* This function takes a bookmark and parses it. This function also */
929 /* validates the bookmark. Passing in NULL for an output variable */
930 /* indicates that value is not requested. */
931 /* ----------------------------------------------------------------------- */
932 static bool parse_bookmark(const char *bookmark,
933 int *resume_index,
934 int *resume_offset,
935 int *resume_seed,
936 int *resume_first_index,
937 char* resume_file,
938 unsigned int resume_file_size,
939 long* ms,
940 int * repeat_mode, bool *shuffle,
941 char* file_name)
943 /* First check to see if a valid line was passed in. */
944 int bookmark_len = strlen(bookmark);
945 int local_resume_index = 0;
946 int local_resume_offset = 0;
947 int local_resume_seed = 0;
948 int local_resume_first_index = 0;
949 int local_mS = 0;
950 int local_shuffle = 0;
951 int local_repeat_mode = 0;
952 char* local_resume_file = NULL;
953 char* local_file_name = NULL;
954 char* field;
955 char* end;
956 static char bookmarkcopy[MAX_BOOKMARK_SIZE];
958 /* Don't do anything if the bookmark length is 0 */
959 if (bookmark_len <= 0)
960 return false;
962 /* Making a dup of the bookmark to use with strtok_r */
963 strncpy(bookmarkcopy, bookmark, sizeof(bookmarkcopy));
964 bookmarkcopy[sizeof(bookmarkcopy) - 1] = 0;
966 /* resume_index */
967 if ((field = strtok_r(bookmarkcopy, ";", &end)))
968 local_resume_index = atoi(field);
969 else
970 return false;
972 /* resume_offset */
973 if ((field = strtok_r(NULL, ";", &end)))
974 local_resume_offset = atoi(field);
975 else
976 return false;
978 /* resume_seed */
979 if ((field = strtok_r(NULL, ";", &end)))
980 local_resume_seed = atoi(field);
981 else
982 return false;
984 /* resume_first_index */
985 if ((field = strtok_r(NULL, ";", &end)))
986 local_resume_first_index = atoi(field);
987 else
988 return false;
990 /* Milliseconds into MP3. Used for the bookmark select menu */
991 if ((field = strtok_r(NULL, ";", &end)))
992 local_mS = atoi(field);
993 else
994 return false;
996 /* repeat_mode */
997 if ((field = strtok_r(NULL, ";", &end)))
998 local_repeat_mode = atoi(field);
999 else
1000 return false;
1002 /* shuffle mode */
1003 if ((field = strtok_r(NULL, ";", &end)))
1004 local_shuffle = atoi(field);
1005 else
1006 return false;
1008 /* resume_file & file_name (for the bookmark select menu)*/
1009 if (end)
1011 local_resume_file = strtok_r(NULL, ";", &end);
1013 if (end)
1014 local_file_name = strtok_r(NULL, ";", &end);
1016 else
1017 return false;
1019 /* Only return the values the calling function wants */
1020 if (resume_index)
1021 *resume_index = local_resume_index;
1023 if (resume_offset)
1024 *resume_offset = local_resume_offset;
1026 if (resume_seed)
1027 *resume_seed = local_resume_seed;
1029 if (resume_first_index)
1030 *resume_first_index = local_resume_first_index;
1032 if (resume_file && local_resume_file)
1034 strncpy(resume_file, local_resume_file,
1035 MIN(strlen(local_resume_file), resume_file_size-1));
1036 resume_file[MIN(strlen(local_resume_file), resume_file_size-1)]=0;
1039 if (ms)
1040 *ms = local_mS;
1042 if (shuffle)
1043 *shuffle = local_shuffle;
1045 if (repeat_mode)
1046 *repeat_mode = local_repeat_mode;
1048 if (file_name && local_file_name)
1050 strncpy(file_name, local_file_name,MAX_PATH-1);
1051 file_name[MAX_PATH-1] = 0;
1054 return true;
1057 /* ----------------------------------------------------------------------- */
1058 /* This function is used by multiple functions and is used to generate a */
1059 /* bookmark named based off of the input. */
1060 /* Changing this function could result in how the bookmarks are stored. */
1061 /* it would be here that the centralized/decentralized bookmark code */
1062 /* could be placed. */
1063 /* ----------------------------------------------------------------------- */
1064 static bool generate_bookmark_file_name(const char *in)
1066 int len = strlen(in);
1068 /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
1069 /* otherwise, name it based on the in variable */
1070 if (!strcmp("/", in))
1071 strcpy(global_bookmark_file_name, "/root_dir.bmark");
1072 else
1074 strcpy(global_bookmark_file_name, in);
1075 if(global_bookmark_file_name[len-1] == '/')
1076 len--;
1077 strcpy(&global_bookmark_file_name[len], ".bmark");
1080 return true;
1083 /* ----------------------------------------------------------------------- */
1084 /* Returns the bookmark name for the current playlist */
1085 /* ----------------------------------------------------------------------- */
1086 bool bookmark_exist(void)
1088 bool exist=false;
1090 if(system_check())
1092 char* name = playlist_get_name(NULL, global_temp_buffer,
1093 sizeof(global_temp_buffer));
1094 if (generate_bookmark_file_name(name))
1096 int fd=open(global_bookmark_file_name, O_RDONLY);
1097 if (fd >=0)
1099 close(fd);
1100 exist=true;
1105 return exist;
1108 /* ----------------------------------------------------------------------- */
1109 /* Checks the current state of the system and returns if it is in a */
1110 /* bookmarkable state. */
1111 /* ----------------------------------------------------------------------- */
1112 /* Inputs: */
1113 /* ----------------------------------------------------------------------- */
1114 /* Outputs: */
1115 /* return bool: Indicates if the system was in a bookmarkable state */
1116 /* ----------------------------------------------------------------------- */
1117 static bool system_check(void)
1119 int resume_index = 0;
1120 struct mp3entry *id3 = audio_current_track();
1122 if (!id3)
1124 /* no track playing */
1125 return false;
1128 /* Checking to see if playing a queued track */
1129 if (playlist_get_resume_info(&resume_index) == -1)
1131 /* something bad happened while getting the queue information */
1132 return false;
1134 else if (playlist_modified(NULL))
1136 /* can't bookmark while in the queue */
1137 return false;
1140 return true;