Player: Save a bit of space by only using 7 bytes/char in the glyph table.
[Rockbox.git] / apps / bookmark.c
bloba86e2a65049a6e05fcb0133da142d4609cfa20e8
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 action_signalscreenchange();
505 while(true)
507 if(bookmark_id < 0)
508 bookmark_id = bookmark_count -1;
509 if(bookmark_id >= bookmark_count)
510 bookmark_id = 0;
512 if (bookmark_id != bookmark_id_prev)
514 bookmark = get_bookmark(bookmark_file_name, bookmark_id);
515 bookmark_id_prev = bookmark_id;
518 if (!bookmark)
520 /* if there were no bookmarks in the file, delete the file and exit. */
521 if(bookmark_id <= 0)
523 gui_syncsplash(HZ, str(LANG_BOOKMARK_LOAD_EMPTY));
524 remove(bookmark_file_name);
525 action_signalscreenchange();
526 return NULL;
528 else
530 bookmark_id_prev = bookmark_id;
531 bookmark_id--;
532 continue;
535 else
537 display_bookmark(bookmark, bookmark_id, bookmark_count);
538 if (global_settings.talk_menu) /* for voice UI */
539 say_bookmark(bookmark, bookmark_id);
542 /* waiting for the user to click a button */
543 key = get_action(CONTEXT_BOOKMARKSCREEN,TIMEOUT_BLOCK);
544 switch(key)
546 case ACTION_BMS_SELECT:
547 /* User wants to use this bookmark */
548 action_signalscreenchange();
549 return bookmark;
551 case ACTION_BMS_DELETE:
552 /* User wants to delete this bookmark */
553 delete_bookmark(bookmark_file_name, bookmark_id);
554 bookmark_id_prev=-2;
555 bookmark_count--;
556 if(bookmark_id >= bookmark_count)
557 bookmark_id = bookmark_count -1;
558 break;
560 case ACTION_STD_PREV:
561 case ACTION_STD_PREVREPEAT:
562 bookmark_id--;
563 break;
565 case ACTION_STD_NEXT:
566 case ACTION_STD_NEXTREPEAT:
567 bookmark_id++;
568 break;
570 case ACTION_BMS_EXIT:
571 action_signalscreenchange();
572 return NULL;
574 default:
575 if(default_event_handler(key) == SYS_USB_CONNECTED)
577 action_signalscreenchange();
578 return NULL;
580 break;
583 action_signalscreenchange();
584 return NULL;
588 /* ----------------------------------------------------------------------- */
589 /* This function takes a location in a bookmark file and deletes that */
590 /* bookmark. */
591 /* ------------------------------------------------------------------------*/
592 static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
594 int temp_bookmark_file = 0;
595 int bookmark_file = 0;
596 int bookmark_count = 0;
598 /* Opening up a temp bookmark file */
599 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
600 "%s.tmp", bookmark_file_name);
601 temp_bookmark_file = open(global_temp_buffer,
602 O_WRONLY | O_CREAT | O_TRUNC);
604 if (temp_bookmark_file < 0)
605 return false; /* can't open the temp file */
607 /* Reading in the previous bookmarks and writing them to the temp file */
608 bookmark_file = open(bookmark_file_name, O_RDONLY);
609 if (bookmark_file >= 0)
611 while (read_line(bookmark_file, global_read_buffer,
612 sizeof(global_read_buffer)) > 0)
614 if (bookmark_id != bookmark_count)
616 write(temp_bookmark_file, global_read_buffer,
617 strlen(global_read_buffer));
618 write(temp_bookmark_file, "\n", 1);
620 bookmark_count++;
622 close(bookmark_file);
624 close(temp_bookmark_file);
626 remove(bookmark_file_name);
627 rename(global_temp_buffer, bookmark_file_name);
629 return true;
632 /* ----------------------------------------------------------------------- */
633 /* This function parses a bookmark and displays it for the user. */
634 /* ------------------------------------------------------------------------*/
635 static void display_bookmark(const char* bookmark,
636 int bookmark_id,
637 int bookmark_count)
639 int resume_index = 0;
640 long ms = 0;
641 int repeat_mode = 0;
642 bool playlist_shuffle = false;
643 char *dot;
644 char time_buf[32];
645 int i;
647 /* getting the index and the time into the file */
648 parse_bookmark(bookmark,
649 &resume_index, NULL, NULL, NULL, NULL, 0,
650 &ms, &repeat_mode, &playlist_shuffle,
651 global_filename);
653 FOR_NB_SCREENS(i)
654 screens[i].clear_display();
656 #ifdef HAVE_LCD_BITMAP
657 /* bookmark shuffle and repeat states*/
658 switch (repeat_mode)
660 #ifdef AB_REPEAT_ENABLE
661 case REPEAT_AB:
662 statusbar_icon_play_mode(Icon_RepeatAB);
663 break;
664 #endif
666 case REPEAT_ONE:
667 statusbar_icon_play_mode(Icon_RepeatOne);
668 break;
670 case REPEAT_ALL:
671 statusbar_icon_play_mode(Icon_Repeat);
672 break;
674 if(playlist_shuffle)
675 statusbar_icon_shuffle();
677 /* File Name */
678 dot = strrchr(global_filename, '.');
680 if (dot)
681 *dot='\0';
683 FOR_NB_SCREENS(i)
684 screens[i].puts_scroll(0, 0, (unsigned char *)global_filename);
686 if (dot)
687 *dot='.';
689 /* bookmark number */
690 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d/%d",
691 str(LANG_BOOKMARK_SELECT_BOOKMARK_TEXT),
692 bookmark_id + 1, bookmark_count);
693 FOR_NB_SCREENS(i)
694 screens[i].puts_scroll(0, 1, (unsigned char *)global_temp_buffer);
696 /* bookmark resume index */
697 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %d",
698 str(LANG_BOOKMARK_SELECT_INDEX_TEXT), resume_index+1);
699 FOR_NB_SCREENS(i)
700 screens[i].puts_scroll(0, 2, (unsigned char *)global_temp_buffer);
702 /* elapsed time*/
703 format_time(time_buf, sizeof(time_buf), ms);
704 snprintf(global_temp_buffer, sizeof(global_temp_buffer), "%s: %s",
705 str(LANG_BOOKMARK_SELECT_TIME_TEXT), time_buf);
706 FOR_NB_SCREENS(i)
707 screens[i].puts_scroll(0, 3, (unsigned char *)global_temp_buffer);
709 /* commands */
710 FOR_NB_SCREENS(i)
712 screens[i].puts_scroll(0, 4, str(LANG_BOOKMARK_SELECT_PLAY));
713 screens[i].puts_scroll(0, 5, str(LANG_BOOKMARK_SELECT_EXIT));
714 screens[i].puts_scroll(0, 6, str(LANG_BOOKMARK_SELECT_DELETE));
715 screens[i].update();
717 #else
718 dot = strrchr(global_filename, '.');
720 if (dot)
721 *dot='\0';
723 format_time(time_buf, sizeof(time_buf), ms);
724 snprintf(global_temp_buffer, sizeof(global_temp_buffer),
725 "%d/%d, %s, %s", (bookmark_id + 1), bookmark_count,
726 time_buf, global_filename);
728 if (dot)
729 *dot='.';
731 gui_syncstatusbar_draw(&statusbars, false);
733 FOR_NB_SCREENS(i)
735 screens[i].puts_scroll(0,0,global_temp_buffer);
736 screens[i].puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER));
738 #endif
742 /* ----------------------------------------------------------------------- */
743 /* This function parses a bookmark, says the voice UI part of it. */
744 /* ------------------------------------------------------------------------*/
745 static void say_bookmark(const char* bookmark,
746 int bookmark_id)
748 int resume_index;
749 long ms;
750 char dir[MAX_PATH];
751 bool enqueue = false; /* only the first voice is not queued */
753 parse_bookmark(bookmark,
754 &resume_index,
755 NULL, NULL, NULL,
756 dir, sizeof(dir),
757 &ms, NULL, NULL,
758 NULL);
759 /* disabled, because transition between talkbox and voice UI clip is not nice */
760 #if 0
761 if (global_settings.talk_dir >= 3)
762 { /* "talkbox" enabled */
763 char* last = strrchr(dir, '/');
764 if (last)
765 { /* compose filename for talkbox */
766 strncpy(last + 1, dir_thumbnail_name, sizeof(dir)-(last-dir)-1);
767 talk_file(dir, enqueue);
768 enqueue = true;
771 #endif
772 talk_id(VOICE_EXT_BMARK, enqueue);
773 talk_number(bookmark_id + 1, true);
774 talk_id(LANG_BOOKMARK_SELECT_INDEX_TEXT, true);
775 talk_number(resume_index + 1, true);
776 talk_id(LANG_BOOKMARK_SELECT_TIME_TEXT, true);
777 if (ms / 60000)
778 talk_value(ms / 60000, UNIT_MIN, true);
779 talk_value((ms % 60000) / 1000, UNIT_SEC, true);
782 /* ----------------------------------------------------------------------- */
783 /* This function parses a bookmark and then plays it. */
784 /* ------------------------------------------------------------------------*/
785 static bool play_bookmark(const char* bookmark)
787 int index;
788 int offset;
789 int seed;
791 if (parse_bookmark(bookmark,
792 &index,
793 &offset,
794 &seed,
795 NULL,
796 global_temp_buffer,
797 sizeof(global_temp_buffer),
798 NULL,
799 &global_settings.repeat_mode,
800 &global_settings.playlist_shuffle,
801 global_filename))
803 bookmark_play(global_temp_buffer, index, offset, seed,
804 global_filename);
805 return true;
808 return false;
811 /* ----------------------------------------------------------------------- */
812 /* This function retrieves a given bookmark from a file. */
813 /* If the bookmark requested is beyond the number of bookmarks available */
814 /* in the file, it will return the last one. */
815 /* It also returns the index number of the bookmark in the file */
816 /* ------------------------------------------------------------------------*/
817 static char* get_bookmark(const char* bookmark_file, int bookmark_count)
819 int read_count = -1;
820 int result = 0;
821 int file = open(bookmark_file, O_RDONLY);
823 if (file < 0)
824 return NULL;
826 /* Get the requested bookmark */
827 while (read_count < bookmark_count)
829 /*Reading in a single bookmark */
830 result = read_line(file,
831 global_read_buffer,
832 sizeof(global_read_buffer));
834 /* Reading past the last bookmark in the file
835 causes the loop to stop */
836 if (result <= 0)
837 break;
839 read_count++;
842 close(file);
843 if ((read_count >= 0) && (read_count == bookmark_count))
844 return global_read_buffer;
845 else
846 return NULL;
849 static const char* skip_token(const char* s)
851 while (*s && *s != ';')
853 s++;
856 if (*s)
858 s++;
861 return s;
864 static const char* int_token(const char* s, int* dest)
866 if (dest != NULL)
868 *dest = atoi(s);
871 return skip_token(s);
874 static const char* long_token(const char* s, long* dest)
876 if (dest != NULL)
878 *dest = atoi(s); /* Should be atol, but we don't have it. */
881 return skip_token(s);
884 static const char* bool_token(const char* s, bool* dest)
886 if (dest != NULL)
888 *dest = atoi(s) != 0;
891 return skip_token(s);
894 /* ----------------------------------------------------------------------- */
895 /* This function takes a bookmark and parses it. This function also */
896 /* validates the bookmark. Passing in NULL for an output variable */
897 /* indicates that value is not requested. */
898 /* ----------------------------------------------------------------------- */
899 static bool parse_bookmark(const char *bookmark,
900 int *resume_index,
901 int *resume_offset,
902 int *resume_seed,
903 int *resume_first_index,
904 char* resume_file,
905 unsigned int resume_file_size,
906 long* ms,
907 int * repeat_mode, bool *shuffle,
908 char* file_name)
910 const char* s = bookmark;
911 const char* end;
913 s = int_token(s, resume_index);
914 s = int_token(s, resume_offset);
915 s = int_token(s, resume_seed);
916 s = int_token(s, resume_first_index);
917 s = long_token(s, ms);
918 s = int_token(s, repeat_mode);
919 s = bool_token(s, shuffle);
921 if (*s == 0)
923 return false;
926 end = strchr(s, ';');
928 if (resume_file != NULL)
930 size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s);
932 len = MIN(resume_file_size - 1, len);
933 strncpy(resume_file, s, len);
934 resume_file[len] = 0;
937 if (end != NULL && file_name != NULL)
939 end++;
940 strncpy(file_name, end, MAX_PATH - 1);
941 file_name[MAX_PATH - 1] = 0;
944 return true;
947 /* ----------------------------------------------------------------------- */
948 /* This function is used by multiple functions and is used to generate a */
949 /* bookmark named based off of the input. */
950 /* Changing this function could result in how the bookmarks are stored. */
951 /* it would be here that the centralized/decentralized bookmark code */
952 /* could be placed. */
953 /* ----------------------------------------------------------------------- */
954 static bool generate_bookmark_file_name(const char *in)
956 int len = strlen(in);
958 /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
959 /* otherwise, name it based on the in variable */
960 if (!strcmp("/", in))
961 strcpy(global_bookmark_file_name, "/root_dir.bmark");
962 else
964 strcpy(global_bookmark_file_name, in);
965 if(global_bookmark_file_name[len-1] == '/')
966 len--;
967 strcpy(&global_bookmark_file_name[len], ".bmark");
970 return true;
973 /* ----------------------------------------------------------------------- */
974 /* Returns true if a bookmark file exists for the current playlist */
975 /* ----------------------------------------------------------------------- */
976 bool bookmark_exist(void)
978 bool exist=false;
980 if(system_check())
982 char* name = playlist_get_name(NULL, global_temp_buffer,
983 sizeof(global_temp_buffer));
984 if (generate_bookmark_file_name(name))
986 int fd=open(global_bookmark_file_name, O_RDONLY);
987 if (fd >=0)
989 close(fd);
990 exist=true;
995 return exist;
998 /* ----------------------------------------------------------------------- */
999 /* Checks the current state of the system and returns if it is in a */
1000 /* bookmarkable state. */
1001 /* ----------------------------------------------------------------------- */
1002 /* Inputs: */
1003 /* ----------------------------------------------------------------------- */
1004 /* Outputs: */
1005 /* return bool: Indicates if the system was in a bookmarkable state */
1006 /* ----------------------------------------------------------------------- */
1007 static bool system_check(void)
1009 int resume_index = 0;
1010 struct mp3entry *id3 = audio_current_track();
1012 if (!id3)
1014 /* no track playing */
1015 return false;
1018 /* Checking to see if playing a queued track */
1019 if (playlist_get_resume_info(&resume_index) == -1)
1021 /* something bad happened while getting the queue information */
1022 return false;
1024 else if (playlist_modified(NULL))
1026 /* can't bookmark while in the queue */
1027 return false;
1030 return true;