summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/playlist.c287
-rw-r--r--apps/playlist.h3
-rw-r--r--firmware/common/pathfuncs.c29
-rw-r--r--firmware/export/pathfuncs.h2
4 files changed, 172 insertions, 149 deletions
diff --git a/apps/playlist.c b/apps/playlist.c
index 8c9bfb4a04..bd2d33ea78 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -536,6 +536,7 @@ int update_playlist_flags_unlocked(struct playlist_info *playlist,
536 * dest: output buffer 536 * dest: output buffer
537 * src: the file name from the playlist 537 * src: the file name from the playlist
538 * dir: the absolute path to the directory where the playlist resides 538 * dir: the absolute path to the directory where the playlist resides
539 * dlen used to truncate dir -- supply -1u to ignore
539 * 540 *
540 * The type of path in "src" determines what will be written to "dest": 541 * The type of path in "src" determines what will be written to "dest":
541 * 542 *
@@ -548,11 +549,10 @@ int update_playlist_flags_unlocked(struct playlist_info *playlist,
548 * the drive letter is accepted but ignored. 549 * the drive letter is accepted but ignored.
549 */ 550 */
550static ssize_t format_track_path(char *dest, char *src, int buf_length, 551static ssize_t format_track_path(char *dest, char *src, int buf_length,
551 const char *dir) 552 const char *dir, size_t dlen)
552{ 553{
553 /* Look for the end of the string (includes NULL) */ 554 /* Look for the end of the string (includes NULL) */
554 size_t len = strcspn(src, "\r\n");; 555 size_t len = strcspn(src, "\r\n");;
555
556 /* Now work back killing white space */ 556 /* Now work back killing white space */
557 while (len > 0) 557 while (len > 0)
558 { 558 {
@@ -574,18 +574,25 @@ static ssize_t format_track_path(char *dest, char *src, int buf_length,
574 #ifdef HAVE_MULTIVOLUME 574 #ifdef HAVE_MULTIVOLUME
575 const char *p; 575 const char *p;
576 path_strip_last_volume(dir, &p, false); 576 path_strip_last_volume(dir, &p, false);
577 dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */ 577 //dir = strmemdupa(dir, p - dir);
578 dlen = (p-dir); /* empty if no volspec on dir */
578 #else 579 #else
579 dir = ""; /* only volume is root */ 580 dir = ""; /* only volume is root */
580 #endif 581 #endif
581 } 582 }
582 583
583 len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length); 584 if (*dir == '\0')
585 {
586 dir = PATH_ROOTSTR;
587 dlen = -1u;
588 }
589
590 len = path_append_ex(dest, dir, dlen, src, buf_length);
584 if (len >= (size_t)buf_length) 591 if (len >= (size_t)buf_length)
585 return -1; /* buffer too small */ 592 return -1; /* buffer too small */
586 593
587 path_remove_dot_segments (dest, dest); 594 path_remove_dot_segments (dest, dest);
588 595 logf("%s %s", __func__, dest);
589 return strlen (dest); 596 return strlen (dest);
590} 597}
591 598
@@ -1033,6 +1040,9 @@ static int get_track_filename(struct playlist_info* playlist, int index,
1033 char dir_buf[MAX_PATH+1]; 1040 char dir_buf[MAX_PATH+1];
1034 bool utf8 = playlist->utf8; 1041 bool utf8 = playlist->utf8;
1035 1042
1043 if (index < 0 || index >= playlist->amount)
1044 return -1;
1045
1036 playlist_write_lock(playlist); 1046 playlist_write_lock(playlist);
1037 1047
1038 bool control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; 1048 bool control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
@@ -1102,10 +1112,10 @@ static int get_track_filename(struct playlist_info* playlist, int index,
1102 } 1112 }
1103 } 1113 }
1104 1114
1105 strmemccpy(dir_buf, playlist->filename, playlist->dirlen);
1106 playlist_write_unlock(playlist); 1115 playlist_write_unlock(playlist);
1107 1116
1108 if (format_track_path(buf, tmp_buf, buf_length, dir_buf) < 0) 1117 if (format_track_path(buf, tmp_buf, buf_length,
1118 playlist->filename, playlist->dirlen) < 0)
1109 return -1; 1119 return -1;
1110 1120
1111 return 0; 1121 return 0;
@@ -1776,7 +1786,7 @@ static void dc_thread_playlist(void)
1776 } 1786 }
1777 1787
1778 /* Load the filename from playlist file. */ 1788 /* Load the filename from playlist file. */
1779 if (get_track_filename(playlist, index, tmp, sizeof(tmp))) 1789 if (get_track_filename(playlist, index, tmp, sizeof(tmp)) != 0)
1780 break; 1790 break;
1781 1791
1782 /* Obtain the dircache file entry cookie. */ 1792 /* Obtain the dircache file entry cookie. */
@@ -2217,16 +2227,20 @@ int playlist_get_display_index(void)
2217unsigned int playlist_get_filename_crc32(struct playlist_info *playlist, 2227unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
2218 int index) 2228 int index)
2219{ 2229{
2220 struct playlist_track_info track_info;
2221 if (playlist_get_track_info(playlist, index, &track_info) == -1)
2222 return -1;
2223 const char *basename; 2230 const char *basename;
2231 char filename[MAX_PATH]; /* path name of mp3 file */
2232 if (!playlist)
2233 playlist = &current_playlist;
2234
2235 if (get_track_filename(playlist, index, filename, sizeof(filename)) != 0)
2236 return -1;
2237
2224#ifdef HAVE_MULTIVOLUME 2238#ifdef HAVE_MULTIVOLUME
2225 /* remove the volume identifier it might change just use the relative part*/ 2239 /* remove the volume identifier it might change just use the relative part*/
2226 path_strip_volume(track_info.filename, &basename, false); 2240 path_strip_volume(filename, &basename, false);
2227 if (basename == NULL) 2241 if (basename == NULL)
2228#endif 2242#endif
2229 basename = track_info.filename; 2243 basename = filename;
2230 NOTEF("%s: %s", __func__, basename); 2244 NOTEF("%s: %s", __func__, basename);
2231 return crc_32(basename, strlen(basename), -1); 2245 return crc_32(basename, strlen(basename), -1);
2232} 2246}
@@ -2298,11 +2312,8 @@ int playlist_get_track_info(struct playlist_info* playlist, int index,
2298 if (!playlist) 2312 if (!playlist)
2299 playlist = &current_playlist; 2313 playlist = &current_playlist;
2300 2314
2301 if (index < 0 || index >= playlist->amount)
2302 return -1;
2303
2304 if (get_track_filename(playlist, index, 2315 if (get_track_filename(playlist, index,
2305 info->filename, sizeof(info->filename))) 2316 info->filename, sizeof(info->filename)) != 0)
2306 return -1; 2317 return -1;
2307 2318
2308 info->attr = 0; 2319 info->attr = 0;
@@ -2482,7 +2493,7 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
2482 2493
2483 /* we need the directory name for formatting purposes */ 2494 /* we need the directory name for formatting purposes */
2484 size_t dirlen = path_dirname(filename, (const char **)&dir); 2495 size_t dirlen = path_dirname(filename, (const char **)&dir);
2485 dir = strmemdupa(dir, dirlen); 2496 //dir = strmemdupa(dir, dirlen);
2486 2497
2487 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0) 2498 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2488 { 2499 {
@@ -2502,7 +2513,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
2502 2513
2503 /* we need to format so that relative paths are correctly 2514 /* we need to format so that relative paths are correctly
2504 handled */ 2515 handled */
2505 if (format_track_path(trackname, temp_buf, sizeof(trackname), dir) < 0) 2516 if (format_track_path(trackname, temp_buf,
2517 sizeof(trackname), dir, dirlen) < 0)
2506 { 2518 {
2507 goto out; 2519 goto out;
2508 } 2520 }
@@ -2683,7 +2695,7 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index)
2683 2695
2684 queue = playlist->indices[index] & PLAYLIST_QUEUED; 2696 queue = playlist->indices[index] & PLAYLIST_QUEUED;
2685 2697
2686 if (get_track_filename(playlist, index, filename, sizeof(filename))) 2698 if (get_track_filename(playlist, index, filename, sizeof(filename)) != 0)
2687 goto out; 2699 goto out;
2688 2700
2689 /* We want to insert the track at the position that was specified by 2701 /* We want to insert the track at the position that was specified by
@@ -2891,9 +2903,8 @@ const char* playlist_peek(int steps, char* buf, size_t buf_size)
2891{ 2903{
2892 struct playlist_info* playlist = &current_playlist; 2904 struct playlist_info* playlist = &current_playlist;
2893 char *temp_ptr; 2905 char *temp_ptr;
2894 int index; 2906 int index = get_next_index(playlist, steps, -1);
2895 2907
2896 index = get_next_index(playlist, steps, -1);
2897 if (index < 0) 2908 if (index < 0)
2898 return NULL; 2909 return NULL;
2899 2910
@@ -2901,7 +2912,7 @@ const char* playlist_peek(int steps, char* buf, size_t buf_size)
2901 if (!buf || !buf_size) 2912 if (!buf || !buf_size)
2902 return ""; 2913 return "";
2903 2914
2904 if (get_track_filename(playlist, index, buf, buf_size)) 2915 if (get_track_filename(playlist, index, buf, buf_size) != 0)
2905 return NULL; 2916 return NULL;
2906 2917
2907 temp_ptr = buf; 2918 temp_ptr = buf;
@@ -2979,6 +2990,48 @@ int playlist_remove_all_tracks(struct playlist_info *playlist)
2979 return result; 2990 return result;
2980} 2991}
2981 2992
2993/* playlist_resume helper function
2994 * only allows comments (#) and PLAYLIST_COMMAND_PLAYLIST (P)
2995 */
2996static enum playlist_command pl_cmds_start(char cmd)
2997{
2998 if (cmd == 'P')
2999 return PLAYLIST_COMMAND_PLAYLIST;
3000 if (cmd == '#')
3001 return PLAYLIST_COMMAND_COMMENT;
3002
3003 return PLAYLIST_COMMAND_ERROR;
3004}
3005
3006/* playlist resume helper function excludes PLAYLIST_COMMAND_PLAYLIST (P) */
3007static enum playlist_command pl_cmds_run(char cmd)
3008{
3009 switch (cmd)
3010 {
3011 case 'A':
3012 return PLAYLIST_COMMAND_ADD;
3013 case 'Q':
3014 return PLAYLIST_COMMAND_QUEUE;
3015 case 'D':
3016 return PLAYLIST_COMMAND_DELETE;
3017 case 'S':
3018 return PLAYLIST_COMMAND_SHUFFLE;
3019 case 'U':
3020 return PLAYLIST_COMMAND_UNSHUFFLE;
3021 case 'R':
3022 return PLAYLIST_COMMAND_RESET;
3023 case 'C':
3024 return PLAYLIST_COMMAND_CLEAR;
3025 case 'F':
3026 return PLAYLIST_COMMAND_FLAGS;
3027 case '#':
3028 return PLAYLIST_COMMAND_COMMENT;
3029 default: /* ERROR */
3030 break;
3031 }
3032 return PLAYLIST_COMMAND_ERROR;
3033}
3034
2982/* 3035/*
2983 * Restore the playlist state based on control file commands. Called to 3036 * Restore the playlist state based on control file commands. Called to
2984 * resume playback after shutdown. 3037 * resume playback after shutdown.
@@ -2992,9 +3045,9 @@ int playlist_resume(void)
2992 int nread; 3045 int nread;
2993 int total_read = 0; 3046 int total_read = 0;
2994 int control_file_size = 0; 3047 int control_file_size = 0;
2995 bool first = true;
2996 bool sorted = true; 3048 bool sorted = true;
2997 int result = -1; 3049 int result = -1;
3050 enum playlist_command (*pl_cmd)(char) = &pl_cmds_start;
2998 3051
2999 splash(0, ID2P(LANG_WAIT)); 3052 splash(0, ID2P(LANG_WAIT));
3000 cpu_boost(true); 3053 cpu_boost(true);
@@ -3060,13 +3113,12 @@ int playlist_resume(void)
3060 bool newline = true; 3113 bool newline = true;
3061 bool exit_loop = false; 3114 bool exit_loop = false;
3062 char *p = buffer; 3115 char *p = buffer;
3063 char *str1 = NULL; 3116 char *strp[3] = {NULL};
3064 char *str2 = NULL;
3065 char *str3 = NULL;
3066 3117
3067 unsigned long last_tick = current_tick; 3118 unsigned long last_tick = current_tick;
3068 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */ 3119 splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
3069 bool useraborted = false; 3120 bool useraborted = false;
3121 bool queue = false;
3070 3122
3071 for(count=0; count<nread && !exit_loop; count++,p++) 3123 for(count=0; count<nread && !exit_loop; count++,p++)
3072 { 3124 {
@@ -3090,25 +3142,35 @@ int playlist_resume(void)
3090 3142
3091 switch (current_command) 3143 switch (current_command)
3092 { 3144 {
3145 case PLAYLIST_COMMAND_ERROR:
3146 {
3147 /* first non-comment line does not specify playlist */
3148 /* ( below handled by pl_cmds_run() ) */
3149 /* OR playlist is specified more than once */
3150 /* OR unknown command -- pl corrupted?? */
3151 result = -12;
3152 exit_loop = true;
3153 break;
3154 }
3093 case PLAYLIST_COMMAND_PLAYLIST: 3155 case PLAYLIST_COMMAND_PLAYLIST:
3094 { 3156 {
3095 /* str1=version str2=dir str3=file */ 3157 /* strp[0]=version strp[1]=dir strp[2]=file */
3096 int version; 3158 int version;
3097 3159
3098 if (!str1) 3160 if (!strp[0])
3099 { 3161 {
3100 result = -2; 3162 result = -2;
3101 exit_loop = true; 3163 exit_loop = true;
3102 break; 3164 break;
3103 } 3165 }
3104 3166
3105 if (!str2) 3167 if (!strp[1])
3106 str2 = ""; 3168 strp[1] = "";
3107 3169
3108 if (!str3) 3170 if (!strp[2])
3109 str3 = ""; 3171 strp[2] = "";
3110 3172
3111 version = atoi(str1); 3173 version = atoi(strp[0]);
3112 3174
3113 /* 3175 /*
3114 * TODO: Playlist control file version upgrades 3176 * TODO: Playlist control file version upgrades
@@ -3126,72 +3188,68 @@ int playlist_resume(void)
3126 goto out; 3188 goto out;
3127 } 3189 }
3128 3190
3129 update_playlist_filename_unlocked(playlist, str2, str3); 3191 update_playlist_filename_unlocked(playlist, strp[1], strp[2]);
3130 3192
3131 if (str3[0] != '\0') 3193 if (strp[2][0] != '\0')
3132 { 3194 {
3133 /* NOTE: add_indices_to_playlist() overwrites the 3195 /* NOTE: add_indices_to_playlist() overwrites the
3134 audiobuf so we need to reload control file 3196 audiobuf so we need to reload control file
3135 data */ 3197 data */
3136 add_indices_to_playlist(playlist, buffer, buflen); 3198 add_indices_to_playlist(playlist, buffer, buflen);
3137 } 3199 }
3138 else if (str2[0] != '\0') 3200 else if (strp[1][0] != '\0')
3139 { 3201 {
3140 playlist->flags |= PLAYLIST_FLAG_DIRPLAY; 3202 playlist->flags |= PLAYLIST_FLAG_DIRPLAY;
3141 } 3203 }
3142 3204
3143 /* load the rest of the data */ 3205 /* load the rest of the data */
3144 first = false;
3145 exit_loop = true; 3206 exit_loop = true;
3146 readsize = buflen; 3207 readsize = buflen;
3208 pl_cmd = &pl_cmds_run;
3147 break; 3209 break;
3148 } 3210 }
3149 case PLAYLIST_COMMAND_ADD:
3150 case PLAYLIST_COMMAND_QUEUE: 3211 case PLAYLIST_COMMAND_QUEUE:
3212 queue = true;
3213 /*Fall-through*/
3214 case PLAYLIST_COMMAND_ADD:
3151 { 3215 {
3152 /* str1=position str2=last_position str3=file */ 3216 /* strp[0]=position strp[1]=last_position strp[2]=file */
3153 int position, last_position; 3217 if (!strp[0] || !strp[1] || !strp[2])
3154 bool queue;
3155
3156 if (!str1 || !str2 || !str3)
3157 { 3218 {
3158 result = -4; 3219 result = -4;
3159 exit_loop = true; 3220 exit_loop = true;
3160 break; 3221 break;
3161 } 3222 }
3162 3223
3163 position = atoi(str1); 3224 int position = atoi(strp[0]);
3164 last_position = atoi(str2); 3225 int last_position = atoi(strp[1]);
3165 3226
3166 queue = (current_command == PLAYLIST_COMMAND_ADD)? 3227 /* seek position is based on strp[2]'s position in
3167 false:true;
3168
3169 /* seek position is based on str3's position in
3170 buffer */ 3228 buffer */
3171 if (add_track_to_playlist_unlocked(playlist, str3, 3229 if (add_track_to_playlist_unlocked(playlist, strp[2],
3172 position, queue, total_read+(str3-buffer)) < 0) 3230 position, queue, total_read+(strp[2]-buffer)) < 0)
3173 { 3231 {
3174 result = -5; 3232 result = -5;
3175 goto out; 3233 goto out;
3176 } 3234 }
3177 3235
3178 playlist->last_insert_pos = last_position; 3236 playlist->last_insert_pos = last_position;
3179 3237 queue = false;
3180 break; 3238 break;
3181 } 3239 }
3182 case PLAYLIST_COMMAND_DELETE: 3240 case PLAYLIST_COMMAND_DELETE:
3183 { 3241 {
3184 /* str1=position */ 3242 /* strp[0]=position */
3185 int position; 3243 int position;
3186 3244
3187 if (!str1) 3245 if (!strp[0])
3188 { 3246 {
3189 result = -6; 3247 result = -6;
3190 exit_loop = true; 3248 exit_loop = true;
3191 break; 3249 break;
3192 } 3250 }
3193 3251
3194 position = atoi(str1); 3252 position = atoi(strp[0]);
3195 3253
3196 if (remove_track_unlocked(playlist, position, false) < 0) 3254 if (remove_track_unlocked(playlist, position, false) < 0)
3197 { 3255 {
@@ -3203,10 +3261,10 @@ int playlist_resume(void)
3203 } 3261 }
3204 case PLAYLIST_COMMAND_SHUFFLE: 3262 case PLAYLIST_COMMAND_SHUFFLE:
3205 { 3263 {
3206 /* str1=seed str2=first_index */ 3264 /* strp[0]=seed strp[1]=first_index */
3207 int seed; 3265 int seed;
3208 3266
3209 if (!str1 || !str2) 3267 if (!strp[0] || !strp[1])
3210 { 3268 {
3211 result = -8; 3269 result = -8;
3212 exit_loop = true; 3270 exit_loop = true;
@@ -3219,8 +3277,8 @@ int playlist_resume(void)
3219 sort_playlist_unlocked(playlist, false, false); 3277 sort_playlist_unlocked(playlist, false, false);
3220 } 3278 }
3221 3279
3222 seed = atoi(str1); 3280 seed = atoi(strp[0]);
3223 playlist->first_index = atoi(str2); 3281 playlist->first_index = atoi(strp[1]);
3224 3282
3225 if (randomise_playlist_unlocked(playlist, seed, false, 3283 if (randomise_playlist_unlocked(playlist, seed, false,
3226 false) < 0) 3284 false) < 0)
@@ -3234,15 +3292,15 @@ int playlist_resume(void)
3234 } 3292 }
3235 case PLAYLIST_COMMAND_UNSHUFFLE: 3293 case PLAYLIST_COMMAND_UNSHUFFLE:
3236 { 3294 {
3237 /* str1=first_index */ 3295 /* strp[0]=first_index */
3238 if (!str1) 3296 if (!strp[0])
3239 { 3297 {
3240 result = -10; 3298 result = -10;
3241 exit_loop = true; 3299 exit_loop = true;
3242 break; 3300 break;
3243 } 3301 }
3244 3302
3245 playlist->first_index = atoi(str1); 3303 playlist->first_index = atoi(strp[0]);
3246 3304
3247 if (sort_playlist_unlocked(playlist, false, false) < 0) 3305 if (sort_playlist_unlocked(playlist, false, false) < 0)
3248 { 3306 {
@@ -3269,8 +3327,14 @@ int playlist_resume(void)
3269 } 3327 }
3270 case PLAYLIST_COMMAND_FLAGS: 3328 case PLAYLIST_COMMAND_FLAGS:
3271 { 3329 {
3272 unsigned int setf = atoi(str1); 3330 if (!strp[0] || !strp[1])
3273 unsigned int clearf = atoi(str2); 3331 {
3332 result = -18;
3333 exit_loop = true;
3334 break;
3335 }
3336 unsigned int setf = atoi(strp[0]);
3337 unsigned int clearf = atoi(strp[1]);
3274 3338
3275 playlist->flags = (playlist->flags & ~clearf) | setf; 3339 playlist->flags = (playlist->flags & ~clearf) | setf;
3276 break; 3340 break;
@@ -3290,69 +3354,13 @@ int playlist_resume(void)
3290 else if(newline) 3354 else if(newline)
3291 { 3355 {
3292 newline = false; 3356 newline = false;
3293 3357 current_command = (*pl_cmd)(*p);
3294 switch (*p)
3295 {
3296 case 'P':
3297 /* playlist can only be specified once */
3298 if (!first)
3299 {
3300 result = -13;
3301 exit_loop = true;
3302 break;
3303 }
3304
3305 current_command = PLAYLIST_COMMAND_PLAYLIST;
3306 break;
3307 case 'A':
3308 current_command = PLAYLIST_COMMAND_ADD;
3309 break;
3310 case 'Q':
3311 current_command = PLAYLIST_COMMAND_QUEUE;
3312 break;
3313 case 'D':
3314 current_command = PLAYLIST_COMMAND_DELETE;
3315 break;
3316 case 'S':
3317 current_command = PLAYLIST_COMMAND_SHUFFLE;
3318 break;
3319 case 'U':
3320 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
3321 break;
3322 case 'R':
3323 current_command = PLAYLIST_COMMAND_RESET;
3324 break;
3325 case 'C':
3326 current_command = PLAYLIST_COMMAND_CLEAR;
3327 break;
3328 case 'F':
3329 current_command = PLAYLIST_COMMAND_FLAGS;
3330 break;
3331 case '#':
3332 current_command = PLAYLIST_COMMAND_COMMENT;
3333 break;
3334 default:
3335 result = -14;
3336 exit_loop = true;
3337 break;
3338 }
3339
3340 /* first non-comment line must always specify playlist */
3341 if (first &&
3342 (current_command != PLAYLIST_COMMAND_PLAYLIST) &&
3343 (current_command != PLAYLIST_COMMAND_COMMENT))
3344 {
3345 result = -12;
3346 exit_loop = true;
3347 break;
3348 }
3349
3350 str_count = -1; 3358 str_count = -1;
3351 str1 = NULL; 3359 strp[0] = NULL;
3352 str2 = NULL; 3360 strp[1] = NULL;
3353 str3 = NULL; 3361 strp[2] = NULL;
3354 } 3362 }
3355 else if(current_command != PLAYLIST_COMMAND_COMMENT) 3363 else if(current_command < PLAYLIST_COMMAND_COMMENT)
3356 { 3364 {
3357 /* all control file strings are separated with a colon. 3365 /* all control file strings are separated with a colon.
3358 Replace the colon with 0 to get proper strings that can be 3366 Replace the colon with 0 to get proper strings that can be
@@ -3367,13 +3375,9 @@ int playlist_resume(void)
3367 switch (str_count) 3375 switch (str_count)
3368 { 3376 {
3369 case 0: 3377 case 0:
3370 str1 = p+1;
3371 break;
3372 case 1: 3378 case 1:
3373 str2 = p+1;
3374 break;
3375 case 2: 3379 case 2:
3376 str3 = p+1; 3380 strp[str_count] = p+1;
3377 break; 3381 break;
3378 default: 3382 default:
3379 /* allow last string to contain colons */ 3383 /* allow last string to contain colons */
@@ -3385,7 +3389,7 @@ int playlist_resume(void)
3385 } 3389 }
3386 } 3390 }
3387 3391
3388 if (result < 0) 3392 if (result < 0 || current_command == PLAYLIST_COMMAND_ERROR)
3389 { 3393 {
3390 splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID)); 3394 splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
3391 goto out; 3395 goto out;
@@ -3443,20 +3447,14 @@ void playlist_resume_track(int start_index, unsigned int crc,
3443 unsigned int tmp_crc; 3447 unsigned int tmp_crc;
3444 struct playlist_info* playlist = &current_playlist; 3448 struct playlist_info* playlist = &current_playlist;
3445 3449
3446 tmp_crc = playlist_get_filename_crc32(playlist, start_index);
3447
3448 if (tmp_crc == crc)
3449 {
3450 playlist_start(start_index, elapsed, offset);
3451 return;
3452 }
3453
3454 for (i = 0 ; i < playlist->amount; i++) 3450 for (i = 0 ; i < playlist->amount; i++)
3455 { 3451 {
3456 tmp_crc = playlist_get_filename_crc32(playlist, i); 3452 int index = (i + start_index) % playlist->amount;
3453
3454 tmp_crc = playlist_get_filename_crc32(playlist, index);
3457 if (tmp_crc == crc) 3455 if (tmp_crc == crc)
3458 { 3456 {
3459 playlist_start(i, elapsed, offset); 3457 playlist_start(index, elapsed, offset);
3460 return; 3458 return;
3461 } 3459 }
3462 } 3460 }
@@ -3739,7 +3737,7 @@ static int pl_save_playlist(struct playlist_info* playlist,
3739 if (playlist->indices[index] & PLAYLIST_QUEUED) 3737 if (playlist->indices[index] & PLAYLIST_QUEUED)
3740 continue; 3738 continue;
3741 3739
3742 if (get_track_filename(playlist, index, tmpbuf, tmpsize)) 3740 if (get_track_filename(playlist, index, tmpbuf, tmpsize) != 0)
3743 { 3741 {
3744 err = -2; 3742 err = -2;
3745 goto error; 3743 goto error;
@@ -3914,7 +3912,8 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3914 if (!playlist) 3912 if (!playlist)
3915 playlist = &current_playlist; 3913 playlist = &current_playlist;
3916 3914
3917 pathlen = format_track_path(save_path, filename, sizeof(save_path), PATH_ROOTSTR); 3915 pathlen = format_track_path(save_path, filename,
3916 sizeof(save_path), PATH_ROOTSTR, -1u);
3918 if (pathlen < 0) 3917 if (pathlen < 0)
3919 return -1; 3918 return -1;
3920 3919
diff --git a/apps/playlist.h b/apps/playlist.h
index c7b672a2ef..5ad90db6f1 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -51,7 +51,8 @@ enum playlist_command {
51 PLAYLIST_COMMAND_RESET, 51 PLAYLIST_COMMAND_RESET,
52 PLAYLIST_COMMAND_CLEAR, 52 PLAYLIST_COMMAND_CLEAR,
53 PLAYLIST_COMMAND_FLAGS, 53 PLAYLIST_COMMAND_FLAGS,
54 PLAYLIST_COMMAND_COMMENT 54 PLAYLIST_COMMAND_COMMENT,
55 PLAYLIST_COMMAND_ERROR = PLAYLIST_COMMAND_COMMENT + 1 /* Internal */
55}; 56};
56 57
57enum { 58enum {
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c
index b942fdf022..6b70078eb1 100644
--- a/firmware/common/pathfuncs.c
+++ b/firmware/common/pathfuncs.c
@@ -448,6 +448,10 @@ void path_remove_dot_segments (char *dstpath, const char *path)
448} 448}
449 449
450/* Appends one path to another, adding separators between components if needed. 450/* Appends one path to another, adding separators between components if needed.
451 * basepath_max can be used to truncate the basepath if desired
452 * NOTE: basepath is truncated after copying to the buffer so there must be enough
453 * free space for the entirety of the basepath even if the resulting string would fit
454 *
451 * Return value and behavior is otherwise as strlcpy so that truncation may be 455 * Return value and behavior is otherwise as strlcpy so that truncation may be
452 * detected. 456 * detected.
453 * 457 *
@@ -455,9 +459,11 @@ void path_remove_dot_segments (char *dstpath, const char *path)
455 * PA_SEP_HARD adds a separator even if the base path is empty 459 * PA_SEP_HARD adds a separator even if the base path is empty
456 * PA_SEP_SOFT adds a separator only if the base path is not empty 460 * PA_SEP_SOFT adds a separator only if the base path is not empty
457 */ 461 */
458size_t path_append(char *buf, const char *basepath, 462size_t path_append_ex(char *buf, const char *basepath, size_t basepath_max,
459 const char *component, size_t bufsize) 463 const char *component, size_t bufsize)
460{ 464{
465 size_t len;
466 bool separate = false;
461 const char *base = basepath && basepath[0] ? basepath : buf; 467 const char *base = basepath && basepath[0] ? basepath : buf;
462 if (!base) 468 if (!base)
463 return bufsize; /* won't work to get lengths from buf */ 469 return bufsize; /* won't work to get lengths from buf */
@@ -474,11 +480,20 @@ size_t path_append(char *buf, const char *basepath,
474 480
475 /* if basepath is not null or empty, buffer contents are replaced, 481 /* if basepath is not null or empty, buffer contents are replaced,
476 otherwise buf contains the base path */ 482 otherwise buf contains the base path */
477 size_t len = base == buf ? strlen(buf) : strlcpy(buf, basepath, bufsize);
478 483
479 bool separate = false; 484 if (base == buf)
485 len = strlen(buf);
486 else
487 {
488 len = strlcpy(buf, basepath, bufsize);
489 if (basepath_max < len && basepath != component)
490 {
491 len = basepath_max;
492 buf[basepath_max] = '\0';
493 }
494 }
480 495
481 if (!basepath || !component) 496 if (!basepath || !component || basepath_max == 0)
482 separate = !len || base[len-1] != PATH_SEPCH; 497 separate = !len || base[len-1] != PATH_SEPCH;
483 else if (component[0]) 498 else if (component[0])
484 separate = len && base[len-1] != PATH_SEPCH; 499 separate = len && base[len-1] != PATH_SEPCH;
@@ -496,6 +511,12 @@ size_t path_append(char *buf, const char *basepath,
496 return len + strlcpy(buf, component ?: "", bufsize); 511 return len + strlcpy(buf, component ?: "", bufsize);
497} 512}
498 513
514
515size_t path_append(char *buf, const char *basepath,
516 const char *component, size_t bufsize)
517{
518 return path_append_ex(buf, basepath, -1u, component, bufsize);
519}
499/* Returns the location and length of the next path component, consuming the 520/* Returns the location and length of the next path component, consuming the
500 * input in the process. 521 * input in the process.
501 * 522 *
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h
index d4fa4eb460..1b18f22d06 100644
--- a/firmware/export/pathfuncs.h
+++ b/firmware/export/pathfuncs.h
@@ -94,6 +94,8 @@ void path_remove_dot_segments(char *dstpath, const char *path);
94/* constants useable in basepath and component */ 94/* constants useable in basepath and component */
95#define PA_SEP_HARD NULL /* separate even if base is empty */ 95#define PA_SEP_HARD NULL /* separate even if base is empty */
96#define PA_SEP_SOFT "" /* separate only if base is nonempty */ 96#define PA_SEP_SOFT "" /* separate only if base is nonempty */
97size_t path_append_ex(char *buf, const char *basepath, size_t basepath_max,
98 const char *component, size_t bufsize);
97size_t path_append(char *buffer, const char *basepath, const char *component, 99size_t path_append(char *buffer, const char *basepath, const char *component,
98 size_t bufsize); 100 size_t bufsize);
99ssize_t parse_path_component(const char **pathp, const char **namep); 101ssize_t parse_path_component(const char **pathp, const char **namep);