Index: apps/misc.h =================================================================== --- apps/misc.h (revision 18527) +++ apps/misc.h (working copy) @@ -108,6 +108,7 @@ #endif int open_utf8(const char* pathname, int flags); +bool file_copy(const char* src, const char* target); #ifdef BOOTFILE #if !defined(USB_NONE) && !defined(USB_IPODSTYLE) Index: apps/playlist.c =================================================================== --- apps/playlist.c (revision 18527) +++ apps/playlist.c (working copy) @@ -71,6 +71,7 @@ #include #include #include +#include "sscanf.h" #include "playlist.h" #include "ata_idle_notify.h" #include "file.h" @@ -705,6 +706,8 @@ return -1; } + playlist->filename[0] = '\0'; + switch (position) { case PLAYLIST_PREPEND: @@ -1853,6 +1856,11 @@ mutex_lock(&playlist->control_mutex); + if (playlist->bmark_fd >= 0) + { + close(playlist->bmark_fd); + playlist->bmark_fd = -1; + } cache = &(playlist->control_cache[playlist->num_cached++]); cache->command = command; @@ -1935,6 +1943,7 @@ "%s", PLAYLIST_CONTROL_FILE); playlist->fd = -1; playlist->control_fd = -1; + playlist->bmark_fd = -1; playlist->max_playlist_size = global_settings.max_files_in_playlist; playlist->indices = buffer_alloc( playlist->max_playlist_size * sizeof(int)); @@ -2290,6 +2299,9 @@ case '#': current_command = PLAYLIST_COMMAND_COMMENT; break; + case 'B': + current_command = PLAYLIST_COMMAND_RESUMEPOINT; + break; default: result = -1; exit_loop = true; @@ -3607,3 +3619,130 @@ return result; } + +bool playlist_add_bookmark(const char *bmark_filename, bool createnew) +{ + struct playlist_info *pl = ¤t_playlist; + struct playlist_resume_point resume_pt; + /* grab the currently playing track */ + struct mp3entry *id3 = audio_current_track(); + if(!id3) + return false; + resume_pt.track_position = id3->offset; + playlist_get_resume_info(&resume_pt.playlist_index); + sync_control(pl, true); + + if (createnew || pl->bmark_fd < 0) + { + if (pl->bmark_fd >= 0) + close(pl->bmark_fd); + if (file_copy(PLAYLIST_CONTROL_FILE, bmark_filename) == false) + return false; + pl->bmark_fd = open(bmark_filename, O_RDWR|O_APPEND); + if (pl->bmark_fd < 0) + return false; + } + fdprintf(pl->bmark_fd, "B:%d:%ld\n", resume_pt.playlist_index, resume_pt.track_position); + return true; +} + +/* get the resume points from a bookmark which is already setup for the current playlist */ +bool playlist_get_bookmark_resumes(struct playlist_resume_point *resumes, + int *resumes_count, bool get_last_point) +{ + struct playlist_info *pl = ¤t_playlist; + if (pl->bmark_fd < 0) + return false; + int maxresumes = *resumes_count, i=0; + char buf[MAX_PATH]; + *resumes_count = 0; + lseek(pl->bmark_fd, 0, SEEK_SET); + + while (read_line(pl->bmark_fd, buf, MAX_PATH) > 0 && + ((*resumes_count < maxresumes) || get_last_point)) + { + if (buf[0] == 'B') + { + uint32_t vals = 0; + struct playlist_resume_point *r = &resumes[i]; + parse_list("dd" , &vals, ':', &buf[2], &r->playlist_index, &r->track_position); + if (vals == 0x3) + { + (*resumes_count)++; + if (!get_last_point) + i++; + } + } + } + lseek(pl->bmark_fd, 0, SEEK_END); + return true; +} + +/* setup the playlist from a bmark file and find any resume points in the bmark + if successful *resumes_count will be set to the number of available resumes + current_playlist.bmark_fd will also be set the to correct file and at the end + and ready to add new points if the playlist isnt changed */ +bool playlist_resume_bookmark(const char *bmark_filename, bool get_last_point, + struct playlist_resume_point *resumes, int *resumes_count) +{ + struct playlist_info *pl = ¤t_playlist; + playlist_close(pl); /* may not be the best way to do this */ + if (file_copy(bmark_filename, PLAYLIST_CONTROL_FILE) == false) + return false; + if (playlist_resume() >= 0) + { + /* playlist is loaded, open the origional bmark file and find any resume points */ + pl->bmark_fd = open(bmark_filename, O_RDWR); + if (pl->bmark_fd < 0) + return false; + if (!playlist_get_bookmark_resumes(resumes, resumes_count, get_last_point)) + return false; + return true; + } + return false; +} + + + +#define PREVIOUS_BMARK_FILE ROCKBOX_DIR "/previous.bmark" +bool playlist_backup_current(void) +{ + struct playlist_info *pl = ¤t_playlist; + struct playlist_resume_point resume_pt; + /* grab the currently playing track */ + struct mp3entry *id3 = audio_current_track(); + int fd; + if(!id3) + return false; + resume_pt.track_position = id3->offset; + playlist_get_resume_info(&resume_pt.playlist_index); + sync_control(pl, true); + if (file_copy(PLAYLIST_CONTROL_FILE, PREVIOUS_BMARK_FILE) == false) + return false; + fd = open(PREVIOUS_BMARK_FILE, O_WRONLY|O_APPEND); + if (fd < 0) + return false; + fdprintf(fd, "B:%d:%ld\n", resume_pt.playlist_index, resume_pt.track_position); + close(fd); + return true; +} +bool playlist_resume_backup(void) +{ + struct playlist_resume_point resumes; + int resume_count = 1; + if (!playlist_resume_bookmark(PREVIOUS_BMARK_FILE, true, &resumes, &resume_count) || resume_count < 1) + return false; + playlist_start(resumes.playlist_index,resumes.track_position); + return true; +} + +bool playlist_bookmark_exists(void) +{ + struct playlist_info *pl = ¤t_playlist; + if (pl->bmark_fd >= 0) + return true; + else + return false; +} + + Index: apps/playlist.h =================================================================== --- apps/playlist.h (revision 18527) +++ apps/playlist.h (working copy) @@ -42,7 +42,8 @@ PLAYLIST_COMMAND_SHUFFLE, PLAYLIST_COMMAND_UNSHUFFLE, PLAYLIST_COMMAND_RESET, - PLAYLIST_COMMAND_COMMENT + PLAYLIST_COMMAND_COMMENT, + PLAYLIST_COMMAND_RESUMEPOINT }; enum { @@ -76,6 +77,8 @@ int fd; /* descriptor of the open playlist file */ int control_fd; /* descriptor of the open control file */ bool control_created; /* has control file been created? */ + int bmark_fd; /* fd of the bmark file if it exists. set to -1 if + the control changes and it exists */ int dirlen; /* Length of the path to the playlist file */ unsigned long *indices; /* array of indices */ const struct dircache_entry **filenames; /* Entries from dircache */ @@ -113,6 +116,12 @@ int display_index; /* index of track for display */ }; +struct playlist_resume_point +{ + int playlist_index; /* index into the playlist for the current track */ + long track_position; /* position in the current song to resume from */ +}; + /* Exported functions only for current playlist. */ void playlist_init(void); void playlist_shutdown(void); @@ -168,4 +177,14 @@ void* context); int playlist_remove_all_tracks(struct playlist_info *playlist); +/* bookmark stuff */ +bool playlist_add_bookmark(const char *bmark_filename, bool createnew); +bool playlist_get_bookmark_resumes(struct playlist_resume_point *resumes, + int *resumes_count, bool get_last_point); +bool playlist_resume_bookmark(const char *bmark_filename, bool get_last_point, + struct playlist_resume_point *resumes, int *resumes_count); +bool playlist_backup_current(void); +bool playlist_resume_backup(void); +bool playlist_bookmark_exists(void); + #endif /* __PLAYLIST_H__ */ Index: apps/onplay.c =================================================================== --- apps/onplay.c (revision 18527) +++ apps/onplay.c (working copy) @@ -113,7 +113,7 @@ case ACTION_REQUEST_MENUITEM: if (this_item == &bookmark_load_menu_item) { - if (bookmark_exist() == 0) + if (playlist_bookmark_exists() == false) return ACTION_EXIT_MENUITEM; } /* hide the bookmark menu if there is no playback */ @@ -685,77 +685,10 @@ /* Paste a file to a new directory. Will overwrite always. */ static bool clipboard_pastefile(const char *src, const char *target, bool copy) { - int src_fd, target_fd; - size_t buffersize; - ssize_t size, bytesread, byteswritten; - char *buffer; bool result = false; if (copy) { - /* See if we can get the plugin buffer for the file copy buffer */ - buffer = (char *) plugin_get_buffer(&buffersize); - if (buffer == NULL || buffersize < 512) { - /* Not large enough, try for a disk sector worth of stack - instead */ - buffersize = 512; - buffer = (char *) __builtin_alloca(buffersize); - } - - if (buffer == NULL) { - return false; - } - - buffersize &= ~0x1ff; /* Round buffer size to multiple of sector - size */ - - src_fd = open(src, O_RDONLY); - - if (src_fd >= 0) { - target_fd = creat(target); - - if (target_fd >= 0) { - result = true; - - size = filesize(src_fd); - - if (size == -1) { - result = false; - } - - while(size > 0) { - bytesread = read(src_fd, buffer, buffersize); - - if (bytesread == -1) { - result = false; - break; - } - - size -= bytesread; - - while(bytesread > 0) { - byteswritten = write(target_fd, buffer, bytesread); - - if (byteswritten == -1) { - result = false; - size = 0; - break; - } - - bytesread -= byteswritten; - draw_slider(); - } - } - - close(target_fd); - - /* Copy failed. Cleanup. */ - if (!result) { - remove(target); - } - } - - close(src_fd); - } + result = file_copy(src, target); } else { result = rename(src, target) == 0; #ifdef HAVE_MULTIVOLUME Index: apps/filetree.c =================================================================== --- apps/filetree.c (revision 18527) +++ apps/filetree.c (working copy) @@ -490,7 +490,10 @@ case FILE_ATTR_BMARK: splash(0, ID2P(LANG_WAIT)); - bookmark_load(buf, false); + if (bookmark_load(buf, false) == true) + { + start_wps = true; + } reload_dir = true; break; Index: apps/bookmark.c =================================================================== --- apps/bookmark.c (revision 18527) +++ apps/bookmark.c (working copy) @@ -110,7 +110,32 @@ /* ----------------------------------------------------------------------- */ bool bookmark_create_menu(void) { - write_bookmark(true, create_bookmark()); + char buffer[MAX_PATH]; + char* name = playlist_get_name(NULL, buffer, MAX_PATH); + if (name == NULL) + { +#if CONFIG_RTC == 0 + create_numbered_filename(buffer, "/", "bmark_", + ".bmark", 2 IF_CNFN_NUM_(, NULL)); +#else + create_datetime_filename(buffer, "/", "bmark_", ".bmark", true); +#endif + } + else if (!strcmp("/", buffer)) + { + strcpy(buffer, "/root_dir.bmark"); + } + else + { + int len = strlen(name); + if(buffer[len-1] == '/') + len--; + strcpy(&buffer[len], ".bmark"); + } + int lang_id = playlist_add_bookmark(buffer, false) ? + LANG_BOOKMARK_CREATE_SUCCESS : + LANG_BOOKMARK_CREATE_FAILURE; + splash(HZ, ID2P(lang_id)); return false; } @@ -122,21 +147,18 @@ /* ----------------------------------------------------------------------- */ bool bookmark_load_menu(void) { - if (system_check()) + struct playlist_resume_point resumes[MAX_BOOKMARKS]; + int resume_count = MAX_BOOKMARKS; + int selection; + if (!playlist_get_bookmark_resumes(resumes, &resume_count, false)) + return false; + selection = bookmark_display_points(resumes, resume_count); + if (selection > -1) { - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) - { - char* bookmark = select_bookmark(global_bookmark_file_name, false); - - if (bookmark != NULL) - { - return play_bookmark(bookmark); - } - } + playlist_start(resumes[selection].playlist_index, + resumes[selection].track_position); + return true; } - return false; } @@ -415,44 +437,84 @@ /* ----------------------------------------------------------------------- */ /* This function loads the bookmark information into the resume memory. */ -/* This is an interface function. */ /* ------------------------------------------------------------------------*/ -bool bookmark_load(const char* file, bool autoload) + +static char bookmark_resume_points[MAX_BOOKMARKS][MAX_BOOKMARK_SIZE]; +static int selection = -1; +char * bmark_get_name(int selected_item, void * data, + char * buffer, size_t buffer_len) { - int fd; - char* bookmark = NULL; - - if(autoload) + (void)buffer; (void)buffer_len; (void)data; + return bookmark_resume_points[selected_item]; +} +int bmark_action_callback(int action, struct gui_synclist *lists) +{ + if (action == ACTION_STD_OK) { - fd = open(file, O_RDONLY); - if(fd >= 0) + selection = lists->selected_item; + return ACTION_STD_CANCEL; + } + if (action == ACTION_STD_CANCEL) + selection = -1; + return action; +} +/* returns the bookmark index to start, or -1 to cancel */ +int bookmark_display_points(struct playlist_resume_point *resumes, int resume_count) +{ + struct playlist_track_info info; + int i; + for (i=0;i -1) { - if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0) - bookmark=global_read_buffer; - close(fd); + snprintf(bookmark_resume_points[i], MAX_BOOKMARK_SIZE, "%s: %d", + info.filename, resumes[i].track_position); } } - else - { - /* This is not an auto-load, so list the bookmarks */ - bookmark = select_bookmark(file, false); - } + struct simplelist_info list; + simplelist_info_init(&list, ID2P(LANG_BOOKMARK_SELECT_BOOKMARK), + resume_count, NULL); + list.action_callback = bmark_action_callback; + list.get_name = bmark_get_name; + selection = -1; + simplelist_show_list(&list); + return selection; +} - if (bookmark != NULL) +bool bookmark_load(const char* file, bool autoload) +{ + struct playlist_resume_point resumes[MAX_BOOKMARKS]; + int resume_count = MAX_BOOKMARKS; + int i; + struct playlist_track_info info; + int audio_playing = audio_status(); + /* dump the current playlist and position incase the user wants to cancel */ + if (audio_playing) + playlist_backup_current(); + + if (playlist_resume_bookmark(file, false, resumes, &resume_count) && resume_count > 0) { - if (!play_bookmark(bookmark)) + if (resume_count > 1) { - /* Selected bookmark not found. */ - if (!autoload) + selection = bookmark_display_points(resumes, resume_count); + if (selection == -1) { - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); + if (audio_playing) + return playlist_resume_backup(); + return false; } - - return false; } + else + selection = 0; + + playlist_start(resumes[selection].playlist_index, + resumes[selection].track_position); + return true; } - - return true; + else if (audio_playing) + return playlist_resume_backup(); + + return false; } @@ -1049,26 +1111,6 @@ } /* ----------------------------------------------------------------------- */ -/* Returns true if a bookmark file exists for the current playlist */ -/* ----------------------------------------------------------------------- */ -bool bookmark_exist(void) -{ - bool exist=false; - - if(system_check()) - { - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) - { - exist = file_exists(global_bookmark_file_name); - } - } - - return exist; -} - -/* ----------------------------------------------------------------------- */ /* Checks the current state of the system and returns if it is in a */ /* bookmarkable state. */ /* ----------------------------------------------------------------------- */ Index: apps/misc.c =================================================================== --- apps/misc.c (revision 18527) +++ apps/misc.c (working copy) @@ -60,6 +60,7 @@ #include "sound.h" #include "playlist.h" #include "yesno.h" +#include "plugin.h" #ifdef HAVE_MMC #include "ata_mmc.h" @@ -1215,7 +1216,79 @@ return fd; } +/** Copy a file from src to dst **/ +bool file_copy(const char* src, const char* target) +{ + int src_fd, target_fd; + size_t buffersize; + ssize_t size, bytesread, byteswritten; + char *buffer; + bool result = false; + /* See if we can get the plugin buffer for the file copy buffer */ + buffer = (char *) plugin_get_buffer(&buffersize); + if (buffer == NULL || buffersize < 512) { + /* Not large enough, try for a disk sector worth of stack + instead */ + buffersize = 512; + buffer = (char *) __builtin_alloca(buffersize); + } + if (buffer == NULL) { + return false; + } + + buffersize &= ~0x1ff; /* Round buffer size to multiple of sector + size */ + + src_fd = open(src, O_RDONLY); + + if (src_fd >= 0) { + target_fd = creat(target); + + if (target_fd >= 0) { + result = true; + + size = filesize(src_fd); + + if (size == -1) { + result = false; + } + + while(size > 0) { + bytesread = read(src_fd, buffer, buffersize); + + if (bytesread == -1) { + result = false; + break; + } + + size -= bytesread; + + while(bytesread > 0) { + byteswritten = write(target_fd, buffer, bytesread); + + if (byteswritten == -1) { + result = false; + size = 0; + break; + } + + bytesread -= byteswritten; + // draw_slider(); + } + } + + close(target_fd); + + /* Copy failed. Cleanup. */ + if (!result) { + remove(target); + } + } + close(src_fd); + } + return result; +} #ifdef HAVE_LCD_COLOR /* * Helper function to convert a string of 6 hex digits to a native colour