Fix the wavplay icon
[Rockbox.git] / apps / bookmark.c
blobb5c0e4a14894cfaf9370481743ee2109d320f220
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 "action.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 #if LCD_DEPTH > 1
55 #include "backdrop.h"
56 #endif
58 #define MAX_BOOKMARKS 10
59 #define MAX_BOOKMARK_SIZE 350
60 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
62 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
63 bool most_recent);
64 static bool check_bookmark(const char* bookmark);
65 static char* create_bookmark(void);
66 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
67 static void display_bookmark(const char* bookmark,
68 int bookmark_id,
69 int bookmark_count);
70 static void say_bookmark(const char* bookmark,
71 int bookmark_id);
72 static bool play_bookmark(const char* bookmark);
73 static bool generate_bookmark_file_name(const char *in);
74 static char* get_bookmark(const char* bookmark_file, int bookmark_count);
75 static const char* skip_token(const char* s);
76 static const char* int_token(const char* s, int* dest);
77 static const char* long_token(const char* s, long* dest);
78 static const char* bool_token(const char* s, bool* dest);
79 static bool parse_bookmark(const char *bookmark,
80 int *resume_index,
81 int *resume_offset,
82 int *resume_seed,
83 int *resume_first_index,
84 char* resume_file,
85 unsigned int resume_file_size,
86 long* ms,
87 int * repeat_mode,
88 bool *shuffle,
89 char* file_name);
90 static char* select_bookmark(const char* bookmark_file_name);
91 static bool system_check(void);
92 static bool write_bookmark(bool create_bookmark_file);
93 static int get_bookmark_count(const char* bookmark_file_name);
95 static char global_temp_buffer[MAX_PATH+1];
96 static char global_bookmark_file_name[MAX_PATH];
97 static char global_read_buffer[MAX_BOOKMARK_SIZE];
98 static char global_bookmark[MAX_BOOKMARK_SIZE];
99 static char global_filename[MAX_PATH];
101 /* ----------------------------------------------------------------------- */
102 /* This is the interface function from the main menu. */
103 /* ----------------------------------------------------------------------- */
104 bool bookmark_create_menu(void)
106 write_bookmark(true);
107 return false;
110 /* ----------------------------------------------------------------------- */
111 /* This function acts as the load interface from the main menu */
112 /* This function determines the bookmark file name and then loads that file*/
113 /* for the user. The user can then select a bookmark to load. */
114 /* If no file/directory is currently playing, the menu item does not work. */
115 /* ----------------------------------------------------------------------- */
116 bool bookmark_load_menu(void)
118 if (system_check())
120 char* name = playlist_get_name(NULL, global_temp_buffer,
121 sizeof(global_temp_buffer));
122 if (generate_bookmark_file_name(name))
124 char* bookmark = select_bookmark(global_bookmark_file_name);
126 if (bookmark != NULL)
128 return play_bookmark(bookmark);
133 return false;
136 /* ----------------------------------------------------------------------- */
137 /* Gives the user a list of the Most Recent Bookmarks. This is an */
138 /* interface function */
139 /* ----------------------------------------------------------------------- */
140 bool bookmark_mrb_load()
142 char* bookmark = select_bookmark(RECENT_BOOKMARK_FILE);
144 if (bookmark != NULL)
146 return play_bookmark(bookmark);
149 return false;
152 /* ----------------------------------------------------------------------- */
153 /* This function handles an autobookmark creation. This is an interface */
154 /* function. */
155 /* ----------------------------------------------------------------------- */
156 bool bookmark_autobookmark(void)
158 if (!system_check())
159 return false;
161 audio_pause(); /* first pause playback */
162 switch (global_settings.autocreatebookmark)
164 case BOOKMARK_YES:
165 return write_bookmark(true);
167 case BOOKMARK_NO:
168 return false;
170 case BOOKMARK_RECENT_ONLY_YES:
171 return write_bookmark(false);
173 #ifdef HAVE_LCD_BITMAP
174 unsigned char *lines[]={str(LANG_AUTO_BOOKMARK_QUERY)};
175 struct text_message message={(char **)lines, 1};
176 #else
177 unsigned char *lines[]={str(LANG_AUTO_BOOKMARK_QUERY),
178 str(LANG_RESUME_CONFIRM_PLAYER)};
179 struct text_message message={(char **)lines, 2};
180 #endif
181 #if LCD_DEPTH > 1
182 show_main_backdrop(); /* switch to main backdrop as we may come from wps */
183 #endif
184 gui_syncstatusbar_draw(&statusbars, false);
185 if(gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
187 if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK)
188 return write_bookmark(false);
189 else
190 return write_bookmark(true);
192 return false;
195 /* ----------------------------------------------------------------------- */
196 /* This function takes the current current resume information and writes */
197 /* that to the beginning of the bookmark file. */
198 /* This file will contain N number of bookmarks in the following format: */
199 /* resume_index*resume_offset*resume_seed*resume_first_index* */
200 /* resume_file*milliseconds*MP3 Title* */
201 /* ------------------------------------------------------------------------*/
202 static bool write_bookmark(bool create_bookmark_file)
204 bool success=false;
205 char* bookmark;
207 if (!system_check())
208 return false; /* something didn't happen correctly, do nothing */
210 bookmark = create_bookmark();
211 if (!bookmark)
212 return false; /* something didn't happen correctly, do nothing */
214 if (global_settings.usemrb)
215 success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true);
218 /* writing the bookmark */
219 if (create_bookmark_file)
221 char* name = playlist_get_name(NULL, global_temp_buffer,
222 sizeof(global_temp_buffer));
223 if (generate_bookmark_file_name(name))
225 success = add_bookmark(global_bookmark_file_name, bookmark, false);
229 gui_syncsplash(HZ, str(success ? LANG_BOOKMARK_CREATE_SUCCESS
230 : LANG_BOOKMARK_CREATE_FAILURE));
232 return true;
235 /* ----------------------------------------------------------------------- */
236 /* This function adds a bookmark to a file. */
237 /* ------------------------------------------------------------------------*/
238 static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
239 bool most_recent)
241 int temp_bookmark_file = 0;
242 int bookmark_file = 0;
243 int bookmark_count = 0;
244 char* playlist = NULL;
245 char* cp;
246 char* tmp;
247 int len = 0;
248 bool unique = false;
250 /* Opening up a temp bookmark file */
251 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
252 "%s.tmp", bookmark_file_name);
253 temp_bookmark_file = open(global_temp_buffer,
254 O_WRONLY | O_CREAT | O_TRUNC);
255 if (temp_bookmark_file < 0)
256 return false; /* can't open the temp file */
258 if (most_recent && (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY))
260 playlist = strchr(bookmark,'/');
261 cp = strrchr(bookmark,';');
262 len = cp - playlist;
263 unique = true;
266 /* Writing the new bookmark to the begining of the temp file */
267 write(temp_bookmark_file, bookmark, strlen(bookmark));
268 write(temp_bookmark_file, "\n", 1);
269 bookmark_count++;
271 /* Reading in the previous bookmarks and writing them to the temp file */
272 bookmark_file = open(bookmark_file_name, O_RDONLY);
273 if (bookmark_file >= 0)
275 while (read_line(bookmark_file, global_read_buffer,
276 sizeof(global_read_buffer)) > 0)
278 /* The MRB has a max of MAX_BOOKMARKS in it */
279 /* This keeps it from getting too large */
280 if (most_recent && (bookmark_count >= MAX_BOOKMARKS))
281 break;
283 cp = strchr(global_read_buffer,'/');
284 tmp = strrchr(global_read_buffer,';');
285 if (check_bookmark(global_read_buffer) &&
286 (!unique || len != tmp -cp || strncmp(playlist,cp,len)))
288 bookmark_count++;
289 write(temp_bookmark_file, global_read_buffer,
290 strlen(global_read_buffer));
291 write(temp_bookmark_file, "\n", 1);
294 close(bookmark_file);
296 close(temp_bookmark_file);
298 remove(bookmark_file_name);
299 rename(global_temp_buffer, bookmark_file_name);
301 return true;
305 /* ----------------------------------------------------------------------- */
306 /* This function takes the system resume data and formats it into a valid */
307 /* bookmark. */
308 /* ----------------------------------------------------------------------- */
309 static char* create_bookmark()
311 int resume_index = 0;
312 char *file;
314 /* grab the currently playing track */
315 struct mp3entry *id3 = audio_current_track();
316 if(!id3)
317 return NULL;
319 /* Get some basic resume information */
320 /* queue_resume and queue_resume_index are not used and can be ignored.*/
321 playlist_get_resume_info(&resume_index);
323 /* Get the currently playing file minus the path */
324 /* This is used when displaying the available bookmarks */
325 file = strrchr(id3->path,'/');
326 if(NULL == file)
327 return NULL;
329 /* create the bookmark */
330 snprintf(global_bookmark, sizeof(global_bookmark),
331 "%d;%ld;%d;%d;%ld;%d;%d;%s;%s",
332 resume_index,
333 id3->offset,
334 playlist_get_seed(NULL),
336 id3->elapsed,
337 global_settings.repeat_mode,
338 global_settings.playlist_shuffle,
339 playlist_get_name(NULL, global_temp_buffer,
340 sizeof(global_temp_buffer)),
341 file+1);
343 /* checking to see if the bookmark is valid */
344 if (check_bookmark(global_bookmark))
345 return global_bookmark;
346 else
347 return NULL;
350 static bool check_bookmark(const char* bookmark)
352 return parse_bookmark(bookmark,
353 NULL,NULL,NULL, NULL,
354 NULL,0,NULL,NULL,
355 NULL, NULL);
358 /* ----------------------------------------------------------------------- */
359 /* This function will determine if an autoload is necessary. This is an */
360 /* interface function. */
361 /* ------------------------------------------------------------------------*/
362 bool bookmark_autoload(const char* file)
364 int key;
365 int fd;
366 int i;
368 if(global_settings.autoloadbookmark == BOOKMARK_NO)
369 return false;
371 /*Checking to see if a bookmark file exists.*/
372 if(!generate_bookmark_file_name(file))
374 return false;
376 fd = open(global_bookmark_file_name, O_RDONLY);
377 if(fd<0)
378 return false;
379 close(fd);
380 if(global_settings.autoloadbookmark == BOOKMARK_YES)
382 return bookmark_load(global_bookmark_file_name, true);
384 else
386 /* Prompting user to confirm bookmark load */
387 FOR_NB_SCREENS(i)
388 screens[i].clear_display();
390 gui_syncstatusbar_draw(&statusbars, false);
392 FOR_NB_SCREENS(i)
394 #ifdef HAVE_LCD_BITMAP
395 screens[i].setmargins(0, STATUSBAR_HEIGHT);
396 screens[i].puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
397 screens[i].puts(0,1, str(LANG_CONFIRM_WITH_PLAY_RECORDER));
398 screens[i].puts(0,2, str(LANG_BOOKMARK_SELECT_LIST_BOOKMARKS));
399 screens[i].puts(0,3, str(LANG_CANCEL_WITH_ANY_RECORDER));
400 screens[i].update();
401 #else
402 screens[i].puts_scroll(0,0, str(LANG_BOOKMARK_AUTOLOAD_QUERY));
403 screens[i].puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
404 #endif
407 /* Wait for a key to be pushed */
408 key = get_action(CONTEXT_BOOKMARKSCREEN,TIMEOUT_BLOCK);
409 switch(key)
411 #ifdef HAVE_LCD_BITMAP
412 case ACTION_STD_NEXT:
413 action_signalscreenchange();
414 return bookmark_load(global_bookmark_file_name, false);
415 #endif
416 case ACTION_BMS_SELECT:
417 action_signalscreenchange();
418 return bookmark_load(global_bookmark_file_name, true);
420 default:
421 break;
424 action_signalscreenchange();
425 return false;
429 /* ----------------------------------------------------------------------- */
430 /* This function loads the bookmark information into the resume memory. */
431 /* This is an interface function. */
432 /* ------------------------------------------------------------------------*/
433 bool bookmark_load(const char* file, bool autoload)
435 int fd;
436 char* bookmark = NULL;;
438 if(autoload)
440 fd = open(file, O_RDONLY);
441 if(fd >= 0)
443 if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0)
444 bookmark=global_read_buffer;
445 close(fd);
448 else
450 /* This is not an auto-load, so list the bookmarks */
451 bookmark = select_bookmark(file);
454 if (bookmark != NULL)
456 return play_bookmark(bookmark);
459 return true;
463 static int get_bookmark_count(const char* bookmark_file_name)
465 int read_count = 0;
466 int file = open(bookmark_file_name, O_RDONLY);
468 if(file < 0)
469 return -1;
471 /* Get the requested bookmark */
472 while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0)
474 read_count++;
477 close(file);
478 return read_count;
483 /* ----------------------------------------------------------------------- */
484 /* This displays a the bookmarks in a file and allows the user to */
485 /* select one to play. */
486 /* ------------------------------------------------------------------------*/
487 static char* select_bookmark(const char* bookmark_file_name)
489 int bookmark_id = 0;
490 int bookmark_id_prev = -1;
491 int key;
492 char* bookmark = NULL;
493 int bookmark_count = 0;
495 #ifdef HAVE_LCD_BITMAP
496 int i;
498 FOR_NB_SCREENS(i)
499 screens[i].setmargins(0, global_settings.statusbar
500 ? STATUSBAR_HEIGHT : 0);
501 #endif
503 bookmark_count = get_bookmark_count(bookmark_file_name);
504 if (bookmark_count < 1) /* error opening file, or empty file */
506 gui_syncsplash(HZ, str(LANG_BOOKMARK_LOAD_EMPTY));
507 return NULL;
509 action_signalscreenchange();
510 while(true)
512 if(bookmark_id < 0)
513 bookmark_id = bookmark_count -1;
514 if(bookmark_id >= bookmark_count)
515 bookmark_id = 0;
517 if (bookmark_id != bookmark_id_prev)
519 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
520 bookmark_id_prev = bookmark_id;
523 if (!bookmark)
525 /* if there were no bookmarks in the file, delete the file and exit. */
526 if(bookmark_id <= 0)
528 gui_syncsplash(HZ, str(LANG_BOOKMARK_LOAD_EMPTY));
529 remove(bookmark_file_name);
530 action_signalscreenchange();
531 return NULL;
533 else
535 bookmark_id_prev = bookmark_id;
536 bookmark_id--;
537 continue;
540 else
542 display_bookmark(bookmark, bookmark_id, bookmark_count);
543 if (global_settings.talk_menu) /* for voice UI */
544 say_bookmark(bookmark, bookmark_id);
547 /* waiting for the user to click a button */
548 key = get_action(CONTEXT_BOOKMARKSCREEN,TIMEOUT_BLOCK);
549 switch(key)
551 case ACTION_BMS_SELECT:
552 /* User wants to use this bookmark */
553 action_signalscreenchange();
554 return bookmark;
556 case ACTION_BMS_DELETE:
557 /* User wants to delete this bookmark */
558 delete_bookmark(bookmark_file_name, bookmark_id);
559 bookmark_id_prev=-2;
560 bookmark_count--;
561 if(bookmark_id >= bookmark_count)
562 bookmark_id = bookmark_count -1;
563 break;
565 case ACTION_STD_PREV:
566 case ACTION_STD_PREVREPEAT:
567 bookmark_id--;
568 break;
570 case ACTION_STD_NEXT:
571 case ACTION_STD_NEXTREPEAT:
572 bookmark_id++;
573 break;
575 case ACTION_BMS_EXIT:
576 action_signalscreenchange();
577 return NULL;
579 default:
580 if(default_event_handler(key) == SYS_USB_CONNECTED)
582 action_signalscreenchange();
583 return NULL;
585 break;
588 action_signalscreenchange();
589 return NULL;
593 /* ----------------------------------------------------------------------- */
594 /* This function takes a location in a bookmark file and deletes that */
595 /* bookmark. */
596 /* ------------------------------------------------------------------------*/
597 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
599 int temp_bookmark_file = 0;
600 int bookmark_file = 0;
601 int bookmark_count = 0;
603 /* Opening up a temp bookmark file */
604 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
605 "%s.tmp", bookmark_file_name);
606 temp_bookmark_file = open(global_temp_buffer,
607 O_WRONLY | O_CREAT | O_TRUNC);
609 if (temp_bookmark_file < 0)
610 return false; /* can't open the temp file */
612 /* Reading in the previous bookmarks and writing them to the temp file */
613 bookmark_file = open(bookmark_file_name, O_RDONLY);
614 if (bookmark_file >= 0)
616 while (read_line(bookmark_file, global_read_buffer,
617 sizeof(global_read_buffer)) > 0)
619 if (bookmark_id != bookmark_count)
621 write(temp_bookmark_file, global_read_buffer,
622 strlen(global_read_buffer));
623 write(temp_bookmark_file, "\n", 1);
625 bookmark_count++;
627 close(bookmark_file);
629 close(temp_bookmark_file);
631 remove(bookmark_file_name);
632 rename(global_temp_buffer, bookmark_file_name);
634 return true;
637 /* ----------------------------------------------------------------------- */
638 /* This function parses a bookmark and displays it for the user. */
639 /* ------------------------------------------------------------------------*/
640 static void display_bookmark(const char* bookmark,
641 int bookmark_id,
642 int bookmark_count)
644 int resume_index = 0;
645 long ms = 0;
646 int repeat_mode = 0;
647 bool playlist_shuffle = false;
648 char *dot;
649 char time_buf[32];
650 int i;
652 /* getting the index and the time into the file */
653 parse_bookmark(bookmark,
654 &resume_index, NULL, NULL, NULL, NULL, 0,
655 &ms, &repeat_mode, &playlist_shuffle,
656 global_filename);
658 FOR_NB_SCREENS(i)
659 screens[i].clear_display();
661 #ifdef HAVE_LCD_BITMAP
662 /* bookmark shuffle and repeat states*/
663 switch (repeat_mode)
665 #ifdef AB_REPEAT_ENABLE
666 case REPEAT_AB:
667 statusbar_icon_play_mode(Icon_RepeatAB);
668 break;
669 #endif
671 case REPEAT_ONE:
672 statusbar_icon_play_mode(Icon_RepeatOne);
673 break;
675 case REPEAT_ALL:
676 statusbar_icon_play_mode(Icon_Repeat);
677 break;
679 if(playlist_shuffle)
680 statusbar_icon_shuffle();
682 /* File Name */
683 dot = strrchr(global_filename, '.');
685 if (dot)
686 *dot='\0';
688 FOR_NB_SCREENS(i)
689 screens[i].puts_scroll(0, 0, (unsigned char *)global_filename);
691 if (dot)
692 *dot='.';
694 /* bookmark number */
695 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d/%d",
696 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
697 bookmark_id + 1, bookmark_count);
698 FOR_NB_SCREENS(i)
699 screens[i].puts_scroll(0, 1, (unsigned char *)global_temp_buffer);
701 /* bookmark resume index */
702 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d",
703 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
704 FOR_NB_SCREENS(i)
705 screens[i].puts_scroll(0, 2, (unsigned char *)global_temp_buffer);
707 /* elapsed time*/
708 format_time(time_buf, sizeof(time_buf), ms);
709 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %s",
710 str(LANG_BOOKMARK_SELECT_TIME_TEXT), time_buf);
711 FOR_NB_SCREENS(i)
712 screens[i].puts_scroll(0, 3, (unsigned char *)global_temp_buffer);
714 /* commands */
715 FOR_NB_SCREENS(i)
717 screens[i].puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
718 screens[i].puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
719 screens[i].puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
720 screens[i].update();
722 #else
723 dot = strrchr(global_filename, '.');
725 if (dot)
726 *dot='\0';
728 format_time(time_buf, sizeof(time_buf), ms);
729 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
730 "%d/%d, %s, %s", (bookmark_id + 1), bookmark_count,
731 time_buf, global_filename);
733 if (dot)
734 *dot='.';
736 gui_syncstatusbar_draw(&statusbars, false);
738 FOR_NB_SCREENS(i)
740 screens[i].puts_scroll(0,0,global_temp_buffer);
741 screens[i].puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
743 #endif
747 /* ----------------------------------------------------------------------- */
748 /* This function parses a bookmark, says the voice UI part of it. */
749 /* ------------------------------------------------------------------------*/
750 static void say_bookmark(const char* bookmark,
751 int bookmark_id)
753 int resume_index;
754 long ms;
755 char dir[MAX_PATH];
756 bool enqueue = false; /* only the first voice is not queued */
758 parse_bookmark(bookmark,
759 &resume_index,
760 NULL, NULL, NULL,
761 dir, sizeof(dir),
762 &ms, NULL, NULL,
763 NULL);
764 /* disabled, because transition between talkbox and voice UI clip is not nice */
765 #if 0
766 if (global_settings.talk_dir >= 3)
767 { /* "talkbox" enabled */
768 char* last = strrchr(dir, '/');
769 if (last)
770 { /* compose filename for talkbox */
771 strncpy(last + 1, dir_thumbnail_name, sizeof(dir)-(last-dir)-1);
772 talk_file(dir, enqueue);
773 enqueue = true;
776 #endif
777 talk_id(VOICE_EXT_BMARK, enqueue);
778 talk_number(bookmark_id + 1, true);
779 talk_id(LANG_BOOKMARK_SELECT_INDEX_TEXT, true);
780 talk_number(resume_index + 1, true);
781 talk_id(LANG_BOOKMARK_SELECT_TIME_TEXT, true);
782 if (ms / 60000)
783 talk_value(ms / 60000, UNIT_MIN, true);
784 talk_value((ms % 60000) / 1000, UNIT_SEC, true);
787 /* ----------------------------------------------------------------------- */
788 /* This function parses a bookmark and then plays it. */
789 /* ------------------------------------------------------------------------*/
790 static bool play_bookmark(const char* bookmark)
792 int index;
793 int offset;
794 int seed;
796 if (parse_bookmark(bookmark,
797 &index,
798 &offset,
799 &seed,
800 NULL,
801 global_temp_buffer,
802 sizeof(global_temp_buffer),
803 NULL,
804 &global_settings.repeat_mode,
805 &global_settings.playlist_shuffle,
806 global_filename))
808 bookmark_play(global_temp_buffer, index, offset, seed,
809 global_filename);
810 return true;
813 return false;
816 /* ----------------------------------------------------------------------- */
817 /* This function retrieves a given bookmark from a file. */
818 /* If the bookmark requested is beyond the number of bookmarks available */
819 /* in the file, it will return the last one. */
820 /* It also returns the index number of the bookmark in the file */
821 /* ------------------------------------------------------------------------*/
822 static char* get_bookmark(const char* bookmark_file, int bookmark_count)
824 int read_count = -1;
825 int result = 0;
826 int file = open(bookmark_file, O_RDONLY);
828 if (file < 0)
829 return NULL;
831 /* Get the requested bookmark */
832 while (read_count < bookmark_count)
834 /*Reading in a single bookmark */
835 result = read_line(file,
836 global_read_buffer,
837 sizeof(global_read_buffer));
839 /* Reading past the last bookmark in the file
840 causes the loop to stop */
841 if (result <= 0)
842 break;
844 read_count++;
847 close(file);
848 if ((read_count >= 0) && (read_count == bookmark_count))
849 return global_read_buffer;
850 else
851 return NULL;
854 static const char* skip_token(const char* s)
856 while (*s && *s != ';')
858 s++;
861 if (*s)
863 s++;
866 return s;
869 static const char* int_token(const char* s, int* dest)
871 if (dest != NULL)
873 *dest = atoi(s);
876 return skip_token(s);
879 static const char* long_token(const char* s, long* dest)
881 if (dest != NULL)
883 *dest = atoi(s); /* Should be atol, but we don't have it. */
886 return skip_token(s);
889 static const char* bool_token(const char* s, bool* dest)
891 if (dest != NULL)
893 *dest = atoi(s) != 0;
896 return skip_token(s);
899 /* ----------------------------------------------------------------------- */
900 /* This function takes a bookmark and parses it. This function also */
901 /* validates the bookmark. Passing in NULL for an output variable */
902 /* indicates that value is not requested. */
903 /* ----------------------------------------------------------------------- */
904 static bool parse_bookmark(const char *bookmark,
905 int *resume_index,
906 int *resume_offset,
907 int *resume_seed,
908 int *resume_first_index,
909 char* resume_file,
910 unsigned int resume_file_size,
911 long* ms,
912 int * repeat_mode, bool *shuffle,
913 char* file_name)
915 const char* s = bookmark;
916 const char* end;
918 s = int_token(s, resume_index);
919 s = int_token(s, resume_offset);
920 s = int_token(s, resume_seed);
921 s = int_token(s, resume_first_index);
922 s = long_token(s, ms);
923 s = int_token(s, repeat_mode);
924 s = bool_token(s, shuffle);
926 if (*s == 0)
928 return false;
931 end = strchr(s, ';');
933 if (resume_file != NULL)
935 size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s);
937 len = MIN(resume_file_size - 1, len);
938 strncpy(resume_file, s, len);
939 resume_file[len] = 0;
942 if (end != NULL && file_name != NULL)
944 end++;
945 strncpy(file_name, end, MAX_PATH - 1);
946 file_name[MAX_PATH - 1] = 0;
949 return true;
952 /* ----------------------------------------------------------------------- */
953 /* This function is used by multiple functions and is used to generate a */
954 /* bookmark named based off of the input. */
955 /* Changing this function could result in how the bookmarks are stored. */
956 /* it would be here that the centralized/decentralized bookmark code */
957 /* could be placed. */
958 /* ----------------------------------------------------------------------- */
959 static bool generate_bookmark_file_name(const char *in)
961 int len = strlen(in);
963 /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
964 /* otherwise, name it based on the in variable */
965 if (!strcmp("/", in))
966 strcpy(global_bookmark_file_name, "/root_dir.bmark");
967 else
969 strcpy(global_bookmark_file_name, in);
970 if(global_bookmark_file_name[len-1] == '/')
971 len--;
972 strcpy(&global_bookmark_file_name[len], ".bmark");
975 return true;
978 /* ----------------------------------------------------------------------- */
979 /* Returns true if a bookmark file exists for the current playlist */
980 /* ----------------------------------------------------------------------- */
981 bool bookmark_exist(void)
983 bool exist=false;
985 if(system_check())
987 char* name = playlist_get_name(NULL, global_temp_buffer,
988 sizeof(global_temp_buffer));
989 if (generate_bookmark_file_name(name))
991 int fd=open(global_bookmark_file_name, O_RDONLY);
992 if (fd >=0)
994 close(fd);
995 exist=true;
1000 return exist;
1003 /* ----------------------------------------------------------------------- */
1004 /* Checks the current state of the system and returns if it is in a */
1005 /* bookmarkable state. */
1006 /* ----------------------------------------------------------------------- */
1007 /* Inputs: */
1008 /* ----------------------------------------------------------------------- */
1009 /* Outputs: */
1010 /* return bool: Indicates if the system was in a bookmarkable state */
1011 /* ----------------------------------------------------------------------- */
1012 static bool system_check(void)
1014 int resume_index = 0;
1015 struct mp3entry *id3 = audio_current_track();
1017 if (!id3)
1019 /* no track playing */
1020 return false;
1023 /* Checking to see if playing a queued track */
1024 if (playlist_get_resume_info(&resume_index) == -1)
1026 /* something bad happened while getting the queue information */
1027 return false;
1029 else if (playlist_modified(NULL))
1031 /* can't bookmark while in the queue */
1032 return false;
1035 return true;