Index: apps/misc.h =================================================================== --- apps/misc.h (revision 18563) +++ 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 18563) +++ 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" @@ -695,6 +696,8 @@ return -1; } + playlist->filename[0] = '\0'; + switch (position) { case PLAYLIST_PREPEND: @@ -1816,6 +1819,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; @@ -1898,6 +1906,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)); @@ -2252,6 +2261,9 @@ case '#': current_command = PLAYLIST_COMMAND_COMMENT; break; + case 'B': + current_command = PLAYLIST_COMMAND_RESUMEPOINT; + break; default: result = -1; exit_loop = true; @@ -3552,3 +3564,135 @@ 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:%ld\n", + resume_pt.playlist_index, resume_pt.track_position, + id3->elapsed/1000 /* ms->s */); + 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("ddd" , &vals, ':', &buf[2], &r->playlist_index, + &r->track_position, &r->track_seconds); + if (vals == 0x7) + { + (*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; + resume_pt.track_seconds = id3->elapsed / 1000; /* ms -> s */ + 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:%d\n", resume_pt.playlist_index, + resume_pt.track_position, resume_pt.track_seconds); + 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 18563) +++ 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,13 @@ 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 */ + int track_seconds; /* position in seconds so the ui is a bit nicer */ +}; + /* Exported functions only for current playlist. */ void playlist_init(void); void playlist_shutdown(void); @@ -168,4 +178,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/tree.c =================================================================== --- apps/tree.c (revision 18563) +++ apps/tree.c (working copy) @@ -949,87 +949,6 @@ tree_get_filetypes(&filetypes, &filetypes_count); } -bool bookmark_play(char *resume_file, int index, int offset, int seed, - char *filename) -{ - int i; - char* suffix = strrchr(resume_file, '.'); - bool started = false; - - if (suffix != NULL && - (!strcasecmp(suffix, ".m3u") || !strcasecmp(suffix, ".m3u8"))) - { - /* Playlist playback */ - char* slash; - /* check that the file exists */ - if(!file_exists(resume_file)) - return false; - - slash = strrchr(resume_file,'/'); - if (slash) - { - char* cp; - *slash=0; - - cp=resume_file; - if (!cp[0]) - cp="/"; - - if (playlist_create(cp, slash+1) != -1) - { - if (global_settings.playlist_shuffle) - playlist_shuffle(seed, -1); - playlist_start(index,offset); - started = true; - } - *slash='/'; - } - } - else - { - /* Directory playback */ - lastdir[0]='\0'; - if (playlist_create(resume_file, NULL) != -1) - { - char* peek_filename; - resume_directory(resume_file); - if (global_settings.playlist_shuffle) - playlist_shuffle(seed, -1); - - /* Check if the file is at the same spot in the directory, - else search for it */ - peek_filename = playlist_peek(index); - - if (peek_filename == NULL) - return false; - - if (strcmp(strrchr(peek_filename, '/') + 1, filename)) - { - for ( i=0; i < playlist_amount(); i++ ) - { - peek_filename = playlist_peek(i); - - if (peek_filename == NULL) - return false; - - if (!strcmp(strrchr(peek_filename, '/') + 1, filename)) - break; - } - if (i < playlist_amount()) - index = i; - else - return false; - } - playlist_start(index,offset); - started = true; - } - } - - if (started) - start_wps = true; - return started; -} - static void say_filetype(int attr) { /* try to find a voice ID for the extension, if known */ Index: apps/onplay.c =================================================================== --- apps/onplay.c (revision 18563) +++ 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/tree.h =================================================================== --- apps/tree.h (revision 18563) +++ apps/tree.h (working copy) @@ -84,9 +84,6 @@ void tree_flush(void); void tree_restore(void); -bool bookmark_play(char* resume_file, int index, int offset, int seed, - char *filename); - extern struct gui_synclist tree_lists; extern struct gui_syncstatusbar statusbars; #endif Index: apps/settings.h =================================================================== --- apps/settings.h (revision 18563) +++ apps/settings.h (working copy) @@ -80,6 +80,7 @@ #define RECPRESETS_DIR ROCKBOX_DIR "/recpresets" #define FMPRESET_PATH ROCKBOX_DIR "/fmpresets" #define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists" +#define BOOKMARK_DIR ROCKBOX_DIR "/bookmarks" #define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config" #define CONFIGFILE ROCKBOX_DIR "/config.cfg" Index: apps/filetree.c =================================================================== --- apps/filetree.c (revision 18563) +++ 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 18563) +++ apps/bookmark.c (working copy) @@ -26,94 +26,69 @@ #include "config.h" #include "action.h" #include "audio.h" +#include "backdrop.h" #include "playlist.h" #include "settings.h" -#include "tree.h" #include "bookmark.h" -#include "system.h" -#include "icons.h" -#include "menu.h" #include "lang.h" #include "talk.h" #include "misc.h" #include "splash.h" #include "yesno.h" #include "list.h" -#include "plugin.h" -#include "backdrop.h" -#include "file.h" #include "statusbar.h" #define MAX_BOOKMARKS 10 -#define MAX_BOOKMARK_SIZE 350 #define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark" -/* Used to buffer bookmarks while displaying the bookmark list. */ -struct bookmark_list -{ - const char* filename; - size_t buffer_size; - int start; - int count; - int total_count; - bool show_dont_resume; - bool reload; - bool show_playlist_name; - char* items[]; +struct recent_bookmarks { + char bmark_filename[MAX_PATH]; + char track_filename[MAX_PATH]; + struct playlist_resume_point resume_pt; }; -static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, - bool most_recent); -static bool check_bookmark(const char* bookmark); -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 const char* skip_token(const char* s); -static const char* int_token(const char* s, int* dest); -static const char* long_token(const char* s, long* dest); -static const char* bool_token(const char* s, bool* dest); -static bool parse_bookmark(const char *bookmark, - int *resume_index, - int *resume_offset, - int *resume_seed, - int *resume_first_index, - char* resume_file, - unsigned int resume_file_size, - long* ms, - int * repeat_mode, - bool *shuffle, - char* file_name); -static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line); -static char* get_bookmark_info(int list_index, - void* data, - char *buffer, - size_t buffer_len); -static char* select_bookmark(const char* bookmark_file_name, bool show_dont_resume); -static bool system_check(void); -static bool write_bookmark(bool create_bookmark_file, const char *bookmark); -static int get_bookmark_count(const char* bookmark_file_name); +static int bookmark_display_points(struct playlist_resume_point *resumes, + int resume_count); -static char global_temp_buffer[MAX_PATH+1]; -/* 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 the interface function from the main menu. */ /* ----------------------------------------------------------------------- */ 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; } - +static bool create_bookmark(bool add_to_mrb) +{ + bookmark_create_menu(); + //TODO + (void)add_to_mrb; +} /* ----------------------------------------------------------------------- */ /* This function acts as the load interface from the main menu */ /* This function determines the bookmark file name and then loads that file*/ @@ -122,21 +97,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; } @@ -144,15 +116,17 @@ /* Gives the user a list of the Most Recent Bookmarks. This is an */ /* interface function */ /* ----------------------------------------------------------------------- */ +//TODO: bool bookmark_mrb_load() { + /* char* bookmark = select_bookmark(RECENT_BOOKMARK_FILE, false); if (bookmark != NULL) { return play_bookmark(bookmark); } - +*/ return false; } @@ -163,11 +137,6 @@ bool bookmark_autobookmark(void) { char* bookmark; - if (!system_check()) - return false; - - audio_pause(); /* first pause playback */ - bookmark = create_bookmark(); /* Workaround for inability to speak when paused: all callers will just do audio_stop() when we return, so we can do it right away. This makes it possible to speak the "Create a Bookmark?" @@ -176,13 +145,13 @@ switch (global_settings.autocreatebookmark) { case BOOKMARK_YES: - return write_bookmark(true, bookmark); + return bookmark_create_menu();//write_bookmark(true, bookmark); case BOOKMARK_NO: return false; - +/* TODO case BOOKMARK_RECENT_ONLY_YES: - return write_bookmark(false, bookmark); + return write_bookmark(false, bookmark);*/ } #ifdef HAVE_LCD_BITMAP const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; @@ -201,180 +170,25 @@ gui_syncstatusbar_draw(&statusbars, false); if(gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) { + //TODO: +#if 0 if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) return write_bookmark(false, bookmark); else return write_bookmark(true, bookmark); +#endif } 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* */ -/* ------------------------------------------------------------------------*/ -static bool write_bookmark(bool create_bookmark_file, const char *bookmark) -{ - bool success=false; - if (!bookmark) - return false; /* something didn't happen correctly, do nothing */ - - if (global_settings.usemrb) - success = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true); - - - /* writing the bookmark */ - if (create_bookmark_file) - { - char* name = playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)); - if (generate_bookmark_file_name(name)) - { - success = add_bookmark(global_bookmark_file_name, bookmark, false); - } - } - - splash(HZ, success ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) - : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); - - return true; -} - -/* ----------------------------------------------------------------------- */ -/* This function adds a bookmark to a file. */ -/* ------------------------------------------------------------------------*/ -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* playlist = NULL; - char* cp; - char* tmp; - int len = 0; - bool unique = false; - - /* 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); - if (temp_bookmark_file < 0) - return false; /* can't open the temp file */ - - if (most_recent && (global_settings.usemrb == BOOKMARK_UNIQUE_ONLY)) - { - playlist = strchr(bookmark,'/'); - cp = strrchr(bookmark,';'); - len = cp - playlist; - unique = true; - } - - /* 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++; - - /* 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_read_buffer, - sizeof(global_read_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; - - cp = strchr(global_read_buffer,'/'); - tmp = strrchr(global_read_buffer,';'); - if (check_bookmark(global_read_buffer) && - (!unique || len != tmp -cp || strncmp(playlist,cp,len))) - { - bookmark_count++; - write(temp_bookmark_file, global_read_buffer, - strlen(global_read_buffer)); - write(temp_bookmark_file, "\n", 1); - } - } - close(bookmark_file); - } - close(temp_bookmark_file); - - remove(bookmark_file_name); - rename(global_temp_buffer, bookmark_file_name); - - return true; -} - - -/* ----------------------------------------------------------------------- */ -/* This function takes the system resume data and formats it into a valid */ -/* bookmark. */ -/* ----------------------------------------------------------------------- */ -static char* create_bookmark() -{ - int resume_index = 0; - char *file; - - if (!system_check()) - return NULL; /* something didn't happen correctly, do nothing */ - - /* grab the currently playing track */ - struct mp3entry *id3 = audio_current_track(); - if(!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 */ - snprintf(global_bookmark, sizeof(global_bookmark), - "%d;%ld;%d;%d;%ld;%d;%d;%s;%s", - resume_index, - id3->offset, - playlist_get_seed(NULL), - 0, - id3->elapsed, - global_settings.repeat_mode, - global_settings.playlist_shuffle, - playlist_get_name(NULL, global_temp_buffer, - sizeof(global_temp_buffer)), - file+1); - - /* checking to see if the bookmark is valid */ - if (check_bookmark(global_bookmark)) - return global_bookmark; - else - return NULL; -} - -static bool check_bookmark(const char* bookmark) -{ - return parse_bookmark(bookmark, - NULL,NULL,NULL, NULL, - NULL,0,NULL,NULL, - NULL, NULL); -} - -/* ----------------------------------------------------------------------- */ /* This function will determine if an autoload is necessary. This is an */ /* interface function. */ /* ------------------------------------------------------------------------*/ +//TODO: bool bookmark_autoload(const char* file) { +#if 0 if(global_settings.autoloadbookmark == BOOKMARK_NO) return false; @@ -411,233 +225,123 @@ return false; } +#endif + return false; } -/* ----------------------------------------------------------------------- */ -/* This function loads the bookmark information into the resume memory. */ -/* This is an interface function. */ -/* ------------------------------------------------------------------------*/ -bool bookmark_load(const char* file, bool autoload) +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) + struct playlist_resume_point *resumes = (struct playlist_resume_point*)data; + static struct playlist_track_info info; + int i = selected_item/2; + if (selected_item%2 == 0) /* need to update the info struct */ { - fd = open(file, O_RDONLY); - if(fd >= 0) - { - if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0) - bookmark=global_read_buffer; - close(fd); - } + if (playlist_get_track_info(NULL, resumes[i].playlist_index, &info) == -1) + return str(LANG_PLAYLIST_ACCESS_ERROR); + return info.filename; } - else + else { - /* This is not an auto-load, so list the bookmarks */ - bookmark = select_bookmark(file, false); + snprintf(buffer, buffer_len, "%d:%d, %d", resumes[i].track_seconds/60, + resumes[i].track_seconds%60, resumes[i].playlist_index+1); + return buffer; } - - if (bookmark != NULL) - { - if (!play_bookmark(bookmark)) - { - /* Selected bookmark not found. */ - if (!autoload) - { - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); - } - - return false; - } - } - - return true; } - - -static int get_bookmark_count(const char* bookmark_file_name) +int bmark_action_callback(int action, struct gui_synclist *lists) { - int read_count = 0; - int file = open(bookmark_file_name, O_RDONLY); - - if(file < 0) - return -1; - - while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0) + if (action == ACTION_STD_OK) { - read_count++; + selection = lists->selected_item; + return ACTION_STD_CANCEL; } - - close(file); - return read_count; + if (action == ACTION_STD_CANCEL) + selection = -1; + return action; } - -static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line) +int bmark_speak_item(int selected_item, void * data) { - char* dest = ((char*) bookmarks) + bookmarks->buffer_size - 1; - int read_count = 0; - int file = open(bookmarks->filename, O_RDONLY); + struct playlist_resume_point *resumes = (struct playlist_resume_point*)data; + struct playlist_track_info info; + int i = selected_item/2; + if (playlist_get_track_info(NULL, resumes[i].playlist_index, &info) == -1) + return talk_id(LANG_PLAYLIST_ACCESS_ERROR, false); + talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); + talk_number(resumes[i].playlist_index+1, true); + talk_id(LANG_TIME, true); + talk_value(resumes[i].track_seconds, UNIT_SEC, true); - if (file < 0) - { - return -1; - } - - if ((first_line != 0) && ((size_t) filesize(file) < bookmarks->buffer_size - - sizeof(*bookmarks) - (sizeof(char*) * bookmarks->total_count))) - { - /* 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) - { - read_count++; - - if (read_count >= first_line) - { - dest -= strlen(global_read_buffer) + 1; - - if (dest < ((char*) bookmarks) + sizeof(*bookmarks) - + (sizeof(char*) * (bookmarks->count + 1))) - { - break; - } - - strcpy(dest, global_read_buffer); - bookmarks->items[bookmarks->count] = dest; - bookmarks->count++; - } - } - - close(file); - return bookmarks->start + bookmarks->count; +#if CONFIG_CODEC == SWCODEC + /* Track filename */ + talk_id(VOICE_FILE, true); + talk_spell(info.filename, true); +#endif + return 0; } - -static char* get_bookmark_info(int list_index, - void* data, - char *buffer, - size_t buffer_len) +/* returns the bookmark index to start, or -1 to cancel */ +int bookmark_display_points(struct playlist_resume_point *resumes, int resume_count) { - struct bookmark_list* bookmarks = (struct bookmark_list*) data; - int index = list_index / 2; - int resume_index = 0; - long resume_time = 0; - bool shuffle = false; + struct simplelist_info list; + simplelist_info_init(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK), + resume_count, NULL); + list.action_callback = bmark_action_callback; + list.get_name = bmark_get_name; + list.callback_data = (void*)resumes; + list.selection_size = 2; + if (global_settings.talk_menu) + list.get_talk = bmark_speak_item; + selection = -1; + simplelist_show_list(&list); + return selection; +} - if (bookmarks->show_dont_resume) - { - if (index == 0) - { - return list_index % 2 == 0 - ? (char*) str(LANG_BOOKMARK_DONT_RESUME) : " "; - } - - index--; - } - - 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, - * but don't go too close to the end. - */ - 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 ""; - } - } +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 (!parse_bookmark(bookmarks->items[index - bookmarks->start], - &resume_index, NULL, NULL, NULL, global_temp_buffer, - sizeof(global_temp_buffer), &resume_time, NULL, &shuffle, - global_filename)) + if (playlist_resume_bookmark(file, false, resumes, &resume_count) && resume_count > 0) { - return list_index % 2 == 0 ? (char*) str(LANG_BOOKMARK_INVALID) : " "; - } - - if (list_index % 2 == 0) - { - char *name; - char *format; - int len = strlen(global_temp_buffer); - - if (bookmarks->show_playlist_name && len > 0) + if (resume_count > 1 || !autoload) { - name = global_temp_buffer; - len--; - - if (name[len] != '/') + /* if autoload is on we want the bookmark to start playing immediatly + from the first saved position, so start it then show the menu */ + if (autoload) { - strrsplt(name, '.'); + playlist_start(resumes[0].playlist_index, + resumes[0].track_position); } - else if (len > 1) + selection = bookmark_display_points(resumes, resume_count); + if (selection == -1) { - name[len] = '\0'; + if (audio_playing) + return playlist_resume_backup(); + return false; } - - if (len > 1) - { - name = strrsplt(name, '/'); - } - - format = "%s : %s"; } else - { - name = global_filename; - format = "%s"; - } + selection = 0; - strrsplt(global_filename, '.'); - snprintf(buffer, buffer_len, format, name, global_filename); - return buffer; + playlist_start(resumes[selection].playlist_index, + resumes[selection].track_position); + return true; } - else - { - char time_buf[32]; - - format_time(time_buf, sizeof(time_buf), resume_time); - snprintf(buffer, buffer_len, "%s, %d%s", time_buf, resume_index + 1, - shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : ""); - return buffer; - } + + /* Selected bookmark not found. */ + splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); + if (audio_playing) + return playlist_resume_backup(); + + return false; } - +#if 0 static int bookmark_list_voice_cb(int list_index, void* data) { struct bookmark_list* bookmarks = (struct bookmark_list*) data; @@ -653,192 +357,25 @@ bookmarks->show_playlist_name); return 0; } - +#endif /* ----------------------------------------------------------------------- */ -/* This displays a the bookmarks in a file and allows the user to */ -/* select one to play. */ -/* ------------------------------------------------------------------------*/ -static char* select_bookmark(const char* bookmark_file_name, bool show_dont_resume) -{ - struct bookmark_list* bookmarks; - struct gui_synclist list; - int item = 0; - int action; - size_t size; - bool exit = false; - bool refresh = true; - - bookmarks = plugin_get_buffer(&size); - bookmarks->buffer_size = size; - bookmarks->show_dont_resume = 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); - 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); - gui_syncstatusbar_draw(&statusbars, true); - - while (!exit) - { - gui_syncstatusbar_draw(&statusbars, false); - - if (refresh) - { - int count = get_bookmark_count(bookmark_file_name); - bookmarks->total_count = count; - - if (bookmarks->total_count < 1) - { - /* No more bookmarks, delete file and exit */ - splash(HZ, ID2P(LANG_BOOKMARK_LOAD_EMPTY)); - remove(bookmark_file_name); - return NULL; - } - - if (bookmarks->show_dont_resume) - { - count++; - item++; - } - - gui_synclist_set_nb_items(&list, count * 2); - - if (item >= count) - { - /* Selected item has been deleted */ - item = count - 1; - gui_synclist_select_item(&list, item * 2); - } - - buffer_bookmarks(bookmarks, bookmarks->start); - 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); - item = gui_synclist_get_sel_pos(&list) / 2; - - if (bookmarks->show_dont_resume) - { - item--; - } - - if (action == ACTION_STD_CONTEXT) - { - MENUITEM_STRINGLIST(menu_items, ID2P(LANG_BOOKMARK_CONTEXT_MENU), - NULL, ID2P(LANG_BOOKMARK_CONTEXT_RESUME), - ID2P(LANG_BOOKMARK_CONTEXT_DELETE)); - 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 <= - (int) (sizeof(menu_actions) / sizeof(menu_actions[0]))) - { - action = menu_actions[selection]; - } - } - - switch (action) - { - case ACTION_STD_OK: - if (item >= 0) - { - talk_shutup(); - return bookmarks->items[item - bookmarks->start]; - } - - /* Else fall through */ - - case ACTION_TREE_WPS: - case ACTION_STD_CANCEL: - exit = true; - break; - - case ACTION_BMS_DELETE: - if (item >= 0) - { - delete_bookmark(bookmark_file_name, item); - bookmarks->reload = true; - refresh = true; - } - break; - - default: - if (default_event_handler(action) == SYS_USB_CONNECTED) - { - exit = true; - } - - break; - } - } - - talk_shutup(); - return NULL; -} - -/* ----------------------------------------------------------------------- */ /* This function takes a location in a bookmark file and deletes that */ /* bookmark. */ /* ------------------------------------------------------------------------*/ +//TODO: 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 */ - 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); - - 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_read_buffer, - sizeof(global_read_buffer)) > 0) - { - if (bookmark_id != bookmark_count) - { - write(temp_bookmark_file, global_read_buffer, - strlen(global_read_buffer)); - write(temp_bookmark_file, "\n", 1); - } - bookmark_count++; - } - close(bookmark_file); - } - close(temp_bookmark_file); - - remove(bookmark_file_name); - rename(global_temp_buffer, bookmark_file_name); - return true; } /* ----------------------------------------------------------------------- */ /* This function parses a bookmark, says the voice UI part of it. */ /* ------------------------------------------------------------------------*/ +//TODO: static void say_bookmark(const char* bookmark, int bookmark_id, bool show_playlist_name) { +#if 0 int resume_index; long ms; bool playlist_shuffle = false; @@ -894,211 +431,5 @@ talk_spell(global_filename, true); } #endif +#endif } - -/* ----------------------------------------------------------------------- */ -/* This function parses a bookmark and then plays it. */ -/* ------------------------------------------------------------------------*/ -static bool play_bookmark(const char* bookmark) -{ - int index; - int offset; - int seed; - - if (parse_bookmark(bookmark, - &index, - &offset, - &seed, - NULL, - global_temp_buffer, - sizeof(global_temp_buffer), - NULL, - &global_settings.repeat_mode, - &global_settings.playlist_shuffle, - global_filename)) - { - return bookmark_play(global_temp_buffer, index, offset, seed, - global_filename); - } - - return false; -} - -static const char* skip_token(const char* s) -{ - while (*s && *s != ';') - { - s++; - } - - if (*s) - { - s++; - } - - return s; -} - -static const char* int_token(const char* s, int* dest) -{ - if (dest != NULL) - { - *dest = atoi(s); - } - - return skip_token(s); -} - -static const char* long_token(const char* s, long* dest) -{ - if (dest != NULL) - { - *dest = atoi(s); /* Should be atol, but we don't have it. */ - } - - return skip_token(s); -} - -static const char* bool_token(const char* s, bool* dest) -{ - if (dest != NULL) - { - *dest = atoi(s) != 0; - } - - return skip_token(s); -} - -/* ----------------------------------------------------------------------- */ -/* This function takes a bookmark and parses it. This function also */ -/* validates the bookmark. Passing in NULL for an output variable */ -/* indicates that value is not requested. */ -/* ----------------------------------------------------------------------- */ -static bool parse_bookmark(const char *bookmark, - int *resume_index, - int *resume_offset, - int *resume_seed, - int *resume_first_index, - char* resume_file, - unsigned int resume_file_size, - long* ms, - int * repeat_mode, bool *shuffle, - char* file_name) -{ - const char* s = bookmark; - const char* end; - - s = int_token(s, resume_index); - s = int_token(s, resume_offset); - s = int_token(s, resume_seed); - s = int_token(s, resume_first_index); - s = long_token(s, ms); - s = int_token(s, repeat_mode); - s = bool_token(s, shuffle); - - if (*s == 0) - { - return false; - } - - end = strchr(s, ';'); - - if (resume_file != NULL) - { - size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); - - len = MIN(resume_file_size - 1, len); - strncpy(resume_file, s, len); - resume_file[len] = 0; - } - - if (end != NULL && file_name != NULL) - { - end++; - strncpy(file_name, end, MAX_PATH - 1); - file_name[MAX_PATH - 1] = 0; - } - - 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. */ -/* ----------------------------------------------------------------------- */ -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 - { - strcpy(global_bookmark_file_name, in); - if(global_bookmark_file_name[len-1] == '/') - len--; - strcpy(&global_bookmark_file_name[len], ".bmark"); - } - - return true; -} - -/* ----------------------------------------------------------------------- */ -/* 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. */ -/* ----------------------------------------------------------------------- */ -/* Inputs: */ -/* ----------------------------------------------------------------------- */ -/* Outputs: */ -/* return bool: Indicates if the system was in a bookmarkable state */ -/* ----------------------------------------------------------------------- */ -static bool system_check(void) -{ - int resume_index = 0; - - if (!(audio_status() && audio_current_track())) - { - /* no track playing */ - return false; - } - - /* Checking to see if playing a queued track */ - if (playlist_get_resume_info(&resume_index) == -1) - { - /* something bad happened while getting the queue information */ - return false; - } - else if (playlist_modified(NULL)) - { - /* can't bookmark while in the queue */ - return false; - } - - return true; -} - Index: apps/misc.c =================================================================== --- apps/misc.c (revision 18563) +++ 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