diff options
Diffstat (limited to 'apps/bookmark.c')
-rw-r--r-- | apps/bookmark.c | 1264 |
1 files changed, 708 insertions, 556 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c index 07751c2d4c..2411ddb0ee 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c @@ -43,6 +43,9 @@ #include "file.h" #include "pathfuncs.h" +/*#define LOGF_ENABLE*/ +#include "logf.h" + #define MAX_BOOKMARKS 10 #define MAX_BOOKMARK_SIZE 350 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark" @@ -66,286 +69,289 @@ struct bookmark_list #define BM_SPEED 0x02 /* bookmark values */ -static struct { +struct resume_info{ + const struct mp3entry *id3; int resume_index; unsigned long resume_offset; int resume_seed; - long resume_time; + long resume_elapsed; int repeat_mode; bool shuffle; /* optional values */ int pitch; - int speed; -} bm; - -static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, - bool most_recent); -static char* create_bookmark(void); -static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id); -static void say_bookmark(const char* bookmark, - int bookmark_id, bool show_playlist_name); -static bool play_bookmark(const char* bookmark); -static bool generate_bookmark_file_name(const char *in); -static bool parse_bookmark(const char *bookmark, const bool get_filenames, const bool strip_dir); -static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line); -static const char* get_bookmark_info(int list_index, - void* data, - char *buffer, - size_t buffer_len); -static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark); -static bool write_bookmark(bool create_bookmark_file, const char *bookmark); -static int get_bookmark_count(const char* bookmark_file_name); + int speed; +}; -#define TEMP_BUF_SIZE (MAX_PATH + 1) +/* Temp buffer used for reading, create_bookmark and filename creation */ +#define TEMP_BUF_SIZE (MAX(MAX_BOOKMARK_SIZE, MAX_PATH + 1)) static char global_temp_buffer[TEMP_BUF_SIZE]; -/* File name created by generate_bookmark_file_name */ -static char global_bookmark_file_name[MAX_PATH]; -static char global_read_buffer[MAX_BOOKMARK_SIZE]; -/* Bookmark created by create_bookmark*/ -static char global_bookmark[MAX_BOOKMARK_SIZE]; -/* Filename from parsed bookmark (can be made local where needed) */ -static char global_filename[MAX_PATH]; -/* ----------------------------------------------------------------------- */ -/* This is an interface function from the context menu. */ -/* Returns true on successful bookmark creation. */ -/* ----------------------------------------------------------------------- */ -bool bookmark_create_menu(void) +static inline void get_hash(const char *key, uint32_t *hash, int len) { - return write_bookmark(true, create_bookmark()); + *hash = crc_32(key, len, *hash); /* this is probably sufficient */ } -/* ----------------------------------------------------------------------- */ -/* This function acts as the load interface from the context menu. */ -/* This function determines the bookmark file name and then loads that file*/ -/* for the user. The user can then select or delete previous bookmarks. */ -/* This function returns BOOKMARK_SUCCESS on the selection of a track to */ -/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */ -/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */ -/* connection. */ -/* ----------------------------------------------------------------------- */ -int bookmark_load_menu(void) +static const char* skip_tokens(const char* s, int ntokens) { - char* bookmark; - int ret = BOOKMARK_FAIL; - - push_current_activity(ACTIVITY_BOOKMARKSLIST); - - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) + for (int i = 0; i < ntokens; i++) { - ret = select_bookmark(global_bookmark_file_name, false, &bookmark); - if (bookmark != NULL) + while (*s && *s != ';') { - ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL; + s++; + } + + if (*s) + { + s++; } } + return s; +} - pop_current_activity(); +static int int_token(const char **s) +{ + int ret = atoi(*s); + *s = skip_tokens(*s, 1); return ret; } -/* ----------------------------------------------------------------------- */ -/* Gives the user a list of the Most Recent Bookmarks. This is an */ -/* interface function */ -/* Returns true on the successful selection of a recent bookmark. */ -/* ----------------------------------------------------------------------- */ -bool bookmark_mrb_load() +static long long_token(const char **s) { - char* bookmark; - bool ret = false; + /* Should be atol, but we don't have it. */ + return int_token(s); +} - push_current_activity(ACTIVITY_BOOKMARKSLIST); - select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark); - if (bookmark != NULL) +/*-------------------------------------------------------------------------*/ +/* Get the name of the playlist and the name of the track from a bookmark. */ +/* Returns true iff both were extracted. */ +/*-------------------------------------------------------------------------*/ +static bool bookmark_get_playlist_and_track_hash(const char *bookmark, + uint32_t *pl_hash, + uint32_t *track_hash) +{ + *pl_hash = 0; + *track_hash = 0; + int pl_len; + const char *pl_start, *pl_end, *track; + + logf("%s", __func__); + + pl_start = strchr(bookmark,'/'); + if (!(pl_start)) + return false; + + pl_end = skip_tokens(pl_start, 1) - 1; + pl_len = pl_end - pl_start; + + track = pl_end + 1; + get_hash(pl_start, pl_hash, pl_len); + + if (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK) { - ret = play_bookmark(bookmark); + get_hash(track, track_hash, strlen(track)); } - pop_current_activity(); - return ret; + + return true; } /* ----------------------------------------------------------------------- */ -/* This function handles an autobookmark creation. This is an interface */ -/* function. */ -/* Returns true on successful bookmark creation. */ +/* This function takes a bookmark and parses it. This function also */ +/* validates the bookmark. Valid filenamebuf indicates whether */ +/* the filename tokens are to be extracted. */ +/* Returns true on successful bookmark parse. */ /* ----------------------------------------------------------------------- */ -bool bookmark_autobookmark(bool prompt_ok) +static bool parse_bookmark(char *filenamebuf, + size_t filenamebufsz, + const char *bookmark, + struct resume_info *resume_info, + const bool strip_dir) { - char* bookmark; - bool update; - - if (!bookmark_is_bookmarkable_state()) - return false; + const char* s = bookmark; + const char* end; - audio_pause(); /* first pause playback */ - update = (global_settings.autoupdatebookmark && bookmark_exists()); - bookmark = create_bookmark(); +#define GET_INT_TOKEN(var) var = int_token(&s) +#define GET_LONG_TOKEN(var) var = long_token(&s) +#define GET_BOOL_TOKEN(var) var = (int_token(&s) != 0) - if (update) - return write_bookmark(true, bookmark); + /* if new format bookmark, extract the optional content flags, + otherwise treat as an original format bookmark */ + int opt_flags = 0; + int opt_pitch = 0; + int opt_speed = 0; + int old_format = ((strchr(s, '>') == s) ? 0 : 1); + if (old_format == 0) /* this is a new format bookmark */ + { + s++; + GET_INT_TOKEN(opt_flags); + opt_pitch = (opt_flags & BM_PITCH) ? 1:0; + opt_speed = (opt_flags & BM_SPEED) ? 1:0; + } - switch (global_settings.autocreatebookmark) + /* extract all original bookmark tokens */ + if (resume_info) { - case BOOKMARK_YES: - return write_bookmark(true, bookmark); + GET_INT_TOKEN(resume_info->resume_index); + GET_LONG_TOKEN(resume_info->resume_offset); + GET_INT_TOKEN(resume_info->resume_seed); - case BOOKMARK_NO: - return false; + s = skip_tokens(s, old_format); /* skip deprecated token */ - case BOOKMARK_RECENT_ONLY_YES: - return write_bookmark(false, bookmark); - } - const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; - const struct text_message message={lines, 1}; + GET_LONG_TOKEN(resume_info->resume_elapsed); + GET_INT_TOKEN(resume_info->repeat_mode); + GET_BOOL_TOKEN(resume_info->shuffle); - if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) + /* extract all optional bookmark tokens */ + if (opt_pitch != 0) + GET_INT_TOKEN(resume_info->pitch); + if (opt_speed != 0) + GET_INT_TOKEN(resume_info->speed); + } + else /* no resume info we just want the file name strings */ { - if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) - return write_bookmark(false, bookmark); - else - return write_bookmark(true, bookmark); + #define DEFAULT_BM_TOKENS 6 + int skipct = DEFAULT_BM_TOKENS + old_format + opt_pitch + opt_speed; + s = skip_tokens(s, skipct); + #undef DEFAULT_BM_TOKENS } - return false; -} -/* ----------------------------------------------------------------------- */ -/* This function takes the current current resume information and writes */ -/* that to the beginning of the bookmark file. */ -/* This file will contain N number of bookmarks in the following format: */ -/* resume_index*resume_offset*resume_seed*resume_first_index* */ -/* resume_file*milliseconds*MP3 Title* */ -/* Returns true on successful bookmark write. */ -/* Returns false if any part of the bookmarking process fails. It is */ -/* possible that a bookmark is successfully added to the most recent */ -/* bookmark list but fails to be added to the bookmark file or vice versa. */ -/* ------------------------------------------------------------------------*/ -static bool write_bookmark(bool create_bookmark_file, const char *bookmark) -{ - bool ret=true; - - if (!bookmark) + if (*s == 0) { - ret = false; /* something didn't happen correctly, do nothing */ + return false; } - else - { - if (global_settings.usemrb) - ret = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true); + end = strchr(s, ';'); - /* writing the bookmark */ - if (create_bookmark_file) + /* extract file names */ + if(filenamebuf) + { + size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); + len = MIN(TEMP_BUF_SIZE - 1, len); + strmemccpy(global_temp_buffer, s, len + 1); + + if (end != NULL) { - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) - { - ret = ret & add_bookmark(global_bookmark_file_name, bookmark, false); - } - else + end++; + if (strip_dir) { - ret = false; /* generating bookmark file failed */ + s = strrchr(end, '/'); + if (s) + { + end = s; + end++; + } } + strmemccpy(filenamebuf, end, filenamebufsz); } - } - - splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) - : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); + } - return ret; + return true; } -/* Get the name of the playlist and the name of the track from a bookmark. */ -/* Returns true iff both were extracted. */ -static bool get_playlist_and_track(const char *bookmark, char **pl_start, - char **pl_end, char **track) +/* ------------------------------------------------------------------------- */ +/* This function takes a filename and appends .tmp. This function also opens */ +/* the resulting file based on oflags, filename will be in buf on return */ +/* Returns file descriptor */ +/* --------------------------------------------------------------------------*/ +static int open_temp_bookmark(char *buf, + size_t bufsz, + int oflags, + const char* filename) { - *pl_start = strchr(bookmark,'/'); - if (!(*pl_start)) - return false; - *pl_end = strrchr(bookmark,';'); - *track = *pl_end + 1; - return true; + if(filename[0] == '/') + filename++; + /* Opening up a temp bookmark file */ + int fd = open_pathfmt(buf, bufsz, oflags, "/%s.tmp", filename); +#ifdef LOGF_ENABLE + if (oflags & O_PATH) + logf("tempfile path %s", buf); + else + logf("opening tempfile %s", buf); +#endif + return fd; } /* ----------------------------------------------------------------------- */ /* This function adds a bookmark to a file. */ /* Returns true on successful bookmark add. */ /* ------------------------------------------------------------------------*/ -static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, +static bool add_bookmark(const char* bookmark_file_name, + const char* bookmark, bool most_recent) { - int temp_bookmark_file = 0; - int bookmark_file = 0; - int bookmark_count = 0; - char *pl_start = NULL, *bm_pl_start; - char *pl_end = NULL, *bm_pl_end; - int pl_len = 0, bm_pl_len; - char *track = NULL, *bm_track; - bool comp_playlist = false; - bool comp_track = false; - bool equal; + char fnamebuf[MAX_PATH]; + int temp_bookmark_file = 0; + int bookmark_file = 0; + int bookmark_count = 0; + bool comp_playlist = false; + bool comp_track = false; + bool equal; + uint32_t pl_hash, pl_track_hash; + uint32_t bm_pl_hash, bm_pl_track_hash; /* Opening up a temp bookmark file */ - snprintf(global_temp_buffer, sizeof(global_temp_buffer), - "%s.tmp", bookmark_file_name); - temp_bookmark_file = open(global_temp_buffer, - O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (temp_bookmark_file < 0) - return false; /* can't open the temp file */ + temp_bookmark_file = open_temp_bookmark(fnamebuf, + sizeof(fnamebuf), + O_WRONLY | O_CREAT | O_TRUNC, + bookmark_file_name); + + if (temp_bookmark_file < 0 || !bookmark) + return false; /* can't open the temp file or no bookmark */ if (most_recent && ((global_settings.usemrb == BOOKMARK_ONE_PER_PLAYLIST) || (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK))) { - if (get_playlist_and_track(bookmark, &pl_start, &pl_end, &track)) + + if (bookmark_get_playlist_and_track_hash(bookmark, &pl_hash, &pl_track_hash)) { comp_playlist = true; - pl_len = pl_end - pl_start; - if (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK) - comp_track = true; + comp_track = (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK); } } + logf("adding bookmark to %s [%s]", fnamebuf, bookmark); /* Writing the new bookmark to the begining of the temp file */ write(temp_bookmark_file, bookmark, strlen(bookmark)); write(temp_bookmark_file, "\n", 1); bookmark_count++; + /* WARNING underlying buffer to *bookmrk gets overwritten after this point! */ + /* Reading in the previous bookmarks and writing them to the temp file */ + logf("opening old bookmark %s", bookmark_file_name); bookmark_file = open(bookmark_file_name, O_RDONLY); if (bookmark_file >= 0) { - while (read_line(bookmark_file, global_read_buffer, - sizeof(global_read_buffer)) > 0) + while (read_line(bookmark_file, global_temp_buffer, + sizeof(global_temp_buffer)) > 0) { /* The MRB has a max of MAX_BOOKMARKS in it */ /* This keeps it from getting too large */ if (most_recent && (bookmark_count >= MAX_BOOKMARKS)) break; - if (!parse_bookmark(global_read_buffer, false, false)) + if (!parse_bookmark(NULL, 0, global_temp_buffer, NULL, false)) break; equal = false; if (comp_playlist) { - if (get_playlist_and_track(global_read_buffer, &bm_pl_start, - &bm_pl_end, &bm_track)) + if (bookmark_get_playlist_and_track_hash(global_temp_buffer, + &bm_pl_hash, &bm_pl_track_hash)) { - bm_pl_len = bm_pl_end - bm_pl_start; - equal = (pl_len == bm_pl_len) && !strncmp(pl_start, bm_pl_start, pl_len); + equal = (pl_hash == bm_pl_hash); if (equal && comp_track) - equal = !strcmp(track, bm_track); + { + equal = (pl_track_hash == bm_pl_track_hash); + } } } if (!equal) { bookmark_count++; - write(temp_bookmark_file, global_read_buffer, - strlen(global_read_buffer)); + /*logf("copying old bookmark [%s]", global_temp_buffer);*/ + write(temp_bookmark_file, global_temp_buffer, + strlen(global_temp_buffer)); write(temp_bookmark_file, "\n", 1); } } @@ -354,180 +360,237 @@ static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, close(temp_bookmark_file); remove(bookmark_file_name); - rename(global_temp_buffer, bookmark_file_name); + rename(fnamebuf, bookmark_file_name); return true; } +/* ----------------------------------------------------------------------- */ +/* This function is used by multiple functions and is used to generate a */ +/* bookmark named based off of the input. */ +/* Changing this function could result in how the bookmarks are stored. */ +/* it would be here that the centralized/decentralized bookmark code */ +/* could be placed. */ +/* Returns true if the file name is generated, false if it was too long */ +/* ----------------------------------------------------------------------- */ +static bool generate_bookmark_file_name(char *filenamebuf, + size_t filenamebufsz, + const char *bmarknamein, + size_t bmarknamelen) +{ + /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */ + /* otherwise, name it based on the bmarknamein variable */ + if (!strncmp("/", bmarknamein, bmarknamelen)) + strmemccpy(filenamebuf, "/root_dir.bmark", filenamebufsz); + else + { + size_t buflen, len; + /* strmemccpy considers the NULL so bmarknamelen is one off */ + buflen = MIN(filenamebufsz -1 , bmarknamelen); + if (buflen >= filenamebufsz) + return false; + + strmemccpy(filenamebuf, bmarknamein, buflen + 1); + + len = strlen(filenamebuf); + +#ifdef HAVE_MULTIVOLUME + /* The "root" of an extra volume need special handling too. */ + const char *filename; + path_strip_volume(filenamebuf, &filename, true); + bool volume_root = *filename == '\0'; +#endif + if(filenamebuf[len-1] == '/') { + filenamebuf[len-1] = '\0'; + } + + const char *name = ".bmark"; +#ifdef HAVE_MULTIVOLUME + if (volume_root) + name = "/volume_dir.bmark"; +#endif + len = strlcat(filenamebuf, name, filenamebufsz); + + if(len >= filenamebufsz) + return false; + } + logf ("generated name '%s' from '%.*s'", + filenamebuf, (int)bmarknamelen, bmarknamein); + return true; +} + /* GCC 7 and up complain about the snprintf in create_bookmark() when compiled with -D_FORTIFY_SOURCE or -Wformat-truncation This is a false positive, so disable it here only */ -#if __GNUC__ >= 7 +/* SHOULD NO LONGER BE NEEDED --Bilgus 11-2022 */ +#if 0 /* __GNUC__ >= 7 */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* ----------------------------------------------------------------------- */ /* This function takes the system resume data and formats it into a valid */ /* bookmark. */ -/* Returns not NULL on successful bookmark format. */ +/* playlist name and name len are passed back through the name/namelen */ +/* Return is not NULL on successful bookmark format. */ /* ----------------------------------------------------------------------- */ -static char* create_bookmark() +static char* create_bookmark(char **name, + size_t *namelen, + struct resume_info *resume_info) { - int resume_index = 0; - char *file; - - if (!bookmark_is_bookmarkable_state()) - return NULL; /* something didn't happen correctly, do nothing */ + const char *file; + char *buf = global_temp_buffer; + size_t bufsz = sizeof(global_temp_buffer); - /* grab the currently playing track */ - struct mp3entry *id3 = audio_current_track(); - if(!id3) + if(!resume_info->id3) return NULL; - /* Get some basic resume information */ - /* queue_resume and queue_resume_index are not used and can be ignored.*/ - playlist_get_resume_info(&resume_index); - - /* Get the currently playing file minus the path */ - /* This is used when displaying the available bookmarks */ - file = strrchr(id3->path,'/'); - if(NULL == file) - return NULL; - - /* create the bookmark */ - playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); - if (global_temp_buffer[strlen(global_temp_buffer) - 1] != '/') - file = id3->path; - else file++; - snprintf(global_bookmark, sizeof(global_bookmark), - /* new optional bookmark token descriptors should be inserted - just before the "%s;%s" in this line... */ + size_t bmarksz= snprintf(buf, bufsz, + /* new optional bookmark token descriptors should + be inserted just after ';"' in this line... */ #if defined(HAVE_PITCHCONTROL) - ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;%s;%s", + ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;", #else - ">%d;%d;%ld;%d;%ld;%d;%d;%s;%s", + ">%d;%d;%ld;%d;%ld;%d;%d;", #endif - /* ... their flags should go here ... */ + /* ... their flags should go here ... */ #if defined(HAVE_PITCHCONTROL) - BM_PITCH | BM_SPEED, + BM_PITCH | BM_SPEED, #else - 0, + 0, #endif - resume_index, - id3->offset, - playlist_get_seed(NULL), - id3->elapsed, - global_settings.repeat_mode, - global_settings.playlist_shuffle, - /* ...and their values should go here */ + resume_info->resume_index, + resume_info->id3->offset, + resume_info->resume_seed, + resume_info->id3->elapsed, + resume_info->repeat_mode, + resume_info->shuffle, + /* ...and their values should go here */ #if defined(HAVE_PITCHCONTROL) - (long)sound_get_pitch(), - (long)dsp_get_timestretch(), + (long)resume_info->pitch, + (long)resume_info->speed #endif - /* more mandatory tokens */ - global_temp_buffer, - file); + ); /*sprintf*/ +/* mandatory tokens */ + if (bmarksz >= bufsz) /* include NULL*/ + return NULL; + buf += bmarksz; + bufsz -= bmarksz; + /* create the bookmark */ + playlist_get_name(NULL, buf, bufsz); + bmarksz = strlen(buf); + + if (bmarksz == 0 || (bmarksz + 1) >= bufsz) /* include the separator & NULL*/ + return NULL; + + *name = buf; /* return the playlist name through the *pointer */ + *namelen = bmarksz; /* return the name length through the pointer */ + + /* Get the currently playing file minus the path */ + /* This is used when displaying the available bookmarks */ + file = strrchr(resume_info->id3->path,'/'); + if(NULL == file) + return NULL; + + if (buf[bmarksz - 1] != '/') + file = resume_info->id3->path; + else file++; + + buf += bmarksz; + bufsz -= (bmarksz + 1); + buf[0] = ';'; + buf[1] = '\0'; + + strlcat(buf, file, bufsz); + + logf("%s [%s]", __func__, global_temp_buffer); /* checking to see if the bookmark is valid */ - if (parse_bookmark(global_bookmark, false, false)) - return global_bookmark; + if (parse_bookmark(NULL, 0, global_temp_buffer, NULL, false)) + return global_temp_buffer; else return NULL; } -#if __GNUC__ >= 7 +#if 0/* __GNUC__ >= 7*/ #pragma GCC diagnostic pop /* -Wformat-truncation */ #endif /* ----------------------------------------------------------------------- */ -/* This function will determine if an autoload is necessary. This is an */ -/* interface function. */ -/* Returns true on bookmark load or bookmark selection. */ -/* ------------------------------------------------------------------------*/ -bool bookmark_autoload(const char* file) +/* This function gets some basic resume information for the current song */ +/* from rockbox, */ +/* ----------------------------------------------------------------------- */ +static void get_track_resume_info(struct resume_info *resume_info) { - char* bookmark; - - if(global_settings.autoloadbookmark == BOOKMARK_NO) - return false; - - /*Checking to see if a bookmark file exists.*/ - if(!generate_bookmark_file_name(file)) - { - return false; - } - - if(!file_exists(global_bookmark_file_name)) - return false; - - if(global_settings.autoloadbookmark == BOOKMARK_YES) - { - return bookmark_load(global_bookmark_file_name, true); - } + if (global_settings.playlist_shuffle) + playlist_get_resume_info(&(resume_info->resume_index)); else - { - int ret = select_bookmark(global_bookmark_file_name, true, &bookmark); - - if (bookmark != NULL) - { - if (!play_bookmark(bookmark)) - { - /* Selected bookmark not found. */ - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); - } + resume_info->resume_index = playlist_get_display_index() - 1; - /* Act as if autoload was done even if it failed, since the - * user did make an active selection. - */ - return true; - } - - return ret != BOOKMARK_SUCCESS; - } + resume_info->resume_seed = playlist_get_seed(NULL); + resume_info->id3 = audio_current_track(); + resume_info->repeat_mode = global_settings.repeat_mode; + resume_info->shuffle = global_settings.playlist_shuffle; +#if defined(HAVE_PITCHCONTROL) + resume_info->pitch = sound_get_pitch(); + resume_info->speed = dsp_get_timestretch(); +#endif } /* ----------------------------------------------------------------------- */ -/* This function loads the bookmark information into the resume memory. */ -/* This is an interface function. */ -/* Returns true on successful bookmark load. */ +/* This function takes the current current resume information and writes */ +/* that to the beginning of the bookmark file. */ +/* This file will contain N number of bookmarks in the following format: */ +/* resume_index*resume_offset*resume_seed*resume_first_index* */ +/* resume_file*milliseconds*MP3 Title* */ +/* Returns true on successful bookmark write. */ +/* Returns false if any part of the bookmarking process fails. It is */ +/* possible that a bookmark is successfully added to the most recent */ +/* bookmark list but fails to be added to the bookmark file or vice versa. */ /* ------------------------------------------------------------------------*/ -bool bookmark_load(const char* file, bool autoload) +static bool write_bookmark(bool create_bookmark_file) { - int fd; - char* bookmark = NULL; + logf("%s", __func__); + char bm_filename[MAX_PATH]; + bool ret=true; - if(autoload) + char *name = NULL; + size_t namelen = 0; + char* bm; + struct resume_info resume_info; + + if (bookmark_is_bookmarkable_state()) { - fd = open(file, O_RDONLY); - if(fd >= 0) + get_track_resume_info(&resume_info); + /* writing the most recent bookmark */ + if (global_settings.usemrb) { - if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0) - bookmark=global_read_buffer; - close(fd); + /* since we use the same buffer bookmark needs created each time */ + bm = create_bookmark(&name, &namelen, &resume_info); + ret = add_bookmark(RECENT_BOOKMARK_FILE, bm, true); } - } - else - { - /* This is not an auto-load, so list the bookmarks */ - select_bookmark(file, false, &bookmark); - } - if (bookmark != NULL) - { - if (!play_bookmark(bookmark)) + /* writing the directory bookmark */ + if (create_bookmark_file) { - /* Selected bookmark not found. */ - if (!autoload) + bm = create_bookmark(&name, &namelen, &resume_info); + if (generate_bookmark_file_name(bm_filename, + sizeof(bm_filename), name, namelen)) { - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); + ret &= add_bookmark(bm_filename, bm, false); + } + else + { + ret = false; /* generating bookmark file failed */ } - - return false; } } + else + ret = false; - return true; -} + splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) + : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); + return ret; +} static int get_bookmark_count(const char* bookmark_file_name) { @@ -537,11 +600,11 @@ static int get_bookmark_count(const char* bookmark_file_name) if(file < 0) return -1; - while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0) + while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0) { read_count++; } - + close(file); return read_count; } @@ -563,26 +626,26 @@ static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line) /* Entire file fits in buffer */ first_line = 0; } - + bookmarks->start = first_line; bookmarks->count = 0; bookmarks->reload = false; - - while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0) + + while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0) { read_count++; - + if (read_count >= first_line) { - dest -= strlen(global_read_buffer) + 1; - + dest -= strlen(global_temp_buffer) + 1; + if (dest < ((char*) bookmarks) + sizeof(*bookmarks) + (sizeof(char*) * (bookmarks->count + 1))) { break; } - - strcpy(dest, global_read_buffer); + + strcpy(dest, global_temp_buffer); bookmarks->items[bookmarks->count] = dest; bookmarks->count++; } @@ -597,6 +660,8 @@ static const char* get_bookmark_info(int list_index, char *buffer, size_t buffer_len) { + char fnamebuf[MAX_PATH]; + struct resume_info resume_info; struct bookmark_list* bookmarks = (struct bookmark_list*) data; int index = list_index / 2; @@ -604,22 +669,22 @@ static const char* get_bookmark_info(int list_index, { if (index == 0) { - return list_index % 2 == 0 + return list_index % 2 == 0 ? (char*) str(LANG_BOOKMARK_DONT_RESUME) : " "; } - + index--; } - if (bookmarks->reload || (index >= bookmarks->start + bookmarks->count) + if (bookmarks->reload || (index >= bookmarks->start + bookmarks->count) || (index < bookmarks->start)) { int read_index = index; - + /* Using count as a guide on how far to move could possibly fail * sometimes. Use byte count if that is a problem? */ - + if (read_index != 0) { /* Move count * 3 / 4 items in the direction the user is moving, @@ -627,32 +692,33 @@ static const char* get_bookmark_info(int list_index, */ int offset = bookmarks->count; int max = bookmarks->total_count - (bookmarks->count / 2); - + if (read_index < bookmarks->start) { offset *= 3; } - + read_index = index - offset / 4; if (read_index > max) { read_index = max; } - + if (read_index < 0) { read_index = 0; } } - + if (buffer_bookmarks(bookmarks, read_index) <= index) { return ""; } } - - if (!parse_bookmark(bookmarks->items[index - bookmarks->start], true, true)) + + if (!parse_bookmark(fnamebuf, sizeof(fnamebuf), + bookmarks->items[index - bookmarks->start], &resume_info, true)) { return list_index % 2 == 0 ? (char*) str(LANG_BOOKMARK_INVALID) : " "; } @@ -662,12 +728,12 @@ static const char* get_bookmark_info(int list_index, char *name; char *format; int len = strlen(global_temp_buffer); - + if (bookmarks->show_playlist_name && len > 0) { name = global_temp_buffer; len--; - + if (name[len] != '/') { strrsplt(name, '.'); @@ -686,25 +752,73 @@ static const char* get_bookmark_info(int list_index, } else { - name = global_filename; + name = fnamebuf; format = "%s"; } - - strrsplt(global_filename, '.'); - snprintf(buffer, buffer_len, format, name, global_filename); + + strrsplt(fnamebuf, '.'); + snprintf(buffer, buffer_len, format, name, fnamebuf); return buffer; } else { char time_buf[32]; - format_time(time_buf, sizeof(time_buf), bm.resume_time); - snprintf(buffer, buffer_len, "%s, %d%s", time_buf, bm.resume_index + 1, - bm.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : ""); + format_time(time_buf, sizeof(time_buf), resume_info.resume_elapsed); + snprintf(buffer, buffer_len, "%s, %d%s", time_buf, + resume_info.resume_index + 1, + resume_info.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : ""); return buffer; } } +/* ----------------------------------------------------------------------- */ +/* This function parses a bookmark, says the voice UI part of it. */ +/* ------------------------------------------------------------------------*/ +static void say_bookmark(const char* bookmark, + int bookmark_id, + bool show_playlist_name) +{ + char fnamebuf[MAX_PATH]; + struct resume_info resume_info; + if (!parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, false)) + { + talk_id(LANG_BOOKMARK_INVALID, false); + return; + } + + talk_number(bookmark_id + 1, false); + + bool is_dir = (global_temp_buffer[0] + && global_temp_buffer[strlen(global_temp_buffer)-1] == '/'); + + /* HWCODEC cannot enqueue voice file entries and .talk thumbnails + together, because there is no guarantee that the same mp3 + parameters are used. */ + if(show_playlist_name) + { /* It's useful to know which playlist this is */ + if(is_dir) + talk_dir_or_spell(global_temp_buffer, + TALK_IDARRAY(VOICE_DIR), true); + else talk_file_or_spell(NULL, global_temp_buffer, + TALK_IDARRAY(LANG_PLAYLIST), true); + } + + if(resume_info.shuffle) + talk_id(LANG_SHUFFLE, true); + + talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); + talk_number(resume_info.resume_index + 1, true); + talk_id(LANG_TIME, true); + talk_value(resume_info.resume_elapsed / 1000, UNIT_TIME, true); + + /* Track filename */ + if(!is_dir) + global_temp_buffer[0] = 0; + talk_file_or_spell(global_temp_buffer, fnamebuf, + TALK_IDARRAY(VOICE_FILE), true); +} + static int bookmark_list_voice_cb(int list_index, void* data) { struct bookmark_list* bookmarks = (struct bookmark_list*) data; @@ -722,6 +836,57 @@ static int bookmark_list_voice_cb(int list_index, void* data) } /* ----------------------------------------------------------------------- */ +/* This function takes a location in a bookmark file and deletes that */ +/* bookmark. */ +/* Returns true on successful bookmark deletion. */ +/* ------------------------------------------------------------------------*/ +static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id) +{ + int temp_bookmark_file = 0; + int bookmark_file = 0; + int bookmark_count = 0; + + /* Opening up a temp bookmark file */ + temp_bookmark_file = open_temp_bookmark(global_temp_buffer, + sizeof(global_temp_buffer), + O_WRONLY | O_CREAT | O_TRUNC, + bookmark_file_name); + + if (temp_bookmark_file < 0) + return false; /* can't open the temp file */ + + /* Reading in the previous bookmarks and writing them to the temp file */ + bookmark_file = open(bookmark_file_name, O_RDONLY); + if (bookmark_file >= 0) + { + while (read_line(bookmark_file, global_temp_buffer, + sizeof(global_temp_buffer)) > 0) + { + if (bookmark_id != bookmark_count) + { + write(temp_bookmark_file, global_temp_buffer, + strlen(global_temp_buffer)); + write(temp_bookmark_file, "\n", 1); + } + bookmark_count++; + } + close(bookmark_file); + } + close(temp_bookmark_file); + + /* only retrieve the path*/ + open_temp_bookmark(global_temp_buffer, + sizeof(global_temp_buffer), + O_PATH, + bookmark_file_name); + + remove(bookmark_file_name); + rename(global_temp_buffer, bookmark_file_name); + + return true; +} + +/* ----------------------------------------------------------------------- */ /* This displays the bookmarks in a file and allows the user to */ /* select one to play. */ /* *selected_bookmark contains a non NULL value on successful bookmark */ @@ -730,7 +895,9 @@ static int bookmark_list_voice_cb(int list_index, void* data) /* if no selection was made and BOOKMARK_USB_CONNECTED if the selection */ /* menu is forced to exit due to a USB connection. */ /* ------------------------------------------------------------------------*/ -static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark) +static int select_bookmark(const char* bookmark_file_name, + bool show_dont_resume, + char** selected_bookmark) { struct bookmark_list* bookmarks; struct gui_synclist list; @@ -747,16 +914,17 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume bookmarks->filename = bookmark_file_name; bookmarks->start = 0; bookmarks->show_playlist_name - = strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0; - gui_synclist_init(&list, &get_bookmark_info, (void*) bookmarks, false, 2, NULL); + = (strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0); + + gui_synclist_init(&list, &get_bookmark_info, + (void*) bookmarks, false, 2, NULL); + if(global_settings.talk_menu) gui_synclist_set_voice_callback(&list, bookmark_list_voice_cb); - gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK), - Icon_Bookmark); while (!exit) { - + if (refresh) { int count = get_bookmark_count(bookmark_file_name); @@ -787,14 +955,15 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume } buffer_bookmarks(bookmarks, bookmarks->start); + gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK), + Icon_Bookmark); gui_synclist_draw(&list); cond_talk_ids_fq(VOICE_EXT_BMARK); gui_synclist_speak_item(&list); refresh = false; } - list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2, - &list, &action, LIST_WRAP_UNLESS_HELD); + list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2, &list, &action); item = gui_synclist_get_sel_pos(&list) / 2; if (bookmarks->show_dont_resume) @@ -805,17 +974,17 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume if (action == ACTION_STD_CONTEXT) { MENUITEM_STRINGLIST(menu_items, ID2P(LANG_BOOKMARK_CONTEXT_MENU), - NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME), + NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME), ID2P(LANG_BOOKMARK_CONTEXT_DELETE)); - static const int menu_actions[] = + static const int menu_actions[] = { ACTION_STD_OK, ACTION_BMS_DELETE }; int selection = do_menu(&menu_items, NULL, NULL, false); - + refresh = true; - if (selection >= 0 && selection <= + if (selection >= 0 && selection <= (int) (sizeof(menu_actions) / sizeof(menu_actions[0]))) { action = menu_actions[selection]; @@ -842,19 +1011,9 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume case ACTION_BMS_DELETE: if (item >= 0) - { - const char *lines[]={ - ID2P(LANG_REALLY_DELETE) - }; - const char *yes_lines[]={ - ID2P(LANG_DELETING) - }; - - const struct text_message message={lines, 1}; - const struct text_message yes_message={yes_lines, 1}; - - if(gui_syncyesno_run(&message, &yes_message, NULL)==YESNO_YES) - { + { + if (confirm_delete_yesno("") == YESNO_YES) + { delete_bookmark(bookmark_file_name, item); bookmarks->reload = true; } @@ -879,257 +1038,247 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume } /* ----------------------------------------------------------------------- */ -/* This function takes a location in a bookmark file and deletes that */ -/* bookmark. */ -/* Returns true on successful bookmark deletion. */ +/* This function parses a bookmark and then plays it. */ +/* Returns true on successful bookmark play. */ /* ------------------------------------------------------------------------*/ -static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id) +static bool play_bookmark(const char* bookmark) { - int temp_bookmark_file = 0; - int bookmark_file = 0; - int bookmark_count = 0; - - /* Opening up a temp bookmark file */ - snprintf(global_temp_buffer, sizeof(global_temp_buffer), - "%s.tmp", bookmark_file_name); - temp_bookmark_file = open(global_temp_buffer, - O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if (temp_bookmark_file < 0) - return false; /* can't open the temp file */ + char fnamebuf[MAX_PATH]; + struct resume_info resume_info; +#if defined(HAVE_PITCHCONTROL) + /* preset pitch and speed to 100% in case bookmark doesn't have info */ + resume_info.pitch = sound_get_pitch(); + resume_info.speed = dsp_get_timestretch(); +#endif - /* Reading in the previous bookmarks and writing them to the temp file */ - bookmark_file = open(bookmark_file_name, O_RDONLY); - if (bookmark_file >= 0) + if (parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, true)) { - while (read_line(bookmark_file, global_read_buffer, - sizeof(global_read_buffer)) > 0) + global_settings.repeat_mode = resume_info.repeat_mode; + global_settings.playlist_shuffle = resume_info.shuffle; +#if defined(HAVE_PITCHCONTROL) + sound_set_pitch(resume_info.pitch); + dsp_set_timestretch(resume_info.speed); +#endif + if (!warn_on_pl_erase()) + return false; + bool success = bookmark_play(global_temp_buffer, resume_info.resume_index, + resume_info.resume_elapsed, resume_info.resume_offset, + resume_info.resume_seed, fnamebuf); + if (success) /* verify we loaded the correct track */ { - if (bookmark_id != bookmark_count) + const struct mp3entry *id3 = audio_current_track(); + if (id3) { - write(temp_bookmark_file, global_read_buffer, - strlen(global_read_buffer)); - write(temp_bookmark_file, "\n", 1); + const char *path; + const char *track; + path_basename(id3->path, &path); + path_basename(fnamebuf, &track); + if (strcmp(path, track) == 0) + { + return true; + } } - bookmark_count++; + audio_stop(); } - close(bookmark_file); } - close(temp_bookmark_file); - remove(bookmark_file_name); - rename(global_temp_buffer, bookmark_file_name); - - return true; + return false; } +/*-------------------------------------------------------------------------*/ +/* PUBLIC INTERFACE -------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + + /* ----------------------------------------------------------------------- */ -/* This function parses a bookmark, says the voice UI part of it. */ -/* ------------------------------------------------------------------------*/ -static void say_bookmark(const char* bookmark, - int bookmark_id, bool show_playlist_name) +/* This is an interface function from the context menu. */ +/* Returns true on successful bookmark creation. */ +/* ----------------------------------------------------------------------- */ +bool bookmark_create_menu(void) { - if (!parse_bookmark(bookmark, true, false)) + return write_bookmark(true); +} +/* ----------------------------------------------------------------------- */ +/* This function acts as the load interface from the context menu. */ +/* This function determines the bookmark file name and then loads that file*/ +/* for the user. The user can then select or delete previous bookmarks. */ +/* This function returns BOOKMARK_SUCCESS on the selection of a track to */ +/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */ +/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */ +/* connection. */ +/* ----------------------------------------------------------------------- */ +int bookmark_load_menu(void) +{ + char bm_filename[MAX_PATH]; + char* bookmark; + int ret = BOOKMARK_FAIL; + + push_current_activity(ACTIVITY_BOOKMARKSLIST); + + char* name = playlist_get_name(NULL, global_temp_buffer, + sizeof(global_temp_buffer)); + if (generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1)) { - talk_id(LANG_BOOKMARK_INVALID, false); - return; + ret = select_bookmark(bm_filename, false, &bookmark); + if (bookmark != NULL) + { + ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL; + } } - talk_number(bookmark_id + 1, false); + pop_current_activity(); + return ret; +} - bool is_dir = (global_temp_buffer[0] - && global_temp_buffer[strlen(global_temp_buffer)-1] == '/'); +/* ----------------------------------------------------------------------- */ +/* Gives the user a list of the Most Recent Bookmarks. This is an */ +/* interface function */ +/* Returns true on the successful selection of a recent bookmark. */ +/* ----------------------------------------------------------------------- */ +bool bookmark_mrb_load() +{ + char* bookmark; + bool ret = false; - /* HWCODEC cannot enqueue voice file entries and .talk thumbnails - together, because there is no guarantee that the same mp3 - parameters are used. */ - if(show_playlist_name) - { /* It's useful to know which playlist this is */ - if(is_dir) - talk_dir_or_spell(global_temp_buffer, - TALK_IDARRAY(VOICE_DIR), true); - else talk_file_or_spell(NULL, global_temp_buffer, - TALK_IDARRAY(LANG_PLAYLIST), true); + push_current_activity(ACTIVITY_BOOKMARKSLIST); + select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark); + if (bookmark != NULL) + { + ret = play_bookmark(bookmark); } - if(bm.shuffle) - talk_id(LANG_SHUFFLE, true); - - talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); - talk_number(bm.resume_index + 1, true); - talk_id(LANG_TIME, true); - talk_value(bm.resume_time / 1000, UNIT_TIME, true); - - /* Track filename */ - if(!is_dir) - global_temp_buffer[0] = 0; - talk_file_or_spell(global_temp_buffer, global_filename, - TALK_IDARRAY(VOICE_FILE), true); + pop_current_activity(); + return ret; } /* ----------------------------------------------------------------------- */ -/* This function parses a bookmark and then plays it. */ -/* Returns true on successful bookmark play. */ -/* ------------------------------------------------------------------------*/ -static bool play_bookmark(const char* bookmark) +/* This function handles an autobookmark creation. This is an interface */ +/* function. */ +/* Returns true on successful bookmark creation. */ +/* ----------------------------------------------------------------------- */ +bool bookmark_autobookmark(bool prompt_ok) { -#if defined(HAVE_PITCHCONTROL) - /* preset pitch and speed to 100% in case bookmark doesn't have info */ - bm.pitch = sound_get_pitch(); - bm.speed = dsp_get_timestretch(); -#endif + logf("%s", __func__); + bool update; + + if (!bookmark_is_bookmarkable_state()) + return false; + + audio_pause(); /* first pause playback */ + update = (global_settings.autoupdatebookmark && bookmark_exists()); + + if (update) + return write_bookmark(true); - if (parse_bookmark(bookmark, true, true)) + switch (global_settings.autocreatebookmark) { - global_settings.repeat_mode = bm.repeat_mode; - global_settings.playlist_shuffle = bm.shuffle; -#if defined(HAVE_PITCHCONTROL) - sound_set_pitch(bm.pitch); - dsp_set_timestretch(bm.speed); -#endif - if (!warn_on_pl_erase()) + case BOOKMARK_YES: + return write_bookmark(true); + + case BOOKMARK_NO: return false; - return bookmark_play(global_temp_buffer, bm.resume_index, - bm.resume_time, bm.resume_offset, bm.resume_seed, global_filename); + + case BOOKMARK_RECENT_ONLY_YES: + return write_bookmark(false); } + const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; + const struct text_message message={lines, 1}; + if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) + { + if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) + return write_bookmark(false); + else + return write_bookmark(true); + } return false; } -static const char* skip_token(const char* s) +/* ----------------------------------------------------------------------- */ +/* This function will determine if an autoload is necessary. This is an */ +/* interface function. */ +/* Returns */ +/* BOOKMARK_DO_RESUME on bookmark load or bookmark selection. */ +/* BOOKMARK_DONT_RESUME if we're not going to resume */ +/* BOOKMARK_CANCEL if user canceled */ +/* ------------------------------------------------------------------------*/ +int bookmark_autoload(const char* file) { - while (*s && *s != ';') + logf("%s", __func__); + char bm_filename[MAX_PATH]; + char* bookmark; + + if(global_settings.autoloadbookmark == BOOKMARK_NO) + return BOOKMARK_DONT_RESUME; + + /*Checking to see if a bookmark file exists.*/ + if(!generate_bookmark_file_name(bm_filename, sizeof(bm_filename), file, -1)) { - s++; + return BOOKMARK_DONT_RESUME; } - - if (*s) + + if(!file_exists(bm_filename)) + return BOOKMARK_DONT_RESUME; + + if(global_settings.autoloadbookmark == BOOKMARK_YES) { - s++; + return (bookmark_load(bm_filename, true) + ? BOOKMARK_DO_RESUME : BOOKMARK_DONT_RESUME); } - - return s; -} + else + { + int ret = select_bookmark(bm_filename, true, &bookmark); -static const char* int_token(const char* s, int* dest) -{ - *dest = atoi(s); - return skip_token(s); -} + if (bookmark != NULL) + { + if (!play_bookmark(bookmark)) + return BOOKMARK_CANCEL; + return BOOKMARK_DO_RESUME; + } -static const char* long_token(const char* s, long* dest) -{ - *dest = atoi(s); /* Should be atol, but we don't have it. */ - return skip_token(s); + return (ret != BOOKMARK_SUCCESS) ? BOOKMARK_CANCEL : BOOKMARK_DONT_RESUME; + } } /* ----------------------------------------------------------------------- */ -/* This function takes a bookmark and parses it. This function also */ -/* validates the bookmark. The parse_filenames flag indicates whether */ -/* the filename tokens are to be extracted. */ -/* Returns true on successful bookmark parse. */ -/* ----------------------------------------------------------------------- */ -static bool parse_bookmark(const char *bookmark, const bool parse_filenames, const bool strip_dir) +/* This function loads the bookmark information into the resume memory. */ +/* This is an interface function. */ +/* Returns true on successful bookmark load. */ +/* ------------------------------------------------------------------------*/ +bool bookmark_load(const char* file, bool autoload) { - const char* s = bookmark; - const char* end; - -#define GET_INT_TOKEN(var) s = int_token(s, &var) -#define GET_LONG_TOKEN(var) s = long_token(s, &var) -#define GET_BOOL_TOKEN(var) var = (atoi(s)!=0); s = skip_token(s) - - /* if new format bookmark, extract the optional content flags, - otherwise treat as an original format bookmark */ - int opt_flags = 0; - bool new_format = (strchr(s, '>') == s); - if (new_format) + logf("%s", __func__); + int fd; + char* bookmark = NULL; + + if(autoload) { - s++; - GET_INT_TOKEN(opt_flags); + fd = open(file, O_RDONLY); + if(fd >= 0) + { + if(read_line(fd, global_temp_buffer, sizeof(global_temp_buffer)) > 0) + bookmark=global_temp_buffer; + close(fd); + } } - - /* extract all original bookmark tokens */ - GET_INT_TOKEN(bm.resume_index); - GET_LONG_TOKEN(bm.resume_offset); - GET_INT_TOKEN(bm.resume_seed); - if (!new_format) /* skip deprecated token */ - s = skip_token(s); - GET_LONG_TOKEN(bm.resume_time); - GET_INT_TOKEN(bm.repeat_mode); - GET_BOOL_TOKEN(bm.shuffle); - - /* extract all optional bookmark tokens */ - if (opt_flags & BM_PITCH) - GET_INT_TOKEN(bm.pitch); - if (opt_flags & BM_SPEED) - GET_INT_TOKEN(bm.speed); - - if (*s == 0) + else { - return false; + /* This is not an auto-load, so list the bookmarks */ + select_bookmark(file, false, &bookmark); } - - end = strchr(s, ';'); - /* extract file names */ - if (parse_filenames) + if (bookmark != NULL) { - size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); - len = MIN(TEMP_BUF_SIZE - 1, len); - strlcpy(global_temp_buffer, s, len + 1); - - if (end != NULL) + if (!play_bookmark(bookmark)) { - end++; - if (strip_dir) + /* Selected bookmark not found. */ + if (!autoload) { - s = strrchr(end, '/'); - if (s) - { - end = s; - end++; - } + splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); } - strlcpy(global_filename, end, MAX_PATH); - } - } - - return true; -} - -/* ----------------------------------------------------------------------- */ -/* This function is used by multiple functions and is used to generate a */ -/* bookmark named based off of the input. */ -/* Changing this function could result in how the bookmarks are stored. */ -/* it would be here that the centralized/decentralized bookmark code */ -/* could be placed. */ -/* Always returns true */ -/* ----------------------------------------------------------------------- */ -static bool generate_bookmark_file_name(const char *in) -{ - int len = strlen(in); - /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */ - /* otherwise, name it based on the in variable */ - if (!strcmp("/", in)) - strcpy(global_bookmark_file_name, "/root_dir.bmark"); - else - { -#ifdef HAVE_MULTIVOLUME - /* The "root" of an extra volume need special handling too. */ - const char *filename; - path_strip_volume(in, &filename, true); - bool volume_root = *filename == '\0'; -#endif - strcpy(global_bookmark_file_name, in); - if(global_bookmark_file_name[len-1] == '/') - len--; -#ifdef HAVE_MULTIVOLUME - if (volume_root) - strcpy(&global_bookmark_file_name[len], "/volume_dir.bmark"); - else -#endif - strcpy(&global_bookmark_file_name[len], ".bmark"); + return false; + } } return true; @@ -1141,13 +1290,15 @@ static bool generate_bookmark_file_name(const char *in) /* ----------------------------------------------------------------------- */ bool bookmark_exists(void) { + char bm_filename[MAX_PATH]; bool exist=false; char* name = playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) + if (!playlist_dynamic_only() && + generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1)) { - exist = file_exists(global_bookmark_file_name); + exist = file_exists(bm_filename); } return exist; } @@ -1165,12 +1316,13 @@ bool bookmark_is_bookmarkable_state(void) /* no track playing */ (playlist_get_resume_info(&resume_index) == -1) || /* invalid queue info */ - (playlist_modified(NULL))) - /* can't bookmark while in the queue */ + (playlist_modified(NULL)) || + /* can't bookmark playlists modified by user */ + (playlist_dynamic_only())) + /* can't bookmark playlists without associated folder or playlist file */ { return false; } return true; } - |