Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

bookmark.c clean-up

No functional changes

Change-Id: If82f3e58ca848ade2a49f31357d39de9c5ba9ece

authored by

William Wilgus and committed by
William Wilgus
8b522b89 1e6d643c

+478 -483
+478 -483
apps/bookmark.c
··· 78 78 int speed; 79 79 } bm; 80 80 81 - static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, 82 - bool most_recent); 83 - static char* create_bookmark(void); 84 - static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id); 85 - static void say_bookmark(const char* bookmark, 86 - int bookmark_id, bool show_playlist_name); 87 - static bool play_bookmark(const char* bookmark); 88 - static bool generate_bookmark_file_name(const char *in); 89 - static bool parse_bookmark(const char *bookmark, const bool get_filenames, const bool strip_dir); 90 - static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line); 91 - static const char* get_bookmark_info(int list_index, 92 - void* data, 93 - char *buffer, 94 - size_t buffer_len); 95 - static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark); 96 - static bool write_bookmark(bool create_bookmark_file, const char *bookmark); 97 - static int get_bookmark_count(const char* bookmark_file_name); 98 - 99 81 #define TEMP_BUF_SIZE (MAX_PATH + 1) 100 82 static char global_temp_buffer[TEMP_BUF_SIZE]; 101 83 /* File name created by generate_bookmark_file_name */ ··· 106 88 /* Filename from parsed bookmark (can be made local where needed) */ 107 89 static char global_filename[MAX_PATH]; 108 90 109 - /* ----------------------------------------------------------------------- */ 110 - /* This is an interface function from the context menu. */ 111 - /* Returns true on successful bookmark creation. */ 112 - /* ----------------------------------------------------------------------- */ 113 - bool bookmark_create_menu(void) 91 + static const char* skip_token(const char* s) 114 92 { 115 - return write_bookmark(true, create_bookmark()); 116 - } 93 + while (*s && *s != ';') 94 + { 95 + s++; 96 + } 117 97 118 - /* ----------------------------------------------------------------------- */ 119 - /* This function acts as the load interface from the context menu. */ 120 - /* This function determines the bookmark file name and then loads that file*/ 121 - /* for the user. The user can then select or delete previous bookmarks. */ 122 - /* This function returns BOOKMARK_SUCCESS on the selection of a track to */ 123 - /* resume, BOOKMARK_FAIL if the menu is exited without a selection and */ 124 - /* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */ 125 - /* connection. */ 126 - /* ----------------------------------------------------------------------- */ 127 - int bookmark_load_menu(void) 128 - { 129 - char* bookmark; 130 - int ret = BOOKMARK_FAIL; 131 - 132 - push_current_activity(ACTIVITY_BOOKMARKSLIST); 133 - 134 - char* name = playlist_get_name(NULL, global_temp_buffer, 135 - sizeof(global_temp_buffer)); 136 - if (generate_bookmark_file_name(name)) 98 + if (*s) 137 99 { 138 - ret = select_bookmark(global_bookmark_file_name, false, &bookmark); 139 - if (bookmark != NULL) 140 - { 141 - ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL; 142 - } 100 + s++; 143 101 } 144 102 145 - pop_current_activity(); 146 - return ret; 103 + return s; 147 104 } 148 105 149 - /* ----------------------------------------------------------------------- */ 150 - /* Gives the user a list of the Most Recent Bookmarks. This is an */ 151 - /* interface function */ 152 - /* Returns true on the successful selection of a recent bookmark. */ 153 - /* ----------------------------------------------------------------------- */ 154 - bool bookmark_mrb_load() 106 + static const char* int_token(const char* s, int* dest) 155 107 { 156 - char* bookmark; 157 - bool ret = false; 108 + *dest = atoi(s); 109 + return skip_token(s); 110 + } 158 111 159 - push_current_activity(ACTIVITY_BOOKMARKSLIST); 160 - select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark); 161 - if (bookmark != NULL) 162 - { 163 - ret = play_bookmark(bookmark); 164 - } 112 + static const char* long_token(const char* s, long* dest) 113 + { 114 + *dest = atoi(s); /* Should be atol, but we don't have it. */ 115 + return skip_token(s); 116 + } 165 117 166 - pop_current_activity(); 167 - return ret; 118 + /*-------------------------------------------------------------------------*/ 119 + /* Get the name of the playlist and the name of the track from a bookmark. */ 120 + /* Returns true iff both were extracted. */ 121 + /*-------------------------------------------------------------------------*/ 122 + static bool get_playlist_and_track(const char *bookmark, 123 + char **pl_start, 124 + char **pl_end, 125 + char **track) 126 + { 127 + *pl_start = strchr(bookmark,'/'); 128 + if (!(*pl_start)) 129 + return false; 130 + *pl_end = strrchr(bookmark,';'); 131 + *track = *pl_end + 1; 132 + return true; 168 133 } 169 134 170 135 /* ----------------------------------------------------------------------- */ 171 - /* This function handles an autobookmark creation. This is an interface */ 172 - /* function. */ 173 - /* Returns true on successful bookmark creation. */ 136 + /* This function takes a bookmark and parses it. This function also */ 137 + /* validates the bookmark. The parse_filenames flag indicates whether */ 138 + /* the filename tokens are to be extracted. */ 139 + /* Returns true on successful bookmark parse. */ 174 140 /* ----------------------------------------------------------------------- */ 175 - bool bookmark_autobookmark(bool prompt_ok) 141 + static bool parse_bookmark(const char *bookmark, 142 + const bool parse_filenames, 143 + const bool strip_dir) 176 144 { 177 - char* bookmark; 178 - bool update; 145 + const char* s = bookmark; 146 + const char* end; 179 147 180 - if (!bookmark_is_bookmarkable_state()) 181 - return false; 148 + #define GET_INT_TOKEN(var) s = int_token(s, &var) 149 + #define GET_LONG_TOKEN(var) s = long_token(s, &var) 150 + #define GET_BOOL_TOKEN(var) var = (atoi(s)!=0); s = skip_token(s) 182 151 183 - audio_pause(); /* first pause playback */ 184 - update = (global_settings.autoupdatebookmark && bookmark_exists()); 185 - bookmark = create_bookmark(); 186 - 187 - if (update) 188 - return write_bookmark(true, bookmark); 189 - 190 - switch (global_settings.autocreatebookmark) 152 + /* if new format bookmark, extract the optional content flags, 153 + otherwise treat as an original format bookmark */ 154 + int opt_flags = 0; 155 + bool new_format = (strchr(s, '>') == s); 156 + if (new_format) 191 157 { 192 - case BOOKMARK_YES: 193 - return write_bookmark(true, bookmark); 158 + s++; 159 + GET_INT_TOKEN(opt_flags); 160 + } 194 161 195 - case BOOKMARK_NO: 196 - return false; 162 + /* extract all original bookmark tokens */ 163 + GET_INT_TOKEN(bm.resume_index); 164 + GET_LONG_TOKEN(bm.resume_offset); 165 + GET_INT_TOKEN(bm.resume_seed); 166 + if (!new_format) /* skip deprecated token */ 167 + s = skip_token(s); 168 + GET_LONG_TOKEN(bm.resume_time); 169 + GET_INT_TOKEN(bm.repeat_mode); 170 + GET_BOOL_TOKEN(bm.shuffle); 197 171 198 - case BOOKMARK_RECENT_ONLY_YES: 199 - return write_bookmark(false, bookmark); 200 - } 201 - const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; 202 - const struct text_message message={lines, 1}; 172 + /* extract all optional bookmark tokens */ 173 + if (opt_flags & BM_PITCH) 174 + GET_INT_TOKEN(bm.pitch); 175 + if (opt_flags & BM_SPEED) 176 + GET_INT_TOKEN(bm.speed); 203 177 204 - if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) 178 + if (*s == 0) 205 179 { 206 - if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) 207 - return write_bookmark(false, bookmark); 208 - else 209 - return write_bookmark(true, bookmark); 180 + return false; 210 181 } 211 - return false; 212 - } 213 182 214 - /* ----------------------------------------------------------------------- */ 215 - /* This function takes the current current resume information and writes */ 216 - /* that to the beginning of the bookmark file. */ 217 - /* This file will contain N number of bookmarks in the following format: */ 218 - /* resume_index*resume_offset*resume_seed*resume_first_index* */ 219 - /* resume_file*milliseconds*MP3 Title* */ 220 - /* Returns true on successful bookmark write. */ 221 - /* Returns false if any part of the bookmarking process fails. It is */ 222 - /* possible that a bookmark is successfully added to the most recent */ 223 - /* bookmark list but fails to be added to the bookmark file or vice versa. */ 224 - /* ------------------------------------------------------------------------*/ 225 - static bool write_bookmark(bool create_bookmark_file, const char *bookmark) 226 - { 227 - bool ret=true; 183 + end = strchr(s, ';'); 228 184 229 - if (!bookmark) 230 - { 231 - ret = false; /* something didn't happen correctly, do nothing */ 232 - } 233 - else 185 + /* extract file names */ 186 + if (parse_filenames) 234 187 { 235 - if (global_settings.usemrb) 236 - ret = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true); 237 - 188 + size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); 189 + len = MIN(TEMP_BUF_SIZE - 1, len); 190 + strmemccpy(global_temp_buffer, s, len + 1); 238 191 239 - /* writing the bookmark */ 240 - if (create_bookmark_file) 192 + if (end != NULL) 241 193 { 242 - char* name = playlist_get_name(NULL, global_temp_buffer, 243 - sizeof(global_temp_buffer)); 244 - if (generate_bookmark_file_name(name)) 194 + end++; 195 + if (strip_dir) 245 196 { 246 - ret = ret & add_bookmark(global_bookmark_file_name, bookmark, false); 197 + s = strrchr(end, '/'); 198 + if (s) 199 + { 200 + end = s; 201 + end++; 202 + } 247 203 } 248 - else 249 - { 250 - ret = false; /* generating bookmark file failed */ 251 - } 204 + strmemccpy(global_filename, end, MAX_PATH); 252 205 } 253 - } 254 - 255 - splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) 256 - : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); 257 - 258 - return ret; 259 - } 206 + } 260 207 261 - /* Get the name of the playlist and the name of the track from a bookmark. */ 262 - /* Returns true iff both were extracted. */ 263 - static bool get_playlist_and_track(const char *bookmark, char **pl_start, 264 - char **pl_end, char **track) 265 - { 266 - *pl_start = strchr(bookmark,'/'); 267 - if (!(*pl_start)) 268 - return false; 269 - *pl_end = strrchr(bookmark,';'); 270 - *track = *pl_end + 1; 271 208 return true; 272 209 } 273 210 ··· 275 212 /* This function adds a bookmark to a file. */ 276 213 /* Returns true on successful bookmark add. */ 277 214 /* ------------------------------------------------------------------------*/ 278 - static bool add_bookmark(const char* bookmark_file_name, const char* bookmark, 215 + static bool add_bookmark(const char* bookmark_file_name, 216 + const char* bookmark, 279 217 bool most_recent) 280 218 { 281 219 int temp_bookmark_file = 0; ··· 357 295 return true; 358 296 } 359 297 360 - /* GCC 7 and up complain about the snprintf in create_bookmark() when 361 - compiled with -D_FORTIFY_SOURCE or -Wformat-truncation 362 - This is a false positive, so disable it here only */ 363 - #if __GNUC__ >= 7 364 - #pragma GCC diagnostic push 365 - #pragma GCC diagnostic ignored "-Wformat-truncation" 366 - #endif 367 298 /* ----------------------------------------------------------------------- */ 368 - /* This function takes the system resume data and formats it into a valid */ 369 - /* bookmark. */ 370 - /* Returns not NULL on successful bookmark format. */ 299 + /* This function is used by multiple functions and is used to generate a */ 300 + /* bookmark named based off of the input. */ 301 + /* Changing this function could result in how the bookmarks are stored. */ 302 + /* it would be here that the centralized/decentralized bookmark code */ 303 + /* could be placed. */ 304 + /* Returns true if the file name is generated, false if it was too long */ 371 305 /* ----------------------------------------------------------------------- */ 372 - static char* create_bookmark() 306 + static bool generate_bookmark_file_name(const char *in) 373 307 { 374 - int resume_index = 0; 375 - char *file; 376 - 377 - if (!bookmark_is_bookmarkable_state()) 378 - return NULL; /* something didn't happen correctly, do nothing */ 379 - 380 - /* grab the currently playing track */ 381 - struct mp3entry *id3 = audio_current_track(); 382 - if(!id3) 383 - return NULL; 384 - 385 - /* Get some basic resume information */ 386 - /* queue_resume and queue_resume_index are not used and can be ignored.*/ 387 - playlist_get_resume_info(&resume_index); 388 - 389 - /* Get the currently playing file minus the path */ 390 - /* This is used when displaying the available bookmarks */ 391 - file = strrchr(id3->path,'/'); 392 - if(NULL == file) 393 - return NULL; 394 - 395 - /* create the bookmark */ 396 - playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); 397 - if (global_temp_buffer[strlen(global_temp_buffer) - 1] != '/') 398 - file = id3->path; 399 - else file++; 400 - snprintf(global_bookmark, sizeof(global_bookmark), 401 - /* new optional bookmark token descriptors should be inserted 402 - just before the "%s;%s" in this line... */ 403 - #if defined(HAVE_PITCHCONTROL) 404 - ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;%s;%s", 405 - #else 406 - ">%d;%d;%ld;%d;%ld;%d;%d;%s;%s", 407 - #endif 408 - /* ... their flags should go here ... */ 409 - #if defined(HAVE_PITCHCONTROL) 410 - BM_PITCH | BM_SPEED, 411 - #else 412 - 0, 413 - #endif 414 - resume_index, 415 - id3->offset, 416 - playlist_get_seed(NULL), 417 - id3->elapsed, 418 - global_settings.repeat_mode, 419 - global_settings.playlist_shuffle, 420 - /* ...and their values should go here */ 421 - #if defined(HAVE_PITCHCONTROL) 422 - (long)sound_get_pitch(), 423 - (long)dsp_get_timestretch(), 424 - #endif 425 - /* more mandatory tokens */ 426 - global_temp_buffer, 427 - file); 428 - 429 - /* checking to see if the bookmark is valid */ 430 - if (parse_bookmark(global_bookmark, false, false)) 431 - return global_bookmark; 308 + /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */ 309 + /* otherwise, name it based on the in variable */ 310 + if (!strcmp("/", in)) 311 + strcpy(global_bookmark_file_name, "/root_dir.bmark"); 432 312 else 433 - return NULL; 434 - } 435 - #if __GNUC__ >= 7 436 - #pragma GCC diagnostic pop /* -Wformat-truncation */ 313 + { 314 + #ifdef HAVE_MULTIVOLUME 315 + /* The "root" of an extra volume need special handling too. */ 316 + const char *filename; 317 + path_strip_volume(in, &filename, true); 318 + bool volume_root = *filename == '\0'; 437 319 #endif 320 + size_t len = strlcpy(global_bookmark_file_name, in, MAX_PATH); 321 + if(len >= MAX_PATH) 322 + return false; 438 323 439 - /* ----------------------------------------------------------------------- */ 440 - /* This function will determine if an autoload is necessary. This is an */ 441 - /* interface function. */ 442 - /* Returns */ 443 - /* BOOKMARK_DO_RESUME on bookmark load or bookmark selection. */ 444 - /* BOOKMARK_DONT_RESUME if we're not going to resume */ 445 - /* BOOKMARK_CANCEL if user canceled */ 446 - /* ------------------------------------------------------------------------*/ 447 - int bookmark_autoload(const char* file) 448 - { 449 - char* bookmark; 324 + if(global_bookmark_file_name[len-1] == '/') { 325 + global_bookmark_file_name[len-1] = '\0'; 326 + len--; 327 + } 450 328 451 - if(global_settings.autoloadbookmark == BOOKMARK_NO) 452 - return BOOKMARK_DONT_RESUME; 329 + #ifdef HAVE_MULTIVOLUME 330 + if (volume_root) 331 + len = strlcat(global_bookmark_file_name, "/volume_dir.bmark", MAX_PATH); 332 + else 333 + #endif 334 + len = strlcat(global_bookmark_file_name, ".bmark", MAX_PATH); 453 335 454 - /*Checking to see if a bookmark file exists.*/ 455 - if(!generate_bookmark_file_name(file)) 456 - { 457 - return BOOKMARK_DONT_RESUME; 336 + if(len >= MAX_PATH) 337 + return false; 458 338 } 459 339 460 - if(!file_exists(global_bookmark_file_name)) 461 - return BOOKMARK_DONT_RESUME; 462 - 463 - if(global_settings.autoloadbookmark == BOOKMARK_YES) 464 - { 465 - return bookmark_load(global_bookmark_file_name, true) ? BOOKMARK_DO_RESUME : 466 - BOOKMARK_DONT_RESUME; 467 - } 468 - else 469 - { 470 - int ret = select_bookmark(global_bookmark_file_name, true, &bookmark); 471 - 472 - if (bookmark != NULL) 473 - { 474 - if (!play_bookmark(bookmark)) 475 - { 476 - /* Selected bookmark not found. */ 477 - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); 478 - } 479 - 480 - /* Act as if autoload was done even if it failed, since the 481 - * user did make an active selection. 482 - */ 483 - return BOOKMARK_DO_RESUME; 484 - } 485 - 486 - return ret != BOOKMARK_SUCCESS ? BOOKMARK_CANCEL : BOOKMARK_DONT_RESUME; 487 - } 340 + return true; 488 341 } 489 342 490 343 /* ----------------------------------------------------------------------- */ 491 - /* This function loads the bookmark information into the resume memory. */ 492 - /* This is an interface function. */ 493 - /* Returns true on successful bookmark load. */ 344 + /* This function takes the current current resume information and writes */ 345 + /* that to the beginning of the bookmark file. */ 346 + /* This file will contain N number of bookmarks in the following format: */ 347 + /* resume_index*resume_offset*resume_seed*resume_first_index* */ 348 + /* resume_file*milliseconds*MP3 Title* */ 349 + /* Returns true on successful bookmark write. */ 350 + /* Returns false if any part of the bookmarking process fails. It is */ 351 + /* possible that a bookmark is successfully added to the most recent */ 352 + /* bookmark list but fails to be added to the bookmark file or vice versa. */ 494 353 /* ------------------------------------------------------------------------*/ 495 - bool bookmark_load(const char* file, bool autoload) 354 + static bool write_bookmark(bool create_bookmark_file, const char *bookmark) 496 355 { 497 - int fd; 498 - char* bookmark = NULL; 356 + bool ret=true; 499 357 500 - if(autoload) 358 + if (!bookmark) 501 359 { 502 - fd = open(file, O_RDONLY); 503 - if(fd >= 0) 504 - { 505 - if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0) 506 - bookmark=global_read_buffer; 507 - close(fd); 508 - } 360 + ret = false; /* something didn't happen correctly, do nothing */ 509 361 } 510 362 else 511 363 { 512 - /* This is not an auto-load, so list the bookmarks */ 513 - select_bookmark(file, false, &bookmark); 514 - } 364 + if (global_settings.usemrb) 365 + ret = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true); 515 366 516 - if (bookmark != NULL) 517 - { 518 - if (!play_bookmark(bookmark)) 367 + 368 + /* writing the bookmark */ 369 + if (create_bookmark_file) 519 370 { 520 - /* Selected bookmark not found. */ 521 - if (!autoload) 371 + char* name = playlist_get_name(NULL, global_temp_buffer, 372 + sizeof(global_temp_buffer)); 373 + if (generate_bookmark_file_name(name)) 374 + { 375 + ret = ret & add_bookmark(global_bookmark_file_name, bookmark, false); 376 + } 377 + else 522 378 { 523 - splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); 379 + ret = false; /* generating bookmark file failed */ 524 380 } 525 - 526 - return false; 527 381 } 528 382 } 529 383 530 - return true; 531 - } 384 + splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS) 385 + : ID2P(LANG_BOOKMARK_CREATE_FAILURE)); 532 386 387 + return ret; 388 + } 533 389 534 390 static int get_bookmark_count(const char* bookmark_file_name) 535 391 { ··· 707 563 } 708 564 } 709 565 566 + /* ----------------------------------------------------------------------- */ 567 + /* This function parses a bookmark, says the voice UI part of it. */ 568 + /* ------------------------------------------------------------------------*/ 569 + static void say_bookmark(const char* bookmark, 570 + int bookmark_id, 571 + bool show_playlist_name) 572 + { 573 + if (!parse_bookmark(bookmark, true, false)) 574 + { 575 + talk_id(LANG_BOOKMARK_INVALID, false); 576 + return; 577 + } 578 + 579 + talk_number(bookmark_id + 1, false); 580 + 581 + bool is_dir = (global_temp_buffer[0] 582 + && global_temp_buffer[strlen(global_temp_buffer)-1] == '/'); 583 + 584 + /* HWCODEC cannot enqueue voice file entries and .talk thumbnails 585 + together, because there is no guarantee that the same mp3 586 + parameters are used. */ 587 + if(show_playlist_name) 588 + { /* It's useful to know which playlist this is */ 589 + if(is_dir) 590 + talk_dir_or_spell(global_temp_buffer, 591 + TALK_IDARRAY(VOICE_DIR), true); 592 + else talk_file_or_spell(NULL, global_temp_buffer, 593 + TALK_IDARRAY(LANG_PLAYLIST), true); 594 + } 595 + 596 + if(bm.shuffle) 597 + talk_id(LANG_SHUFFLE, true); 598 + 599 + talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); 600 + talk_number(bm.resume_index + 1, true); 601 + talk_id(LANG_TIME, true); 602 + talk_value(bm.resume_time / 1000, UNIT_TIME, true); 603 + 604 + /* Track filename */ 605 + if(!is_dir) 606 + global_temp_buffer[0] = 0; 607 + talk_file_or_spell(global_temp_buffer, global_filename, 608 + TALK_IDARRAY(VOICE_FILE), true); 609 + } 610 + 710 611 static int bookmark_list_voice_cb(int list_index, void* data) 711 612 { 712 613 struct bookmark_list* bookmarks = (struct bookmark_list*) data; ··· 724 625 } 725 626 726 627 /* ----------------------------------------------------------------------- */ 628 + /* This function takes a location in a bookmark file and deletes that */ 629 + /* bookmark. */ 630 + /* Returns true on successful bookmark deletion. */ 631 + /* ------------------------------------------------------------------------*/ 632 + static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id) 633 + { 634 + int temp_bookmark_file = 0; 635 + int bookmark_file = 0; 636 + int bookmark_count = 0; 637 + 638 + /* Opening up a temp bookmark file */ 639 + temp_bookmark_file = open_pathfmt(global_temp_buffer, sizeof(global_temp_buffer), 640 + O_WRONLY | O_CREAT | O_TRUNC, "%s.tmp", bookmark_file_name); 641 + 642 + if (temp_bookmark_file < 0) 643 + return false; /* can't open the temp file */ 644 + 645 + /* Reading in the previous bookmarks and writing them to the temp file */ 646 + bookmark_file = open(bookmark_file_name, O_RDONLY); 647 + if (bookmark_file >= 0) 648 + { 649 + while (read_line(bookmark_file, global_read_buffer, 650 + sizeof(global_read_buffer)) > 0) 651 + { 652 + if (bookmark_id != bookmark_count) 653 + { 654 + write(temp_bookmark_file, global_read_buffer, 655 + strlen(global_read_buffer)); 656 + write(temp_bookmark_file, "\n", 1); 657 + } 658 + bookmark_count++; 659 + } 660 + close(bookmark_file); 661 + } 662 + close(temp_bookmark_file); 663 + 664 + remove(bookmark_file_name); 665 + rename(global_temp_buffer, bookmark_file_name); 666 + 667 + return true; 668 + } 669 + 670 + /* ----------------------------------------------------------------------- */ 727 671 /* This displays the bookmarks in a file and allows the user to */ 728 672 /* select one to play. */ 729 673 /* *selected_bookmark contains a non NULL value on successful bookmark */ ··· 732 676 /* if no selection was made and BOOKMARK_USB_CONNECTED if the selection */ 733 677 /* menu is forced to exit due to a USB connection. */ 734 678 /* ------------------------------------------------------------------------*/ 735 - static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark) 679 + static int select_bookmark(const char* bookmark_file_name, 680 + bool show_dont_resume, 681 + char** selected_bookmark) 736 682 { 737 683 struct bookmark_list* bookmarks; 738 684 struct gui_synclist list; ··· 880 826 } 881 827 882 828 /* ----------------------------------------------------------------------- */ 883 - /* This function takes a location in a bookmark file and deletes that */ 884 - /* bookmark. */ 885 - /* Returns true on successful bookmark deletion. */ 886 - /* ------------------------------------------------------------------------*/ 887 - static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id) 888 - { 889 - int temp_bookmark_file = 0; 890 - int bookmark_file = 0; 891 - int bookmark_count = 0; 892 - 893 - /* Opening up a temp bookmark file */ 894 - temp_bookmark_file = open_pathfmt(global_temp_buffer, sizeof(global_temp_buffer), 895 - O_WRONLY | O_CREAT | O_TRUNC, "%s.tmp", bookmark_file_name); 896 - 897 - if (temp_bookmark_file < 0) 898 - return false; /* can't open the temp file */ 899 - 900 - /* Reading in the previous bookmarks and writing them to the temp file */ 901 - bookmark_file = open(bookmark_file_name, O_RDONLY); 902 - if (bookmark_file >= 0) 903 - { 904 - while (read_line(bookmark_file, global_read_buffer, 905 - sizeof(global_read_buffer)) > 0) 906 - { 907 - if (bookmark_id != bookmark_count) 908 - { 909 - write(temp_bookmark_file, global_read_buffer, 910 - strlen(global_read_buffer)); 911 - write(temp_bookmark_file, "\n", 1); 912 - } 913 - bookmark_count++; 914 - } 915 - close(bookmark_file); 916 - } 917 - close(temp_bookmark_file); 918 - 919 - remove(bookmark_file_name); 920 - rename(global_temp_buffer, bookmark_file_name); 921 - 922 - return true; 923 - } 924 - 925 - /* ----------------------------------------------------------------------- */ 926 - /* This function parses a bookmark, says the voice UI part of it. */ 927 - /* ------------------------------------------------------------------------*/ 928 - static void say_bookmark(const char* bookmark, 929 - int bookmark_id, bool show_playlist_name) 930 - { 931 - if (!parse_bookmark(bookmark, true, false)) 932 - { 933 - talk_id(LANG_BOOKMARK_INVALID, false); 934 - return; 935 - } 936 - 937 - talk_number(bookmark_id + 1, false); 938 - 939 - bool is_dir = (global_temp_buffer[0] 940 - && global_temp_buffer[strlen(global_temp_buffer)-1] == '/'); 941 - 942 - /* HWCODEC cannot enqueue voice file entries and .talk thumbnails 943 - together, because there is no guarantee that the same mp3 944 - parameters are used. */ 945 - if(show_playlist_name) 946 - { /* It's useful to know which playlist this is */ 947 - if(is_dir) 948 - talk_dir_or_spell(global_temp_buffer, 949 - TALK_IDARRAY(VOICE_DIR), true); 950 - else talk_file_or_spell(NULL, global_temp_buffer, 951 - TALK_IDARRAY(LANG_PLAYLIST), true); 952 - } 953 - 954 - if(bm.shuffle) 955 - talk_id(LANG_SHUFFLE, true); 956 - 957 - talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true); 958 - talk_number(bm.resume_index + 1, true); 959 - talk_id(LANG_TIME, true); 960 - talk_value(bm.resume_time / 1000, UNIT_TIME, true); 961 - 962 - /* Track filename */ 963 - if(!is_dir) 964 - global_temp_buffer[0] = 0; 965 - talk_file_or_spell(global_temp_buffer, global_filename, 966 - TALK_IDARRAY(VOICE_FILE), true); 967 - } 968 - 969 - /* ----------------------------------------------------------------------- */ 970 829 /* This function parses a bookmark and then plays it. */ 971 830 /* Returns true on successful bookmark play. */ 972 831 /* ------------------------------------------------------------------------*/ ··· 995 854 return false; 996 855 } 997 856 998 - static const char* skip_token(const char* s) 857 + /* GCC 7 and up complain about the snprintf in create_bookmark() when 858 + compiled with -D_FORTIFY_SOURCE or -Wformat-truncation 859 + This is a false positive, so disable it here only */ 860 + #if __GNUC__ >= 7 861 + #pragma GCC diagnostic push 862 + #pragma GCC diagnostic ignored "-Wformat-truncation" 863 + #endif 864 + /* ----------------------------------------------------------------------- */ 865 + /* This function takes the system resume data and formats it into a valid */ 866 + /* bookmark. */ 867 + /* Returns not NULL on successful bookmark format. */ 868 + /* ----------------------------------------------------------------------- */ 869 + static char* create_bookmark(void) 999 870 { 1000 - while (*s && *s != ';') 1001 - { 1002 - s++; 1003 - } 871 + int resume_index = 0; 872 + char *file; 1004 873 1005 - if (*s) 1006 - { 1007 - s++; 1008 - } 874 + if (!bookmark_is_bookmarkable_state()) 875 + return NULL; /* something didn't happen correctly, do nothing */ 876 + 877 + /* grab the currently playing track */ 878 + struct mp3entry *id3 = audio_current_track(); 879 + if(!id3) 880 + return NULL; 881 + 882 + /* Get some basic resume information */ 883 + /* queue_resume and queue_resume_index are not used and can be ignored.*/ 884 + playlist_get_resume_info(&resume_index); 1009 885 1010 - return s; 886 + /* Get the currently playing file minus the path */ 887 + /* This is used when displaying the available bookmarks */ 888 + file = strrchr(id3->path,'/'); 889 + if(NULL == file) 890 + return NULL; 891 + 892 + /* create the bookmark */ 893 + playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); 894 + if (global_temp_buffer[strlen(global_temp_buffer) - 1] != '/') 895 + file = id3->path; 896 + else file++; 897 + snprintf(global_bookmark, sizeof(global_bookmark), 898 + /* new optional bookmark token descriptors should be inserted 899 + just before the "%s;%s" in this line... */ 900 + #if defined(HAVE_PITCHCONTROL) 901 + ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;%s;%s", 902 + #else 903 + ">%d;%d;%ld;%d;%ld;%d;%d;%s;%s", 904 + #endif 905 + /* ... their flags should go here ... */ 906 + #if defined(HAVE_PITCHCONTROL) 907 + BM_PITCH | BM_SPEED, 908 + #else 909 + 0, 910 + #endif 911 + resume_index, 912 + id3->offset, 913 + playlist_get_seed(NULL), 914 + id3->elapsed, 915 + global_settings.repeat_mode, 916 + global_settings.playlist_shuffle, 917 + /* ...and their values should go here */ 918 + #if defined(HAVE_PITCHCONTROL) 919 + (long)sound_get_pitch(), 920 + (long)dsp_get_timestretch(), 921 + #endif 922 + /* more mandatory tokens */ 923 + global_temp_buffer, 924 + file); 925 + 926 + /* checking to see if the bookmark is valid */ 927 + if (parse_bookmark(global_bookmark, false, false)) 928 + return global_bookmark; 929 + else 930 + return NULL; 1011 931 } 932 + #if __GNUC__ >= 7 933 + #pragma GCC diagnostic pop /* -Wformat-truncation */ 934 + #endif 1012 935 1013 - static const char* int_token(const char* s, int* dest) 936 + /*-------------------------------------------------------------------------*/ 937 + /* PUBLIC INTERFACE -------------------------------------------------------*/ 938 + /*-------------------------------------------------------------------------*/ 939 + 940 + 941 + /* ----------------------------------------------------------------------- */ 942 + /* This is an interface function from the context menu. */ 943 + /* Returns true on successful bookmark creation. */ 944 + /* ----------------------------------------------------------------------- */ 945 + bool bookmark_create_menu(void) 1014 946 { 1015 - *dest = atoi(s); 1016 - return skip_token(s); 947 + return write_bookmark(true, create_bookmark()); 1017 948 } 1018 949 1019 - static const char* long_token(const char* s, long* dest) 950 + /* ----------------------------------------------------------------------- */ 951 + /* This function acts as the load interface from the context menu. */ 952 + /* This function determines the bookmark file name and then loads that file*/ 953 + /* for the user. The user can then select or delete previous bookmarks. */ 954 + /* This function returns BOOKMARK_SUCCESS on the selection of a track to */ 955 + /* resume, BOOKMARK_FAIL if the menu is exited without a selection and */ 956 + /* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */ 957 + /* connection. */ 958 + /* ----------------------------------------------------------------------- */ 959 + int bookmark_load_menu(void) 1020 960 { 1021 - *dest = atoi(s); /* Should be atol, but we don't have it. */ 1022 - return skip_token(s); 961 + char* bookmark; 962 + int ret = BOOKMARK_FAIL; 963 + 964 + push_current_activity(ACTIVITY_BOOKMARKSLIST); 965 + 966 + char* name = playlist_get_name(NULL, global_temp_buffer, 967 + sizeof(global_temp_buffer)); 968 + if (generate_bookmark_file_name(name)) 969 + { 970 + ret = select_bookmark(global_bookmark_file_name, false, &bookmark); 971 + if (bookmark != NULL) 972 + { 973 + ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL; 974 + } 975 + } 976 + 977 + pop_current_activity(); 978 + return ret; 1023 979 } 1024 980 1025 981 /* ----------------------------------------------------------------------- */ 1026 - /* This function takes a bookmark and parses it. This function also */ 1027 - /* validates the bookmark. The parse_filenames flag indicates whether */ 1028 - /* the filename tokens are to be extracted. */ 1029 - /* Returns true on successful bookmark parse. */ 982 + /* Gives the user a list of the Most Recent Bookmarks. This is an */ 983 + /* interface function */ 984 + /* Returns true on the successful selection of a recent bookmark. */ 1030 985 /* ----------------------------------------------------------------------- */ 1031 - static bool parse_bookmark(const char *bookmark, const bool parse_filenames, const bool strip_dir) 986 + bool bookmark_mrb_load() 1032 987 { 1033 - const char* s = bookmark; 1034 - const char* end; 988 + char* bookmark; 989 + bool ret = false; 1035 990 1036 - #define GET_INT_TOKEN(var) s = int_token(s, &var) 1037 - #define GET_LONG_TOKEN(var) s = long_token(s, &var) 1038 - #define GET_BOOL_TOKEN(var) var = (atoi(s)!=0); s = skip_token(s) 991 + push_current_activity(ACTIVITY_BOOKMARKSLIST); 992 + select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark); 993 + if (bookmark != NULL) 994 + { 995 + ret = play_bookmark(bookmark); 996 + } 1039 997 1040 - /* if new format bookmark, extract the optional content flags, 1041 - otherwise treat as an original format bookmark */ 1042 - int opt_flags = 0; 1043 - bool new_format = (strchr(s, '>') == s); 1044 - if (new_format) 998 + pop_current_activity(); 999 + return ret; 1000 + } 1001 + 1002 + /* ----------------------------------------------------------------------- */ 1003 + /* This function handles an autobookmark creation. This is an interface */ 1004 + /* function. */ 1005 + /* Returns true on successful bookmark creation. */ 1006 + /* ----------------------------------------------------------------------- */ 1007 + bool bookmark_autobookmark(bool prompt_ok) 1008 + { 1009 + char* bookmark; 1010 + bool update; 1011 + 1012 + if (!bookmark_is_bookmarkable_state()) 1013 + return false; 1014 + 1015 + audio_pause(); /* first pause playback */ 1016 + update = (global_settings.autoupdatebookmark && bookmark_exists()); 1017 + bookmark = create_bookmark(); 1018 + 1019 + if (update) 1020 + return write_bookmark(true, bookmark); 1021 + 1022 + switch (global_settings.autocreatebookmark) 1045 1023 { 1046 - s++; 1047 - GET_INT_TOKEN(opt_flags); 1024 + case BOOKMARK_YES: 1025 + return write_bookmark(true, bookmark); 1026 + 1027 + case BOOKMARK_NO: 1028 + return false; 1029 + 1030 + case BOOKMARK_RECENT_ONLY_YES: 1031 + return write_bookmark(false, bookmark); 1048 1032 } 1033 + const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)}; 1034 + const struct text_message message={lines, 1}; 1049 1035 1050 - /* extract all original bookmark tokens */ 1051 - GET_INT_TOKEN(bm.resume_index); 1052 - GET_LONG_TOKEN(bm.resume_offset); 1053 - GET_INT_TOKEN(bm.resume_seed); 1054 - if (!new_format) /* skip deprecated token */ 1055 - s = skip_token(s); 1056 - GET_LONG_TOKEN(bm.resume_time); 1057 - GET_INT_TOKEN(bm.repeat_mode); 1058 - GET_BOOL_TOKEN(bm.shuffle); 1036 + if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES) 1037 + { 1038 + if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK) 1039 + return write_bookmark(false, bookmark); 1040 + else 1041 + return write_bookmark(true, bookmark); 1042 + } 1043 + return false; 1044 + } 1059 1045 1060 - /* extract all optional bookmark tokens */ 1061 - if (opt_flags & BM_PITCH) 1062 - GET_INT_TOKEN(bm.pitch); 1063 - if (opt_flags & BM_SPEED) 1064 - GET_INT_TOKEN(bm.speed); 1046 + /* ----------------------------------------------------------------------- */ 1047 + /* This function will determine if an autoload is necessary. This is an */ 1048 + /* interface function. */ 1049 + /* Returns */ 1050 + /* BOOKMARK_DO_RESUME on bookmark load or bookmark selection. */ 1051 + /* BOOKMARK_DONT_RESUME if we're not going to resume */ 1052 + /* BOOKMARK_CANCEL if user canceled */ 1053 + /* ------------------------------------------------------------------------*/ 1054 + int bookmark_autoload(const char* file) 1055 + { 1056 + char* bookmark; 1057 + 1058 + if(global_settings.autoloadbookmark == BOOKMARK_NO) 1059 + return BOOKMARK_DONT_RESUME; 1065 1060 1066 - if (*s == 0) 1061 + /*Checking to see if a bookmark file exists.*/ 1062 + if(!generate_bookmark_file_name(file)) 1067 1063 { 1068 - return false; 1064 + return BOOKMARK_DONT_RESUME; 1069 1065 } 1070 1066 1071 - end = strchr(s, ';'); 1067 + if(!file_exists(global_bookmark_file_name)) 1068 + return BOOKMARK_DONT_RESUME; 1072 1069 1073 - /* extract file names */ 1074 - if (parse_filenames) 1070 + if(global_settings.autoloadbookmark == BOOKMARK_YES) 1075 1071 { 1076 - size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s); 1077 - len = MIN(TEMP_BUF_SIZE - 1, len); 1078 - strmemccpy(global_temp_buffer, s, len + 1); 1072 + return bookmark_load(global_bookmark_file_name, true) ? BOOKMARK_DO_RESUME : 1073 + BOOKMARK_DONT_RESUME; 1074 + } 1075 + else 1076 + { 1077 + int ret = select_bookmark(global_bookmark_file_name, true, &bookmark); 1079 1078 1080 - if (end != NULL) 1079 + if (bookmark != NULL) 1081 1080 { 1082 - end++; 1083 - if (strip_dir) 1081 + if (!play_bookmark(bookmark)) 1084 1082 { 1085 - s = strrchr(end, '/'); 1086 - if (s) 1087 - { 1088 - end = s; 1089 - end++; 1090 - } 1083 + /* Selected bookmark not found. */ 1084 + splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); 1091 1085 } 1092 - strmemccpy(global_filename, end, MAX_PATH); 1086 + 1087 + /* Act as if autoload was done even if it failed, since the 1088 + * user did make an active selection. 1089 + */ 1090 + return BOOKMARK_DO_RESUME; 1093 1091 } 1094 - } 1095 1092 1096 - return true; 1093 + return ret != BOOKMARK_SUCCESS ? BOOKMARK_CANCEL : BOOKMARK_DONT_RESUME; 1094 + } 1097 1095 } 1098 1096 1099 1097 /* ----------------------------------------------------------------------- */ 1100 - /* This function is used by multiple functions and is used to generate a */ 1101 - /* bookmark named based off of the input. */ 1102 - /* Changing this function could result in how the bookmarks are stored. */ 1103 - /* it would be here that the centralized/decentralized bookmark code */ 1104 - /* could be placed. */ 1105 - /* Returns true if the file name is generated, false if it was too long */ 1106 - /* ----------------------------------------------------------------------- */ 1107 - static bool generate_bookmark_file_name(const char *in) 1098 + /* This function loads the bookmark information into the resume memory. */ 1099 + /* This is an interface function. */ 1100 + /* Returns true on successful bookmark load. */ 1101 + /* ------------------------------------------------------------------------*/ 1102 + bool bookmark_load(const char* file, bool autoload) 1108 1103 { 1109 - /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */ 1110 - /* otherwise, name it based on the in variable */ 1111 - if (!strcmp("/", in)) 1112 - strcpy(global_bookmark_file_name, "/root_dir.bmark"); 1104 + int fd; 1105 + char* bookmark = NULL; 1106 + 1107 + if(autoload) 1108 + { 1109 + fd = open(file, O_RDONLY); 1110 + if(fd >= 0) 1111 + { 1112 + if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0) 1113 + bookmark=global_read_buffer; 1114 + close(fd); 1115 + } 1116 + } 1113 1117 else 1114 1118 { 1115 - #ifdef HAVE_MULTIVOLUME 1116 - /* The "root" of an extra volume need special handling too. */ 1117 - const char *filename; 1118 - path_strip_volume(in, &filename, true); 1119 - bool volume_root = *filename == '\0'; 1120 - #endif 1121 - size_t len = strlcpy(global_bookmark_file_name, in, MAX_PATH); 1122 - if(len >= MAX_PATH) 1123 - return false; 1119 + /* This is not an auto-load, so list the bookmarks */ 1120 + select_bookmark(file, false, &bookmark); 1121 + } 1124 1122 1125 - if(global_bookmark_file_name[len-1] == '/') { 1126 - global_bookmark_file_name[len-1] = '\0'; 1127 - len--; 1128 - } 1129 - 1130 - #ifdef HAVE_MULTIVOLUME 1131 - if (volume_root) 1132 - len = strlcat(global_bookmark_file_name, "/volume_dir.bmark", MAX_PATH); 1133 - else 1134 - #endif 1135 - len = strlcat(global_bookmark_file_name, ".bmark", MAX_PATH); 1123 + if (bookmark != NULL) 1124 + { 1125 + if (!play_bookmark(bookmark)) 1126 + { 1127 + /* Selected bookmark not found. */ 1128 + if (!autoload) 1129 + { 1130 + splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); 1131 + } 1136 1132 1137 - if(len >= MAX_PATH) 1138 1133 return false; 1134 + } 1139 1135 } 1140 1136 1141 1137 return true; ··· 1179 1175 1180 1176 return true; 1181 1177 } 1182 -